diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-10-25 19:13:36 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-10-25 19:13:36 +0100 |
| commit | 7d9a0b7bb11ca99513cb48a6611d7121ecc35868 (patch) | |
| tree | dd8b0df7df4ac80152406005c5a97e2b4acedcbd | |
| parent | 28f43d02149c96b87555e08f79e0c33c7a07e1af (diff) | |
Split options parsing and config code from opentracker.c, it has grown humongous
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | opentracker.c | 543 | ||||
| -rw-r--r-- | ot_config.c | 565 | ||||
| -rw-r--r-- | ot_config.h | 35 |
4 files changed, 617 insertions, 530 deletions
| @@ -68,8 +68,8 @@ CFLAGS+=-I$(LIBOWFAT_HEADERS) -DGIT_VERSION=$(GIT_VERSION) -Wall -pipe -pthread | |||
| 68 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread | 68 | LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread |
| 69 | 69 | ||
| 70 | BINARY =opentracker | 70 | BINARY =opentracker |
| 71 | HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_scan.h ot_socket.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h | 71 | HEADERS=trackerlogic.h scan_urlencoded_query.h ot_buf.h ot_hex.h ot_ip.h ot_config.h ot_scan.h ot_socket.h ot_mutex.h ot_random.h ot_stats.h ot_vector.h ot_clean.h ot_udp.h ot_iovec.h ot_fullscrape.h ot_accesslist.h ot_http.h ot_livesync.h ot_rijndael.h |
| 72 | SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_scan.c ot_socket.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c | 72 | SOURCES=opentracker.c trackerlogic.c scan_urlencoded_query.c ot_buf.c ot_hex.c ot_ip.c ot_config.c ot_scan.c ot_socket.c ot_mutex.c ot_random.c ot_stats.c ot_vector.c ot_clean.c ot_udp.c ot_iovec.c ot_fullscrape.c ot_accesslist.c ot_http.c ot_livesync.c ot_rijndael.c |
| 73 | SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c | 73 | SOURCES_proxy=proxy.c ot_vector.c ot_mutex.c ot_random.c ot_scan.c ot_ip.c ot_socket.c |
| 74 | 74 | ||
| 75 | OBJECTS = $(SOURCES:%.c=%.o) | 75 | OBJECTS = $(SOURCES:%.c=%.o) |
diff --git a/opentracker.c b/opentracker.c index 1b0e119..698669c 100644 --- a/opentracker.c +++ b/opentracker.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | 27 | ||
| 28 | /* Opentracker */ | 28 | /* Opentracker */ |
| 29 | #include "ot_accesslist.h" | 29 | #include "ot_accesslist.h" |
| 30 | #include "ot_config.h" | ||
| 30 | #include "ot_hex.h" | 31 | #include "ot_hex.h" |
| 31 | #include "ot_http.h" | 32 | #include "ot_http.h" |
| 32 | #include "ot_ip.h" | 33 | #include "ot_ip.h" |
| @@ -41,29 +42,10 @@ | |||
| 41 | 42 | ||
| 42 | /* Globals */ | 43 | /* Globals */ |
| 43 | time_t g_now_seconds; | 44 | time_t g_now_seconds; |
| 44 | char *g_redirecturl; | ||
| 45 | uint32_t g_tracker_id; | 45 | uint32_t g_tracker_id; |
| 46 | volatile int g_opentracker_running = 1; | 46 | volatile int g_opentracker_running = 1; |
| 47 | int g_self_pipe[2]; | 47 | int g_self_pipe[2]; |
| 48 | 48 | ||
| 49 | static char *g_serverdir; | ||
| 50 | static char *g_serveruser; | ||
| 51 | |||
| 52 | typedef struct { | ||
| 53 | ot_ip6 ip; | ||
| 54 | uint16_t port; | ||
| 55 | PROTO_FLAG proto; | ||
| 56 | int shared; | ||
| 57 | int workers; | ||
| 58 | int64_t sock; | ||
| 59 | } ot_listen; | ||
| 60 | #define OT_LISTEN_INHERIT (-1) | ||
| 61 | |||
| 62 | /* Config entries collected from command line and config files */ | ||
| 63 | static ot_listen *g_listen; | ||
| 64 | static size_t g_listen_count; | ||
| 65 | static int g_listen_shared; | ||
| 66 | static int g_listen_workers; | ||
| 67 | 49 | ||
| 68 | static void panic(const char *routine) __attribute__((noreturn)); | 50 | static void panic(const char *routine) __attribute__((noreturn)); |
| 69 | static void panic(const char *routine) { | 51 | static void panic(const char *routine) { |
| @@ -115,42 +97,6 @@ static void install_signal_handlers(void) { | |||
| 115 | pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL); | 97 | pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL); |
| 116 | } | 98 | } |
| 117 | 99 | ||
| 118 | static void usage(char *name) { | ||
| 119 | fprintf(stderr, | ||
| 120 | "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport]" | ||
| 121 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 122 | " [-b blacklistfile]" | ||
| 123 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 124 | " [-w whitelistfile]" | ||
| 125 | #endif | ||
| 126 | "\n", | ||
| 127 | name); | ||
| 128 | } | ||
| 129 | |||
| 130 | #define HELPLINE(opt, desc) fprintf(stderr, "\t%-10s%s\n", opt, desc) | ||
| 131 | static void help(char *name) { | ||
| 132 | usage(name); | ||
| 133 | |||
| 134 | HELPLINE("-f config", "include and execute the config file"); | ||
| 135 | HELPLINE("-i ip", "specify ip to bind to with next -[pP] (default: any, overrides preceeding ones)"); | ||
| 136 | HELPLINE("-p port", "do bind to tcp port (default: 6969, you may specify more than one)"); | ||
| 137 | HELPLINE("-P port", "do bind to udp port (default: 6969, you may specify more than one)"); | ||
| 138 | HELPLINE("-r redirecturl", "specify url where / should be redirected to (default none)"); | ||
| 139 | HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")"); | ||
| 140 | HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")"); | ||
| 141 | HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)"); | ||
| 142 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 143 | HELPLINE("-b file", "specify blacklist file."); | ||
| 144 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 145 | HELPLINE("-w file", "specify whitelist file."); | ||
| 146 | #endif | ||
| 147 | |||
| 148 | 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"); | ||
| 149 | fprintf(stderr, " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969.\n"); | ||
| 150 | fprintf(stderr, " If no port is bound from config file or command line, the last address given\n"); | ||
| 151 | fprintf(stderr, " (or ::1 if none is set) will be used on port 6969.\n"); | ||
| 152 | } | ||
| 153 | #undef HELPLINE | ||
| 154 | 100 | ||
| 155 | static ssize_t header_complete(char *request, ssize_t byte_count) { | 101 | static ssize_t header_complete(char *request, ssize_t byte_count) { |
| 156 | ssize_t i = 0, state = 0; | 102 | ssize_t i = 0, state = 0; |
| @@ -353,91 +299,9 @@ static void *server_mainloop(void *args) { | |||
| 353 | return 0; | 299 | return 0; |
| 354 | } | 300 | } |
| 355 | 301 | ||
| 356 | static void listen_append(const ot_ip6 ip, uint16_t port, PROTO_FLAG proto, int shared, int workers) { | ||
| 357 | ot_listen *grown = realloc(g_listen, (g_listen_count + 1) * sizeof(ot_listen)); | ||
| 358 | ot_listen *entry; | ||
| 359 | |||
| 360 | if (!grown) | ||
| 361 | panic("listen_append"); | ||
| 362 | g_listen = grown; | ||
| 363 | |||
| 364 | entry = g_listen + g_listen_count++; | ||
| 365 | memcpy(entry->ip, ip, sizeof(ot_ip6)); | ||
| 366 | entry->port = port; | ||
| 367 | entry->proto = proto; | ||
| 368 | entry->shared = shared; | ||
| 369 | entry->workers = workers; | ||
| 370 | entry->sock = -1; | ||
| 371 | } | ||
| 372 | |||
| 373 | typedef enum { LISTEN_OPTION_UNKNOWN, LISTEN_OPTION_SHARED, LISTEN_OPTION_WORKERS } listen_option_kind; | ||
| 374 | |||
| 375 | typedef struct { | ||
| 376 | const char *name; | ||
| 377 | size_t len; | ||
| 378 | listen_option_kind kind; | ||
| 379 | int takes_value; | ||
| 380 | } listen_option_key; | ||
| 381 | |||
| 382 | #define LISTEN_OPTION_KEY(s, k, v) {s, sizeof(s) - 1, k, v} | ||
| 383 | |||
| 384 | static const listen_option_key listen_option_keys[] = { | ||
| 385 | LISTEN_OPTION_KEY("shared", LISTEN_OPTION_SHARED, 0), | ||
| 386 | LISTEN_OPTION_KEY("workers", LISTEN_OPTION_WORKERS, 1), | ||
| 387 | }; | ||
| 388 | static const size_t listen_option_count = sizeof(listen_option_keys) / sizeof(listen_option_keys[0]); | ||
| 389 | |||
| 390 | static listen_option_kind parse_listen_option(char **p) { | ||
| 391 | size_t i; | ||
| 392 | |||
| 393 | for (i = 0; i < listen_option_count; ++i) { | ||
| 394 | const listen_option_key *key = &listen_option_keys[i]; | ||
| 395 | char delimiter = (*p)[key->len]; | ||
| 396 | |||
| 397 | if (strncmp(*p, key->name, key->len)) | ||
| 398 | continue; | ||
| 399 | if (key->takes_value ? delimiter != '=' : (delimiter && !isspace((unsigned char)delimiter))) | ||
| 400 | continue; | ||
| 401 | |||
| 402 | *p += key->len + (key->takes_value ? 1 : 0); | ||
| 403 | return key->kind; | ||
| 404 | } | ||
| 405 | return LISTEN_OPTION_UNKNOWN; | ||
| 406 | } | ||
| 407 | |||
| 408 | /* Options trailing an address in a listen statement, in any order */ | ||
| 409 | static int listen_scan_options(char *p, int *shared, int *workers) { | ||
| 410 | uint64_t value; | ||
| 411 | |||
| 412 | for (;;) { | ||
| 413 | while (isspace((unsigned char)*p)) | ||
| 414 | ++p; | ||
| 415 | if (!*p) | ||
| 416 | return 1; | ||
| 417 | |||
| 418 | switch (parse_listen_option(&p)) { | ||
| 419 | case LISTEN_OPTION_SHARED: | ||
| 420 | *shared = 1; | ||
| 421 | break; | ||
| 422 | case LISTEN_OPTION_WORKERS: { | ||
| 423 | size_t off = scan_udec(p, &value, UINT_MAX); | ||
| 424 | |||
| 425 | if (!off) | ||
| 426 | return 0; | ||
| 427 | *workers = (int)value; | ||
| 428 | p += off; | ||
| 429 | break; | ||
| 430 | } | ||
| 431 | default: | ||
| 432 | return 0; | ||
| 433 | } | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | static int listen_option(int value, int fallback) { return value == OT_LISTEN_INHERIT ? fallback : value; } | ||
| 438 | 302 | ||
| 439 | static void listen_bind_one(ot_listen *entry) { | 303 | static void listen_bind_one(ot_listen *entry) { |
| 440 | socket_sharing sharing = listen_option(entry->shared, g_listen_shared) ? SOCKET_SHARED : SOCKET_EXCLUSIVE; | 304 | socket_sharing sharing = entry->shared ? SOCKET_SHARED : SOCKET_EXCLUSIVE; |
| 441 | 305 | ||
| 442 | #ifdef _DEBUG | 306 | #ifdef _DEBUG |
| 443 | { | 307 | { |
| @@ -482,305 +346,31 @@ static void listen_bind_one(ot_listen *entry) { | |||
| 482 | /* Bound in the order the entries were given. | 346 | /* Bound in the order the entries were given. |
| 483 | Note: On macOS SO_REUSEPORT delivers everything to the socket that bound last. */ | 347 | Note: On macOS SO_REUSEPORT delivers everything to the socket that bound last. */ |
| 484 | static void listen_bind_all(void) { | 348 | static void listen_bind_all(void) { |
| 485 | size_t i; | 349 | size_t count, i; |
| 350 | ot_listen *entries = config_listen_entries(&count); | ||
| 486 | 351 | ||
| 487 | for (i = 0; i < g_listen_count; ++i) | 352 | for (i = 0; i < count; ++i) |
| 488 | listen_bind_one(g_listen + i); | 353 | listen_bind_one(entries + i); |
| 489 | } | 354 | } |
| 490 | 355 | ||
| 491 | static void listen_start_udp_workers(void) { | 356 | static void listen_start_udp_workers(void) { |
| 492 | size_t i; | 357 | size_t count, i; |
| 358 | ot_listen *entries = config_listen_entries(&count); | ||
| 493 | 359 | ||
| 494 | udp_init(); | 360 | udp_init(); |
| 495 | 361 | ||
| 496 | for (i = 0; i < g_listen_count; ++i) { | 362 | for (i = 0; i < count; ++i) { |
| 497 | ot_listen *entry = g_listen + i; | 363 | ot_listen *entry = entries + i; |
| 498 | int workers = listen_option(entry->workers, g_listen_workers); | ||
| 499 | 364 | ||
| 500 | if (entry->proto != FLAG_UDP || workers <= 0) | 365 | if (entry->proto != FLAG_UDP || entry->workers <= 0) |
| 501 | continue; | 366 | continue; |
| 502 | 367 | ||
| 503 | io_block(entry->sock); | 368 | io_block(entry->sock); |
| 504 | udp_workers_install(entry->sock, (unsigned int)workers); | 369 | udp_workers_install(entry->sock, (unsigned int)entry->workers); |
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | char *set_config_option(char **option, char *value) { | ||
| 509 | #ifdef _DEBUG | ||
| 510 | fprintf(stderr, "Setting config option: %s\n", value); | ||
| 511 | #endif | ||
| 512 | while (isspace(*value)) | ||
| 513 | ++value; | ||
| 514 | free(*option); | ||
| 515 | return *option = strdup(value); | ||
| 516 | } | ||
| 517 | |||
| 518 | static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | ||
| 519 | const char *s = src; | ||
| 520 | uint64_t value; | ||
| 521 | int off, bracket = 0; | ||
| 522 | while (isspace(*s)) | ||
| 523 | ++s; | ||
| 524 | if (*s == '[') | ||
| 525 | ++s, ++bracket; /* for v6 style notation */ | ||
| 526 | if (!(off = ip_scan(s, ip))) | ||
| 527 | return 0; | ||
| 528 | s += off; | ||
| 529 | if (bracket && *s == ']') | ||
| 530 | ++s; | ||
| 531 | if (*s == 0 || isspace(*s)) | ||
| 532 | return s - src; | ||
| 533 | if (!ip_is_v4mapped(ip)) { | ||
| 534 | if (*s != ':' && *s != '.') | ||
| 535 | return 0; | ||
| 536 | if (!bracket && *(s) == ':') | ||
| 537 | return 0; | ||
| 538 | s++; | ||
| 539 | } else { | ||
| 540 | if (*(s++) != ':') | ||
| 541 | return 0; | ||
| 542 | } | ||
| 543 | if (!(off = scan_udec(s, &value, 65535))) | ||
| 544 | return 0; | ||
| 545 | *port = (uint16_t)value; | ||
| 546 | return off + s - src; | ||
| 547 | } | ||
| 548 | |||
| 549 | static int scan_ip6_net(const char *src, ot_net *net) { | ||
| 550 | const char *s = src; | ||
| 551 | uint64_t value; | ||
| 552 | int off; | ||
| 553 | while (isspace(*s)) | ||
| 554 | ++s; | ||
| 555 | if (!(off = ip_scan(s, net->address))) | ||
| 556 | return 0; | ||
| 557 | s += off; | ||
| 558 | if (*s != '/') | ||
| 559 | net->bits = 128; | ||
| 560 | else { | ||
| 561 | s++; | ||
| 562 | if (!(off = scan_udec(s, &value, 128))) | ||
| 563 | return 0; | ||
| 564 | net->bits = (int)value; | ||
| 565 | if (ip_is_v4mapped(net->address)) | ||
| 566 | net->bits += 96; | ||
| 567 | if (net->bits > 128) | ||
| 568 | return 0; | ||
| 569 | s += off; | ||
| 570 | } | 370 | } |
| 571 | return off + s - src; | ||
| 572 | } | ||
| 573 | |||
| 574 | typedef enum { | ||
| 575 | CONFIG_UNKNOWN, | ||
| 576 | CONFIG_TRACKER_ROOTDIR, | ||
| 577 | CONFIG_TRACKER_USER, | ||
| 578 | CONFIG_LISTEN_TCP_UDP, | ||
| 579 | CONFIG_LISTEN_TCP, | ||
| 580 | CONFIG_LISTEN_UDP, | ||
| 581 | CONFIG_LISTEN_UDP_WORKERS, | ||
| 582 | CONFIG_LISTEN_SHARED, | ||
| 583 | CONFIG_ACCESS_WHITELIST, | ||
| 584 | CONFIG_ACCESS_BLACKLIST, | ||
| 585 | CONFIG_ACCESS_FIFO_ADD, | ||
| 586 | CONFIG_ACCESS_FIFO_DELETE, | ||
| 587 | CONFIG_ACCESS_STATS, | ||
| 588 | CONFIG_ACCESS_STATS_PATH, | ||
| 589 | CONFIG_ACCESS_PROXY, | ||
| 590 | CONFIG_TRACKER_REDIRECT_URL, | ||
| 591 | CONFIG_LIVESYNC_CLUSTER_NODE_IP, | ||
| 592 | CONFIG_LIVESYNC_CLUSTER_LISTEN, | ||
| 593 | } config_kind; | ||
| 594 | |||
| 595 | typedef struct { | ||
| 596 | const char *name; | ||
| 597 | size_t len; | ||
| 598 | config_kind kind; | ||
| 599 | } config_key; | ||
| 600 | |||
| 601 | #define CONFIG_KEY(s, k) { s, sizeof(s) - 1, k } | ||
| 602 | |||
| 603 | static const config_key config_keys[] = { | ||
| 604 | CONFIG_KEY("tracker.rootdir", CONFIG_TRACKER_ROOTDIR), | ||
| 605 | CONFIG_KEY("tracker.user", CONFIG_TRACKER_USER), | ||
| 606 | CONFIG_KEY("listen.tcp_udp", CONFIG_LISTEN_TCP_UDP), | ||
| 607 | CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP), | ||
| 608 | CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP), | ||
| 609 | CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS), | ||
| 610 | CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED), | ||
| 611 | #ifdef WANT_ACCESSLIST_WHITE | ||
| 612 | CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST), | ||
| 613 | #elif defined(WANT_ACCESSLIST_BLACK) | ||
| 614 | CONFIG_KEY("access.blacklist", CONFIG_ACCESS_BLACKLIST), | ||
| 615 | #endif | ||
| 616 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 617 | CONFIG_KEY("access.fifo_add", CONFIG_ACCESS_FIFO_ADD), | ||
| 618 | CONFIG_KEY("access.fifo_delete", CONFIG_ACCESS_FIFO_DELETE), | ||
| 619 | #endif | ||
| 620 | #ifdef WANT_RESTRICT_STATS | ||
| 621 | CONFIG_KEY("access.stats", CONFIG_ACCESS_STATS), | ||
| 622 | #endif | ||
| 623 | CONFIG_KEY("access.stats_path", CONFIG_ACCESS_STATS_PATH), | ||
| 624 | #ifdef WANT_IP_FROM_PROXY | ||
| 625 | CONFIG_KEY("access.proxy", CONFIG_ACCESS_PROXY), | ||
| 626 | #endif | ||
| 627 | CONFIG_KEY("tracker.redirect_url", CONFIG_TRACKER_REDIRECT_URL), | ||
| 628 | #ifdef WANT_SYNC_LIVE | ||
| 629 | CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP), | ||
| 630 | CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN), | ||
| 631 | #endif | ||
| 632 | }; | ||
| 633 | static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]); | ||
| 634 | |||
| 635 | static config_kind parse_config_key(char **p) { | ||
| 636 | size_t i; | ||
| 637 | for (i = 0; i < config_entry_count; ++i) { | ||
| 638 | const config_key *key = &config_keys[i]; | ||
| 639 | if (strncmp(*p, key->name, key->len) == 0 && isspace((unsigned char)(*p)[key->len])) { | ||
| 640 | *p += key->len + 1; | ||
| 641 | /* Skip white spaces */ | ||
| 642 | while (isspace((unsigned char)**p)) | ||
| 643 | ++(*p); | ||
| 644 | return key->kind; | ||
| 645 | } | ||
| 646 | } | ||
| 647 | return CONFIG_UNKNOWN; | ||
| 648 | } | 371 | } |
| 649 | 372 | ||
| 650 | int parse_configfile(char *config_filename) { | ||
| 651 | FILE *config_file_handle; | ||
| 652 | char inbuf[512]; | ||
| 653 | ot_ip6 tmpip; | ||
| 654 | #if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE) | ||
| 655 | ot_net tmpnet; | ||
| 656 | #endif | ||
| 657 | int bound = 0; | ||
| 658 | 373 | ||
| 659 | config_file_handle = fopen(config_filename, "r"); | ||
| 660 | |||
| 661 | if (config_file_handle == NULL) { | ||
| 662 | fprintf(stderr, "Warning: Can't open config file: %s.", config_filename); | ||
| 663 | return 0; | ||
| 664 | } | ||
| 665 | |||
| 666 | while (fgets(inbuf, sizeof(inbuf), config_file_handle)) { | ||
| 667 | char *p = inbuf, *end; | ||
| 668 | uint64_t value; | ||
| 669 | uint16_t tmpport = 6969; | ||
| 670 | config_kind kind; | ||
| 671 | |||
| 672 | /* Skip white spaces */ | ||
| 673 | while (isspace((unsigned char)*p)) | ||
| 674 | ++p; | ||
| 675 | |||
| 676 | /* Ignore comments and empty lines */ | ||
| 677 | if ((*p == '#') || (*p == '\n') || (*p == 0)) | ||
| 678 | continue; | ||
| 679 | |||
| 680 | /* consume trailing new lines and spaces */ | ||
| 681 | end = p + strlen(p); | ||
| 682 | while (end > p && isspace((unsigned char)end[-1])) | ||
| 683 | *--end = 0; | ||
| 684 | |||
| 685 | /* Scan for commands */ | ||
| 686 | kind = parse_config_key(&p); | ||
| 687 | switch (kind) { | ||
| 688 | case CONFIG_TRACKER_ROOTDIR: | ||
| 689 | set_config_option(&g_serverdir, p); | ||
| 690 | break; | ||
| 691 | case CONFIG_TRACKER_USER: | ||
| 692 | set_config_option(&g_serveruser, p); | ||
| 693 | break; | ||
| 694 | case CONFIG_LISTEN_TCP_UDP: | ||
| 695 | case CONFIG_LISTEN_TCP: | ||
| 696 | case CONFIG_LISTEN_UDP: { | ||
| 697 | int shared = OT_LISTEN_INHERIT; | ||
| 698 | int workers = OT_LISTEN_INHERIT; | ||
| 699 | int off = scan_ip6_port(p, tmpip, &tmpport); | ||
| 700 | |||
| 701 | if (!off || !listen_scan_options(p + off, &shared, &workers)) | ||
| 702 | goto parse_error; | ||
| 703 | if (kind != CONFIG_LISTEN_UDP) | ||
| 704 | listen_append(tmpip, tmpport, FLAG_TCP, shared, OT_LISTEN_INHERIT), ++bound; | ||
| 705 | if (kind != CONFIG_LISTEN_TCP) | ||
| 706 | listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound; | ||
| 707 | break; | ||
| 708 | } | ||
| 709 | case CONFIG_LISTEN_UDP_WORKERS: | ||
| 710 | if (!scan_udec(p, &value, UINT_MAX)) | ||
| 711 | goto parse_error; | ||
| 712 | g_listen_workers = (int)value; | ||
| 713 | break; | ||
| 714 | case CONFIG_LISTEN_SHARED: | ||
| 715 | if (!scan_udec(p, &value, 1)) | ||
| 716 | goto parse_error; | ||
| 717 | g_listen_shared = (int)value; | ||
| 718 | break; | ||
| 719 | #ifdef WANT_ACCESSLIST_WHITE | ||
| 720 | case CONFIG_ACCESS_WHITELIST: | ||
| 721 | set_config_option(&g_accesslist_filename, p); | ||
| 722 | break; | ||
| 723 | #elif defined(WANT_ACCESSLIST_BLACK) | ||
| 724 | case CONFIG_ACCESS_BLACKLIST: | ||
| 725 | set_config_option(&g_accesslist_filename, p); | ||
| 726 | break; | ||
| 727 | #endif | ||
| 728 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 729 | case CONFIG_ACCESS_FIFO_ADD: | ||
| 730 | set_config_option(&g_accesslist_pipe_add, p); | ||
| 731 | break; | ||
| 732 | case CONFIG_ACCESS_FIFO_DELETE: | ||
| 733 | set_config_option(&g_accesslist_pipe_delete, p); | ||
| 734 | break; | ||
| 735 | #endif | ||
| 736 | #ifdef WANT_RESTRICT_STATS | ||
| 737 | case CONFIG_ACCESS_STATS: | ||
| 738 | if (!scan_ip6_net(p, &tmpnet)) | ||
| 739 | goto parse_error; | ||
| 740 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT); | ||
| 741 | break; | ||
| 742 | #endif | ||
| 743 | case CONFIG_ACCESS_STATS_PATH: | ||
| 744 | set_config_option(&g_stats_path, p); | ||
| 745 | break; | ||
| 746 | #ifdef WANT_IP_FROM_PROXY | ||
| 747 | case CONFIG_ACCESS_PROXY: | ||
| 748 | if (!scan_ip6_net(p, &tmpnet)) | ||
| 749 | goto parse_error; | ||
| 750 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY); | ||
| 751 | break; | ||
| 752 | #endif | ||
| 753 | case CONFIG_TRACKER_REDIRECT_URL: | ||
| 754 | set_config_option(&g_redirecturl, p); | ||
| 755 | break; | ||
| 756 | #ifdef WANT_SYNC_LIVE | ||
| 757 | case CONFIG_LIVESYNC_CLUSTER_NODE_IP: | ||
| 758 | if (!scan_ip6_net(p, &tmpnet)) | ||
| 759 | goto parse_error; | ||
| 760 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC); | ||
| 761 | break; | ||
| 762 | case CONFIG_LIVESYNC_CLUSTER_LISTEN: | ||
| 763 | tmpport = LIVESYNC_PORT; | ||
| 764 | { | ||
| 765 | int shared = OT_LISTEN_INHERIT, dummy = OT_LISTEN_INHERIT; | ||
| 766 | int off = scan_ip6_port(p, tmpip, &tmpport); | ||
| 767 | |||
| 768 | if (!off || !listen_scan_options(p + off, &shared, &dummy)) | ||
| 769 | goto parse_error; | ||
| 770 | listen_append(tmpip, tmpport, FLAG_MCA, shared, OT_LISTEN_INHERIT); | ||
| 771 | } | ||
| 772 | break; | ||
| 773 | #endif | ||
| 774 | default: | ||
| 775 | fprintf(stderr, "Unhandled line in config file: %s\n", inbuf); | ||
| 776 | break; | ||
| 777 | parse_error: | ||
| 778 | fprintf(stderr, "Parse error in config file: %s\n", inbuf); | ||
| 779 | } | ||
| 780 | } | ||
| 781 | fclose(config_file_handle); | ||
| 782 | return bound; | ||
| 783 | } | ||
| 784 | 374 | ||
| 785 | void load_state(const char *const state_filename) { | 375 | void load_state(const char *const state_filename) { |
| 786 | FILE *state_filehandle; | 376 | FILE *state_filehandle; |
| @@ -873,114 +463,11 @@ static void *time_caching_worker(void *args) { | |||
| 873 | } | 463 | } |
| 874 | 464 | ||
| 875 | int main(int argc, char **argv) { | 465 | int main(int argc, char **argv) { |
| 876 | ot_ip6 serverip; | ||
| 877 | ot_net tmpnet; | ||
| 878 | int bound = 0, scanon = 1; | ||
| 879 | uint64_t value; | ||
| 880 | char *statefile = 0; | ||
| 881 | pthread_t thread_id; /* time cacher */ | 466 | pthread_t thread_id; /* time cacher */ |
| 882 | 467 | ||
| 883 | memset(serverip, 0, sizeof(ot_ip6)); | ||
| 884 | #ifdef WANT_V4_ONLY | ||
| 885 | serverip[10] = serverip[11] = -1; | ||
| 886 | #endif | ||
| 887 | |||
| 888 | random_init(); | 468 | random_init(); |
| 469 | config_parse(argc, argv); | ||
| 889 | 470 | ||
| 890 | while (scanon) { | ||
| 891 | switch (getopt(argc, argv, | ||
| 892 | ":i:p:A:P:d:u:r:s:f:l:v" | ||
| 893 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 894 | "b:" | ||
| 895 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 896 | "w:" | ||
| 897 | #endif | ||
| 898 | "h")) { | ||
| 899 | case -1: | ||
| 900 | scanon = 0; | ||
| 901 | break; | ||
| 902 | case 'i': | ||
| 903 | if (!ip_scan(optarg, serverip)) { | ||
| 904 | usage(argv[0]); | ||
| 905 | exit(1); | ||
| 906 | } | ||
| 907 | break; | ||
| 908 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 909 | case 'b': | ||
| 910 | set_config_option(&g_accesslist_filename, optarg); | ||
| 911 | break; | ||
| 912 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 913 | case 'w': | ||
| 914 | set_config_option(&g_accesslist_filename, optarg); | ||
| 915 | break; | ||
| 916 | #endif | ||
| 917 | case 'p': | ||
| 918 | if (!scan_udec(optarg, &value, 65535)) { | ||
| 919 | usage(argv[0]); | ||
| 920 | exit(1); | ||
| 921 | } | ||
| 922 | listen_append(serverip, (uint16_t)value, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 923 | bound++; | ||
| 924 | break; | ||
| 925 | case 'P': | ||
| 926 | if (!scan_udec(optarg, &value, 65535)) { | ||
| 927 | usage(argv[0]); | ||
| 928 | exit(1); | ||
| 929 | } | ||
| 930 | listen_append(serverip, (uint16_t)value, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 931 | bound++; | ||
| 932 | break; | ||
| 933 | #ifdef WANT_SYNC_LIVE | ||
| 934 | case 's': | ||
| 935 | if (!scan_udec(optarg, &value, 65535)) { | ||
| 936 | usage(argv[0]); | ||
| 937 | exit(1); | ||
| 938 | } | ||
| 939 | listen_append(serverip, (uint16_t)value, FLAG_MCA, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 940 | break; | ||
| 941 | #endif | ||
| 942 | case 'd': | ||
| 943 | set_config_option(&g_serverdir, optarg); | ||
| 944 | break; | ||
| 945 | case 'u': | ||
| 946 | set_config_option(&g_serveruser, optarg); | ||
| 947 | break; | ||
| 948 | case 'r': | ||
| 949 | set_config_option(&g_redirecturl, optarg); | ||
| 950 | break; | ||
| 951 | case 'l': | ||
| 952 | statefile = optarg; | ||
| 953 | break; | ||
| 954 | case 'A': | ||
| 955 | if (!scan_ip6_net(optarg, &tmpnet)) { | ||
| 956 | usage(argv[0]); | ||
| 957 | exit(1); | ||
| 958 | } | ||
| 959 | accesslist_bless_net(&tmpnet, 0xffff); /* Allow everything for now */ | ||
| 960 | break; | ||
| 961 | case 'f': | ||
| 962 | bound += parse_configfile(optarg); | ||
| 963 | break; | ||
| 964 | case 'h': | ||
| 965 | help(argv[0]); | ||
| 966 | exit(0); | ||
| 967 | case 'v': { | ||
| 968 | char buffer[8192]; | ||
| 969 | stats_return_tracker_version(buffer); | ||
| 970 | fputs(buffer, stderr); | ||
| 971 | exit(0); | ||
| 972 | } | ||
| 973 | default: | ||
| 974 | case '?': | ||
| 975 | usage(argv[0]); | ||
| 976 | exit(1); | ||
| 977 | } | ||
| 978 | } | ||
| 979 | |||
| 980 | if (!bound) { | ||
| 981 | listen_append(serverip, 6969, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 982 | listen_append(serverip, 6969, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 983 | } | ||
| 984 | 471 | ||
| 985 | listen_bind_all(); | 472 | listen_bind_all(); |
| 986 | 473 | ||
| @@ -1017,8 +504,8 @@ int main(int argc, char **argv) { | |||
| 1017 | fprintf(stderr, "... done.\n"); | 504 | fprintf(stderr, "... done.\n"); |
| 1018 | #endif | 505 | #endif |
| 1019 | 506 | ||
| 1020 | if (statefile) | 507 | if (g_statefile) |
| 1021 | load_state(statefile); | 508 | load_state(g_statefile); |
| 1022 | 509 | ||
| 1023 | install_signal_handlers(); | 510 | install_signal_handlers(); |
| 1024 | 511 | ||
diff --git a/ot_config.c b/ot_config.c new file mode 100644 index 0000000..43c07c5 --- /dev/null +++ b/ot_config.c | |||
| @@ -0,0 +1,565 @@ | |||
| 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_config.h" | ||
| 18 | #include "ot_http.h" | ||
| 19 | #include "ot_ip.h" | ||
| 20 | #include "ot_livesync.h" | ||
| 21 | #include "ot_scan.h" | ||
| 22 | #include "ot_stats.h" | ||
| 23 | #include "trackerlogic.h" | ||
| 24 | |||
| 25 | char *g_redirecturl; | ||
| 26 | char *g_serverdir; | ||
| 27 | char *g_serveruser; | ||
| 28 | char *g_statefile; | ||
| 29 | |||
| 30 | #define OT_LISTEN_INHERIT (-1) | ||
| 31 | |||
| 32 | static ot_listen *g_listen; | ||
| 33 | static size_t g_listen_count; | ||
| 34 | static int g_listen_shared; | ||
| 35 | static int g_listen_workers; | ||
| 36 | |||
| 37 | static void panic(const char *routine) __attribute__((noreturn)); | ||
| 38 | static void panic(const char *routine) { | ||
| 39 | fprintf(stderr, "%s: %s\n", routine, strerror(errno)); | ||
| 40 | exit(111); | ||
| 41 | } | ||
| 42 | |||
| 43 | static void listen_append(const ot_ip6 ip, uint16_t port, PROTO_FLAG proto, int shared, int workers) { | ||
| 44 | ot_listen *grown = realloc(g_listen, (g_listen_count + 1) * sizeof(ot_listen)); | ||
| 45 | ot_listen *entry; | ||
| 46 | |||
| 47 | if (!grown) | ||
| 48 | panic("listen_append"); | ||
| 49 | g_listen = grown; | ||
| 50 | |||
| 51 | entry = g_listen + g_listen_count++; | ||
| 52 | memcpy(entry->ip, ip, sizeof(ot_ip6)); | ||
| 53 | entry->port = port; | ||
| 54 | entry->proto = proto; | ||
| 55 | entry->shared = shared; | ||
| 56 | entry->workers = workers; | ||
| 57 | entry->sock = -1; | ||
| 58 | } | ||
| 59 | |||
| 60 | typedef enum { LISTEN_OPTION_UNKNOWN, LISTEN_OPTION_SHARED, LISTEN_OPTION_WORKERS } listen_option_kind; | ||
| 61 | |||
| 62 | typedef struct { | ||
| 63 | const char *name; | ||
| 64 | size_t len; | ||
| 65 | listen_option_kind kind; | ||
| 66 | int takes_value; | ||
| 67 | } listen_option_key; | ||
| 68 | |||
| 69 | #define LISTEN_OPTION_KEY(s, k, v) {s, sizeof(s) - 1, k, v} | ||
| 70 | |||
| 71 | static const listen_option_key listen_option_keys[] = { | ||
| 72 | LISTEN_OPTION_KEY("shared", LISTEN_OPTION_SHARED, 0), | ||
| 73 | LISTEN_OPTION_KEY("workers", LISTEN_OPTION_WORKERS, 1), | ||
| 74 | }; | ||
| 75 | static const size_t listen_option_count = sizeof(listen_option_keys) / sizeof(listen_option_keys[0]); | ||
| 76 | |||
| 77 | static listen_option_kind parse_listen_option(char **p) { | ||
| 78 | size_t i; | ||
| 79 | |||
| 80 | for (i = 0; i < listen_option_count; ++i) { | ||
| 81 | const listen_option_key *key = &listen_option_keys[i]; | ||
| 82 | char delimiter = (*p)[key->len]; | ||
| 83 | |||
| 84 | if (strncmp(*p, key->name, key->len)) | ||
| 85 | continue; | ||
| 86 | if (key->takes_value ? delimiter != '=' : (delimiter && !isspace((unsigned char)delimiter))) | ||
| 87 | continue; | ||
| 88 | |||
| 89 | *p += key->len + (key->takes_value ? 1 : 0); | ||
| 90 | return key->kind; | ||
| 91 | } | ||
| 92 | return LISTEN_OPTION_UNKNOWN; | ||
| 93 | } | ||
| 94 | |||
| 95 | /* Options trailing an address in a listen statement, in any order */ | ||
| 96 | static int listen_scan_options(char *p, int *shared, int *workers) { | ||
| 97 | uint64_t value; | ||
| 98 | |||
| 99 | for (;;) { | ||
| 100 | while (isspace((unsigned char)*p)) | ||
| 101 | ++p; | ||
| 102 | if (!*p) | ||
| 103 | return 1; | ||
| 104 | |||
| 105 | switch (parse_listen_option(&p)) { | ||
| 106 | case LISTEN_OPTION_SHARED: | ||
| 107 | *shared = 1; | ||
| 108 | break; | ||
| 109 | case LISTEN_OPTION_WORKERS: { | ||
| 110 | size_t off = scan_udec(p, &value, UINT_MAX); | ||
| 111 | |||
| 112 | if (!off) | ||
| 113 | return 0; | ||
| 114 | *workers = (int)value; | ||
| 115 | p += off; | ||
| 116 | break; | ||
| 117 | } | ||
| 118 | default: | ||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | |||
| 125 | char *set_config_option(char **option, char *value) { | ||
| 126 | #ifdef _DEBUG | ||
| 127 | fprintf(stderr, "Setting config option: %s\n", value); | ||
| 128 | #endif | ||
| 129 | while (isspace(*value)) | ||
| 130 | ++value; | ||
| 131 | free(*option); | ||
| 132 | return *option = strdup(value); | ||
| 133 | } | ||
| 134 | |||
| 135 | static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) { | ||
| 136 | const char *s = src; | ||
| 137 | uint64_t value; | ||
| 138 | int off, bracket = 0; | ||
| 139 | while (isspace(*s)) | ||
| 140 | ++s; | ||
| 141 | if (*s == '[') | ||
| 142 | ++s, ++bracket; /* for v6 style notation */ | ||
| 143 | if (!(off = ip_scan(s, ip))) | ||
| 144 | return 0; | ||
| 145 | s += off; | ||
| 146 | if (bracket && *s == ']') | ||
| 147 | ++s; | ||
| 148 | if (*s == 0 || isspace(*s)) | ||
| 149 | return s - src; | ||
| 150 | if (!ip_is_v4mapped(ip)) { | ||
| 151 | if (*s != ':' && *s != '.') | ||
| 152 | return 0; | ||
| 153 | if (!bracket && *(s) == ':') | ||
| 154 | return 0; | ||
| 155 | s++; | ||
| 156 | } else { | ||
| 157 | if (*(s++) != ':') | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | if (!(off = scan_udec(s, &value, 65535))) | ||
| 161 | return 0; | ||
| 162 | *port = (uint16_t)value; | ||
| 163 | return off + s - src; | ||
| 164 | } | ||
| 165 | |||
| 166 | static int scan_ip6_net(const char *src, ot_net *net) { | ||
| 167 | const char *s = src; | ||
| 168 | uint64_t value; | ||
| 169 | int off; | ||
| 170 | while (isspace(*s)) | ||
| 171 | ++s; | ||
| 172 | if (!(off = ip_scan(s, net->address))) | ||
| 173 | return 0; | ||
| 174 | s += off; | ||
| 175 | if (*s != '/') | ||
| 176 | net->bits = 128; | ||
| 177 | else { | ||
| 178 | s++; | ||
| 179 | if (!(off = scan_udec(s, &value, 128))) | ||
| 180 | return 0; | ||
| 181 | net->bits = (int)value; | ||
| 182 | if (ip_is_v4mapped(net->address)) | ||
| 183 | net->bits += 96; | ||
| 184 | if (net->bits > 128) | ||
| 185 | return 0; | ||
| 186 | s += off; | ||
| 187 | } | ||
| 188 | return off + s - src; | ||
| 189 | } | ||
| 190 | |||
| 191 | typedef enum { | ||
| 192 | CONFIG_UNKNOWN, | ||
| 193 | CONFIG_TRACKER_ROOTDIR, | ||
| 194 | CONFIG_TRACKER_USER, | ||
| 195 | CONFIG_LISTEN_TCP_UDP, | ||
| 196 | CONFIG_LISTEN_TCP, | ||
| 197 | CONFIG_LISTEN_UDP, | ||
| 198 | CONFIG_LISTEN_UDP_WORKERS, | ||
| 199 | CONFIG_LISTEN_SHARED, | ||
| 200 | CONFIG_ACCESS_WHITELIST, | ||
| 201 | CONFIG_ACCESS_BLACKLIST, | ||
| 202 | CONFIG_ACCESS_FIFO_ADD, | ||
| 203 | CONFIG_ACCESS_FIFO_DELETE, | ||
| 204 | CONFIG_ACCESS_STATS, | ||
| 205 | CONFIG_ACCESS_STATS_PATH, | ||
| 206 | CONFIG_ACCESS_PROXY, | ||
| 207 | CONFIG_TRACKER_REDIRECT_URL, | ||
| 208 | CONFIG_LIVESYNC_CLUSTER_NODE_IP, | ||
| 209 | CONFIG_LIVESYNC_CLUSTER_LISTEN, | ||
| 210 | } config_kind; | ||
| 211 | |||
| 212 | typedef struct { | ||
| 213 | const char *name; | ||
| 214 | size_t len; | ||
| 215 | config_kind kind; | ||
| 216 | } config_key; | ||
| 217 | |||
| 218 | #define CONFIG_KEY(s, k) { s, sizeof(s) - 1, k } | ||
| 219 | |||
| 220 | static const config_key config_keys[] = { | ||
| 221 | CONFIG_KEY("tracker.rootdir", CONFIG_TRACKER_ROOTDIR), | ||
| 222 | CONFIG_KEY("tracker.user", CONFIG_TRACKER_USER), | ||
| 223 | CONFIG_KEY("listen.tcp_udp", CONFIG_LISTEN_TCP_UDP), | ||
| 224 | CONFIG_KEY("listen.tcp", CONFIG_LISTEN_TCP), | ||
| 225 | CONFIG_KEY("listen.udp", CONFIG_LISTEN_UDP), | ||
| 226 | CONFIG_KEY("listen.udp.workers", CONFIG_LISTEN_UDP_WORKERS), | ||
| 227 | CONFIG_KEY("listen.shared", CONFIG_LISTEN_SHARED), | ||
| 228 | #ifdef WANT_ACCESSLIST_WHITE | ||
| 229 | CONFIG_KEY("access.whitelist", CONFIG_ACCESS_WHITELIST), | ||
| 230 | #elif defined(WANT_ACCESSLIST_BLACK) | ||
| 231 | CONFIG_KEY("access.blacklist", CONFIG_ACCESS_BLACKLIST), | ||
| 232 | #endif | ||
| 233 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 234 | CONFIG_KEY("access.fifo_add", CONFIG_ACCESS_FIFO_ADD), | ||
| 235 | CONFIG_KEY("access.fifo_delete", CONFIG_ACCESS_FIFO_DELETE), | ||
| 236 | #endif | ||
| 237 | #ifdef WANT_RESTRICT_STATS | ||
| 238 | CONFIG_KEY("access.stats", CONFIG_ACCESS_STATS), | ||
| 239 | #endif | ||
| 240 | CONFIG_KEY("access.stats_path", CONFIG_ACCESS_STATS_PATH), | ||
| 241 | #ifdef WANT_IP_FROM_PROXY | ||
| 242 | CONFIG_KEY("access.proxy", CONFIG_ACCESS_PROXY), | ||
| 243 | #endif | ||
| 244 | CONFIG_KEY("tracker.redirect_url", CONFIG_TRACKER_REDIRECT_URL), | ||
| 245 | #ifdef WANT_SYNC_LIVE | ||
| 246 | CONFIG_KEY("livesync.cluster.node_ip", CONFIG_LIVESYNC_CLUSTER_NODE_IP), | ||
| 247 | CONFIG_KEY("livesync.cluster.listen", CONFIG_LIVESYNC_CLUSTER_LISTEN), | ||
| 248 | #endif | ||
| 249 | }; | ||
| 250 | static const size_t config_entry_count = sizeof(config_keys) / sizeof(config_keys[0]); | ||
| 251 | |||
| 252 | static config_kind parse_config_key(char **p) { | ||
| 253 | size_t i; | ||
| 254 | for (i = 0; i < config_entry_count; ++i) { | ||
| 255 | const config_key *key = &config_keys[i]; | ||
| 256 | if (strncmp(*p, key->name, key->len) == 0 && isspace((unsigned char)(*p)[key->len])) { | ||
| 257 | *p += key->len + 1; | ||
| 258 | /* Skip white spaces */ | ||
| 259 | while (isspace((unsigned char)**p)) | ||
| 260 | ++(*p); | ||
| 261 | return key->kind; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | return CONFIG_UNKNOWN; | ||
| 265 | } | ||
| 266 | |||
| 267 | int parse_configfile(char *config_filename) { | ||
| 268 | FILE *config_file_handle; | ||
| 269 | char inbuf[512]; | ||
| 270 | ot_ip6 tmpip; | ||
| 271 | #if defined(WANT_RESTRICT_STATS) || defined(WANT_IP_FROM_PROXY) || defined(WANT_SYNC_LIVE) | ||
| 272 | ot_net tmpnet; | ||
| 273 | #endif | ||
| 274 | int bound = 0; | ||
| 275 | |||
| 276 | config_file_handle = fopen(config_filename, "r"); | ||
| 277 | |||
| 278 | if (config_file_handle == NULL) { | ||
| 279 | fprintf(stderr, "Warning: Can't open config file: %s.", config_filename); | ||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | |||
| 283 | while (fgets(inbuf, sizeof(inbuf), config_file_handle)) { | ||
| 284 | char *p = inbuf, *end; | ||
| 285 | uint64_t value; | ||
| 286 | uint16_t tmpport = 6969; | ||
| 287 | config_kind kind; | ||
| 288 | |||
| 289 | /* Skip white spaces */ | ||
| 290 | while (isspace((unsigned char)*p)) | ||
| 291 | ++p; | ||
| 292 | |||
| 293 | /* Ignore comments and empty lines */ | ||
| 294 | if ((*p == '#') || (*p == '\n') || (*p == 0)) | ||
| 295 | continue; | ||
| 296 | |||
| 297 | /* consume trailing new lines and spaces */ | ||
| 298 | end = p + strlen(p); | ||
| 299 | while (end > p && isspace((unsigned char)end[-1])) | ||
| 300 | *--end = 0; | ||
| 301 | |||
| 302 | /* Scan for commands */ | ||
| 303 | kind = parse_config_key(&p); | ||
| 304 | switch (kind) { | ||
| 305 | case CONFIG_TRACKER_ROOTDIR: | ||
| 306 | set_config_option(&g_serverdir, p); | ||
| 307 | break; | ||
| 308 | case CONFIG_TRACKER_USER: | ||
| 309 | set_config_option(&g_serveruser, p); | ||
| 310 | break; | ||
| 311 | case CONFIG_LISTEN_TCP_UDP: | ||
| 312 | case CONFIG_LISTEN_TCP: | ||
| 313 | case CONFIG_LISTEN_UDP: { | ||
| 314 | int shared = OT_LISTEN_INHERIT; | ||
| 315 | int workers = OT_LISTEN_INHERIT; | ||
| 316 | int off = scan_ip6_port(p, tmpip, &tmpport); | ||
| 317 | |||
| 318 | if (!off || !listen_scan_options(p + off, &shared, &workers)) | ||
| 319 | goto parse_error; | ||
| 320 | if (kind != CONFIG_LISTEN_UDP) | ||
| 321 | listen_append(tmpip, tmpport, FLAG_TCP, shared, OT_LISTEN_INHERIT), ++bound; | ||
| 322 | if (kind != CONFIG_LISTEN_TCP) | ||
| 323 | listen_append(tmpip, tmpport, FLAG_UDP, shared, workers), ++bound; | ||
| 324 | break; | ||
| 325 | } | ||
| 326 | case CONFIG_LISTEN_UDP_WORKERS: | ||
| 327 | if (!scan_udec(p, &value, UINT_MAX)) | ||
| 328 | goto parse_error; | ||
| 329 | g_listen_workers = (int)value; | ||
| 330 | break; | ||
| 331 | case CONFIG_LISTEN_SHARED: | ||
| 332 | if (!scan_udec(p, &value, 1)) | ||
| 333 | goto parse_error; | ||
| 334 | g_listen_shared = (int)value; | ||
| 335 | break; | ||
| 336 | #ifdef WANT_ACCESSLIST_WHITE | ||
| 337 | case CONFIG_ACCESS_WHITELIST: | ||
| 338 | set_config_option(&g_accesslist_filename, p); | ||
| 339 | break; | ||
| 340 | #elif defined(WANT_ACCESSLIST_BLACK) | ||
| 341 | case CONFIG_ACCESS_BLACKLIST: | ||
| 342 | set_config_option(&g_accesslist_filename, p); | ||
| 343 | break; | ||
| 344 | #endif | ||
| 345 | #ifdef WANT_DYNAMIC_ACCESSLIST | ||
| 346 | case CONFIG_ACCESS_FIFO_ADD: | ||
| 347 | set_config_option(&g_accesslist_pipe_add, p); | ||
| 348 | break; | ||
| 349 | case CONFIG_ACCESS_FIFO_DELETE: | ||
| 350 | set_config_option(&g_accesslist_pipe_delete, p); | ||
| 351 | break; | ||
| 352 | #endif | ||
| 353 | #ifdef WANT_RESTRICT_STATS | ||
| 354 | case CONFIG_ACCESS_STATS: | ||
| 355 | if (!scan_ip6_net(p, &tmpnet)) | ||
| 356 | goto parse_error; | ||
| 357 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_STAT); | ||
| 358 | break; | ||
| 359 | #endif | ||
| 360 | case CONFIG_ACCESS_STATS_PATH: | ||
| 361 | set_config_option(&g_stats_path, p); | ||
| 362 | break; | ||
| 363 | #ifdef WANT_IP_FROM_PROXY | ||
| 364 | case CONFIG_ACCESS_PROXY: | ||
| 365 | if (!scan_ip6_net(p, &tmpnet)) | ||
| 366 | goto parse_error; | ||
| 367 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_PROXY); | ||
| 368 | break; | ||
| 369 | #endif | ||
| 370 | case CONFIG_TRACKER_REDIRECT_URL: | ||
| 371 | set_config_option(&g_redirecturl, p); | ||
| 372 | break; | ||
| 373 | #ifdef WANT_SYNC_LIVE | ||
| 374 | case CONFIG_LIVESYNC_CLUSTER_NODE_IP: | ||
| 375 | if (!scan_ip6_net(p, &tmpnet)) | ||
| 376 | goto parse_error; | ||
| 377 | accesslist_bless_net(&tmpnet, OT_PERMISSION_MAY_LIVESYNC); | ||
| 378 | break; | ||
| 379 | case CONFIG_LIVESYNC_CLUSTER_LISTEN: | ||
| 380 | tmpport = LIVESYNC_PORT; | ||
| 381 | { | ||
| 382 | int shared = OT_LISTEN_INHERIT, dummy = OT_LISTEN_INHERIT; | ||
| 383 | int off = scan_ip6_port(p, tmpip, &tmpport); | ||
| 384 | |||
| 385 | if (!off || !listen_scan_options(p + off, &shared, &dummy)) | ||
| 386 | goto parse_error; | ||
| 387 | listen_append(tmpip, tmpport, FLAG_MCA, shared, OT_LISTEN_INHERIT); | ||
| 388 | } | ||
| 389 | break; | ||
| 390 | #endif | ||
| 391 | default: | ||
| 392 | fprintf(stderr, "Unhandled line in config file: %s\n", inbuf); | ||
| 393 | break; | ||
| 394 | parse_error: | ||
| 395 | fprintf(stderr, "Parse error in config file: %s\n", inbuf); | ||
| 396 | } | ||
| 397 | } | ||
| 398 | fclose(config_file_handle); | ||
| 399 | return bound; | ||
| 400 | } | ||
| 401 | |||
| 402 | static void usage(char *name) { | ||
| 403 | fprintf(stderr, | ||
| 404 | "Usage: %s [-i ip] [-p port] [-P port] [-r redirect] [-d dir] [-u user] [-A ip[/bits]] [-f config] [-s livesyncport]" | ||
| 405 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 406 | " [-b blacklistfile]" | ||
| 407 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 408 | " [-w whitelistfile]" | ||
| 409 | #endif | ||
| 410 | "\n", | ||
| 411 | name); | ||
| 412 | } | ||
| 413 | |||
| 414 | #define HELPLINE(opt, desc) fprintf(stderr, "\t%-10s%s\n", opt, desc) | ||
| 415 | static void help(char *name) { | ||
| 416 | usage(name); | ||
| 417 | |||
| 418 | HELPLINE("-f config", "include and execute the config file"); | ||
| 419 | HELPLINE("-i ip", "specify ip to bind to with next -[pP] (default: any, overrides preceeding ones)"); | ||
| 420 | HELPLINE("-p port", "do bind to tcp port (default: 6969, you may specify more than one)"); | ||
| 421 | HELPLINE("-P port", "do bind to udp port (default: 6969, you may specify more than one)"); | ||
| 422 | HELPLINE("-r redirecturl", "specify url where / should be redirected to (default none)"); | ||
| 423 | HELPLINE("-d dir", "specify directory to try to chroot to (default: \".\")"); | ||
| 424 | HELPLINE("-u user", "specify user under whose privileges opentracker should run (default: \"nobody\")"); | ||
| 425 | HELPLINE("-A ip[/bits]", "bless an ip address or net as admin address (e.g. to allow syncs from this address)"); | ||
| 426 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 427 | HELPLINE("-b file", "specify blacklist file."); | ||
| 428 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 429 | HELPLINE("-w file", "specify whitelist file."); | ||
| 430 | #endif | ||
| 431 | |||
| 432 | 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"); | ||
| 433 | fprintf(stderr, " Here -i 127.0.0.1 selects the ip address for the next -p 6969 and -P 6969.\n"); | ||
| 434 | fprintf(stderr, " If no port is bound from config file or command line, the last address given\n"); | ||
| 435 | fprintf(stderr, " (or ::1 if none is set) will be used on port 6969.\n"); | ||
| 436 | } | ||
| 437 | #undef HELPLINE | ||
| 438 | |||
| 439 | |||
| 440 | static void listen_resolve_defaults(void) { | ||
| 441 | size_t i; | ||
| 442 | |||
| 443 | for (i = 0; i < g_listen_count; ++i) { | ||
| 444 | ot_listen *entry = g_listen + i; | ||
| 445 | |||
| 446 | if (entry->shared == OT_LISTEN_INHERIT) | ||
| 447 | entry->shared = g_listen_shared; | ||
| 448 | if (entry->workers == OT_LISTEN_INHERIT) | ||
| 449 | entry->workers = g_listen_workers; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | ot_listen *config_listen_entries(size_t *count) { | ||
| 454 | *count = g_listen_count; | ||
| 455 | return g_listen; | ||
| 456 | } | ||
| 457 | |||
| 458 | void config_parse(int argc, char **argv) { | ||
| 459 | ot_ip6 serverip; | ||
| 460 | ot_net tmpnet; | ||
| 461 | int bound = 0, scanon = 1; | ||
| 462 | uint64_t value; | ||
| 463 | |||
| 464 | memset(serverip, 0, sizeof(ot_ip6)); | ||
| 465 | #ifdef WANT_V4_ONLY | ||
| 466 | serverip[10] = serverip[11] = -1; | ||
| 467 | #endif | ||
| 468 | |||
| 469 | while (scanon) { | ||
| 470 | switch (getopt(argc, argv, | ||
| 471 | ":i:p:A:P:d:u:r:s:f:l:v" | ||
| 472 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 473 | "b:" | ||
| 474 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 475 | "w:" | ||
| 476 | #endif | ||
| 477 | "h")) { | ||
| 478 | case -1: | ||
| 479 | scanon = 0; | ||
| 480 | break; | ||
| 481 | case 'i': | ||
| 482 | if (!ip_scan(optarg, serverip)) { | ||
| 483 | usage(argv[0]); | ||
| 484 | exit(1); | ||
| 485 | } | ||
| 486 | break; | ||
| 487 | #ifdef WANT_ACCESSLIST_BLACK | ||
| 488 | case 'b': | ||
| 489 | set_config_option(&g_accesslist_filename, optarg); | ||
| 490 | break; | ||
| 491 | #elif defined(WANT_ACCESSLIST_WHITE) | ||
| 492 | case 'w': | ||
| 493 | set_config_option(&g_accesslist_filename, optarg); | ||
| 494 | break; | ||
| 495 | #endif | ||
| 496 | case 'p': | ||
| 497 | if (!scan_udec(optarg, &value, 65535)) { | ||
| 498 | usage(argv[0]); | ||
| 499 | exit(1); | ||
| 500 | } | ||
| 501 | listen_append(serverip, (uint16_t)value, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 502 | bound++; | ||
| 503 | break; | ||
| 504 | case 'P': | ||
| 505 | if (!scan_udec(optarg, &value, 65535)) { | ||
| 506 | usage(argv[0]); | ||
| 507 | exit(1); | ||
| 508 | } | ||
| 509 | listen_append(serverip, (uint16_t)value, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 510 | bound++; | ||
| 511 | break; | ||
| 512 | #ifdef WANT_SYNC_LIVE | ||
| 513 | case 's': | ||
| 514 | if (!scan_udec(optarg, &value, 65535)) { | ||
| 515 | usage(argv[0]); | ||
| 516 | exit(1); | ||
| 517 | } | ||
| 518 | listen_append(serverip, (uint16_t)value, FLAG_MCA, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 519 | break; | ||
| 520 | #endif | ||
| 521 | case 'd': | ||
| 522 | set_config_option(&g_serverdir, optarg); | ||
| 523 | break; | ||
| 524 | case 'u': | ||
| 525 | set_config_option(&g_serveruser, optarg); | ||
| 526 | break; | ||
| 527 | case 'r': | ||
| 528 | set_config_option(&g_redirecturl, optarg); | ||
| 529 | break; | ||
| 530 | case 'l': | ||
| 531 | g_statefile = optarg; | ||
| 532 | break; | ||
| 533 | case 'A': | ||
| 534 | if (!scan_ip6_net(optarg, &tmpnet)) { | ||
| 535 | usage(argv[0]); | ||
| 536 | exit(1); | ||
| 537 | } | ||
| 538 | accesslist_bless_net(&tmpnet, 0xffff); /* Allow everything for now */ | ||
| 539 | break; | ||
| 540 | case 'f': | ||
| 541 | bound += parse_configfile(optarg); | ||
| 542 | break; | ||
| 543 | case 'h': | ||
| 544 | help(argv[0]); | ||
| 545 | exit(0); | ||
| 546 | case 'v': { | ||
| 547 | char buffer[8192]; | ||
| 548 | stats_return_tracker_version(buffer); | ||
| 549 | fputs(buffer, stderr); | ||
| 550 | exit(0); | ||
| 551 | } | ||
| 552 | default: | ||
| 553 | case '?': | ||
| 554 | usage(argv[0]); | ||
| 555 | exit(1); | ||
| 556 | } | ||
| 557 | } | ||
| 558 | |||
| 559 | if (!bound) { | ||
| 560 | listen_append(serverip, 6969, FLAG_TCP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 561 | listen_append(serverip, 6969, FLAG_UDP, OT_LISTEN_INHERIT, OT_LISTEN_INHERIT); | ||
| 562 | } | ||
| 563 | |||
| 564 | listen_resolve_defaults(); | ||
| 565 | } | ||
diff --git a/ot_config.h b/ot_config.h new file mode 100644 index 0000000..ab25e2c --- /dev/null +++ b/ot_config.h | |||
| @@ -0,0 +1,35 @@ | |||
| 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 | #ifndef OT_CONFIG_H__ | ||
| 7 | #define OT_CONFIG_H__ | ||
| 8 | |||
| 9 | #include <stdint.h> | ||
| 10 | |||
| 11 | #include "trackerlogic.h" | ||
| 12 | |||
| 13 | /* An address opentracker was asked to listen on. Options the statement did | ||
| 14 | not name have been filled in from the file wide defaults by the time these | ||
| 15 | are handed out. sock is filled in by whoever binds them. */ | ||
| 16 | typedef struct { | ||
| 17 | ot_ip6 ip; | ||
| 18 | uint16_t port; | ||
| 19 | PROTO_FLAG proto; | ||
| 20 | int shared; | ||
| 21 | int workers; | ||
| 22 | int64_t sock; | ||
| 23 | } ot_listen; | ||
| 24 | |||
| 25 | extern char *g_redirecturl; | ||
| 26 | extern char *g_serverdir; | ||
| 27 | extern char *g_serveruser; | ||
| 28 | extern char *g_statefile; | ||
| 29 | |||
| 30 | /* Reads the command line and every config file it names. Exits on error. */ | ||
| 31 | void config_parse(int argc, char **argv); | ||
| 32 | |||
| 33 | ot_listen *config_listen_entries(size_t *count); | ||
| 34 | |||
| 35 | #endif | ||
