ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
negotiate.c
Go to the documentation of this file.
1
7#include "negotiate.h"
8
9#include <ascii-chat/common.h>
10#include <ascii-chat/log/logging.h>
11#include <ascii-chat/network/acip/acds.h>
12
13#include <string.h>
14
15// MAX_PARTICIPANTS is defined in session.h (included via negotiate.h)
16
17void negotiate_init(negotiate_ctx_t *ctx, const uint8_t session_id[16], const uint8_t participant_id[16],
18 bool is_initiator) {
19 if (!ctx)
20 return;
21
22 memset(ctx, 0, sizeof(negotiate_ctx_t));
23
24 if (session_id)
25 memcpy(ctx->session_id, session_id, 16);
27 memcpy(ctx->participant_id, participant_id, 16);
28
29 ctx->is_initiator = is_initiator;
31 ctx->peer_quality_received = false;
32 ctx->we_are_host = false;
33
36
37 log_debug("Negotiation initialized (initiator=%d)", is_initiator);
38}
39
40asciichat_error_t negotiate_start_detection(negotiate_ctx_t *ctx, const char *stun_server, uint16_t local_port) {
41 if (!ctx) {
42 return SET_ERRNO(ERROR_INVALID_PARAM, "ctx is NULL");
43 }
44
46 log_info("Starting NAT detection for host negotiation...");
47
48 asciichat_error_t result = nat_detect_quality(&ctx->our_quality, stun_server, local_port);
49 if (result != ASCIICHAT_OK) {
51 ctx->error = result;
52 log_error("NAT detection failed");
53 return result;
54 }
55
56 // If we already have peer quality, we can determine result
57 if (ctx->peer_quality_received) {
60 }
61
63 log_info("NAT detection complete, waiting for peer quality...");
64 return ASCIICHAT_OK;
65}
66
67asciichat_error_t negotiate_receive_peer_quality(negotiate_ctx_t *ctx, const acip_nat_quality_t *peer_quality) {
68 if (!ctx || !peer_quality) {
69 return SET_ERRNO(ERROR_INVALID_PARAM, "ctx or peer_quality is NULL");
70 }
71
72 nat_quality_from_acip(peer_quality, &ctx->peer_quality);
73 ctx->peer_quality_received = true;
74
75 log_info("Received peer NAT quality: tier=%d, upload=%u kbps", nat_compute_tier(&ctx->peer_quality),
77
78 // If our detection is complete, we can determine result
82 }
83
84 // Otherwise, keep waiting for our detection to complete
85 return ASCIICHAT_OK;
86}
87
97static uint8_t determine_connection_type(const nat_quality_t *quality) {
98 if (!quality) {
99 return ACIP_CONNECTION_TYPE_STUN; // Default fallback
100 }
101
102 if (quality->has_public_ip) {
103 return ACIP_CONNECTION_TYPE_DIRECT_PUBLIC;
104 } else if (quality->upnp_available) {
105 return ACIP_CONNECTION_TYPE_UPNP;
106 } else {
107 return ACIP_CONNECTION_TYPE_STUN;
108 }
109}
110
112 if (!ctx) {
113 return SET_ERRNO(ERROR_INVALID_PARAM, "ctx is NULL");
114 }
115
117 return SET_ERRNO(ERROR_INVALID_STATE, "Cannot determine result: detection incomplete");
118 }
119
121
122 // Compare NAT qualities
123 int result = nat_compare_quality(&ctx->our_quality, &ctx->peer_quality, ctx->is_initiator);
124
125 if (result <= 0) {
126 // We should host
127 ctx->we_are_host = true;
129
130 // Set our address as host address
131 if (ctx->our_quality.public_address[0]) {
132 SAFE_STRNCPY(ctx->host_address, ctx->our_quality.public_address, sizeof(ctx->host_address));
133 } else {
134 // Fall back to localhost for testing
135 SAFE_STRNCPY(ctx->host_address, "127.0.0.1", sizeof(ctx->host_address));
136 }
137
138 ctx->host_port = ctx->our_quality.upnp_available ? ctx->our_quality.upnp_mapped_port : ACIP_HOST_DEFAULT_PORT;
139 ctx->connection_type = determine_connection_type(&ctx->our_quality);
140
141 log_info("Negotiation result: WE ARE HOST (addr=%s:%u, type=%d)", ctx->host_address, ctx->host_port,
142 ctx->connection_type);
143 } else {
144 // They should host
145 ctx->we_are_host = false;
147
148 // Set peer address as host address
149 if (ctx->peer_quality.public_address[0]) {
150 SAFE_STRNCPY(ctx->host_address, ctx->peer_quality.public_address, sizeof(ctx->host_address));
151 }
152
153 ctx->host_port = ctx->peer_quality.upnp_available ? ctx->peer_quality.upnp_mapped_port : ACIP_HOST_DEFAULT_PORT;
154 ctx->connection_type = determine_connection_type(&ctx->peer_quality);
155
156 log_info("Negotiation result: THEY ARE HOST (addr=%s:%u, type=%d)", ctx->host_address, ctx->host_port,
157 ctx->connection_type);
158 }
159
161 return ASCIICHAT_OK;
162}
163
165 if (!ctx)
167 return ctx->state;
168}
169
171 if (!ctx)
172 return true;
174}
175
176asciichat_error_t negotiate_get_error(const negotiate_ctx_t *ctx) {
177 if (!ctx)
178 return ERROR_INVALID_PARAM;
179 return ctx->error;
180}
181
182asciichat_error_t negotiate_elect_future_host(const acip_nat_quality_t collected_quality[],
183 const uint8_t participant_ids[][16], size_t num_participants,
184 uint8_t out_future_host_id[16]) {
185 if (!collected_quality || !participant_ids || !out_future_host_id) {
186 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
187 return ERROR_INVALID_PARAM;
188 }
189
190 if (num_participants == 0 || num_participants > MAX_PARTICIPANTS) {
191 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid participant count");
192 return ERROR_INVALID_PARAM;
193 }
194
195 // Special case: only one participant
196 if (num_participants == 1) {
197 memcpy(out_future_host_id, participant_ids[0], 16);
198 log_info("Only one participant, electing as future host");
199 return ASCIICHAT_OK;
200 }
201
202 // Convert all acip_nat_quality_t to nat_quality_t for comparison
204 for (size_t i = 0; i < num_participants; i++) {
205 nat_quality_from_acip(&collected_quality[i], &qualities[i]);
206 }
207
208 // Find the best candidate by comparing all pairs
209 // Winner is the one who "wins" most comparisons
210 size_t best_idx = 0;
211 int best_wins = 0;
212
213 for (size_t i = 0; i < num_participants; i++) {
214 int wins = 0;
215
216 // Compare against all others
217 for (size_t j = 0; j < num_participants; j++) {
218 if (i == j)
219 continue;
220
221 // Compare: -1 means i wins, 1 means j wins, 0 means tie
222 // Note: we_are_initiator parameter doesn't matter for multiparty comparison
223 // We use false consistently for deterministic results
224 int result = nat_compare_quality(&qualities[i], &qualities[j], false);
225
226 if (result <= 0) {
227 wins++; // i wins or tie
228 }
229 }
230
231 // Select as winner if has more wins than current best
232 // In case of tie, lexicographically smaller participant_id wins (as tiebreaker)
233 if (wins > best_wins || (wins == best_wins && memcmp(participant_ids[i], participant_ids[best_idx], 16) < 0)) {
234 best_wins = wins;
235 best_idx = i;
236 }
237 }
238
239 // Copy elected host ID
240 memcpy(out_future_host_id, participant_ids[best_idx], 16);
241
242 log_info("Future host elected (participant index %zu with %d wins)", best_idx, best_wins);
243 return ASCIICHAT_OK;
244}
int nat_compare_quality(const nat_quality_t *ours, const nat_quality_t *theirs, bool we_are_initiator)
Compare two NAT qualities and determine who should host.
Definition nat.c:52
void nat_quality_init(nat_quality_t *quality)
Initialize NAT quality structure with defaults.
Definition nat.c:30
asciichat_error_t nat_detect_quality(nat_quality_t *quality, const char *stun_server, uint16_t local_port)
Detect NAT quality using all available methods.
Definition nat.c:283
int nat_compute_tier(const nat_quality_t *quality)
Compute NAT tier for host selection (0=best, 4=worst)
Definition nat.c:37
void nat_quality_from_acip(const acip_nat_quality_t *acip, nat_quality_t *out)
Convert acip_nat_quality_t to nat_quality_t.
Definition nat.c:487
asciichat_error_t negotiate_start_detection(negotiate_ctx_t *ctx, const char *stun_server, uint16_t local_port)
Start NAT detection phase.
Definition negotiate.c:40
asciichat_error_t negotiate_receive_peer_quality(negotiate_ctx_t *ctx, const acip_nat_quality_t *peer_quality)
Process received peer NAT quality.
Definition negotiate.c:67
void negotiate_init(negotiate_ctx_t *ctx, const uint8_t session_id[16], const uint8_t participant_id[16], bool is_initiator)
Initialize negotiation context.
Definition negotiate.c:17
asciichat_error_t negotiate_determine_result(negotiate_ctx_t *ctx)
Determine negotiation result.
Definition negotiate.c:111
bool negotiate_is_complete(const negotiate_ctx_t *ctx)
Check if negotiation is complete.
Definition negotiate.c:170
asciichat_error_t negotiate_get_error(const negotiate_ctx_t *ctx)
Get negotiation error (if failed)
Definition negotiate.c:176
asciichat_error_t negotiate_elect_future_host(const acip_nat_quality_t collected_quality[], const uint8_t participant_ids[][16], size_t num_participants, uint8_t out_future_host_id[16])
Elect future host from multiple participants (NEW P2P design)
Definition negotiate.c:182
negotiate_state_t negotiate_get_state(const negotiate_ctx_t *ctx)
Get current negotiation state.
Definition negotiate.c:164
Host negotiation logic for discovery mode.
negotiate_state_t
Negotiation state.
Definition negotiate.h:26
@ NEGOTIATE_STATE_INIT
Initial state.
Definition negotiate.h:27
@ NEGOTIATE_STATE_WE_HOST
We won, becoming host.
Definition negotiate.h:31
@ NEGOTIATE_STATE_THEY_HOST
They won, connecting as client.
Definition negotiate.h:32
@ NEGOTIATE_STATE_WAITING_PEER
Waiting for peer's NAT quality.
Definition negotiate.h:29
@ NEGOTIATE_STATE_FAILED
Negotiation failed.
Definition negotiate.h:33
@ NEGOTIATE_STATE_COMPARING
Comparing qualities.
Definition negotiate.h:30
@ NEGOTIATE_STATE_COMPLETE
Negotiation complete.
Definition negotiate.h:34
@ NEGOTIATE_STATE_DETECTING_NAT
Running NAT detection.
Definition negotiate.h:28
#define MAX_PARTICIPANTS
Definition session.h:33
uint8_t session_id[16]
uint8_t participant_id[16]
NAT quality assessment result.
Definition nat.h:27
bool detection_complete
All probes finished.
Definition nat.h:51
uint32_t upload_kbps
Upload bandwidth in Kbps.
Definition nat.h:39
char public_address[64]
Public IP address.
Definition nat.h:35
bool has_public_ip
STUN reflexive == local IP.
Definition nat.h:29
bool upnp_available
UPnP/NAT-PMP mapping succeeded.
Definition nat.h:30
uint16_t upnp_mapped_port
Mapped external port (if upnp_available)
Definition nat.h:31
Host negotiation context.
Definition negotiate.h:40
bool we_are_host
True if we should become host.
Definition negotiate.h:56
uint8_t connection_type
acip_connection_type_t
Definition negotiate.h:59
uint8_t participant_id[16]
Definition negotiate.h:43
uint8_t session_id[16]
Definition negotiate.h:42
asciichat_error_t error
Definition negotiate.h:53
char host_address[64]
Host's address (ours if we_are_host)
Definition negotiate.h:57
nat_quality_t our_quality
Our NAT quality.
Definition negotiate.h:47
bool peer_quality_received
Have we received peer's quality?
Definition negotiate.h:49
nat_quality_t peer_quality
Peer's NAT quality (when received)
Definition negotiate.h:48
bool is_initiator
Did we create this session?
Definition negotiate.h:44
negotiate_state_t state
Definition negotiate.h:52
uint16_t host_port
Host's port.
Definition negotiate.h:58