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