chiark / gitweb /
server/tripe.c: Formalize the main loop machinery.
[tripe] / server / tripe.c
1 /* -*-c-*-
2  *
3  * Main program
4  *
5  * (c) 2001 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Trivial IP Encryption (TrIPE).
11  *
12  * TrIPE is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your
15  * option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
24  */
25
26 /*----- Header files ------------------------------------------------------*/
27
28 #include "tripe.h"
29
30 /*----- Global variables --------------------------------------------------*/
31
32 sel_state sel;
33
34 /*----- Static variables --------------------------------------------------*/
35
36 static sel_timer it;
37 #define T_INTERVAL MIN(1)
38
39 static unsigned iv_nreasons = 0;
40 static struct timeval iv_next = { 0, 0 };
41
42 /*----- The interval timer ------------------------------------------------*/
43
44 /* --- @interval@ --- *
45  *
46  * Arguments:   @struct timeval *tv@ = time when called
47  *              @void *v@ = boring pointer
48  *
49  * Returns:     ---
50  *
51  * Use:         Called periodically to do housekeeping tasks.
52  */
53
54 static void interval(struct timeval *tv, void *v)
55 {
56   T( trace(T_PEER, "peer: interval timer"); )
57   iv_next = *tv;
58   rand_seed(RAND_GLOBAL, MAXHASHSZ);
59   p_interval();
60   iv_next.tv_sec += T_INTERVAL;
61   sel_addtimer(&sel, &it, &iv_next, interval, v);
62 }
63
64 /* --- @iv_addreason@ --- *
65  *
66  * Arguments:   ---
67  *
68  * Returns:     ---
69  *
70  * Use:         Adds an `interval timer reason'; if there are no others, the
71  *              interval timer is engaged.
72  */
73
74 void iv_addreason(void)
75 {
76   struct timeval tv;
77
78   if (!iv_nreasons) {
79     gettimeofday(&tv, 0);
80     if (TV_CMP(&tv, >=, &iv_next)) interval(&tv, 0);
81     else sel_addtimer(&sel, &it, &iv_next, interval, 0);
82   }
83   iv_nreasons++;
84 }
85
86 /* --- @iv_rmreason@ --- *
87  *
88  * Arguments:   ---
89  *
90  * Returns:     ---
91  *
92  * Use:         Removes an interval timer reason; if there are none left, the
93  *              interval timer is disengaged.
94  */
95
96 void iv_rmreason(void)
97 {
98   assert(iv_nreasons); iv_nreasons--;
99   if (!iv_nreasons) sel_rmtimer(&it);
100 }
101
102 /*----- The main loop -----------------------------------------------------*/
103
104 /* --- @lp_init@ --- *
105  *
106  * Arguments:   ---
107  *
108  * Returns:     ---
109  *
110  * Use:         Initializes the main loop.  Most importantly, this sets up
111  *              the select multiplexor that everything else hooks onto.
112  */
113
114 void lp_init(void)
115 {
116   rand_noisesrc(RAND_GLOBAL, &noise_source);
117   rand_seed(RAND_GLOBAL, MAXHASHSZ);
118   gettimeofday(&iv_next, 0); iv_next.tv_sec += T_INTERVAL;
119   signal(SIGPIPE, SIG_IGN);
120   sel_init(&sel);
121   sig_init(&sel);
122 }
123
124 /* --- @lp_run@ --- *
125  *
126  * Arguments:   ---
127  *
128  * Returns:     Zero on successful termination; @-1@ if things went wrong.
129  *
130  * Use:         Cranks the main loop until it should be cranked no more.
131  */
132
133 int lp_run(void)
134 {
135   int nerr = 0;
136
137   for (;;) {
138     a_preselect();
139     if (!sel_select(&sel)) nerr = 0;
140     else if (errno != EINTR && errno != EAGAIN) {
141       a_warn("SERVER", "select-error", "?ERRNO", A_END);
142       nerr++;
143       if (nerr > 8) {
144         a_warn("ABORT", "repeated-select-errors", A_END);
145         abort();
146       }
147     }
148   }
149   return (0);
150 }
151
152 /*----- Main code ---------------------------------------------------------*/
153
154 /* --- @main@ --- *
155  *
156  * Arguments:   @int argc@ = number of command line arguments
157  *              @char *argv[]@ = vector of arguments
158  *
159  * Returns:     Zero if OK, nonzero on error.
160  *
161  * Use:         Main program.  Provides a simple VPN.
162  */
163
164 static void usage(FILE *fp)
165 {
166   pquis(fp, "Usage: $ [-DF] [-d DIR] [-b ADDR] [-p PORT] [-n TUNNEL]\n\
167         [-U USER] [-G GROUP] [-a SOCKET] [-m MODE] [-T TRACE-OPTS]\n\
168         [-k PRIV-KEYRING] [-K PUB-KEYRING] [-t KEY-TAG]\n");
169 }
170
171 static void version(FILE *fp) { pquis(fp, "$, version " VERSION "\n"); }
172
173 static void help(FILE *fp)
174 {
175   version(fp);
176   fputc('\n', fp);
177   usage(fp);
178   fputs("\n\
179 Options:\n\
180 \n\
181 -h, --help              Display this help text.\n\
182 -v, --version           Display version number.\n\
183 -u, --usage             Display pointless usage message.\n\
184     --tunnels           Display IP tunnel drivers and exit.\n\
185 \n\
186 -4, --ipv4              Transport over IPv4 only.\n\
187 -6, --ipv6              Transport over IPv6 only.\n\
188 -D, --daemon            Run in the background.\n\
189 -F, --foreground        Quit when stdin reports end-of-file.\n\
190 -d, --directory=DIR     Switch to directory DIR [default " CONFIGDIR "].\n\
191 -b, --bind-address=ADDR Bind UDP socket to this IP ADDR.\n\
192 -p, --port=PORT         Select UDP port to listen to "
193         "[default " STR(TRIPE_PORT) "].\n\
194 -n, --tunnel=TUNNEL     Seelect default tunnel driver.\n\
195 -U, --setuid=USER       Set uid to USER after initialization.\n\
196 -G, --setgid=GROUP      Set gid to GROUP after initialization.\n\
197 -k, --priv-keyring=FILE Get private key from FILE.\n\
198 -K, --pub-keyring=FILE  Get public keys from FILE.\n\
199 -t, --tag=KEYTAG        Use private key labelled TAG.\n\
200 -a, --admin-socket=FILE Use FILE as the adminstration socket.\n\
201 -m, --admin-perms=MODE  Permissions to set on admin socket [default 600].\n\
202 " T( "\
203 -T, --trace=OPTIONS     Turn on tracing options.\n\
204 " ) "\
205 ", fp);
206 }
207
208 int main(int argc, char *argv[])
209 {
210   const char *kr_priv = "keyring", *kr_pub = "keyring.pub";
211   const char *tag_priv = 0;
212   const char *csock = SOCKETDIR "/tripesock";
213   int csockmode = 0600;
214   const char *dir = CONFIGDIR;
215   const char *p;
216   const char *bindhost = 0, *bindsvc = STR(TRIPE_PORT);
217   struct addrinfo aihint = { 0 }, *ailist;
218   unsigned f = 0;
219   int i;
220   int err;
221   unsigned af;
222   uid_t u = -1;
223   gid_t g = -1;
224
225 #define f_bogus 1u
226 #define f_daemon 2u
227 #define f_foreground 4u
228
229   ego(argv[0]);
230   T( trace_on(stderr, 0); )
231
232   if ((p = getenv("TRIPEDIR")) != 0)
233     dir = p;
234   if ((p = getenv("TRIPESOCK")) != 0)
235     csock = p;
236   tun_default = tunnels[0];
237   aihint.ai_family = AF_UNSPEC;
238
239   for (;;) {
240     static const struct option opts[] = {
241       { "help",         0,              0,      'h' },
242       { "version",      0,              0,      'v' },
243       { "usage",        0,              0,      'u' },
244       { "tunnels",      0,              0,      '0' },
245
246       { "ipv4",         0,              0,      '4' },
247       { "ipv6",         0,              0,      '6' },
248       { "daemon",       0,              0,      'D' },
249       { "foreground",   0,              0,      'F' },
250       { "uid",          OPTF_ARGREQ,    0,      'U' },
251       { "setuid",       OPTF_ARGREQ,    0,      'U' },
252       { "gid",          OPTF_ARGREQ,    0,      'G' },
253       { "setgid",       OPTF_ARGREQ,    0,      'G' },
254       { "bind-address", OPTF_ARGREQ,    0,      'b' },
255       { "tunnel",       OPTF_ARGREQ,    0,      'n' },
256       { "port",         OPTF_ARGREQ,    0,      'p' },
257       { "directory",    OPTF_ARGREQ,    0,      'd' },
258       { "priv-keyring", OPTF_ARGREQ,    0,      'k' },
259       { "pub-keyring",  OPTF_ARGREQ,    0,      'K' },
260       { "tag",          OPTF_ARGREQ,    0,      't' },
261       { "admin-socket", OPTF_ARGREQ,    0,      'a' },
262       { "admin-perms",  OPTF_ARGREQ,    0,      'm' },
263 #ifndef NTRACE
264       { "trace",        OPTF_ARGREQ,    0,      'T' },
265 #endif
266
267       { 0,              0,              0,      0 }
268     };
269
270     i = mdwopt(argc, argv, "hvu46DFU:G:b:n:p:d:k:K:t:a:m:" T("T:"),
271                opts, 0, 0, 0);
272     if (i < 0)
273       break;
274     switch (i) {
275       case 'h':
276         help(stdout);
277         exit(0);
278       case 'v':
279         version(stdout);
280         exit(0);
281       case 'u':
282         usage(stdout);
283         exit(0);
284
285       case '4':
286         aihint.ai_family = AF_INET;
287         break;
288       case '6':
289         aihint.ai_family = AF_INET6;
290         break;
291       case 'D':
292         f |= f_daemon;
293         break;
294       case 'U':
295         u = u_getuser(optarg, &g);
296         break;
297       case 'G':
298         g = u_getgroup(optarg);
299         break;
300       case 'F':
301         f |= f_foreground;
302         break;
303
304       case 'b':
305         bindhost = optarg;
306         break;
307       case 'p':
308         bindsvc = optarg;
309         break;
310       case 'n': {
311         int i;
312         for (i = 0;; i++) {
313           if (!tunnels[i])
314             die(EXIT_FAILURE, "unknown tunnel `%s'", optarg);
315           if (mystrieq(optarg, tunnels[i]->name))
316             break;
317         }
318         tun_default = tunnels[i];
319       } break;
320       case 'd':
321         dir = optarg;
322         break;
323       case 'k':
324         kr_priv = optarg;
325         break;
326       case 'K':
327         kr_pub = optarg;
328         break;
329       case 'a':
330         csock = optarg;
331         break;
332       case 'm': {
333         char *p;
334         csockmode = strtol(optarg, &p, 8);
335         if (*p) die(EXIT_FAILURE, "bad permissions: `%s'", optarg);
336       } break;
337       case 't':
338         tag_priv = optarg;
339         break;
340 #ifndef NTRACE
341       case 'T':
342         tr_flags = traceopt(tr_opts, optarg, tr_flags, 0);
343         trace_level(tr_flags);
344         break;
345 #endif
346       case '0': {
347         int i;
348         for (i = 0; tunnels[i]; i++)
349           puts(tunnels[i]->name);
350         exit(0);
351       } break;
352       default:
353         f |= f_bogus;
354         break;
355     }
356   }
357
358   if (optind < argc || (f & f_bogus)) {
359     usage(stderr);
360     exit(EXIT_FAILURE);
361   }
362   if (!(~f & (f_daemon | f_foreground)))
363     die(EXIT_FAILURE, "foreground operation for a daemon is silly");
364
365   aihint.ai_protocol = IPPROTO_UDP;
366   aihint.ai_socktype = SOCK_DGRAM;
367   aihint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
368   if ((err = getaddrinfo(bindhost, bindsvc, &aihint, &ailist)) != 0) {
369     die(EXIT_FAILURE, "couldn't resolve hostname %c%s%c, port `%s': %s",
370         bindhost ? '`' : '<',
371         bindhost ? bindhost : "nil",
372         bindhost ? '\'' : '>',
373         bindsvc, gai_strerror(err));
374   }
375
376   if (chdir(dir)) {
377     die(EXIT_FAILURE, "can't set current directory to `%s': %s",
378         dir, strerror(errno));
379   }
380
381   lp_init();
382
383   if (!(f & f_daemon)) {
384     af = AF_WARN;
385 #ifndef NTRACE
386     af |= AF_TRACE;
387 #endif
388     if (f & f_foreground)
389       af |= AF_FOREGROUND;
390     a_create(STDIN_FILENO, STDOUT_FILENO, af);
391     a_switcherr();
392   }
393
394   p_init();
395   for (i = 0; tunnels[i]; i++)
396     tunnels[i]->init();
397   p_bind(ailist); freeaddrinfo(ailist);
398
399   for (i = 0; tunnels[i]; i++) {
400     if (tunnels[i]->flags&TUNF_PRIVOPEN) {
401       ps_split(f & f_daemon);
402       break;
403     }
404   }
405
406   a_init();
407   a_signals();
408   a_listen(csock, u, g, csockmode);
409   u_setugid(u, g);
410   km_init(kr_priv, kr_pub, tag_priv);
411   kx_init();
412   if (f & f_daemon) {
413     if (daemonize()) {
414       a_warn("SERVER", "daemon-error", "?ERRNO", A_END);
415       exit(EXIT_FAILURE);
416     }
417     a_daemon();
418     a_switcherr();
419   }
420
421   iv_addreason();
422
423   lp_run();
424   return (0);
425 }
426
427 /*----- That's all, folks -------------------------------------------------*/