637400a5c7dce6f3258f9775826a72bdac9a0222
[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 /*----- Tunnel table ------------------------------------------------------*/
167
168 static const tunnel_ops *tunnels[] = {
169 #ifdef TUN_LINUX
170 &tun_linux,
171 #endif
172 #ifdef TUN_BSD
173 &tun_bsd,
174 #endif
175 #ifdef TUN_UNET
176 &tun_unet,
177 #endif
178 &tun_slip,
179 };
180
181 /*----- Main code ---------------------------------------------------------*/
182
183 /* --- @main@ --- *
184 *
185 * Arguments: @int argc@ = number of command line arguments
186 * @char *argv[]@ = vector of arguments
187 *
188 * Returns: Zero if OK, nonzero on error.
189 *
190 * Use: Main program. Provides a simple VPN.
191 */
192
193 static void usage(FILE *fp)
194 {
195 pquis(fp, "Usage: $ [-DF] [-d DIR] [-b ADDR] [-p PORT] [-n TUNNEL]\n\
196 [-U USER] [-G GROUP] [-a SOCKET] [-m MODE] [-T TRACE-OPTS]\n\
197 [-k PRIV-KEYRING] [-K PUB-KEYRING] [-t KEY-TAG]\n");
198 }
199
200 static void version(FILE *fp) { pquis(fp, "$, version " VERSION "\n"); }
201
202 static void help(FILE *fp)
203 {
204 version(fp);
205 fputc('\n', fp);
206 usage(fp);
207 fputs("\n\
208 Options:\n\
209 \n\
210 -h, --help Display this help text.\n\
211 -v, --version Display version number.\n\
212 -u, --usage Display pointless usage message.\n\
213 --tunnels Display IP tunnel drivers and exit.\n\
214 \n\
215 -4, --ipv4 Transport over IPv4 only.\n\
216 -6, --ipv6 Transport over IPv6 only.\n\
217 -D, --daemon Run in the background.\n\
218 -F, --foreground Quit when stdin reports end-of-file.\n\
219 -d, --directory=DIR Switch to directory DIR [default " CONFIGDIR "].\n\
220 -b, --bind-address=ADDR Bind UDP socket to this IP ADDR.\n\
221 -p, --port=PORT Select UDP port to listen to "
222 "[default " STR(TRIPE_PORT) "].\n\
223 -n, --tunnel=TUNNEL Seelect default tunnel driver.\n\
224 -U, --setuid=USER Set uid to USER after initialization.\n\
225 -G, --setgid=GROUP Set gid to GROUP after initialization.\n\
226 -k, --priv-keyring=FILE Get private key from FILE.\n\
227 -K, --pub-keyring=FILE Get public keys from FILE.\n\
228 -t, --tag=KEYTAG Use private key labelled TAG.\n\
229 -a, --admin-socket=FILE Use FILE as the adminstration socket.\n\
230 -m, --admin-perms=MODE Permissions to set on admin socket [default 600].\n\
231 " T( "\
232 -T, --trace=OPTIONS Turn on tracing options.\n\
233 " ) "\
234 ", fp);
235 }
236
237 int main(int argc, char *argv[])
238 {
239 const char *kr_priv = "keyring", *kr_pub = "keyring.pub";
240 const char *tag_priv = 0;
241 const char *csock = SOCKETDIR "/tripesock";
242 int csockmode = 0600;
243 const char *dir = CONFIGDIR;
244 const char *p;
245 const char *bindhost = 0, *bindsvc = STR(TRIPE_PORT);
246 struct addrinfo aihint = { 0 }, *ailist;
247 const tunnel_ops *dflt = 0;
248 unsigned f = 0;
249 int i;
250 int err;
251 unsigned af;
252 uid_t u = -1;
253 gid_t g = -1;
254
255 #define f_bogus 1u
256 #define f_daemon 2u
257 #define f_foreground 4u
258
259 ego(argv[0]);
260 T( trace_on(stderr, 0); )
261
262 if ((p = getenv("TRIPEDIR")) != 0)
263 dir = p;
264 if ((p = getenv("TRIPESOCK")) != 0)
265 csock = p;
266 aihint.ai_family = AF_UNSPEC;
267
268 for (;;) {
269 static const struct option opts[] = {
270 { "help", 0, 0, 'h' },
271 { "version", 0, 0, 'v' },
272 { "usage", 0, 0, 'u' },
273 { "tunnels", 0, 0, '0' },
274
275 { "ipv4", 0, 0, '4' },
276 { "ipv6", 0, 0, '6' },
277 { "daemon", 0, 0, 'D' },
278 { "foreground", 0, 0, 'F' },
279 { "uid", OPTF_ARGREQ, 0, 'U' },
280 { "setuid", OPTF_ARGREQ, 0, 'U' },
281 { "gid", OPTF_ARGREQ, 0, 'G' },
282 { "setgid", OPTF_ARGREQ, 0, 'G' },
283 { "bind-address", OPTF_ARGREQ, 0, 'b' },
284 { "tunnel", OPTF_ARGREQ, 0, 'n' },
285 { "port", OPTF_ARGREQ, 0, 'p' },
286 { "directory", OPTF_ARGREQ, 0, 'd' },
287 { "priv-keyring", OPTF_ARGREQ, 0, 'k' },
288 { "pub-keyring", OPTF_ARGREQ, 0, 'K' },
289 { "tag", OPTF_ARGREQ, 0, 't' },
290 { "admin-socket", OPTF_ARGREQ, 0, 'a' },
291 { "admin-perms", OPTF_ARGREQ, 0, 'm' },
292 #ifndef NTRACE
293 { "trace", OPTF_ARGREQ, 0, 'T' },
294 #endif
295
296 { 0, 0, 0, 0 }
297 };
298
299 i = mdwopt(argc, argv, "hvu46DFU:G:b:n:p:d:k:K:t:a:m:" T("T:"),
300 opts, 0, 0, 0);
301 if (i < 0)
302 break;
303 switch (i) {
304 case 'h':
305 help(stdout);
306 exit(0);
307 case 'v':
308 version(stdout);
309 exit(0);
310 case 'u':
311 usage(stdout);
312 exit(0);
313
314 case '4':
315 aihint.ai_family = AF_INET;
316 break;
317 case '6':
318 aihint.ai_family = AF_INET6;
319 break;
320 case 'D':
321 f |= f_daemon;
322 break;
323 case 'U':
324 u = u_getuser(optarg, &g);
325 break;
326 case 'G':
327 g = u_getgroup(optarg);
328 break;
329 case 'F':
330 f |= f_foreground;
331 break;
332
333 case 'b':
334 bindhost = optarg;
335 break;
336 case 'p':
337 bindsvc = optarg;
338 break;
339 case 'n': {
340 int i;
341 for (i = 0; i < N(tunnels); i++)
342 if (mystrieq(optarg, tunnels[i]->name))
343 { dflt = tunnels[i]; goto found_tun; }
344 die(EXIT_FAILURE, "unknown tunnel `%s'", optarg);
345 found_tun:;
346 } break;
347 case 'd':
348 dir = optarg;
349 break;
350 case 'k':
351 kr_priv = optarg;
352 break;
353 case 'K':
354 kr_pub = optarg;
355 break;
356 case 'a':
357 csock = optarg;
358 break;
359 case 'm': {
360 char *p;
361 csockmode = strtol(optarg, &p, 8);
362 if (*p) die(EXIT_FAILURE, "bad permissions: `%s'", optarg);
363 } break;
364 case 't':
365 tag_priv = optarg;
366 break;
367 #ifndef NTRACE
368 case 'T':
369 tr_flags = traceopt(tr_opts, optarg, tr_flags, 0);
370 trace_level(tr_flags);
371 break;
372 #endif
373 case '0': {
374 int i;
375 for (i = 0; i < N(tunnels); i++)
376 puts(tunnels[i]->name);
377 exit(0);
378 } break;
379 default:
380 f |= f_bogus;
381 break;
382 }
383 }
384
385 if (optind < argc || (f & f_bogus)) {
386 usage(stderr);
387 exit(EXIT_FAILURE);
388 }
389 if (!(~f & (f_daemon | f_foreground)))
390 die(EXIT_FAILURE, "foreground operation for a daemon is silly");
391
392 aihint.ai_protocol = IPPROTO_UDP;
393 aihint.ai_socktype = SOCK_DGRAM;
394 aihint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
395 if ((err = getaddrinfo(bindhost, bindsvc, &aihint, &ailist)) != 0) {
396 die(EXIT_FAILURE, "couldn't resolve hostname %c%s%c, port `%s': %s",
397 bindhost ? '`' : '<',
398 bindhost ? bindhost : "nil",
399 bindhost ? '\'' : '>',
400 bindsvc, gai_strerror(err));
401 }
402
403 if (chdir(dir)) {
404 die(EXIT_FAILURE, "can't set current directory to `%s': %s",
405 dir, strerror(errno));
406 }
407
408 lp_init();
409
410 if (!(f & f_daemon)) {
411 af = AF_WARN;
412 #ifndef NTRACE
413 af |= AF_TRACE;
414 #endif
415 if (f & f_foreground)
416 af |= AF_FOREGROUND;
417 a_create(STDIN_FILENO, STDOUT_FILENO, af);
418 a_switcherr();
419 }
420
421 p_init();
422 for (i = 0; i < N(tunnels); i++)
423 p_addtun(tunnels[i]);
424 if (dflt) p_setdflttun(dflt);
425 p_bind(ailist); freeaddrinfo(ailist);
426
427 for (i = 0; tunnels[i]; i++) {
428 if (tunnels[i]->flags&TUNF_PRIVOPEN) {
429 ps_split(f & f_daemon);
430 break;
431 }
432 }
433
434 a_init();
435 a_signals();
436 a_listen(csock, u, g, csockmode);
437 u_setugid(u, g);
438 km_init(kr_priv, kr_pub, tag_priv);
439 kx_init();
440 if (f & f_daemon) {
441 if (daemonize()) {
442 a_warn("SERVER", "daemon-error", "?ERRNO", A_END);
443 exit(EXIT_FAILURE);
444 }
445 a_daemon();
446 a_switcherr();
447 }
448
449 lp_run();
450
451 p_destroyall();
452 p_unbind();
453 a_unlisten();
454 km_clear();
455 ps_quit();
456 return (0);
457 }
458
459 /*----- That's all, folks -------------------------------------------------*/