#define _POSIX_C_SOURCE 200809L #include "cess_compat.h" #include "cesspool.h" #include #include #include #include #include #include #include #include #include #ifndef EKEYREJECTED #define EKEYREJECTED EACCES #endif #ifdef _WIN32 #include #endif #ifdef _WIN32 #include #include #else #include #endif #define BOOTSTRAP_PATH_BYTES 1400u static volatile sig_atomic_t bootstrap_running = 1; static void stop_bootstrap(int signal_number) { (void)signal_number; bootstrap_running = 0; } static void usage(FILE *out) { fputs("cesspool-bootstrap - dedicated Cesspool public rendezvous node\n" "usage: cesspool-bootstrap [--bind IPv4] [--port UDP-PORT]\n" " [--advertise HOST:PORT] [--state-dir DIR]\n" " cesspool-bootstrap --help\n" "Runs in the foreground for a service manager. It handles peer discovery;\n" "chat and archive payloads remain peer-to-peer.\n", out); } static int parse_port(const char *text, uint16_t *port) { char *end; unsigned long value; if (!text || !*text || !port) { errno = EINVAL; return -1; } value = strtoul(text, &end, 10); if (*end || value == 0 || value > 65535) { errno = EINVAL; return -1; } *port = (uint16_t)value; return 0; } static int make_one_state_directory(const char *path, int secure_existing) { struct stat info; int created = 0; if (!path || !*path) { errno = EINVAL; return -1; } if (mkdir(path, 0700) == 0) created = 1; else if (errno != EEXIST) return -1; if (stat(path, &info) != 0) return -1; if (!S_ISDIR(info.st_mode)) { errno = ENOTDIR; return -1; } if ((created || secure_existing) && chmod(path, S_IRWXU) != 0) return -1; return 0; } static int make_state_directory(const char *path) { char copy[BOOTSTRAP_PATH_BYTES]; size_t length, start = 0, i; if (!path || !*path) { errno = EINVAL; return -1; } length = strlen(path); if (length >= sizeof(copy)) { errno = ENAMETOOLONG; return -1; } memcpy(copy, path, length + 1); #ifdef _WIN32 if (length >= 3 && copy[1] == ':') start = 3; #else if (copy[0] == '/') start = 1; #endif for (i = start; i < length; i++) { if (copy[i] != '/' && copy[i] != '\\') continue; if (i == 0 || (i == 2 && copy[1] == ':')) continue; copy[i] = '\0'; if (make_one_state_directory(copy, 0) != 0) return -1; copy[i] = path[i]; } return make_one_state_directory(copy, 1); } static int default_state_directory(char *out, size_t capacity) { const char *base; int n; if (!out || capacity == 0) { errno = EINVAL; return -1; } #ifdef _WIN32 base = getenv("LOCALAPPDATA"); if (!base || !*base) { errno = ENOENT; return -1; } n = snprintf(out, capacity, "%s/dat-gpt/cesspool-bootstrap-state", base); #else base = getenv("XDG_STATE_HOME"); if (base && *base) n = snprintf(out, capacity, "%s/dat-gpt/cesspool-bootstrap", base); else { base = getenv("HOME"); if (!base || !*base) { errno = ENOENT; return -1; } n = snprintf(out, capacity, "%s/.local/state/dat-gpt/cesspool-bootstrap", base); } #endif if (n < 0 || (size_t)n >= capacity) { errno = ENAMETOOLONG; return -1; } return 0; } static int load_or_create_identity(const char *state_dir, cesspool_keypair_t *identity) { char key_path[BOOTSTRAP_PATH_BYTES]; uint8_t derived[32]; int n; if (!state_dir || !identity) { errno = EINVAL; return -1; } n = snprintf(key_path, sizeof(key_path), "%s/%s", state_dir, "identity.key"); if (n < 0 || (size_t)n >= sizeof(key_path)) { errno = ENAMETOOLONG; return -1; } if (cesspool_identity_init()) return -1; if (cesspool_identity_load(key_path, identity) == 0) { if (crypto_sign_ed25519_sk_to_pk(derived, identity->seckey) != 0 || sodium_memcmp(derived, identity->pubkey, sizeof(derived)) != 0) { sodium_memzero(identity, sizeof(*identity)); errno = EKEYREJECTED; return -1; } if (chmod(key_path, S_IRUSR | S_IWUSR) != 0) return -1; return 0; } if (errno != ENOENT || cesspool_identity_generate(identity) || cesspool_identity_save(key_path, identity) != 0 || chmod(key_path, S_IRUSR | S_IWUSR) != 0) { int saved = errno; sodium_memzero(identity, sizeof(*identity)); errno = saved; return -1; } return 0; } static uint64_t monotonic_milliseconds(void) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) return 0; return (uint64_t)now.tv_sec * 1000u + (uint64_t)now.tv_nsec / 1000000u; } int main(int argc, char **argv) { cesspool_t pool; cesspool_keypair_t identity; const char *bind_host = "0.0.0.0"; const char *state_dir = NULL; const char *advertise = NULL; uint16_t port = CESSNET_PORT; uint64_t next_status = 0; char default_state[BOOTSTRAP_PATH_BYTES]; int i, result = 1; memset(&pool, 0, sizeof(pool)); pool.net.sock = CESS_INVALID_SOCKET; memset(&identity, 0, sizeof(identity)); for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { usage(stdout); return 0; } if ((!strcmp(argv[i], "--bind") || !strcmp(argv[i], "--port") || !strcmp(argv[i], "--advertise") || !strcmp(argv[i], "--state-dir")) && i + 1 >= argc) { fprintf(stderr, "cesspool-bootstrap: %s needs a value\n", argv[i]); return 2; } if (!strcmp(argv[i], "--bind")) bind_host = argv[++i]; else if (!strcmp(argv[i], "--port")) { if (parse_port(argv[++i], &port)) { perror("cesspool-bootstrap --port"); return 2; } } else if (!strcmp(argv[i], "--advertise")) advertise = argv[++i]; else if (!strcmp(argv[i], "--state-dir")) state_dir = argv[++i]; else { fprintf(stderr, "cesspool-bootstrap: unknown option %s\n", argv[i]); usage(stderr); return 2; } } if (!state_dir) { if (default_state_directory(default_state, sizeof(default_state)) != 0) { perror("cesspool-bootstrap default state directory"); return 1; } state_dir = default_state; } if (!bind_host[0] || strchr(bind_host, ':')) { fputs("cesspool-bootstrap: --bind requires an IPv4 host or 0.0.0.0\n", stderr); return 2; } (void)umask(077); if (make_state_directory(state_dir) || load_or_create_identity(state_dir, &identity) || cesspool_init(&pool, identity.pubkey) || cesspool_listen(&pool, bind_host, port) || cesspool_set_bootstrap_server(&pool, 1)) { perror("cesspool-bootstrap startup"); goto done; } signal(SIGINT, stop_bootstrap); signal(SIGTERM, stop_bootstrap); printf("Cesspool public bootstrap ready\n" " bind: %s:%u/udp\n" " state: persistent private identity loaded\n" " peer discovery only; peer payloads are not relayed\n", bind_host, pool.port); if (advertise && *advertise) printf(" advertised endpoint: %s/udp\n" " client setup: dat bootstrap set %s\n" " client setup: cable bootstrap set %s\n" " GUI setup: Settings > Cesspool Node > %s\n", advertise, advertise, advertise, advertise); else printf(" advertised endpoint: not set; configure a public host:port in clients\n"); printf(" Windows firewall (run elevated): netsh advfirewall firewall add rule name=\"Cesspool UDP %u\" dir=in action=allow protocol=UDP localport=%u\n", pool.port, pool.port); puts(" route the same UDP port through the edge router or cloud firewall"); fflush(stdout); while (bootstrap_running) { int polled = cesspool_poll(&pool, 1000); uint64_t now = monotonic_milliseconds(); if (polled < 0 && errno != EINTR) { perror("cesspool-bootstrap poll"); goto done; } if (cesspool_tick(&pool, now) != 0) { perror("cesspool-bootstrap maintenance"); goto done; } if (now >= next_status) { fprintf(stderr, "cesspool-bootstrap: peers=%lu topics=%lu\n", (unsigned long)cesspool_announced_peer_count(&pool), (unsigned long)cesspool_announced_topic_count(&pool)); next_status = now + 60000u; } } result = 0; done: if (pool.net.sock != CESS_INVALID_SOCKET) (void)cesspool_destroy(&pool); sodium_memzero(&identity, sizeof(identity)); return result; }