ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
session.h File Reference

Go to the source code of this file.

Data Structures

struct  host_liveness_t
 When code needs to work with host liveness, it keeps last_ping_sent_ms, last_pong_received_ms, consecutive_failures, max_failures here instead of scattering it across globals. Initialization establishes the handles and sizes; teardown must release any allocation, lock, thread, or callback context recorded in those fields. More...
struct  discovery_session_t
 Discovery session context. More...
struct  discovery_config_t
 Configuration for discovery session. More...

Macros

#define DISCOVERY_SESSION_ACDS_ADDRESS_BUFFER_SIZE   64
#define DISCOVERY_SESSION_ACDS_URL_BUFFER_SIZE   512

Typedefs

typedef bool(* discovery_should_exit_fn) (void *user_data)
 Callback to check if session should exit.

Enumerations

enum  discovery_state_t {
  DISCOVERY_STATE_INIT , DISCOVERY_STATE_CONNECTING_ACDS , DISCOVERY_STATE_CREATING_SESSION , DISCOVERY_STATE_JOINING_SESSION ,
  DISCOVERY_STATE_WAITING_PEER , DISCOVERY_STATE_NEGOTIATING , DISCOVERY_STATE_STARTING_HOST , DISCOVERY_STATE_CONNECTING_HOST ,
  DISCOVERY_STATE_ACTIVE , DISCOVERY_STATE_MIGRATING , DISCOVERY_STATE_FAILED , DISCOVERY_STATE_ENDED
}
 Discovery session state. More...

Functions

discovery_session_t * discovery_session_create (const discovery_config_t *config)
 Configuration for discovery session.
asciichat_error_t discovery_session_destroy (discovery_session_t *session)
 Destroy discovery session and free resources.
asciichat_error_t discovery_session_start (discovery_session_t *session)
 Start the discovery session.
asciichat_error_t discovery_session_process (discovery_session_t *session, int64_t timeout_ns)
 Process session events (call in main loop).
void discovery_session_stop (discovery_session_t *session)
 Stop the discovery session.
discovery_state_t discovery_session_get_state (const discovery_session_t *session)
 Get current session state.
bool discovery_session_is_active (const discovery_session_t *session)
 Check if session is active (call in progress).
const char * discovery_session_get_string (const discovery_session_t *session)
 Get session string.
bool discovery_session_is_host (const discovery_session_t *session)
 Check if we are the host.
session_host_t * discovery_session_get_host (discovery_session_t *session)
 Get host context (if we are host).
session_participant_t * discovery_session_get_participant (discovery_session_t *session)
 Get participant context (if we are participant).
asciichat_error_t discovery_session_check_host_alive (discovery_session_t *session)
 Detect host disconnect (check connection status).
void discovery_session_host_pong (discovery_session_t *session)
asciichat_error_t discovery_session_handle_host_disconnect (discovery_session_t *session, uint32_t disconnect_reason)
 Report host disconnect and await the ACDS recovery decision.
asciichat_error_t discovery_session_apply_handoff (discovery_session_t *session)
 Replace the old role after a committed handoff and media-worker shutdown. Releases borrowed-role resources, resets liveness, and initializes the committed successor connection. Callers must stop media workers before entering this function.

Macro Definition Documentation

◆ DISCOVERY_SESSION_ACDS_ADDRESS_BUFFER_SIZE

#define DISCOVERY_SESSION_ACDS_ADDRESS_BUFFER_SIZE   64

Definition at line 4 of file src/discovery/session.h.

◆ DISCOVERY_SESSION_ACDS_URL_BUFFER_SIZE

#define DISCOVERY_SESSION_ACDS_URL_BUFFER_SIZE   512

Definition at line 5 of file src/discovery/session.h.

Typedef Documentation

◆ discovery_should_exit_fn

typedef bool(* discovery_should_exit_fn) (void *user_data)

Callback to check if session should exit.

Called periodically during blocking operations (e.g., ACDS connect, join). Should return true to gracefully cancel the current operation.

Parameters
user_dataOpaque pointer provided by caller
Returns
true to cancel operation, false to continue

Definition at line 76 of file src/discovery/session.h.

Enumeration Type Documentation

◆ discovery_state_t

Discovery session state.

Enumerator
DISCOVERY_STATE_INIT 

Initial state.

DISCOVERY_STATE_CONNECTING_ACDS 

Connecting to ACDS.

DISCOVERY_STATE_CREATING_SESSION 

Creating new session.

DISCOVERY_STATE_JOINING_SESSION 

Joining existing session.

DISCOVERY_STATE_WAITING_PEER 

Waiting for peer to join (initiator).

DISCOVERY_STATE_NEGOTIATING 

NAT negotiation in progress.

DISCOVERY_STATE_STARTING_HOST 

Starting as host.

DISCOVERY_STATE_CONNECTING_HOST 

Connecting to host as participant.

DISCOVERY_STATE_ACTIVE 

Session active (call in progress).

DISCOVERY_STATE_MIGRATING 

Host migration in progress.

DISCOVERY_STATE_FAILED 

Session failed.

DISCOVERY_STATE_ENDED 

Session ended.

Definition at line 81 of file src/discovery/session.h.

