47static void signal_handler(
int sig) {
55int main(
int argc,
char **argv) {
61 char **acds_argv =
SAFE_MALLOC((
size_t)(argc + 2) *
sizeof(
char *),
char **);
65 acds_argv[0] = argv[0];
66 acds_argv[1] =
"acds";
67 for (
int i = 1; i < argc; i++) {
68 acds_argv[i + 1] = argv[i];
70 acds_argv[argc + 1] = NULL;
81 if (opts && opts->
help) {
87 printf(
"ascii-chat-acds version %s (%s, %s)\n", ASCII_CHAT_VERSION_FULL, ASCII_CHAT_BUILD_TYPE,
88 ASCII_CHAT_BUILD_DATE);
101 rcu_register_thread();
102 log_debug(
"Main thread registered with RCU library");
104 log_info(
"ASCII-Chat Discovery Service (acds) starting...");
105 log_info(
"Version: %s (%s, %s)", ASCII_CHAT_VERSION_FULL, ASCII_CHAT_BUILD_TYPE, ASCII_CHAT_BUILD_DATE);
110 log_error(
"Failed to initialize session string generator");
122 log_info(
"Identity key not found, generating new key...");
126 log_error(
"Failed to generate identity key");
140 char fingerprint[65];
142 log_info(
"Discovery server identity: SHA256:%s", fingerprint);
143 printf(
"🔑 Server fingerprint: SHA256:%s\n", fingerprint);
148 const char *address = opts && opts->
address[0] !=
'\0' ? opts->
address :
"127.0.0.1";
149 const char *address6 = opts && opts->
address6[0] !=
'\0' ? opts->
address6 :
"::1";
162 log_info(
"Security: Requiring signed identity from servers creating sessions");
165 log_info(
"Security: Requiring signed identity from clients joining sessions");
171 const char *stun_servers_str =
GET_OPTION(stun_servers);
172 if (stun_servers_str && stun_servers_str[0] !=
'\0') {
174 SAFE_STRNCPY(stun_copy, stun_servers_str,
sizeof(stun_copy));
176 char *saveptr = NULL;
177 char *token = strtok_r(stun_copy,
",", &saveptr);
180 while (*token ==
' ' || *token ==
'\t')
182 size_t len = strlen(token);
183 while (len > 0 && (token[len - 1] ==
' ' || token[len - 1] ==
'\t')) {
187 if (len > 0 && len <
sizeof(config.
stun_servers[0].host)) {
191 log_info(
"Added STUN server: %s", token);
193 }
else if (len > 0) {
194 log_warn(
"STUN server URL too long (max 63 chars): %s", token);
197 token = strtok_r(NULL,
",", &saveptr);
204 const char *turn_servers_str =
GET_OPTION(turn_servers);
205 const char *turn_username_str =
GET_OPTION(turn_username);
206 const char *turn_credential_str =
GET_OPTION(turn_credential);
208 if (turn_servers_str && turn_servers_str[0] !=
'\0') {
210 SAFE_STRNCPY(turn_copy, turn_servers_str,
sizeof(turn_copy));
212 char *saveptr = NULL;
213 char *token = strtok_r(turn_copy,
",", &saveptr);
216 while (*token ==
' ' || *token ==
'\t')
218 size_t len = strlen(token);
219 while (len > 0 && (token[len - 1] ==
' ' || token[len - 1] ==
'\t')) {
223 if (len > 0 && len <
sizeof(config.
turn_servers[0].url)) {
229 if (turn_username_str && turn_username_str[0] !=
'\0') {
230 size_t username_len = strlen(turn_username_str);
231 if (username_len <
sizeof(config.
turn_servers[0].username)) {
239 if (turn_credential_str && turn_credential_str[0] !=
'\0') {
240 size_t credential_len = strlen(turn_credential_str);
241 if (credential_len <
sizeof(config.
turn_servers[0].credential)) {
248 log_info(
"Added TURN server: %s (username: %s)", token,
249 turn_username_str && turn_username_str[0] ? turn_username_str :
"<none>");
251 }
else if (len > 0) {
252 log_warn(
"TURN server URL too long (max 63 chars): %s", token);
255 token = strtok_r(NULL,
",", &saveptr);
260 const char *turn_secret_str =
GET_OPTION(turn_secret);
261 if (turn_secret_str && turn_secret_str[0] !=
'\0') {
263 log_info(
"TURN dynamic credential generation enabled");
270 memset(&server, 0,
sizeof(server));
275 log_error(
"Server initialization failed");
294 char public_addr[22];
296 printf(
"🌐 Public endpoint: %s (direct TCP)\n", public_addr);
297 log_info(
"UPnP: Port mapping successful, public endpoint: %s", public_addr);
300 log_info(
"UPnP: Port mapping unavailable or failed - will use WebRTC fallback");
301 printf(
"📡 Clients behind strict NATs will use WebRTC fallback\n");
305 log_info(
"UPnP: Disabled via --no-upnp option");
307 log_info(
"UPnP: Disabled via environment variable or configuration");
309 printf(
"📡 WebRTC will be used for all clients\n");
314 log_debug(
"Initializing mDNS for ACDS LAN service discovery...");
317 LOG_ERRNO_IF_SET(
"Failed to initialize mDNS (non-fatal, LAN discovery disabled)");
318 log_warn(
"mDNS disabled for ACDS - LAN discovery of discovery service will not be available");
322 char hostname[256] = {0};
323 gethostname(hostname,
sizeof(hostname) - 1);
326 .
name =
"ASCII-Chat-Discovery-Service",
327 .type =
"_ascii-chat-discovery-service._tcp",
337 log_warn(
"mDNS advertising failed for ACDS - LAN discovery disabled");
341 printf(
"🌐 mDNS: ACDS advertised as '_ascii-chat-discovery-service._tcp.local' on LAN\n");
342 log_info(
"mDNS: ACDS advertised as '_ascii-chat-discovery-service._tcp.local' (port=%d)", config.
port);
347 signal(SIGINT, signal_handler);
348 signal(SIGTERM, signal_handler);
351 log_info(
"Discovery server listening on port %d", config.
port);
352 printf(
"🌐 Listening on port %d\n", config.
port);
354 printf(
"Press Ctrl+C to stop\n\n");
362 log_info(
"Shutting down discovery server...");
381 log_debug(
"Unregistering main thread from RCU library");
382 rcu_unregister_thread();
384 log_info(
"Discovery server stopped");
🔌 Cross-platform abstraction layer umbrella header for ascii-chat
int main(int argc, char **argv)
🔍 ASCII-Chat Discovery Service (acds) main entry point
#define LOG_ERRNO_IF_SET(message)
Check if any error occurred and log it if so.
#define SAFE_STRNCPY(dst, src, size)
asciichat_error_t asciichat_shared_init(const char *default_log_filename, bool is_client)
Initialize common subsystems shared by client and server.
#define SAFE_MALLOC(size, cast)
asciichat_error_t
Error and exit codes - unified status values (0-255)
#define log_warn(...)
Log a WARN message.
#define log_error(...)
Log an ERROR message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
#define log_file(...)
File-only logging - writes to log file only, no stderr output.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
char opt_acds_key_path[OPTIONS_BUFF_SIZE]
Ed25519 identity key path (ACDS mode only)
int opt_acds_port
TCP listen port (ACDS mode only)
const options_t * options_get(void)
Get current options (lock-free read)
#define OPTIONS_BUFF_SIZE
Buffer size for option string values.
asciichat_error_t options_init(int argc, char **argv)
Initialize options by parsing command-line arguments.
void usage(FILE *desc, asciichat_mode_t mode)
Print usage information for client, server, or mirror mode.
char opt_acds_database_path[OPTIONS_BUFF_SIZE]
SQLite database path (ACDS mode only)
@ MODE_ACDS
Discovery service mode - session management and WebRTC signaling.
asciichat_error_t acds_identity_load(const char *path, uint8_t public_key[32], uint8_t secret_key[64])
Load identity from file.
asciichat_error_t acds_identity_save(const char *path, const uint8_t public_key[32], const uint8_t secret_key[64])
Save identity to file.
void acds_identity_fingerprint(const uint8_t public_key[32], char fingerprint[65])
Compute SHA256 fingerprint of public key.
asciichat_error_t acds_identity_generate(uint8_t public_key[32], uint8_t secret_key[64])
Generate new Ed25519 keypair.
Identity key management for discovery server.
Platform initialization and static synchronization helpers.
📝 Logging API with multiple log levels and terminal output control
void asciichat_mdns_shutdown(asciichat_mdns_t *mdns)
Shutdown mDNS context and cleanup.
asciichat_mdns_t * asciichat_mdns_init(void)
Initialize mDNS context.
asciichat_error_t asciichat_mdns_advertise(asciichat_mdns_t *mdns, const asciichat_mdns_service_t *service)
Advertise a service on the local network.
ACDS (Discovery Service) mode option parsing.
⚙️ Command-line options parsing and configuration management for ascii-chat
📂 Path Manipulation Utilities
void acds_server_shutdown(acds_server_t *server)
Shutdown discovery server.
asciichat_error_t acds_server_init(acds_server_t *server, const acds_config_t *config)
Initialize discovery server.
asciichat_error_t acds_server_run(acds_server_t *server)
Run discovery server main loop.
🌐 Discovery server TCP connection manager
asciichat_error_t acds_string_init(void)
Initialize random number generator for string generation.
Session string generation for discovery service.
Discovery server configuration.
char address[256]
IPv4 bind address (empty = all interfaces)
turn_server_t turn_servers[4]
TURN server configurations.
char database_path[512]
SQLite database path.
bool require_server_identity
Require servers to provide signed identity when creating sessions.
uint8_t turn_count
Number of configured TURN servers (0-4)
int port
TCP listen port (default 27225)
char turn_secret[256]
Shared secret for TURN credential generation (HMAC-SHA1)
uint8_t stun_count
Number of configured STUN servers (0-4)
char key_path[512]
Ed25519 identity key file path.
char log_file[512]
Log file path (empty = stderr)
char address6[256]
IPv6 bind address (empty = all interfaces)
bool require_client_identity
Require clients to provide signed identity when joining sessions.
stun_server_t stun_servers[4]
STUN server configurations.
log_level_t log_level
Logging verbosity level.
tcp_server_t tcp_server
TCP server abstraction.
Service information for advertisement.
Internal mDNS context structure.
Consolidated options structure.
bool version
Show version information.
char address6[256]
IPv6 bind address (server only)
bool help
Show help message.
char log_file[256]
Log file path.
char address[256]
Server address (client) or bind address (server)
atomic_bool running
Server running flag (set false to shutdown)
void nat_upnp_close(nat_upnp_context_t **ctx)
Close port mapping and clean up.
asciichat_error_t nat_upnp_open(uint16_t internal_port, const char *description, nat_upnp_context_t **ctx)
Discover and open port via UPnP.
asciichat_error_t nat_upnp_get_address(const nat_upnp_context_t *ctx, char *addr, size_t addr_len)
Get the public address (IP:port) for advertising to clients.
UPnP/NAT-PMP port mapping for direct TCP connectivity.