summaryrefslogtreecommitdiff
path: root/ot_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'ot_config.c')
-rw-r--r--ot_config.c586
1 files changed, 0 insertions, 586 deletions
diff --git a/ot_config.c b/ot_config.c
deleted file mode 100644
index 272acf1..0000000
--- a/ot_config.c
+++ /dev/null
@@ -1,586 +0,0 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <ctype.h>
8#include <errno.h>
9#include <limits.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15/* Opentracker */
16#include "ot_accesslist.h"
17#include "ot_event.h"
18#include "ot_config.h"
19#include "ot_http.h"
20#include "ot_ip.h"
21#include "ot_livesync.h"
22#include "ot_scan.h"
23#include "ot_stats.h"
24#include "trackerlogic.h"
25
26const char *g_redirecturl;
27const char *g_serverdir;
28const char *g_serveruser;
29const char *g_statefile;
30
31#define OT_LISTEN_INHERIT (-1)
32
33static ot_listen *g_listen;
34static size_t g_listen_count;
35static int g_listen_shared;
36static int g_listen_workers_udp = 1;
37static int g_listen_workers_tcp = 0;
38
39static void panic(const char *routine) __attribute__((noreturn));
40static void panic(const char *routine) {
41 fprintf(stderr, "%s: %s\n", routine, strerror(errno));
42 exit(111);
43}
44
45static void listen_append(const ot_ip6 ip, uint16_t port, PROTO_FLAG proto, int shared, int workers) {
46 ot_listen *grown = realloc(g_listen, (g_listen_count + 1) * sizeof(ot_listen));
47 ot_listen *entry;
48
49 if (!grown)
50 panic("listen_append");
51 g_listen = grown;
52
53 entry = g_listen + g_listen_count++;
54 memcpy(entry->ip, ip, sizeof(ot_ip6));
55 entry->port = port;
56 entry->proto = proto;
57 entry->shared = shared;
58 entry->workers = workers;
59 entry->sock = -1;
60}
61
62typedef enum { LISTEN_OPTION_UNKNOWN, LISTEN_OPTION_SHARED, LISTEN_OPTION_WORKERS } listen_option_kind;
63
64typedef struct {
65 const char *name;
66 size_t len;
67 listen_option_kind kind;
68 int takes_value;
69} listen_option_key;
70
71#define LISTEN_OPTION_KEY(s, k, v) {s, sizeof(s) - 1, k, v}
72
73static const listen_option_key listen_option_keys[] = {
74 LISTEN_OPTION_KEY("shared", LISTEN_OPTION_SHARED, 0),
75 LISTEN_OPTION_KEY("workers", LISTEN_OPTION_WORKERS, 1),
76};
77static const size_t listen_option_count = sizeof(listen_option_keys) / sizeof(listen_option_keys[0]);
78
79static listen_option_kind parse_listen_option(char **p) {
80 size_t i;
81
82 for (i = 0; i < listen_option_count; ++i) {
83 const listen_option_key *key = &listen_option_keys[i];
84 char delimiter = (*p)[key->len];
85
86 if (strncmp(*p, key->name, key->len))
87 continue;
88 if (key->takes_value ? delimiter != '=' : (delimiter && !isspace((unsigned char)delimiter)))
89 continue;
90
91 *p += key->len + (key->takes_value ? 1 : 0);
92 return key->kind;
93 }
94 return LISTEN_OPTION_UNKNOWN;
95}
96
97/* Options trailing an address in a listen statement, in any order */
98static int listen_scan_options(char *p, int *shared, int *workers) {
99 uint64_t value;
100
101 for (;;) {
102 while (isspace((unsigned char)*p))
103 ++p;
104 if (!*p)
105 return 1;
106
107 switch (parse_listen_option(&p)) {
108 case LISTEN_OPTION_SHARED:
109 *shared = 1;
110 break;
111 case LISTEN_OPTION_WORKERS: {
112 size_t off = scan_uint(p, &value, UINT_MAX);
113
114 if (!off)
115 return 0;
116 *workers = (int)value;
117 p += off;
118 break;
119 }
120 default:
121 return 0;
122 }
123 }
124}
125
126
127static const char *set_config_option(const char **option, char *value) {
128#ifdef _DEBUG
129 fprintf(stderr, "Setting config option: %s\n", value);
130#endif
131 while (isspace(*value))
132 ++value;
133 free((char *)*option);
134 return *option = strdup(value);
135}
136
137static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
138 const char *s = src;
139 uint64_t value;
140 int off, bracket = 0;
141 while (isspace(*s))
142 ++s;
143 if (*s == '[')
144 ++s, ++bracket; /* for v6 style notation */
145 if (!(off = ip_parse(s, ip)))
146 return 0;
147 s += off;
148 if (bracket && *s == ']')
149 ++s;
150 if (*s == 0 || isspace(*s))
151 return s - src;
152 if (!ip_is_v4mapped(ip)) {
153 if (*s != ':' && *s != '.')
154 return 0;
155 if (!bracket && *(s) == ':')
156 return 0;
157 s++;
158 } else {
159 if (*(s++) != ':')
160 return 0;
161 }
162 if (!(off = scan_uint(s, &value, 65535)))
163 return 0;
164 *port = (uint16_t)value;
165 return off + s - src;
166}
167
168static int scan_ip6_net(const char *src, ot_net *net) {
169 const char *s = src;
170 uint64_t value;
171 int off;
172 while (isspace(*s))
173 ++s;
174 if (!(off = ip_parse(s, net->address)))
175 return 0;
176 s += off;
177 if (*s != '/')
178 net->bits = 128;
179 else {
180 s++;
181 if (!(off = scan_uint(s, &value, 128)))
182 return 0;
183 net->bits = (int)value;
184 if (ip_is_v4mapped(net->address))
185 net->bits += 96;
186 if (net->bits > 128)
187 return 0;
188 s += off;
189 }
190 return off + s - src;
191}
192
193typedef enum {
194 CONFIG_UNKNOWN,
195 CONFIG_TRACKER_ROOTDIR,
196 CONFIG_TRACKER_USER,
197 CONFIG_LISTEN_TCP_UDP,
198 CONFIG_LISTEN_TCP,
199 CONFIG_LISTEN_UDP,
200 CONFIG_LISTEN_UDP_WORKERS,
201 CONFIG_LISTEN_TCP_WORKERS,
202 CONFIG_LISTEN_SHARED,
203 CONFIG_ACCESS_WHITELIST,
204 CONFIG_ACCESS_BLACKLIST,
205 CONFIG_ACCESS_FIFO_ADD,
206 CONFIG_ACCESS_FIFO_DELETE,
207 CONFIG_ACCESS_STATS,
208 CONFIG_ACCESS_STATS_PATH,
209 CONFIG_ACCESS_PROXY,
210 CONFIG_TRACKER_REDIRECT_URL,
211 CONFIG_LIVESYNC_CLUSTER_NODE_IP,
212 CONFIG_LIVESYNC_CLUSTER_LISTEN,
213} config_kind;
214
215typedef struct {
216 const char *name;
217 size_t len;
218 config_kind kind;
219} config_key;
220
221#define CONFIG_KEY(s, k) { s, sizeof(s) - 1, k }
222
223static const config_key config_keys[] = {
224 CONFIG_KEY("tracker.rootdir", CONFIG_TRACKER_ROOTDIR),
225 CONFIG_KEY("tracker.user", CONFIG_TRACKER_USER),
226 CONFIG_KEY("listen.tcp_udp", CONFIG_LISTEN_TCP_UDP),
227 CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP),
228 CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP),
229 CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS),
230 CONFIG_KEY("listen.tcp.workers", CONFIG_LISTEN_TCP_WORKERS),
231 CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED),
232#ifdef WANT_ACCESSLIST_WHITE
233 CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST),
234#elif defined(WANT_ACCESSLIST_BLACK)
235 CONFIG_KEY("access.blacklist", CONFIG_ACCESS_BLACKLIST),
236#endif
237#ifdef WANT_DYNAMIC_ACCESSLIST
238 CONFIG_KEY("access.fifo_add", CONFIG_ACCESS_FIFO_ADD),
239 CONFIG_KEY("access.fifo_delete", CONFIG_ACCESS_FIFO_DELETE),
240#endif
241#ifdef WANT_RESTRICT_STATS
242 CONFIG_KEY("access.stats", CONFIG_ACCESS_STATS),
243#endif
244 CONFIG_KEY("access.stats_path", CONFIG_ACCESS_STATS_PATH),
245#ifdef WANT_IP_FROM_PROXY
246 CONFIG_KEY("access.proxy", CONFIG_ACCESS_PROXY),
247#endif
248 CONFIG_KEY("tracker.redirect_url", CONFIG_TRACKER_REDIRECT_URL),
249#ifdef WANT_SYNC_LIVE
250 CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP),
251 CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN),
252#endif
253};
254static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]);
255
256static config_kind parse_config_key(char **p) {
257 size_t i;
258 for (i = 0; i < config_entry_count; ++i) {
259 const config_key *key = &config_keys[i];
260 if (strncmp(*p, key->name, key->len) == 0 && isspace((unsigned char)(*p)[key->len])) {
261 *p += key->len + 1;
262 /* Skip white spaces */
263 while (isspace((unsigned char)**p))
264 ++(*p);
265 return key->kind;
266 }
267 }
268 return CONFIG_UNKNOWN;
269}
270
271static int parse_configfile(char *config_filename) {
272 FILE *config_file_handle;
273 char inbuf[512];
274 ot_ip6 tmpip;
275#if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE)
276 ot_net tmpnet;
277#endif
278 int bound = 0;
279
280 config_file_handle = fopen(config_filename, "r");
281
282 if (config_file_handle == NULL) {
283 fprintf(stderr, "Warning: Can't open config file: %s.", config_filename);
284 return 0;
285 }
286
287 while (fgets(inbuf, sizeof(inbuf), config_file_handle)) {
288 char *p = inbuf, *end;
289 uint64_t value;
290 uint16_t tmpport = 6969;
291 config_kind kind;
292
293 /* Skip white spaces */
294 while (isspace((unsigned char)*p))
295 ++p;
296
297 /* Ignore comments and empty lines */
298 if ((*p == '#') || (*p == '\n') || (*p == 0))
299 continue;
300
301 /* consume trailing new lines and spaces */
302 end = p + strlen(p);
303 while (end > p && isspace((unsigned char)end[-1]))
304 *--end = 0;
305
306 /* Scan for commands */
307 kind = parse_config_key(&p);
308 switch (kind) {
309 case CONFIG_TRACKER_ROOTDIR:
310 set_config_option(&g_serverdir, p);
311 break;
312 case CONFIG_TRACKER_USER:
313 set_config_option(&g_serveruser, p);
314 break;
315 case CONFIG_LISTEN_TCP_UDP:
316 case CONFIG_LISTEN_TCP:
317 case CONFIG_LISTEN_UDP: {
318 int shared = OT_LISTEN_INHERIT;
319 int workers = OT_LISTEN_INHERIT;
320 int off = scan_ip6_port(p, tmpip, &tmpport);
321
322 if (!off || !listen_scan_options(p + off, &shared, &workers))
323 goto parse_error;
324 if (kind != CONFIG_LISTEN_UDP)
325 listen_append(tmpip, tmpport, FLAG_TCP, shared, workers), ++bound;
326 if (kind != CONFIG_LISTEN_TCP)
327 listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound;
328 break;
329 }
330 case CONFIG_LISTEN_UDP_WORKERS:
331 if (!scan_uint(p, &value, UINT_MAX))
332 goto parse_error;
333 g_listen_workers_udp = (int)value;
334 break;
335 case CONFIG_LISTEN_TCP_WORKERS:
336 if (!scan_uint(p, &value, UINT_MAX))
337 goto parse_error;
338 g_listen_workers_tcp = (int)value;
339 break;
340 case CONFIG_LISTEN_SHARED:
341 if (!scan_uint(p, &value, 1))
342 goto parse_error;
343 g_listen_shared = (int)value;
344 break;
345#ifdef WANT_ACCESSLIST_WHITE
346 case CONFIG_ACCESS_WHITELIST:
347 set_config_option(&g_accesslist_filename, p);
348 break;
349#elif defined(WANT_ACCESSLIST_BLACK)
350 case CONFIG_ACCESS_BLACKLIST:
351 set_config_option(&g_accesslist_filename, p);
352 break;
353#endif
354#ifdef WANT_DYNAMIC_ACCESSLIST
355 case CONFIG_ACCESS_FIFO_ADD:
356 set_config_option(&g_accesslist_pipe_add, p);
357 break;
358 case CONFIG_ACCESS_FIFO_DELETE:
359 set_config_option(&g_accesslist_pipe_delete, p);
360 break;
361#endif
362#ifdef WANT_RESTRICT_STATS
363 case CONFIG_ACCESS_STATS:
364 if (!scan_ip6_net(p, &tmpnet))
365 goto parse_error;
366 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT);
367 break;
368#endif
369 case CONFIG_ACCESS_STATS_PATH:
370 set_config_option(&g_stats_path, p);
371 break;
372#ifdef WANT_IP_FROM_PROXY
373 case CONFIG_ACCESS_PROXY:
374 if (!scan_ip6_net(p, &tmpnet))
375 goto parse_error;
376 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY);
377 break;
378#endif
379 case CONFIG_TRACKER_REDIRECT_URL:
380 set_config_option(&g_redirecturl, p);
381 break;
382#ifdef WANT_SYNC_LIVE
383 case CONFIG_LIVESYNC_CLUSTER_NODE_IP:
384 if (!scan_ip6_net(p, &tmpnet))
385 goto parse_error;
386 accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC);
387 break;
388 case CONFIG_LIVESYNC_CLUSTER_LISTEN:
389 tmpport = LIVESYNC_PORT;
390 {
391 int shared = OT_LISTEN_INHERIT, dummy = OT_LISTEN_INHERIT;
392 int off = scan_ip6_port(p, tmpip, &tmpport);
393
394 if (!off || !listen_scan_options(p + off, &shared, &dummy))
395 goto parse_error;
396 listen_append(tmpip, tmpport, FLAG_MCA, shared, OT_LISTEN_INHERIT);
397 }
398 break;
399#endif
400 default:
401 fprintf(stderr, "Unhandled line in config file: %s\n", inbuf);
402 break;
403 parse_error:
404 fprintf(stderr, "Parse error in config file: %s\n", inbuf);
405 }
406 }
407 fclose(config_file_handle);
408 return bound;
409}
410
411static void usage(char *name) {
412 fprintf(stderr,
413 "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport]"
414#ifdef WANT_ACCESSLIST_BLACK
415 " [-b blacklistfile]"
416#elif defined(WANT_ACCESSLIST_WHITE)
417 " [-w whitelistfile]"
418#endif
419 "\n",
420 name);
421}
422
423#define HELPLINE(opt, desc) fprintf(stderr, "\t%-10s%s\n", opt, desc)
424static void help(char *name) {
425 usage(name);
426
427 HELPLINE("-f config", "include and execute the config file");
428 HELPLINE("-i ip", "specify ip to bind to with next -[pP] (default: any, overrides preceeding ones)");
429 HELPLINE("-p port", "do bind to tcp port (default: 6969, you may specify more than one)");
430 HELPLINE("-P port", "do bind to udp port (default: 6969, you may specify more than one)");
431 HELPLINE("-r redirecturl", "specify url where / should be redirected to (default none)");
432 HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")");
433 HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")");
434 HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)");
435#ifdef WANT_ACCESSLIST_BLACK
436 HELPLINE("-b file", "specify blacklist file.");
437#elif defined(WANT_ACCESSLIST_WHITE)
438 HELPLINE("-w file", "specify whitelist file.");
439#endif
440
441 fprintf(stderr, "\nExample: ./opentracker -i 127.0.0.1 -p 6969 -P 6969 -f ./opentracker.conf -i 10.1.1.23 -p 2710 -p 80\n");
442 fprintf(stderr, " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969.\n");
443 fprintf(stderr, " If no port is bound from config file or command line, the last address given\n");
444 fprintf(stderr, " (or ::1 if none is set) will be used on port 6969.\n");
445}
446#undef HELPLINE
447
448
449static void listen_resolve_defaults(void) {
450 size_t i;
451
452 for (i = 0; i < g_listen_count; ++i) {
453 ot_listen *entry = g_listen + i;
454
455 if (entry->shared == OT_LISTEN_INHERIT)
456 entry->shared = g_listen_shared;
457 if (entry->workers == OT_LISTEN_INHERIT)
458 entry->workers = entry->proto == FLAG_TCP ? g_listen_workers_tcp : g_listen_workers_udp;
459
460 /* A tcp worker hands a connection it cannot finish to the event loop,
461 which only one thread may touch where the set is ours rather than the
462 kernel's */
463 if (entry->proto == FLAG_TCP && entry->workers > 1 && !event_can_share()) {
464 fprintf(stderr, "Warning: Multiple workers impossible on this platform with event mechanism %s chosen.\n",
465 event_mechanism());
466 entry->workers = 1;
467 }
468 }
469}
470
471ot_listen *config_listen_entries(size_t *count) {
472 *count = g_listen_count;
473 return g_listen;
474}
475
476void config_parse(int argc, char **argv) {
477 ot_ip6 serverip;
478 ot_net tmpnet;
479 int bound = 0, scanon = 1;
480 uint64_t value;
481
482 memset(serverip, 0, sizeof(ot_ip6));
483#ifdef WANT_V4_ONLY
484 serverip[10] = serverip[11] = -1;
485#endif
486
487 while (scanon) {
488 switch (getopt(argc, argv,
489 ":i:p:A:P:d:u:r:s:f:l:v"
490#ifdef WANT_ACCESSLIST_BLACK
491 "b:"
492#elif defined(WANT_ACCESSLIST_WHITE)
493 "w:"
494#endif
495 "h")) {
496 case -1:
497 scanon = 0;
498 break;
499 case 'i':
500 if (!ip_parse(optarg, serverip)) {
501 usage(argv[0]);
502 exit(1);
503 }
504 break;
505#ifdef WANT_ACCESSLIST_BLACK
506 case 'b':
507 set_config_option(&g_accesslist_filename, optarg);
508 break;
509#elif defined(WANT_ACCESSLIST_WHITE)
510 case 'w':
511 set_config_option(&g_accesslist_filename, optarg);
512 break;
513#endif
514 case 'p':
515 if (!scan_uint(optarg, &value, 65535)) {
516 usage(argv[0]);
517 exit(1);
518 }
519 listen_append(serverip, (uint16_t)value, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
520 bound++;
521 break;
522 case 'P':
523 if (!scan_uint(optarg, &value, 65535)) {
524 usage(argv[0]);
525 exit(1);
526 }
527 listen_append(serverip, (uint16_t)value, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
528 bound++;
529 break;
530#ifdef WANT_SYNC_LIVE
531 case 's':
532 if (!scan_uint(optarg, &value, 65535)) {
533 usage(argv[0]);
534 exit(1);
535 }
536 listen_append(serverip, (uint16_t)value, FLAG_MCA, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
537 break;
538#endif
539 case 'd':
540 set_config_option(&g_serverdir, optarg);
541 break;
542 case 'u':
543 set_config_option(&g_serveruser, optarg);
544 break;
545 case 'r':
546 set_config_option(&g_redirecturl, optarg);
547 break;
548 case 'l':
549 g_statefile = optarg;
550 break;
551 case 'A':
552 if (!scan_ip6_net(optarg, &tmpnet)) {
553 usage(argv[0]);
554 exit(1);
555 }
556 accesslist_bless_net(&tmpnet, 0xffff); /* Allow everything for now */
557 break;
558 case 'f':
559 bound += parse_configfile(optarg);
560 break;
561 case 'h':
562 help(argv[0]);
563 exit(0);
564 case 'v': {
565 char buffer[8192];
566 char *cursor = buffer;
567
568 stats_return_tracker_version(&cursor, buffer + sizeof(buffer) - 1);
569 *cursor = 0;
570 fputs(buffer, stderr);
571 exit(0);
572 }
573 default:
574 case '?':
575 usage(argv[0]);
576 exit(1);
577 }
578 }
579
580 if (!bound) {
581 listen_append(serverip, 6969, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
582 listen_append(serverip, 6969, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT);
583 }
584
585 listen_resolve_defaults();
586}