81 {
discovery_state_t
Discovery session state.
@ DISCOVERY_STATE_ENDED
Session ended.
@ DISCOVERY_STATE_JOINING_SESSION
Joining existing session.
@ DISCOVERY_STATE_INIT
Initial state.
@ DISCOVERY_STATE_ACTIVE
Session active (call in progress).
@ DISCOVERY_STATE_MIGRATING
Host migration in progress.
@ DISCOVERY_STATE_NEGOTIATING
NAT negotiation in progress.
@ DISCOVERY_STATE_CONNECTING_HOST
Connecting to host as participant.
@ DISCOVERY_STATE_CREATING_SESSION
Creating new session.
@ DISCOVERY_STATE_CONNECTING_ACDS
Connecting to ACDS.
@ DISCOVERY_STATE_WAITING_PEER
Waiting for peer to join (initiator).
@ DISCOVERY_STATE_FAILED
Session failed.
@ DISCOVERY_STATE_STARTING_HOST
Starting as host.

Function Documentation

◆ discovery_session_apply_handoff()

asciichat_error_t discovery_session_apply_handoff ( discovery_session_t * session)

Replace the old role after a committed handoff and media-worker shutdown. Releases borrowed-role resources, resets liveness, and initializes the committed successor connection. Callers must stop media workers before entering this function.

Apply a committed role change after media workers have stopped.

Returns
ASCIICHAT_OK if no handoff is pending or initialization succeeds.

Definition at line 1172 of file src/discovery/session.c.

1172 {
1173 if (!session || !session->handoff_pending)
1174 return ASCIICHAT_OK;
1175 session->handoff_pending = false;
1176 if (session->participant_ctx) {
1178 session->participant_ctx = NULL;
1179 }
1180 if (session->peer_manager) {
1182 session->peer_manager = NULL;
1183 }
1184 session->is_host = !memcmp(session->host_id, session->participant_id, SESSION_ID_SIZE);
1185 if (!session->is_host && session->host_ctx) {
1187 session->host_ctx = NULL;
1188 }
1189 session->webrtc_transport_ready = false;
1190 session->webrtc_connection_initiated = false;
1191 session->webrtc_retry_attempt = 0;
1192 session->webrtc_disconnected_since_ms = 0;
1193 session->liveness.ping_in_flight = false;
1194 session->liveness.consecutive_failures = 0;
1195 session->liveness.last_ping_sent_ms = session_get_current_time_ms();
1196 atomic_store_u64(&session->liveness.last_pong_received_ms, 0);
1197 if (session->session_type == SESSION_TYPE_WEBRTC) {
1198 asciichat_error_t result = initialize_webrtc_peer_manager(session);
1199 if (result != ASCIICHAT_OK)
1200 return result;
1201 }
1202 ascii_otel_metric(ASCII_OTEL_METRIC_HOST_CHANGES_APPLIED, 1);
1203 session->host_loss_detected_ms = 0;
1204
1206 return ASCIICHAT_OK;
1207}
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
#define SESSION_ID_SIZE
@ SESSION_TYPE_WEBRTC
WebRTC P2P mesh with STUN/TURN relay.
Definition acds.h:151
asciichat_error_t session_host_destroy(session_host_t *host)
Destroy session host and free resources.
Definition host.c:289
void session_participant_destroy(session_participant_t *p)
Destroy session participant and free resources.
void webrtc_peer_manager_destroy(webrtc_peer_manager_t *manager)
Destroy peer manager and close all connections.
void ascii_otel_metric(ascii_otel_metric_t metric, double value)
Definition otel.c:161
uint64_t webrtc_disconnected_since_ms
Start of a transient peer disconnection.
uint8_t session_type
0 = DIRECT_TCP, 1 = WEBRTC
uint8_t participant_id[SESSION_ID_SIZE]
int webrtc_retry_attempt
Current retry attempt number (0 = initial, 1+ = retries).
bool webrtc_transport_ready
True when DataChannel is open and transport created.
bool webrtc_connection_initiated
True when we've called webrtc_peer_manager_connect().
session_participant_t * participant_ctx
uint64_t host_loss_detected_ms
Local deadline origin while awaiting ACDS recovery.
struct webrtc_peer_manager * peer_manager
The useful part of discovery session is in state, error, waiting_peer_start_ns, is_initiator: togethe...
uint8_t host_id[SESSION_ID_SIZE]
uint32_t consecutive_failures
Number of consecutive ping failures.
atomic_t last_pong_received_ms
Timestamp of last pong received (monotonic).
uint64_t last_ping_sent_ms
Timestamp of last ping sent (monotonic).
bool ping_in_flight
True if waiting for pong.

References ascii_otel_metric(), ASCIICHAT_OK, host_liveness_t::consecutive_failures, DISCOVERY_STATE_CONNECTING_HOST, DISCOVERY_STATE_STARTING_HOST, discovery_session_t::handoff_pending, discovery_session_t::host_ctx, discovery_session_t::host_id, discovery_session_t::host_loss_detected_ms, discovery_session_t::is_host, host_liveness_t::last_ping_sent_ms, host_liveness_t::last_pong_received_ms, discovery_session_t::liveness, discovery_session_t::participant_ctx, discovery_session_t::participant_id, discovery_session_t::peer_manager, host_liveness_t::ping_in_flight, session_host_destroy(), SESSION_ID_SIZE, session_participant_destroy(), discovery_session_t::session_type, SESSION_TYPE_WEBRTC, discovery_session_t::webrtc_connection_initiated, discovery_session_t::webrtc_disconnected_since_ms, webrtc_peer_manager_destroy(), discovery_session_t::webrtc_retry_attempt, and discovery_session_t::webrtc_transport_ready.

◆ discovery_session_check_host_alive()

asciichat_error_t discovery_session_check_host_alive ( discovery_session_t * session)

Detect host disconnect (check connection status).

Parameters
sessionSession context
Returns
ASCIICHAT_OK if host alive, error if disconnected

Detect host disconnect (check connection status).

Returns
ASCIICHAT_OK for a live host, or a network error after loss is detected.

Definition at line 2210 of file src/discovery/session.c.

2210 {
2211 if (!session || session->is_host) {
2212 // We are the host, so "host" is always alive
2213 return ASCIICHAT_OK;
2214 }
2215
2216 // For WebRTC sessions, check the WebRTC transport instead of participant_ctx
2217 if (session->session_type == SESSION_TYPE_WEBRTC) {
2218 // If we have a WebRTC transport and it's marked as ready, host is alive
2219 if (session->webrtc_transport_ready) {
2220 return ASCIICHAT_OK;
2221 }
2222 // WebRTC transport not ready yet - might still be connecting
2223 return ERROR_NETWORK;
2224 }
2225
2226 // For TCP sessions, check participant context
2227 // If we don't have a participant context yet, we're not connected
2228 if (!session->participant_ctx) {
2229 return ERROR_NETWORK;
2230 }
2231
2232 // Check migration state - if we're already migrating, host is definitely not alive
2233 if (session->state == DISCOVERY_STATE_MIGRATING) {
2234 return ERROR_NETWORK; // Already detected disconnect
2235 }
2236
2237 uint64_t now_ms = session_get_current_time_ms();
2238
2239 if (session->liveness.ping_in_flight &&
2240 atomic_load_u64(&session->liveness.last_pong_received_ms) >= session->liveness.last_ping_sent_ms) {
2241 session->liveness.ping_in_flight = false;
2242 session->liveness.consecutive_failures = 0;
2243 }
2244 // Check if we have a ping in flight that timed out
2245 if (session->liveness.ping_in_flight) {
2246 uint64_t ping_age_ms = now_ms - session->liveness.last_ping_sent_ms;
2247 if (ping_age_ms > session->liveness.timeout_ms) {
2248 // Ping timed out
2249 session->liveness.consecutive_failures++;
2250 session->liveness.ping_in_flight = false;
2251 log_warn("Host ping timeout (attempt %u/%u, age=%llu ms)", session->liveness.consecutive_failures,
2252 session->liveness.max_failures, (unsigned long long)ping_age_ms);
2253
2254 // Check if we've exceeded failure threshold
2255 if (session->liveness.consecutive_failures >= session->liveness.max_failures) {
2256 log_error("Host declared dead after %u consecutive ping failures", session->liveness.consecutive_failures);
2257 return ERROR_NETWORK;
2258 }
2259 }
2260 }
2261
2262 // Check if it's time to send a new ping
2263 uint64_t time_since_last_ping = now_ms - session->liveness.last_ping_sent_ms;
2264 if (!session->liveness.ping_in_flight && time_since_last_ping >= session->liveness.ping_interval_ms) {
2265 // Send ping to host via participant connection
2267 if (!transport || !acip_transport_is_connected(transport))
2268 return SET_ERRNO(ERROR_NETWORK, "Host transport disconnected");
2269 asciichat_error_t result = acip_send_ping(transport);
2270 if (result == ASCIICHAT_OK) {
2271 session->liveness.last_ping_sent_ms = now_ms;
2272 session->liveness.ping_in_flight = true;
2273 log_debug("Sent ping to host (attempt %u/%u)", session->liveness.consecutive_failures + 1,
2274 session->liveness.max_failures);
2275 } else {
2276 log_warn("Failed to send ping to host: %d", result);
2277 session->liveness.consecutive_failures++;
2278 if (session->liveness.consecutive_failures >= session->liveness.max_failures) {
2279 log_error("Host declared dead after %u consecutive send failures", session->liveness.consecutive_failures);
2280 return ERROR_NETWORK;
2281 }
2282 }
2283 }
2284
2285 return ASCIICHAT_OK;
2286}
#define SET_ERRNO(code,...)
unsigned long long uint64_t
Definition common.h:63
@ ERROR_NETWORK
Definition error_codes.h:72
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
acip_transport_t * session_participant_get_transport(session_participant_t *p)
Get the current transport for the participant.
struct acip_transport acip_transport_t
Forward declaration of ACIP transport.
Definition host.h:506
asciichat_error_t acip_send_ping(acip_transport_t *transport)
Send ping packet.
Definition send.c:385
uint32_t max_failures
Threshold for triggering migration (default: 3).
uint64_t ping_interval_ms
Time between pings (default: 3000ms).
uint64_t timeout_ms
Timeout for ping response (default: 10000ms).

References acip_send_ping(), ASCIICHAT_OK, host_liveness_t::consecutive_failures, DISCOVERY_STATE_MIGRATING, ERROR_NETWORK, discovery_session_t::is_host, host_liveness_t::last_ping_sent_ms, host_liveness_t::last_pong_received_ms, discovery_session_t::liveness, log_debug, log_error, log_warn, host_liveness_t::max_failures, discovery_session_t::participant_ctx, host_liveness_t::ping_in_flight, host_liveness_t::ping_interval_ms, session_participant_get_transport(), discovery_session_t::session_type, SESSION_TYPE_WEBRTC, SET_ERRNO, discovery_session_t::state, host_liveness_t::timeout_ms, and discovery_session_t::webrtc_transport_ready.

◆ discovery_session_create()

discovery_session_t * discovery_session_create ( const discovery_config_t * config)

Configuration for discovery session.

Create a new discovery session

Parameters
configSession configuration
Returns
New session or NULL on error

Definition at line 72 of file src/discovery/session.c.

72 {
73 if (!config) {
74 SET_ERRNO(ERROR_INVALID_PARAM, "config is NULL");
75 return NULL;
76 }
77
79 if (!session) {
80 SET_ERRNO(ERROR_MEMORY, "Failed to allocate discovery session");
81 return NULL;
82 }
83
84 session->state = DISCOVERY_STATE_INIT;
85 session->handoff_generation = 1; // Match consensus_handoff_init().
86 session->acds_transport = NULL;
87 session->peer_manager = NULL;
88 session->webrtc_transport_ready = false;
89 session->webrtc_connection_initiated = false;
90 session->webrtc_retry_attempt = 0;
91 session->webrtc_last_attempt_time_ms = 0;
93 session->stun_servers = NULL;
94 session->stun_count = 0;
95 session->turn_servers = NULL;
96 session->turn_count = 0;
97
98 // Initialize host liveness detection
99 session->liveness.last_ping_sent_ms = 0;
100 atomic_store_u64(&session->liveness.last_pong_received_ms, 0);
101 session->liveness.consecutive_failures = 0;
102 session->liveness.max_failures = 3;
103 session->liveness.ping_interval_ms = 3 * MS_PER_SEC_INT; // Ping every 3 seconds
104 session->liveness.timeout_ms = 10 * MS_PER_SEC_INT; // 10 second timeout
105 session->liveness.ping_in_flight = false;
106
107 log_info("After CALLOC - participant_ctx=%p, host_ctx=%p", session->participant_ctx, session->host_ctx);
108
109 // Explicit identity credentials apply to signaling as well as media authentication.
110 const options_t *opts = options_get();
111 bool explicit_identity = opts && opts->encrypt_key[0];
113 asciichat_error_t id_result;
114 if (explicit_identity) {
115 SAFE_STRNCPY(identity_path, opts->encrypt_key, sizeof(identity_path));
116 id_result = ASCIICHAT_OK;
117 } else {
118 id_result = acds_identity_default_path(identity_path, sizeof(identity_path));
119 }
120 if (id_result == ASCIICHAT_OK) {
121 asciichat_errno_scope_t load_scope = {0};
122 bool suppressed = false;
123 if (!explicit_identity) {
124 load_scope = errno_scope_begin();
125 suppressed = errno_notifications_suppress(true);
126 }
127 if (!explicit_identity && platform_access(identity_path, PLATFORM_ACCESS_EXISTS) != 0)
128 id_result = ERROR_CONFIG;
129 else
130 id_result = acds_identity_load(identity_path, session->identity_pubkey, session->identity_seckey);
131 if (!explicit_identity) {
134 }
135 if (id_result != ASCIICHAT_OK && !explicit_identity) {
136 // File doesn't exist or is corrupted - generate new key
137 log_info("Identity key not found, generating new key");
138 id_result = acds_identity_generate(session->identity_pubkey, session->identity_seckey);
139 if (id_result == ASCIICHAT_OK) {
140 // Save for future use
141 acds_identity_save(identity_path, session->identity_pubkey, session->identity_seckey);
142 }
143 }
144 }
145
146 if (id_result != ASCIICHAT_OK && explicit_identity) {
147 SAFE_FREE(session);
148 return NULL;
149 }
150 if (id_result != ASCIICHAT_OK) {
151 NOTICE(DANGER, "DISCOVERY IDENTITY UNAVAILABLE", "Failed to load/generate identity key, using zero key");
152 memset(session->identity_pubkey, 0, sizeof(session->identity_pubkey));
153 memset(session->identity_seckey, 0, sizeof(session->identity_seckey));
154 } else {
155 char fingerprint[HEX_STRING_SIZE_32];
156 acds_identity_fingerprint(session->identity_pubkey, fingerprint);
157 log_info("Using identity key with fingerprint: %.16s...", fingerprint);
158 }
159
160 // Load multiple identity keys from options (multi-key protocol support)
161 if (opts && opts->num_identity_keys > 0) {
162 // Load all identity keys from options
163 for (size_t i = 0; i < opts->num_identity_keys && i < MAX_SESSION_KEYS; i++) {
164 uint8_t pubkey[ED25519_PUBLIC_KEY_SIZE] = {0};
165 uint8_t seckey[ED25519_PRIVATE_KEY_SIZE] = {0};
166 asciichat_error_t key_result = acds_identity_load(opts->identity_keys[i], pubkey, seckey);
167 if (key_result == ASCIICHAT_OK) {
168 memcpy(session->identity_pubkeys[session->num_identity_keys], pubkey, sizeof(pubkey));
169 memcpy(session->identity_seckeys[session->num_identity_keys++], seckey, sizeof(seckey));
170 sodium_memzero(seckey, sizeof(seckey));
171 char fingerprint[HEX_STRING_SIZE_32];
172 acds_identity_fingerprint(pubkey, fingerprint);
173 log_info("Loaded identity key #%zu with fingerprint: %.16s...", i + 1, fingerprint);
174 } else {
175 log_warn("Failed to load identity key #%zu from %s", i + 1, opts->identity_keys[i]);
176 }
177 }
178 if (session->num_identity_keys != opts->num_identity_keys) {
179 sodium_memzero(session->identity_seckey, sizeof(session->identity_seckey));
180 sodium_memzero(session->identity_seckeys, sizeof(session->identity_seckeys));
181 SAFE_FREE(session);
182 SET_ERRNO(ERROR_CRYPTO, "Could not load the complete discovery identity key set");
183 return NULL;
184 }
185 memcpy(session->identity_pubkey, session->identity_pubkeys[0], sizeof(session->identity_pubkey));
186 memcpy(session->identity_seckey, session->identity_seckeys[0], sizeof(session->identity_seckey));
187 log_info("Loaded %zu identity key(s) for multi-key protocol", session->num_identity_keys);
188 } else {
189 // Single-key mode: use only the primary identity key
190 memcpy(session->identity_pubkeys[0], session->identity_pubkey, ED25519_PUBLIC_KEY_SIZE);
191 memcpy(session->identity_seckeys[0], session->identity_seckey, sizeof(session->identity_seckey));
192 session->num_identity_keys = 1;
193 }
194
195 // ACDS connection info
196 if (config->acds_url && config->acds_url[0]) {
197 SAFE_STRNCPY(session->acds_url, config->acds_url, sizeof(session->acds_url));
198 }
199 if (config->acds_address && config->acds_address[0]) {
200 SAFE_STRNCPY(session->acds_address, config->acds_address, sizeof(session->acds_address));
201 } else {
202 SAFE_STRNCPY(session->acds_address, "127.0.0.1", sizeof(session->acds_address));
203 }
204 session->acds_port = config->acds_port > 0 ? config->acds_port : ACIP_DISCOVERY_DEFAULT_PORT;
205
206 // Session string (if joining)
207 if (config->session_string && config->session_string[0]) {
208 SAFE_STRNCPY(session->session_string, config->session_string, sizeof(session->session_string));
209 session->is_initiator = false;
210 } else {
211 session->is_initiator = true;
212 }
213 if (config->password && config->password[0]) {
214 SAFE_STRNCPY(session->password, config->password, sizeof(session->password));
215 }
216
217 // Callbacks
218 session->on_state_change = config->on_state_change;
219 session->on_session_ready = config->on_session_ready;
220 session->on_error = config->on_error;
221 session->callback_user_data = config->callback_user_data;
223 session->exit_callback_data = config->exit_callback_data;
224
225 // Initialize mutex for NAT quality protection
226 if (mutex_init(&session->nat_quality_mutex, "discovery_nat_quality") != 0) {
227 log_warn("Failed to initialize NAT quality mutex");
228 }
229
230 log_debug("Discovery session created (initiator=%d, acds=%s:%u)", session->is_initiator, session->acds_address,
231 session->acds_port);
232
233 NAMED_REGISTER_ATOMIC(&session->liveness.last_pong_received_ms, "discovery.last_pong_received_ms", NULL);
234 return session;
235}
void errno_scope_end(asciichat_errno_scope_t scope, asciichat_errno_outcome_t outcome)
bool errno_notifications_suppress(bool suppress)
asciichat_errno_scope_t errno_scope_begin(void)
@ ASCIICHAT_ERRNO_HANDLED
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
unsigned char uint8_t
Definition common.h:60
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.
@ ERROR_MEMORY
Definition error_codes.h:51
@ ERROR_CONFIG
Definition error_codes.h:52
@ ERROR_CRYPTO
Definition error_codes.h:93
@ ERROR_INVALID_PARAM
#define MAX_SESSION_KEYS
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define ACIP_DISCOVERY_DEFAULT_PORT
Discovery server default port (use OPT_ACDS_PORT_INT_DEFAULT from options.h).
Definition acds.h:1102
#define MS_PER_SEC_INT
Definition time.h:176
struct options_state options_t
Immutable, RCU-published snapshot of the application's runtime configuration.
const options_t * options_get(void)
Get pointer to current options struct (lock-free, thread-safe).
Definition rcu.c:511
asciichat_error_t acds_identity_load(const char *path, uint8_t public_key[32], uint8_t secret_key[64])
Load identity from file.
Definition identity.c:37
void acds_identity_fingerprint(const uint8_t public_key[32], char fingerprint[(32 *2+1)])
Compute SHA256 fingerprint of public key.
Definition identity.c:134
asciichat_error_t acds_identity_default_path(char *path_out, size_t path_size)
Get default identity file path for current platform.
Definition identity.c:155
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.
Definition identity.c:90
asciichat_error_t acds_identity_generate(uint8_t public_key[32], uint8_t secret_key[64])
Generate new Ed25519 keypair.
Definition identity.c:22
#define NOTICE(severity, title,...)
Definition notice.h:68
#define ED25519_PUBLIC_KEY_SIZE
Ed25519 public key size in bytes.
Definition sizes.h:33
#define HEX_STRING_SIZE_32
Hex string size for 32-byte values (64 hex chars + null terminator).
Definition sizes.h:154
#define ED25519_PRIVATE_KEY_SIZE
Ed25519 private key size (seed + public) in bytes.
Definition sizes.h:35
#define DISCOVERY_SESSION_IDENTITY_PATH_BUFFER_SIZE
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...
const char * acds_address
ACDS address (default: "127.0.0.1").
void(* on_error)(asciichat_error_t error, const char *message, void *user_data)
uint16_t acds_port
ACDS port (default: 27225).
const char * password
Session password (optional).
discovery_should_exit_fn should_exit_callback
void(* on_state_change)(discovery_state_t new_state, void *user_data)
const char * session_string
Session string to join (or NULL to create).
const char * acds_url
ACDS WebSocket URL (ws:// or wss://, mutually exclusive with acds_port).
void(* on_session_ready)(const char *session_string, void *user_data)
Discovery session context.
char acds_url[512]
instead of TCP)
uint64_t webrtc_last_attempt_time_ms
Timestamp of last connection attempt (monotonic time).
uint8_t identity_pubkeys[MAX_SESSION_KEYS][ED25519_PUBLIC_KEY_SIZE]
All public keys for multi-key protocol.
uint8_t identity_pubkey[ED25519_PUBLIC_KEY_SIZE]
Ed25519 public key for this participant (primary key).
uint8_t identity_seckey[ED25519_PRIVATE_KEY_SIZE]
Ed25519 secret key for signing (primary key).
char password[ACIP_PASSWORD_SIZE]
Session password used when joining password-protected sessions.
uint8_t identity_seckeys[MAX_SESSION_KEYS][ED25519_PRIVATE_KEY_SIZE]
Matching Ed25519 signing keys.
turn_server_t * turn_servers
TURN servers from ACDS.
size_t stun_count
Number of STUN servers.
void(* on_error)(asciichat_error_t error, const char *message, void *user_data)
size_t num_identity_keys
Number of identity keys loaded.
stun_server_t * stun_servers
STUN servers from ACDS.
void(* on_session_ready)(const char *session_string, void *user_data)
char session_string[SESSION_STRING_BUFFER_SIZE]
discovery_should_exit_fn should_exit_callback
size_t turn_count
Number of TURN servers.
acip_transport_t * acds_transport
Persistent transport for ACDS communication.
void(* on_state_change)(discovery_state_t new_state, void *user_data)
size_t num_identity_keys
Number of identity keys loaded (0 means single-key mode using encrypt_key).
Definition options.h:1191
char identity_keys[32][256]
All identity keys (populated when –key is used multiple times).
Definition options.h:1190
char encrypt_key[256]
SSH/GPG key file path (first –key flag, kept for compatibility).
Definition options.h:1179

References discovery_config_t::acds_address, discovery_session_t::acds_address, acds_identity_default_path(), acds_identity_fingerprint(), acds_identity_generate(), acds_identity_load(), acds_identity_save(), discovery_config_t::acds_port, discovery_session_t::acds_port, discovery_session_t::acds_transport, discovery_config_t::acds_url, discovery_session_t::acds_url, ACIP_DISCOVERY_DEFAULT_PORT, ASCIICHAT_ERRNO_HANDLED, ASCIICHAT_OK, discovery_config_t::callback_user_data, discovery_session_t::callback_user_data, host_liveness_t::consecutive_failures, DISCOVERY_SESSION_IDENTITY_PATH_BUFFER_SIZE, DISCOVERY_STATE_INIT, ED25519_PRIVATE_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, options_state::encrypt_key, errno_notifications_suppress(), errno_scope_begin(), errno_scope_end(), ERROR_CONFIG, ERROR_CRYPTO, ERROR_INVALID_PARAM, ERROR_MEMORY, discovery_config_t::exit_callback_data, discovery_session_t::exit_callback_data, discovery_session_t::handoff_generation, HEX_STRING_SIZE_32, discovery_session_t::host_ctx, options_state::identity_keys, discovery_session_t::identity_pubkey, discovery_session_t::identity_pubkeys, discovery_session_t::identity_seckey, discovery_session_t::identity_seckeys, discovery_session_t::is_initiator, host_liveness_t::last_ping_sent_ms, host_liveness_t::last_pong_received_ms, discovery_session_t::liveness, log_debug, log_info, log_warn, host_liveness_t::max_failures, MAX_SESSION_KEYS, MS_PER_SEC_INT, NAMED_REGISTER_ATOMIC, discovery_session_t::nat_quality_mutex, NOTICE, discovery_session_t::num_identity_keys, options_state::num_identity_keys, discovery_config_t::on_error, discovery_session_t::on_error, discovery_config_t::on_session_ready, discovery_session_t::on_session_ready, discovery_config_t::on_state_change, discovery_session_t::on_state_change, options_get(), discovery_session_t::participant_ctx, discovery_config_t::password, discovery_session_t::password, discovery_session_t::peer_manager, host_liveness_t::ping_in_flight, host_liveness_t::ping_interval_ms, SAFE_CALLOC, SAFE_FREE, SAFE_STRNCPY, discovery_config_t::session_string, discovery_session_t::session_string, SET_ERRNO, discovery_config_t::should_exit_callback, discovery_session_t::should_exit_callback, discovery_session_t::state, discovery_session_t::stun_count, discovery_session_t::stun_servers, host_liveness_t::timeout_ms, discovery_session_t::turn_count, discovery_session_t::turn_servers, discovery_session_t::webrtc_connection_initiated, discovery_session_t::webrtc_disconnected_since_ms, discovery_session_t::webrtc_last_attempt_time_ms, discovery_session_t::webrtc_retry_attempt, and discovery_session_t::webrtc_transport_ready.

Referenced by discovery_main().

◆ discovery_session_destroy()

asciichat_error_t discovery_session_destroy ( discovery_session_t * session)

Destroy discovery session and free resources.

Parameters
sessionSession to destroy

Definition at line 278 of file src/discovery/session.c.

278 {
279 ASCII_OTEL_SCOPE(DISCOVERY_SHUTDOWN);
280 if (!session) {
281 return SET_ERRNO(ERROR_INVALID_PARAM, "session is NULL");
282 }
283
284 asciichat_error_t result = release_participant_transport(session);
285 if (result != ASCIICHAT_OK)
286 return result;
287 // The detector borrows the session and mutex; retain both on a timed join failure.
288 if (session->nat_detection_thread_started) {
289 asciichat_error_t join_result = thread_join_timeout(&session->nat_detection_thread, NULL, 500 * NS_PER_MS_INT);
290 if (join_result != ASCIICHAT_OK)
291 return SET_ERRNO(ERROR_THREAD, "NAT detection worker still owns discovery session");
292 session->nat_detection_thread_started = false;
293 }
294 release_host_control_transport(session);
295
296 // Close ACDS connection
297 if (session->acds_transport) {
299 session->acds_transport = NULL;
300 }
301
302 // Destroy host context if active
303 if (session->host_ctx) {
305 if (result != ASCIICHAT_OK)
306 return result;
307 session->host_ctx = NULL;
308 }
309
310 // Destroy participant context if active
311 if (session->participant_ctx) {
313 session->participant_ctx = NULL;
314 }
315
316 // Clean up WebRTC peer manager (NEW)
317 if (session->peer_manager) {
319 session->peer_manager = NULL;
320 }
321
322 // Clean up STUN/TURN server arrays (NEW)
323 if (session->stun_servers) {
324 SAFE_FREE(session->stun_servers);
325 session->stun_servers = NULL;
326 session->stun_count = 0;
327 }
328
329 if (session->turn_servers) {
330 SAFE_FREE(session->turn_servers);
331 session->turn_servers = NULL;
332 session->turn_count = 0;
333 }
334
335 // Destroy NAT quality mutex
336 mutex_destroy(&session->nat_quality_mutex);
337
339 sodium_memzero(session->password, sizeof(session->password));
340 sodium_memzero(session->identity_seckey, sizeof(session->identity_seckey));
341 sodium_memzero(session->identity_seckeys, sizeof(session->identity_seckeys));
342 SAFE_FREE(session);
343 return ASCIICHAT_OK;
344}
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
@ ERROR_THREAD
#define NS_PER_MS_INT
Definition time.h:168
void network_connection_destroy(network_connection_t **owner)
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72
platform_thread_t nat_detection_thread
network_connection_t * acds_connection

References discovery_session_t::acds_connection, discovery_session_t::acds_transport, ASCII_OTEL_SCOPE, ASCIICHAT_OK, ERROR_INVALID_PARAM, ERROR_THREAD, discovery_session_t::host_ctx, discovery_session_t::identity_seckey, discovery_session_t::identity_seckeys, host_liveness_t::last_pong_received_ms, discovery_session_t::liveness, NAMED_UNREGISTER, discovery_session_t::nat_detection_thread, discovery_session_t::nat_detection_thread_started, discovery_session_t::nat_quality_mutex, network_connection_destroy(), NS_PER_MS_INT, discovery_session_t::participant_ctx, discovery_session_t::password, discovery_session_t::peer_manager, SAFE_FREE, session_host_destroy(), session_participant_destroy(), SET_ERRNO, discovery_session_t::stun_count, discovery_session_t::stun_servers, discovery_session_t::turn_count, discovery_session_t::turn_servers, and webrtc_peer_manager_destroy().

Referenced by discovery_main().

◆ discovery_session_get_host()

session_host_t * discovery_session_get_host ( discovery_session_t * session)

Get host context (if we are host).

Parameters
sessionSession context
Returns
Host context or NULL

Definition at line 2184 of file src/discovery/session.c.

2184 {
2185 if (!session) {
2186 SET_ERRNO(ERROR_INVALID_PARAM, "null session");
2187 return NULL;
2188 }
2189 return session->host_ctx;
2190}

References ERROR_INVALID_PARAM, discovery_session_t::host_ctx, and SET_ERRNO.

◆ discovery_session_get_participant()

session_participant_t * discovery_session_get_participant ( discovery_session_t * session)

Get participant context (if we are participant).

Parameters
sessionSession context
Returns
Participant context or NULL

Definition at line 2192 of file src/discovery/session.c.

2192 {
2193 if (!session) {
2194 SET_ERRNO(ERROR_INVALID_PARAM, "null session");
2195 return NULL;
2196 }
2197 return session->participant_ctx;
2198}

References ERROR_INVALID_PARAM, discovery_session_t::participant_ctx, and SET_ERRNO.

◆ discovery_session_get_state()

discovery_state_t discovery_session_get_state ( const discovery_session_t * session)

Get current session state.

Parameters
sessionSession context
Returns
Current state

Definition at line 2152 of file src/discovery/session.c.

2152 {
2153 if (!session) {
2154 SET_ERRNO(ERROR_INVALID_PARAM, "null session");
2156 }
2157 return session->state;
2158}

References DISCOVERY_STATE_FAILED, ERROR_INVALID_PARAM, SET_ERRNO, and discovery_session_t::state.

◆ discovery_session_get_string()

const char * discovery_session_get_string ( const discovery_session_t * session)

Get session string.

Parameters
sessionSession context
Returns
Session string or NULL if not yet assigned

Definition at line 2168 of file src/discovery/session.c.

2168 {
2169 if (!session || session->session_string[0] == '\0') {
2170 SET_ERRNO(ERROR_INVALID_PARAM, "null session or session string is empty");
2171 return NULL;
2172 }
2173 return session->session_string;
2174}

References ERROR_INVALID_PARAM, discovery_session_t::session_string, and SET_ERRNO.

◆ discovery_session_handle_host_disconnect()

asciichat_error_t discovery_session_handle_host_disconnect ( discovery_session_t * session,
uint32_t disconnect_reason )

Report host disconnect and await the ACDS recovery decision.

Parameters
sessionSession context
disconnect_reasonReason code for disconnect
Returns
ASCIICHAT_OK if the host loss report was sent

Report host disconnect and await the ACDS recovery decision.

Returns
ASCIICHAT_OK if the report was sent; a send error also fails the session.

Definition at line 2294 of file src/discovery/session.c.

2294 {
2295 if (!session) {
2296 SET_ERRNO(ERROR_INVALID_PARAM, "session is NULL");
2297 return ERROR_INVALID_PARAM;
2298 }
2299
2300 log_warn("Host disconnect detected: reason=%u", disconnect_reason);
2301
2302 session->host_loss_detected_ms = session_get_current_time_ms();
2303
2304 // Notify ACDS of host loss (lightweight notification, no NAT data exchanged!)
2305 acip_host_lost_t host_lost = {0};
2306 memcpy(host_lost.session_id, session->session_id, SESSION_ID_SIZE);
2307 memcpy(host_lost.participant_id, session->participant_id, SESSION_ID_SIZE);
2308 memcpy(host_lost.last_host_id, session->host_id, SESSION_ID_SIZE);
2309 host_lost.disconnect_reason = disconnect_reason;
2310 host_lost.disconnect_time_ms = session->host_loss_detected_ms;
2311
2312 asciichat_error_t result =
2313 packet_send_via_transport(session->acds_transport, PACKET_TYPE_ACIP_HOST_LOST, &host_lost, sizeof(host_lost), 0);
2314 if (result != ASCIICHAT_OK) {
2315 set_error(session, result, "Could not report host loss to ACDS");
2316 return result;
2317 }
2318
2319 // A PLAN is advisory. Only ACDS COMMIT authorizes a role change after fresh
2320 // measurement and preparation. Continue dispatching while awaiting that decision.
2321 set_state(session, DISCOVERY_STATE_MIGRATING);
2322
2323 return ASCIICHAT_OK;
2324}
acip_host_lost_t
Definition acds.h:961
@ PACKET_TYPE_ACIP_HOST_LOST
Host lost notification - lightweight (Participant -> ACDS).
Definition packet.h:489
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Definition send.c:190
uint8_t session_id[SESSION_ID_SIZE]

References discovery_session_t::acds_transport, acip_host_lost_t, ASCIICHAT_OK, DISCOVERY_STATE_MIGRATING, ERROR_INVALID_PARAM, discovery_session_t::host_id, discovery_session_t::host_loss_detected_ms, log_warn, packet_send_via_transport(), PACKET_TYPE_ACIP_HOST_LOST, discovery_session_t::participant_id, discovery_session_t::session_id, SESSION_ID_SIZE, and SET_ERRNO.

◆ discovery_session_host_pong()

void discovery_session_host_pong ( discovery_session_t * session)

Record a host reply using the same clock as liveness deadlines.

Definition at line 2200 of file src/discovery/session.c.

2200 {
2201 if (session)
2202 atomic_store_u64(&session->liveness.last_pong_received_ms, session_get_current_time_ms());
2203}

References host_liveness_t::last_pong_received_ms, and discovery_session_t::liveness.

◆ discovery_session_is_active()

bool discovery_session_is_active ( const discovery_session_t * session)

Check if session is active (call in progress).

Parameters
sessionSession context
Returns
True if active

Definition at line 2160 of file src/discovery/session.c.

2160 {
2161 if (!session) {
2162 SET_ERRNO(ERROR_INVALID_PARAM, "null session");
2163 return false;
2164 }
2165 return session->state == DISCOVERY_STATE_ACTIVE;
2166}

References DISCOVERY_STATE_ACTIVE, ERROR_INVALID_PARAM, SET_ERRNO, and discovery_session_t::state.

◆ discovery_session_is_host()

bool discovery_session_is_host ( const discovery_session_t * session)

Check if we are the host.

Parameters
sessionSession context
Returns
True if we are hosting

Definition at line 2176 of file src/discovery/session.c.

2176 {
2177 if (!session) {
2178 SET_ERRNO(ERROR_INVALID_PARAM, "null session");
2179 return false;
2180 }
2181 return session->is_host;
2182}

References ERROR_INVALID_PARAM, discovery_session_t::is_host, and SET_ERRNO.

◆ discovery_session_process()

asciichat_error_t discovery_session_process ( discovery_session_t * session,
int64_t timeout_ns )

Process session events (call in main loop).

Handles incoming ACDS messages, negotiation, and state transitions.

Parameters
sessionSession context
timeout_nsMax time to wait for events in nanoseconds (0 = non-blocking)
Returns
ASCIICHAT_OK on success, error on failure

Definition at line 2065 of file src/discovery/session.c.

2065 {
2066 if (!session) {
2067 return SET_ERRNO(ERROR_INVALID_PARAM, "session is NULL");
2068 }
2069
2070 // Transport callbacks publish readiness; ownership changes and worker joins
2071 // execute here on the session's controlling thread.
2073 if (result != ASCIICHAT_OK)
2074 return result;
2075
2076 result = discovery_session_send_keepalive(session);
2077 if (result != ASCIICHAT_OK) {
2078 return result;
2079 }
2080
2081 if (session->state == DISCOVERY_STATE_ACTIVE || session->state == DISCOVERY_STATE_CONNECTING_HOST ||
2082 session->state == DISCOVERY_STATE_MIGRATING) {
2083 result = discovery_handoff_sample(session);
2084 if (result != ASCIICHAT_OK)
2085 return result;
2086 }
2087
2088 // State handlers preserve deferred failures: recording a failed state does not
2089 // necessarily return an error until the next iteration processes that state.
2090 switch (session->state) {
2092 return discovery_session_wait_for_peer(session, timeout_ns);
2094 return discovery_session_negotiate(session);
2096 return discovery_session_start_host(session);
2098 return session->session_type == SESSION_TYPE_WEBRTC ? discovery_session_connect_webrtc(session)
2099 : discovery_session_connect_tcp(session);
2101 return discovery_session_process_active(session, timeout_ns);
2103 return discovery_session_process_migration(session, timeout_ns);
2105 // Session has failed - return error on further processing
2106 return session->error != ASCIICHAT_OK ? session->error : ERROR_NETWORK;
2107
2109 // Session has ended gracefully - return OK
2110 return ASCIICHAT_OK;
2111
2116 // These states are handled during discovery_session_start(), not in the process loop
2117 // If we reach here, something went wrong - sleep and wait
2118 log_debug("State %d should be handled by discovery_session_start", session->state);
2119 platform_sleep_us(timeout_ns / 1000);
2120 break;
2121
2122 default:
2123 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid session state: %d", session->state);
2124 }
2125
2126 return ASCIICHAT_OK;
2127}
@ ERROR_INVALID_STATE
asciichat_error_t webrtc_peer_manager_dispatch_ready(webrtc_peer_manager_t *manager)

References ASCIICHAT_OK, DISCOVERY_STATE_ACTIVE, DISCOVERY_STATE_CONNECTING_ACDS, DISCOVERY_STATE_CONNECTING_HOST, DISCOVERY_STATE_CREATING_SESSION, DISCOVERY_STATE_ENDED, DISCOVERY_STATE_FAILED, DISCOVERY_STATE_INIT, DISCOVERY_STATE_JOINING_SESSION, DISCOVERY_STATE_MIGRATING, DISCOVERY_STATE_NEGOTIATING, DISCOVERY_STATE_STARTING_HOST, DISCOVERY_STATE_WAITING_PEER, discovery_session_t::error, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_NETWORK, log_debug, discovery_session_t::peer_manager, discovery_session_t::session_type, SESSION_TYPE_WEBRTC, SET_ERRNO, discovery_session_t::state, and webrtc_peer_manager_dispatch_ready().

◆ discovery_session_start()

asciichat_error_t discovery_session_start ( discovery_session_t * session)

Start the discovery session.

Connects to ACDS and either creates or joins a session.

Parameters
sessionSession context
Returns
ASCIICHAT_OK on success

Definition at line 1489 of file src/discovery/session.c.

1489 {
1490 log_info("session=%p", session);
1491
1492 if (!session) {
1493 return SET_ERRNO(ERROR_INVALID_PARAM, "session is NULL");
1494 }
1495
1496 log_info("is_initiator=%d, session_string='%s'", session->is_initiator, session->session_string);
1497
1498 // Check if we should exit before starting blocking operations
1499 if (session->should_exit_callback && session->should_exit_callback(session->exit_callback_data)) {
1500 log_info("Exiting early due to should_exit_callback (before ACDS connect)");
1501 return ASCIICHAT_OK; // Return success to allow clean shutdown
1502 }
1503
1504 log_info("Calling connect_to_acds...");
1505 // Connect to ACDS
1506 asciichat_error_t result = connect_to_acds(session);
1507 if (result != ASCIICHAT_OK) {
1508 log_error("connect_to_acds FAILED %s", asciichat_error_format(NULL, result));
1509 set_state(session, DISCOVERY_STATE_FAILED);
1510 return result;
1511 }
1512 log_info("connect_to_acds succeeded");
1513
1514 // Check again after connection
1515 if (session->should_exit_callback && session->should_exit_callback(session->exit_callback_data)) {
1516 log_info("Exiting early due to should_exit_callback (after ACDS connect)");
1517 return ASCIICHAT_OK; // Return success to allow clean shutdown
1518 }
1519
1521
1522 // Perform crypto handshake with ACDS (same as client-server handshake)
1523 log_info("Performing crypto handshake with ACDS...");
1525
1526 result = crypto_handshake_init("discovery_client", handshake_ctx, false);
1527 if (result != ASCIICHAT_OK) {
1528 log_error("Handshake init failed - %s", asciichat_error_format(NULL, result));
1529 set_error(session, result, "Crypto handshake init failed");
1530 return result;
1531 }
1532
1533 bool has_identity = memcmp(session->identity_pubkey, (uint8_t[32]){0}, ED25519_PUBLIC_KEY_SIZE) != 0;
1534 // Store identity key in handshake context
1535 handshake_ctx->client_public_key.type = has_identity ? KEY_TYPE_ED25519 : KEY_TYPE_UNKNOWN;
1536 handshake_ctx->client_private_key.type = handshake_ctx->client_public_key.type;
1537 memcpy(handshake_ctx->client_public_key.key, session->identity_pubkey, ED25519_PUBLIC_KEY_SIZE);
1538 memcpy(handshake_ctx->client_private_key.key.ed25519, session->identity_seckey, ED25519_PRIVATE_KEY_SIZE);
1539 memcpy(handshake_ctx->client_private_key.public_key, session->identity_pubkey, sizeof(session->identity_pubkey));
1540
1541 crypto_policy_t signaling_policy = {
1542 .transport_secure = strncmp(session->acds_url, "wss://", 6) == 0,
1543 .has_identity = has_identity,
1544 };
1545 uint8_t mode;
1546 result = crypto_policy_resolve(&signaling_policy, &mode);
1547 if (result == ASCIICHAT_OK)
1548 result = network_client_negotiate(session->acds_transport, handshake_ctx, mode, NETWORK_PROTOCOL_DISCOVERY);
1549 if (result != ASCIICHAT_OK) {
1550 set_error(session, result, "Discovery handshake failed");
1551 return result;
1552 }
1553
1554 // Check again after handshake
1555 if (session->should_exit_callback && session->should_exit_callback(session->exit_callback_data)) {
1556 log_info("Exiting early due to should_exit_callback (after handshake)");
1557 return ASCIICHAT_OK; // Return success to allow clean shutdown
1558 }
1559
1560 // Create or join session
1561 if (session->is_initiator) {
1562 log_info("Calling create_session - participant_ctx before=%p", session->participant_ctx);
1563 result = create_session(session);
1564 log_info("create_session returned - participant_ctx after=%p, result=%d", session->participant_ctx, result);
1565
1566 if (result == ASCIICHAT_OK) {
1567 // Initiator also needs to register itself as a participant so it can receive PARTICIPANT_JOINED notifications
1568 // when other peers join. This makes the initiator discoverable to other participants.
1569 log_info("Initiator: now registering self as participant in created session");
1570 result = join_session(session);
1571 log_info("Initiator: join_session result=%d", result);
1572 }
1573 return result;
1574 } else {
1575 log_info("Calling join_session - participant_ctx before=%p", session->participant_ctx);
1576 asciichat_error_t result = join_session(session);
1577 log_info("join_session returned - participant_ctx after=%p, state=%d, result=%d", session->participant_ctx,
1578 session->state, result);
1579 return result;
1580 }
1581}
const char * asciichat_error_format(const char *message, asciichat_error_t code)
asciichat_error_t crypto_handshake_init(const char *name, crypto_handshake_context_t *ctx, bool is_server)
Initialize named crypto handshake context.
uint8_t key[CRYPTO_PUBLIC_KEY_SIZE]
Definition key_types.h:74
uint8_t ed25519[ED25519_PRIVATE_KEY_SIZE]
Definition key_types.h:100
key_type_t type
Definition key_types.h:98
uint8_t public_key[CRYPTO_PUBLIC_KEY_SIZE]
Definition key_types.h:105
key_type_t type
Definition key_types.h:73
union private_key_t::@040247167361101132173265304166142033301213115260 key
@ KEY_TYPE_UNKNOWN
Definition key_types.h:53
@ KEY_TYPE_ED25519
Definition key_types.h:54
#define NS_PER_SEC_INT
Definition time.h:169
crypto_handshake_context_t * network_connection_handshake(network_connection_t *c)
asciichat_error_t network_client_negotiate(acip_transport_t *transport, crypto_handshake_context_t *ctx, uint8_t mode, network_protocol_t protocol)
@ NETWORK_PROTOCOL_DISCOVERY
Definition negotiation.h:5
asciichat_error_t crypto_policy_resolve(const crypto_policy_t *policy, uint8_t *mode)
Definition policy.c:4
uint64_t receive_timeout_ns
Optional TCP receive timeout override; zero uses the normal polling timeout.
Cryptographic handshake context structure.
The useful part of crypto policy is in transport_secure, encrypt, no_encrypt, no_auth: together they ...
Definition policy.h:12

References discovery_session_t::acds_connection, discovery_session_t::acds_transport, discovery_session_t::acds_url, asciichat_error_format(), ASCIICHAT_OK, crypto_handshake_context_t::client_private_key, crypto_handshake_context_t::client_public_key, crypto_handshake_init(), crypto_policy_resolve(), DISCOVERY_STATE_FAILED, private_key_t::ed25519, ED25519_PRIVATE_KEY_SIZE, ED25519_PUBLIC_KEY_SIZE, ERROR_INVALID_PARAM, discovery_session_t::exit_callback_data, discovery_session_t::identity_pubkey, discovery_session_t::identity_seckey, discovery_session_t::is_initiator, private_key_t::key, public_key_t::key, KEY_TYPE_ED25519, KEY_TYPE_UNKNOWN, log_error, log_info, network_client_negotiate(), network_connection_handshake(), NETWORK_PROTOCOL_DISCOVERY, NS_PER_SEC_INT, discovery_session_t::participant_ctx, private_key_t::public_key, acip_transport::receive_timeout_ns, discovery_session_t::session_string, SET_ERRNO, discovery_session_t::should_exit_callback, discovery_session_t::state, private_key_t::type, and public_key_t::type.

◆ discovery_session_stop()

void discovery_session_stop ( discovery_session_t * session)

Stop the discovery session.

Parameters
sessionSession context

Definition at line 2129 of file src/discovery/session.c.

2129 {
2130 ASCII_OTEL_SCOPE(DISCOVERY_SHUTDOWN);
2131 if (!session) {
2132 SET_ERRNO(ERROR_INVALID_PARAM, "session is NULL");
2133 return;
2134 }
2135
2136 log_info("Stopping discovery session...");
2137
2138 // Send SESSION_LEAVE if connected
2139 if (session->acds_transport && session->session_id[0] != 0) {
2140 acip_session_leave_t leave_msg;
2141 memset(&leave_msg, 0, sizeof(leave_msg));
2142 memcpy(leave_msg.session_id, session->session_id, SESSION_ID_SIZE);
2143 memcpy(leave_msg.participant_id, session->participant_id, SESSION_ID_SIZE);
2144
2145 packet_send_via_transport(session->acds_transport, PACKET_TYPE_ACIP_SESSION_LEAVE, &leave_msg, sizeof(leave_msg),
2146 0);
2147 }
2148
2149 set_state(session, DISCOVERY_STATE_ENDED);
2150}
acip_session_leave_t
Definition acds.h:529
@ PACKET_TYPE_ACIP_SESSION_LEAVE
Leave session (Client -> Discovery Server).
Definition packet.h:443

References discovery_session_t::acds_transport, acip_session_leave_t, ASCII_OTEL_SCOPE, DISCOVERY_STATE_ENDED, ERROR_INVALID_PARAM, log_info, packet_send_via_transport(), PACKET_TYPE_ACIP_SESSION_LEAVE, discovery_session_t::participant_id, discovery_session_t::session_id, SESSION_ID_SIZE, and SET_ERRNO.

Referenced by discovery_main().