312 bool first_connection,
bool has_ever_connected) {
313 (void)first_connection;
314 (void)has_ever_connected;
316 if (!client || !address || port <= 0) {
317 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid client, address, or port");
321 if (socket_is_valid(client->sockfd)) {
322 close_socket_safe(client->sockfd);
323 client->sockfd = INVALID_SOCKET_VALUE;
327 if (reconnect_attempt > 0) {
328 uint64_t delay_ns = get_reconnect_delay(reconnect_attempt);
329 platform_sleep_ns(delay_ns);
336 struct addrinfo hints, *res = NULL, *addr_iter;
337 memset(&hints, 0,
sizeof(hints));
338 hints.ai_family = AF_UNSPEC;
339 hints.ai_socktype = SOCK_STREAM;
341 hints.ai_flags = AI_NUMERICSERV;
345 SAFE_SNPRINTF(port_str,
sizeof(port_str),
"%d", port);
349 log_debug(
"Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
350 hints.ai_family = AF_INET6;
351 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
353 int ipv6_result = getaddrinfo(
"::1", port_str, &hints, &res);
354 if (ipv6_result == 0 && res != NULL) {
356 client->sockfd = socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
357 if (client->sockfd != INVALID_SOCKET_VALUE) {
358 log_debug(
"Trying IPv6 loopback connection to [::1]:%s...", port_str);
360 log_debug(
"Connection successful using IPv6 loopback");
361 SAFE_STRNCPY(client->server_ip,
"::1",
sizeof(client->server_ip));
364 goto connection_success;
366 close_socket_safe(client->sockfd);
367 client->sockfd = INVALID_SOCKET_VALUE;
374 log_debug(
"IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
375 hints.ai_family = AF_INET;
377 int ipv4_result = getaddrinfo(
"127.0.0.1", port_str, &hints, &res);
378 if (ipv4_result == 0 && res != NULL) {
379 client->sockfd = socket_create(res->ai_family, res->ai_socktype, res->ai_protocol);
380 if (client->sockfd != INVALID_SOCKET_VALUE) {
381 log_debug(
"Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
383 log_debug(
"Connection successful using IPv4 loopback");
384 SAFE_STRNCPY(client->server_ip,
"127.0.0.1",
sizeof(client->server_ip));
387 goto connection_success;
389 close_socket_safe(client->sockfd);
390 client->sockfd = INVALID_SOCKET_VALUE;
397 log_warn(
"Could not connect to localhost using either IPv6 or IPv4 loopback");
402 log_debug(
"Resolving server address '%s' port %s...", address, port_str);
403 hints.ai_family = AF_UNSPEC;
405 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
406 if (getaddr_result != 0) {
407 log_error(
"Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
412 for (
int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
413 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
414 if (addr_iter->ai_family != address_family) {
418 client->sockfd = socket_create(addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
419 if (client->sockfd == INVALID_SOCKET_VALUE) {
423 if (addr_iter->ai_family == AF_INET) {
424 log_debug(
"Trying IPv4 connection...");
425 }
else if (addr_iter->ai_family == AF_INET6) {
426 log_debug(
"Trying IPv6 connection...");
429 if (
connect_with_timeout(client->sockfd, addr_iter->ai_addr, addr_iter->ai_addrlen, CONNECT_TIMEOUT)) {
430 log_debug(
"Connection successful using %s", addr_iter->ai_family == AF_INET ?
"IPv4"
431 : addr_iter->ai_family == AF_INET6 ?
"IPv6"
432 :
"unknown protocol");
435 if (
format_ip_address(addr_iter->ai_family, addr_iter->ai_addr, client->server_ip,
sizeof(client->server_ip)) ==
437 log_debug(
"Resolved server IP: %s", client->server_ip);
439 log_warn(
"Failed to format server IP address");
442 goto connection_success;
445 close_socket_safe(client->sockfd);
446 client->sockfd = INVALID_SOCKET_VALUE;
457 if (client->sockfd == INVALID_SOCKET_VALUE) {
458 log_warn(
"Could not connect to server %s:%d (tried all addresses)", address, port);
463 struct sockaddr_storage local_addr = {0};
464 socklen_t addr_len =
sizeof(local_addr);
465 if (getsockname(client->sockfd, (
struct sockaddr *)&local_addr, &addr_len) == -1) {
467 close_socket_safe(client->sockfd);
468 client->sockfd = INVALID_SOCKET_VALUE;
474 if (((
struct sockaddr *)&local_addr)->sa_family == AF_INET) {
475 local_port = NET_TO_HOST_U16(((
struct sockaddr_in *)&local_addr)->sin_port);
476 }
else if (((
struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
477 local_port = NET_TO_HOST_U16(((
struct sockaddr_in6 *)&local_addr)->sin6_port);
479 client->my_client_id = (uint32_t)local_port;
482 atomic_store(&client->connection_active,
true);
483 atomic_store(&client->connection_lost,
false);
484 atomic_store(&client->should_reconnect,
false);
490 if (socket_set_keepalive(client->sockfd,
true) < 0) {
495 if (sock_config_result != ASCIICHAT_OK) {
499 log_debug(
"Connection established successfully to %s:%d (client_id=%u)", address, port, client->my_client_id);