#include "cli_parse.h" #include "server_kcp_hub.h" #include "server_udp_relay.h" static void kcpserver_usage(FILE *out) { fprintf(out, "usage: kcpserver [-mode hub|relay] [-listen addr] [-bind-device dev]\n"); fprintf(out, " [-latency-log path] [-kcp-ts-debug-log path]\n"); fprintf(out, " [-kcp-session-stats-log path] [-kcp-session-stats-interval 100ms]\n"); fprintf(out, " [-telemetry-peer peer-id] [-telemetry-interval 500ms]\n"); fprintf(out, " [-relay-remote addr] [-relay-listen addr] [-relay-peer addr]\n"); } int main(int argc, char **argv) { const char *mode = "hub"; const char *listen_addr = ":9002"; const char *bind_device = ""; const char *latency_log_path = ""; const char *packet_log_path = ""; const char *stats_log_path = ""; const char *stats_interval_raw = ""; const char *telemetry_peer_id = ""; const char *telemetry_interval_raw = ""; const char *relay_listen_alias = ""; const char *relay_remote_addr = ""; const char *relay_peer_alias = ""; int stats_interval_ms = KCP_DEFAULT_STATS_INTERVAL_MS; int telemetry_interval_ms = 500; int i; int rc = 1; latency_logger_t *latency_logger = NULL; kcp_packet_debug_logger_t *packet_logger = NULL; kcp_session_stats_logger_t *stats_logger = NULL; kcp_listener_t *listener = NULL; kcp_hub_t *hub = NULL; udp_relay_t *relay = NULL; for (i = 1; i < argc; ++i) { const char *value = NULL; int handled; if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-mode", &value)) < 0) { fprintf(stderr, "kcpserver: flag -mode requires a value\n"); return 1; } else if (handled) { mode = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-listen", &value)) < 0) { fprintf(stderr, "kcpserver: flag -listen requires a value\n"); return 1; } else if (handled) { listen_addr = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-bind-device", &value)) < 0) { fprintf(stderr, "kcpserver: flag -bind-device requires a value\n"); return 1; } else if (handled) { bind_device = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-latency-log", &value)) < 0) { fprintf(stderr, "kcpserver: flag -latency-log requires a value\n"); return 1; } else if (handled) { latency_log_path = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-kcp-ts-debug-log", &value)) < 0) { fprintf(stderr, "kcpserver: flag -kcp-ts-debug-log requires a value\n"); return 1; } else if (handled) { packet_log_path = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-kcp-session-stats-log", &value)) < 0) { fprintf(stderr, "kcpserver: flag -kcp-session-stats-log requires a value\n"); return 1; } else if (handled) { stats_log_path = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-kcp-session-stats-interval", &value)) < 0) { fprintf(stderr, "kcpserver: flag -kcp-session-stats-interval requires a value\n"); return 1; } else if (handled) { stats_interval_raw = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-telemetry-peer", &value)) < 0) { fprintf(stderr, "kcpserver: flag -telemetry-peer requires a value\n"); return 1; } else if (handled) { telemetry_peer_id = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-telemetry-interval", &value)) < 0) { fprintf(stderr, "kcpserver: flag -telemetry-interval requires a value\n"); return 1; } else if (handled) { telemetry_interval_raw = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-relay-listen", &value)) < 0) { fprintf(stderr, "kcpserver: flag -relay-listen requires a value\n"); return 1; } else if (handled) { relay_listen_alias = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-relay-remote", &value)) < 0) { fprintf(stderr, "kcpserver: flag -relay-remote requires a value\n"); return 1; } else if (handled) { relay_remote_addr = value; continue; } if ((handled = cli_parse_value_flag(argc, argv, &i, argv[i], "-relay-peer", &value)) < 0) { fprintf(stderr, "kcpserver: flag -relay-peer requires a value\n"); return 1; } else if (handled) { relay_peer_alias = value; continue; } if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { kcpserver_usage(stdout); return 0; } fprintf(stderr, "kcpserver: unknown argument %s\n", argv[i]); kcpserver_usage(stderr); return 1; } if (kcp_session_stats_parse_interval_ms(stats_interval_raw, &stats_interval_ms) != 0) { fprintf(stderr, "kcpserver: invalid -kcp-session-stats-interval value %s\n", stats_interval_raw); return 1; } if (omni_parse_duration_ms(telemetry_interval_raw, 500, &telemetry_interval_ms) != 0) { fprintf(stderr, "kcpserver: invalid -telemetry-interval value %s\n", telemetry_interval_raw); return 1; } if (relay_peer_alias[0] != '\0' && relay_remote_addr[0] != '\0' && strcmp(relay_peer_alias, relay_remote_addr) != 0) { fprintf(stderr, "kcpserver: flags -relay-remote and -relay-peer must match when both are set\n"); return 1; } if (relay_remote_addr[0] == '\0' && relay_peer_alias[0] != '\0') { relay_remote_addr = relay_peer_alias; } if (relay_peer_alias[0] != '\0') { fprintf(stderr, "warning: flag -relay-peer is deprecated; use -relay-remote instead\n"); } if (relay_listen_alias[0] != '\0') { if (strcmp(mode, "relay") != 0) { fprintf(stderr, "kcpserver: flag -relay-listen may only be used in relay mode\n"); return 1; } if (listen_addr[0] != '\0' && strcmp(listen_addr, ":9002") != 0 && strcmp(listen_addr, relay_listen_alias) != 0) { fprintf(stderr, "kcpserver: flags -listen and -relay-listen must match when both are set in relay mode\n"); return 1; } listen_addr = relay_listen_alias; fprintf(stderr, "warning: flag -relay-listen is deprecated; use -listen with -mode=relay instead\n"); } if (strcmp(mode, "hub") == 0) { if (relay_remote_addr[0] != '\0') { fprintf(stderr, "kcpserver: flag -relay-remote may only be used in relay mode\n"); return 1; } if (latency_log_path[0] != '\0') { latency_logger = latencylog_open_jsonl(latency_log_path); if (latency_logger == NULL) { fprintf(stderr, "kcpserver: open latency logger %s failed\n", latency_log_path); goto cleanup; } } if (packet_log_path[0] != '\0') { packet_logger = kcp_packet_debug_open_jsonl(packet_log_path); if (packet_logger == NULL) { fprintf(stderr, "kcpserver: open packet debug logger %s failed\n", packet_log_path); goto cleanup; } } if (stats_log_path[0] != '\0') { stats_logger = kcp_session_stats_open_jsonl(stats_log_path); if (stats_logger == NULL) { fprintf(stderr, "kcpserver: open session stats logger %s failed\n", stats_log_path); goto cleanup; } } listener = kcp_listener_listen(listen_addr, bind_device, packet_logger, OMNI_NODE_ROLE_SERVER, "hub"); if (listener == NULL) { fprintf(stderr, "kcpserver: listen on %s failed\n", listen_addr); goto cleanup; } hub = kcp_hub_new(latency_logger, stats_logger, stats_interval_ms); if (hub == NULL) { fprintf(stderr, "kcpserver: create hub failed\n"); goto cleanup; } if (telemetry_peer_id[0] != '\0' && kcp_hub_set_telemetry(hub, telemetry_peer_id, telemetry_interval_ms) != 0) { fprintf(stderr, "kcpserver: configure telemetry peer %s failed\n", telemetry_peer_id); goto cleanup; } fprintf(stderr, "kcp hub listening on %s\n", listen_addr); if (kcp_hub_serve_listener(hub, listener) != 0) { fprintf(stderr, "kcpserver: serve listener failed\n"); goto cleanup; } rc = 0; goto cleanup; } if (strcmp(mode, "relay") == 0) { if (telemetry_peer_id[0] != '\0') { fprintf(stderr, "kcpserver: flag -telemetry-peer may only be used in hub mode\n"); return 1; } if (bind_device[0] != '\0') { fprintf(stderr, "kcpserver: flag -bind-device is not supported in relay mode\n"); return 1; } if (relay_remote_addr[0] == '\0') { fprintf(stderr, "kcpserver: flag -relay-remote is required in relay mode\n"); return 1; } relay = udp_relay_open(listen_addr, relay_remote_addr); if (relay == NULL) { fprintf(stderr, "kcpserver: open udp relay %s -> %s failed\n", listen_addr, relay_remote_addr); goto cleanup; } fprintf(stderr, "udp relay listening on %s and forwarding to %s\n", listen_addr, relay_remote_addr); if (udp_relay_serve(relay) != 0) { fprintf(stderr, "kcpserver: udp relay stopped with error\n"); goto cleanup; } rc = 0; goto cleanup; } fprintf(stderr, "kcpserver: unsupported -mode=%s; want hub or relay\n", mode); cleanup: udp_relay_free(relay); kcp_hub_free(hub); kcp_listener_free(listener); kcp_session_stats_close(stats_logger); kcp_packet_debug_close(packet_logger); latencylog_close(latency_logger); return rc; }