175 if (!config || !out_socket) {
176 return SET_ERRNO(ERROR_INVALID_PARAM,
"config or out_socket is NULL");
179 *out_socket = INVALID_SOCKET_VALUE;
182 log_debug(
"PCONN: Resolving %s:%d", config->hostname, config->port);
184 struct addrinfo hints;
185 struct addrinfo *result = NULL;
186 memset(&hints, 0,
sizeof(hints));
187 hints.ai_family = AF_UNSPEC;
188 hints.ai_socktype = SOCK_STREAM;
191 safe_snprintf(port_str,
sizeof(port_str),
"%d", config->port);
193 int gai_result = getaddrinfo(config->hostname, port_str, &hints, &result);
194 if (gai_result != 0) {
195 return SET_ERRNO(ERROR_NETWORK,
"Failed to resolve %s: %s", config->hostname, gai_strerror(gai_result));
199 struct addrinfo *ipv4_addr = NULL;
200 struct addrinfo *ipv6_addr = NULL;
202 for (
struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) {
203 if (rp->ai_family == AF_INET && !ipv4_addr) {
205 log_debug(
"PCONN: Found IPv4 address");
206 }
else if (rp->ai_family == AF_INET6 && !ipv6_addr) {
208 log_debug(
"PCONN: Found IPv6 address");
210 if (ipv4_addr && ipv6_addr)
215 if (!ipv4_addr && !ipv6_addr) {
216 freeaddrinfo(result);
217 return SET_ERRNO(ERROR_NETWORK,
"No IPv4 or IPv6 addresses found for %s", config->hostname);
226 volatile socket_t winner_socket = INVALID_SOCKET_VALUE;
227 volatile bool winner_found =
false;
231 asciichat_thread_t ipv4_thread;
232 asciichat_thread_t ipv6_thread;
234 asciichat_thread_init(&ipv4_thread);
235 asciichat_thread_init(&ipv6_thread);
239 ipv4_attempt.
family = AF_INET;
244 ipv4_attempt.
lock = &lock;
245 ipv4_attempt.
signal = &signal;
248 memcpy(&ipv4_attempt.
addr, ipv4_addr->ai_addr, ipv4_addr->ai_addrlen);
249 ipv4_attempt.
addr_len = ipv4_addr->ai_addrlen;
256 ipv6_attempt.
family = AF_INET6;
261 ipv6_attempt.
lock = &lock;
262 ipv6_attempt.
signal = &signal;
265 memcpy(&ipv6_attempt.
addr, ipv6_addr->ai_addr, ipv6_addr->ai_addrlen);
266 ipv6_attempt.
addr_len = ipv6_addr->ai_addrlen;
272 uint32_t max_wait_ms = config->timeout_ms + 1000;
273 uint32_t elapsed_ms = 0;
276 while (!winner_found && elapsed_ms < max_wait_ms) {
277 cond_timedwait(&signal, &lock, 100 * NS_PER_MS_INT);
281 if (ipv4_addr && ipv6_addr) {
282 if (ipv4_attempt.
done && ipv6_attempt.
done) {
285 }
else if (ipv4_addr && ipv4_attempt.
done) {
287 }
else if (ipv6_addr && ipv6_attempt.
done) {
295 if (asciichat_thread_is_initialized(&ipv4_thread)) {
298 if (asciichat_thread_is_initialized(&ipv6_thread)) {
303 if (ipv4_attempt.
socket != INVALID_SOCKET_VALUE) {
304 socket_close(ipv4_attempt.
socket);
306 if (ipv6_attempt.
socket != INVALID_SOCKET_VALUE) {
307 socket_close(ipv6_attempt.
socket);
312 cond_destroy(&signal);
313 freeaddrinfo(result);
316 if (winner_socket == INVALID_SOCKET_VALUE) {
317 return SET_ERRNO(ERROR_NETWORK,
"Failed to connect to %s:%d (both IPv4 and IPv6 failed)", config->hostname,
321 *out_socket = (
socket_t)winner_socket;
322 log_info(
"PCONN: Successfully connected to %s:%d", config->hostname, config->port);