static int _tcp_bind(uv_tcp_t *handle, struct sockaddr *addr, uv_connection_cb connection) { unsigned flags = 0; if (addr->sa_family == AF_INET6) { flags |= UV_TCP_IPV6ONLY; } int ret = uv_tcp_bind(handle, addr, flags); if (ret != 0) { return ret; } /* TCP_DEFER_ACCEPT delays accepting connections until there is readable data. */ #ifdef TCP_DEFER_ACCEPT if (set_tcp_option((uv_handle_t *)handle, TCP_DEFER_ACCEPT, KR_CONN_RTT_MAX/1000) != 0) { kr_log_info("[ io ] tcp_bind (defer_accept): %s\n", strerror(errno)); } #endif ret = uv_listen((uv_stream_t *)handle, 16, connection); if (ret != 0) { return ret; } return tcp_bind_finalize((uv_handle_t *)handle); }
int main(int argc, char **argv) { int forks = 1; array_t(char*) addr_set; array_init(addr_set); char *keyfile = NULL; const char *config = NULL; char *keyfile_buf = NULL; /* Long options. */ int c = 0, li = 0, ret = 0; struct option opts[] = { {"addr", required_argument, 0, 'a'}, {"config", required_argument, 0, 'c'}, {"keyfile",required_argument, 0, 'k'}, {"forks",required_argument, 0, 'f'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; while ((c = getopt_long(argc, argv, "a:c:f:k:vVh", opts, &li)) != -1) { switch (c) { case 'a': array_push(addr_set, optarg); break; case 'c': config = optarg; break; case 'f': g_interactive = 0; forks = atoi(optarg); if (forks == 0) { kr_log_error("[system] error '-f' requires number, not '%s'\n", optarg); return EXIT_FAILURE; } #if (!defined(UV_VERSION_HEX)) || (!defined(SO_REUSEPORT)) if (forks > 1) { kr_log_error("[system] libuv 1.7+ is required for SO_REUSEPORT support, multiple forks not supported\n"); return EXIT_FAILURE; } #endif break; case 'k': keyfile_buf = malloc(PATH_MAX); assert(keyfile_buf); /* Check if the path is absolute */ if (optarg[0] == '/') { keyfile = strdup(optarg); } else { /* Construct absolute path, the file may not exist */ keyfile = realpath(".", keyfile_buf); if (keyfile) { int len = strlen(keyfile); int namelen = strlen(optarg); if (len + namelen < PATH_MAX - 1) { keyfile[len] = '/'; memcpy(keyfile + len + 1, optarg, namelen + 1); keyfile = strdup(keyfile); /* Duplicate */ } else { keyfile = NULL; /* Invalidate */ } } } free(keyfile_buf); if (!keyfile) { kr_log_error("[system] keyfile '%s': not writeable\n", optarg); return EXIT_FAILURE; } break; case 'v': kr_debug_set(true); break; case 'V': kr_log_info("%s, version %s\n", "Knot DNS Resolver", PACKAGE_VERSION); return EXIT_SUCCESS; case 'h': case '?': help(argc, argv); return EXIT_SUCCESS; default: help(argc, argv); return EXIT_FAILURE; } } /* Switch to rundir. */ if (optind < argc) { const char *rundir = argv[optind]; if (access(rundir, W_OK) != 0) { kr_log_error("[system] rundir '%s': %s\n", rundir, strerror(errno)); return EXIT_FAILURE; } ret = chdir(rundir); if (ret != 0) { kr_log_error("[system] rundir '%s': %s\n", rundir, strerror(errno)); return EXIT_FAILURE; } if(config && access(config, R_OK) != 0) { kr_log_error("[system] rundir '%s'\n", rundir); kr_log_error("[system] config '%s': %s\n", config, strerror(errno)); return EXIT_FAILURE; } } kr_crypto_init(); /* Fork subprocesses if requested */ int fork_count = forks; while (--forks > 0) { int pid = fork(); if (pid < 0) { perror("[system] fork"); return EXIT_FAILURE; } /* Forked process */ if (pid == 0) { kr_crypto_reinit(); break; } } /* Block signals. */ uv_loop_t *loop = uv_default_loop(); uv_signal_t sigint, sigterm; uv_signal_init(loop, &sigint); uv_signal_init(loop, &sigterm); uv_signal_start(&sigint, signal_handler, SIGINT); uv_signal_start(&sigterm, signal_handler, SIGTERM); /* Create a server engine. */ knot_mm_t pool = { .ctx = mp_new (4096), .alloc = (knot_mm_alloc_t) mp_alloc }; struct engine engine; ret = engine_init(&engine, &pool); if (ret != 0) { kr_log_error("[system] failed to initialize engine: %s\n", kr_strerror(ret)); return EXIT_FAILURE; } /* Create worker */ struct worker_ctx *worker = init_worker(loop, &engine, &pool, forks, fork_count); if (!worker) { kr_log_error("[system] not enough memory\n"); return EXIT_FAILURE; } /* Bind to sockets and run */ for (size_t i = 0; i < addr_set.len; ++i) { int port = 53; const char *addr = set_addr(addr_set.at[i], &port); ret = network_listen(&engine.net, addr, (uint16_t)port, NET_UDP|NET_TCP); if (ret != 0) { kr_log_error("[system] bind to '%s#%d' %s\n", addr, port, knot_strerror(ret)); ret = EXIT_FAILURE; } } /* Start the scripting engine */ if (ret == 0) { ret = engine_start(&engine, config ? config : "config"); if (ret == 0) { if (keyfile) { auto_free char *cmd = afmt("trust_anchors.file = '%s'", keyfile); if (!cmd) { kr_log_error("[system] not enough memory\n"); return EXIT_FAILURE; } engine_cmd(&engine, cmd); lua_settop(engine.L, 0); } /* Run the event loop */ ret = run_worker(loop, &engine); } } /* Cleanup. */ array_clear(addr_set); engine_deinit(&engine); worker_reclaim(worker); mp_delete(pool.ctx); if (ret != 0) { ret = EXIT_FAILURE; } kr_crypto_cleanup(); return ret; }