Browse Source

fix: IPv6 bind to permanent SLAAC address instead of ::

- etcp_connections.c: detect interface_addr before bind(),
  use specific IPv6 addr instead of in6addr_any
  (netlink path for Linux, connect trick for Android)
- chat_profile.c: filter PRIVATE/LOCAL sockets from
  node_addresses sync
- conn_mgr_core.c: diagnostic logs in cm_invite_tcp_connect,
  continue to next addr on TCP connect failure
- node_conn_direct.c: diagnostic logs in ncd_create_links
- auto_socket.c: suppress spurious ADDR_CHANGED events
- etcp_api.c: extended socket event log
topo_upd
evgeny 2 months ago
parent
commit
5066cff333
  1. 7
      src/chat/chat_profile.c
  2. 17
      src/routing_layer/conn_mgr_core.c
  3. 14
      src/transport_layer/auto_socket.c
  4. 9
      src/transport_layer/etcp_api.c
  5. 89
      src/transport_layer/etcp_connections.c
  6. 12
      src/transport_layer/node_conn_direct.c

7
src/chat/chat_profile.c

@ -114,9 +114,10 @@ void chat_core_sync_my_addresses(void) {
}
struct ETCP_SOCKET* sock = g_cc.inst->etcp_sockets;
int sock_count = 0, written = 0, tcp_written = 0;
int sock_count = 0, written = 0, tcp_written = 0, skipped = 0;
while (sock) {
sock_count++;
if (sock->type == CFG_SERVER_TYPE_PRIVATE || sock->type == CFG_SERVER_TYPE_LOCAL) { skipped++; sock = sock->next; continue; }
int sock_family = sock->local_addr.ss_family ? sock->local_addr.ss_family : 0;
struct sockaddr_storage* sa = (sock->interface_addr.ss_family == sock_family) ? &sock->interface_addr : NULL;
if (!sa) sa = sock->local_addr.ss_family ? &sock->local_addr : NULL;
@ -200,8 +201,8 @@ void chat_core_sync_my_addresses(void) {
else if (written == 0 && tcp_written == 0)
DEBUG_WARN(DEBUG_CATEGORY_MEMBER_SYNC, "%s: [ADDR_SYNC] %d sockets found but 0 addresses written", CC_ID, sock_count);
else
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: [ADDR_SYNC] DONE: %d UDP + %d TCP = %d total, my_node=0x%016llx",
CC_ID, written, tcp_written, written + tcp_written, (unsigned long long)g_cc.my_node_id);
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "%s: [ADDR_SYNC] DONE: %d UDP + %d TCP = %d total (skipped %d PRIVATE/LOCAL), my_node=0x%016llx",
CC_ID, written, tcp_written, written + tcp_written, skipped, (unsigned long long)g_cc.my_node_id);
sqlite3_stmt* chk = NULL;
sqlite3_prepare_v2(g_cc.db, "SELECT COUNT(*) FROM node_addresses WHERE node_id=? AND addr_type=0", -1, &chk, NULL);

17
src/routing_layer/conn_mgr_core.c

@ -943,7 +943,10 @@ static void cm_tcp_close_cb(struct stcp_link* link, int err, void* arg) {
static int cm_invite_tcp_connect(struct cm_invite_pending* inv, const struct TOPO_NODE* ni) {
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
if (!(a->protocol & TOPO_PROTO_TCP)) continue;
if (!(a->protocol & TOPO_PROTO_TCP)) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[cm_invite] v4 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)inv->node_id);
continue;
}
struct sockaddr_in sin; memset(&sin,0,sizeof(sin)); sin.sin_family=AF_INET;
memcpy(&sin.sin_addr.s_addr,a->addr,4); sin.sin_port=htons(a->port);
struct sockaddr_storage sa; memcpy(&sa,&sin,sizeof(sin));
@ -954,15 +957,21 @@ static int cm_invite_tcp_connect(struct cm_invite_pending* inv, const struct TOP
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: TCP connecting to %d.%d.%d.%d:%d",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], (int)a->port);
return 1; }
return 0;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[cm_invite] v4 connect FAIL %d.%d.%d.%d:%d node=0x%016llx",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], (int)a->port, (unsigned long long)inv->node_id);
}
for (const struct TOPO_ADDR6* a6 = ni->v6_addrs; a6; a6 = a6->next) {
if (!(a6->protocol & TOPO_PROTO_TCP)) continue;
if (!(a6->protocol & TOPO_PROTO_TCP)) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[cm_invite] v6 skip proto=%02x port=%d node=0x%016llx", a6->protocol, (int)a6->port, (unsigned long long)inv->node_id);
continue;
}
struct sockaddr_in6 sin6; memset(&sin6,0,sizeof(sin6)); sin6.sin6_family=AF_INET6;
memcpy(&sin6.sin6_addr,a6->addr,16); sin6.sin6_port=htons(a6->port);
if (cm_classify_v6_addr(a6->addr) == CM_V6_LL) {
struct ETCP_SOCKET* sv = inv->mgr->instance->etcp_sockets;
while (sv) { if (sv->local_addr.ss_family == AF_INET6 && sv->netif_index) { sin6.sin6_scope_id = sv->netif_index; break; } sv = sv->next; }
if (sin6.sin6_scope_id == 0)
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "[cm_invite] v6 LL no scope_id port=%d node=0x%016llx", (int)a6->port, (unsigned long long)inv->node_id);
}
struct sockaddr_storage sa; memcpy(&sa,&sin6,sizeof(sin6));
struct stcp_link_config cfg = {.ua=inv->mgr->instance->ua, .my_keys=&inv->mgr->instance->my_keys, .inst=inv->mgr->instance,
@ -972,7 +981,7 @@ static int cm_invite_tcp_connect(struct cm_invite_pending* inv, const struct TOP
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "invite: TCP6 connecting to port %d",
(int)a6->port);
return 1; }
return 0;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[cm_invite] v6 connect FAIL port=%d node=0x%016llx", (int)a6->port, (unsigned long long)inv->node_id);
}
return 0;
}

14
src/transport_layer/auto_socket.c

@ -384,8 +384,13 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
{ struct ETCP_SOCKET* s = as->instance->etcp_sockets;
while (s) { if (s->netif_index == ifindex && s->local_addr.ss_family == AF_INET) { ifa->v4_sock = s; break; } s = s->next; } }
} else if (has_v4 && sock) {
struct sockaddr_storage old4 = sock->interface_addr;
socket_monitor_update_if_addr(sock);
etcp_socket_cbk_fire(sock, ETCP_SOCKET_EVENT_ADDR_CHANGED);
if (memcmp(&old4, &sock->interface_addr, sizeof(old4)) != 0) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] v4 interface_addr changed: %s -> %s",
sockaddr_storage_to_str(&old4).str, sockaddr_storage_to_str(&sock->interface_addr).str);
etcp_socket_cbk_fire(sock, ETCP_SOCKET_EVENT_ADDR_CHANGED);
}
}
}
@ -414,8 +419,13 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
{ struct ETCP_SOCKET* s = as->instance->etcp_sockets;
while (s) { if (s->netif_index == ifindex && s->local_addr.ss_family == AF_INET6) { ifa->v6_sock = s; break; } s = s->next; } }
} else if (has_v6 && sock) {
struct sockaddr_storage old6 = sock->interface_addr;
socket_monitor_update_if_addr(sock);
etcp_socket_cbk_fire(sock, ETCP_SOCKET_EVENT_ADDR_CHANGED);
if (memcmp(&old6, &sock->interface_addr, sizeof(old6)) != 0) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] v6 interface_addr changed: %s -> %s",
sockaddr_storage_to_str(&old6).str, sockaddr_storage_to_str(&sock->interface_addr).str);
etcp_socket_cbk_fire(sock, ETCP_SOCKET_EVENT_ADDR_CHANGED);
}
}
}

9
src/transport_layer/etcp_api.c

@ -137,7 +137,14 @@ void etcp_socket_cbk_fire(struct ETCP_SOCKET* sock, int event) {
if (!sock || !sock->instance) return;
static const char* names[] = { "ADDR_CHANGED", "STATUS_CHANGED" };
int idx = 0, e = event; while (e >>= 1) idx++;
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket %s event: %s", sock->name, (idx >= 0 && idx < (int)(sizeof(names)/sizeof(names[0]))) ? names[idx] : "?");
const char* name = (idx >= 0 && idx < (int)(sizeof(names)/sizeof(names[0]))) ? names[idx] : "?";
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socket %s event: %s", sock->name, name);
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socket %s event: %s if_addr=%s local=%s nat=%s type=%d netif=%u",
sock->name, name,
sockaddr_storage_to_str(&sock->interface_addr).str,
sockaddr_storage_to_str(&sock->local_addr).str,
sockaddr_storage_to_str(&sock->nat_addr).str,
sock->type, sock->netif_index);
struct etcp_socket_cbk_entry* cbe = sock->instance->socket_cbks;
while (cbe) { struct etcp_socket_cbk_entry* n = cbe->next; if (cbe->event_mask & event) cbe->fn(sock, event, cbe->arg); cbe = n; }
}

89
src/transport_layer/etcp_connections.c

@ -609,36 +609,7 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
}
#endif
// Store the local address and bind socket if provided
if (ip) {
memcpy(&e_sock->local_addr, ip, sizeof(struct sockaddr_storage));
// CRITICAL: Actually bind the socket to the address
socklen_t addr_len = (ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
if (bind(e_sock->fd, (struct sockaddr*)ip, addr_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind socket to address family %d: %s",
ip->ss_family, socket_strerror(socket_get_error()));
if (ip->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ip;
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind to %s:%d", ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
} else if (ip->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ip;
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind to %s:%d", ip_to_str(&sin6->sin6_addr, AF_INET6).str, ntohs(sin6->sin6_port));
}
socket_close_wrapper(e_sock->fd);
u_free(e_sock);
return NULL;
}
if (ip->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ip;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Successfully bound socket to local address, family=AF_INET %s:%d", ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
} else if (ip->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ip;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Successfully bound socket to local address, family=AF_INET6 %s:%d", ip_to_str(&sin6->sin6_addr, AF_INET6).str, ntohs(sin6->sin6_port));
}
}
// Определяем interface_addr: IP интерфейса или из конфига
// Определяем interface_addr до bind, чтобы забиндить на конкретный адрес
memset(&e_sock->interface_addr, 0, sizeof(e_sock->interface_addr));
if (netif_index > 0 && ip && ip->ss_family == AF_INET) {
uint32_t if_ip = get_interface_ip_by_index(netif_index);
@ -651,7 +622,6 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
if (ip->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ip;
if (sin->sin_addr.s_addr == 0) {
// 0.0.0.0 — определяем через default route
struct sockaddr_storage remote;
memset(&remote, 0, sizeof(remote));
struct sockaddr_in* rem_sin = (struct sockaddr_in*)&remote;
@ -665,7 +635,6 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
} else if (ip->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ip;
if (memcmp(&sin6->sin6_addr, &in6addr_any, sizeof(struct in6_addr)) == 0) {
// :: — приоритет: permanent > temporary > get_outgoing_local_ip
uint16_t v6if = (netif_index > 0) ? netif_index : get_default_route_netif_index(AF_INET6);
if (v6if > 0) {
uint8_t v6addr[16];
@ -675,6 +644,7 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
struct sockaddr_in6* if6 = (struct sockaddr_in6*)&e_sock->interface_addr;
if6->sin6_family = AF_INET6;
memcpy(&if6->sin6_addr, v6addr, 16);
if6->sin6_port = sin6->sin6_port;
}
}
if (e_sock->interface_addr.ss_family == 0) {
@ -684,15 +654,15 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
rem_sin6->sin6_family = AF_INET6;
rem_sin6->sin6_port = htons(53);
inet_pton(AF_INET6, "2001:4860:4860::8888", &rem_sin6->sin6_addr);
if (get_outgoing_local_ip(ip, &remote, &e_sock->interface_addr) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "Failed to detect outgoing local IPv6 for socket %s", e_sock->name);
if (get_outgoing_local_ip(ip, &remote, &e_sock->interface_addr) == 0) {
struct sockaddr_in6* if6 = (struct sockaddr_in6*)&e_sock->interface_addr;
if6->sin6_port = sin6->sin6_port;
}
}
}
}
}
if (e_sock->interface_addr.ss_family == 0) {
// Не удалось определить автоматически — используем адрес из конфига
if (ip) memcpy(&e_sock->interface_addr, ip, sizeof(struct sockaddr_storage));
}
// Копируем порт из конфига в interface_addr
@ -708,6 +678,36 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
}
}
// Bind — используем interface_addr если определён, иначе ip из конфига
{
struct sockaddr_storage* bind_addr = (e_sock->interface_addr.ss_family != 0) ? &e_sock->interface_addr : ip;
if (bind_addr && bind_addr->ss_family != 0) {
memcpy(&e_sock->local_addr, bind_addr, sizeof(struct sockaddr_storage));
socklen_t addr_len = (bind_addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
if (bind(e_sock->fd, (struct sockaddr*)bind_addr, addr_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind socket to address family %d: %s",
bind_addr->ss_family, socket_strerror(socket_get_error()));
if (bind_addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)bind_addr;
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind to %s:%d", ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
} else if (bind_addr->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)bind_addr;
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind to %s:%d", ip_to_str(&sin6->sin6_addr, AF_INET6).str, ntohs(sin6->sin6_port));
}
socket_close_wrapper(e_sock->fd);
u_free(e_sock);
return NULL;
}
if (bind_addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)bind_addr;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Successfully bound socket to local address, family=AF_INET %s:%d", ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
} else if (bind_addr->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)bind_addr;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Successfully bound socket to local address, family=AF_INET6 %s:%d", ip_to_str(&sin6->sin6_addr, AF_INET6).str, ntohs(sin6->sin6_port));
}
}
}
char config_str[64] = "none";
char netif_str[64] = "none";
if (e_sock->local_addr.ss_family != 0) {
@ -1065,11 +1065,10 @@ int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
struct sockaddr_storage* addr=&dgram->link->remote_addr;
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
// Debug: print where we're sending the packet
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
// inet_ntop removed - use ip_to_str
if (addr->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[v6_send] fd=%d dst=%-39s scope=%u", dgram->link->conn->fd,
sockaddr_storage_to_str(addr).str, sin6->sin6_scope_id);
}
ssize_t sent = etcp_udp_send(dgram->link, dgram->link->conn->fd, enc_buf, enc_buf_len + dgram->noencrypt_len,
@ -1663,6 +1662,16 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
socklen_t addr_len=sizeof(addr);
memset(&addr, 0, sizeof(addr));
ssize_t recv_len = socket_recvfrom(sock, data, PACKET_DATA_SIZE, (struct sockaddr*)&addr, &addr_len);
if (recv_len > 0 && addr.ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&addr;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[v6_recv] sock=%s fd=%d src=[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d len=%zd scope=%u",
e_sock->name, (int)sock,
sin6->sin6_addr.s6_addr[0],sin6->sin6_addr.s6_addr[1],sin6->sin6_addr.s6_addr[2],sin6->sin6_addr.s6_addr[3],
sin6->sin6_addr.s6_addr[4],sin6->sin6_addr.s6_addr[5],sin6->sin6_addr.s6_addr[6],sin6->sin6_addr.s6_addr[7],
sin6->sin6_addr.s6_addr[8],sin6->sin6_addr.s6_addr[9],sin6->sin6_addr.s6_addr[10],sin6->sin6_addr.s6_addr[11],
sin6->sin6_addr.s6_addr[12],sin6->sin6_addr.s6_addr[13],sin6->sin6_addr.s6_addr[14],sin6->sin6_addr.s6_addr[15],
(int)ntohs(sin6->sin6_port), recv_len, sin6->sin6_scope_id);
}
if (recv_len <= 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "recvfrom failed, error=%zd, sock_err=%d", recv_len, socket_get_error());

12
src/transport_layer/node_conn_direct.c

@ -130,6 +130,7 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
struct ETCP_CONN* conn = entry->conn;
struct UTUN_INSTANCE* inst = conn->instance;
int link_count = 0;
uint64_t nid = entry->node_id;
/* ---- IPv4 ---- */
{
@ -142,13 +143,14 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
if (s->local_addr.ss_family == AF_INET && s->type != CFG_SERVER_TYPE_PRIVATE)
socks[sock_count++] = s;
}
if (sock_count == 0) DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd] v4 sock_count=0 node=0x%016llx", (unsigned long long)nid);
if (sock_count > 0) {
int rr = 0;
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) continue;
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd] v4 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)nid); continue; }
int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; }
if (zero) continue;
if (zero) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd] v4 skip zero-addr node=0x%016llx", (unsigned long long)nid); continue; }
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin));
@ -191,12 +193,14 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
socks[sock_count++] = s;
}
if (sock_count == 0) DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd] v6 sock_count=0 node=0x%016llx", (unsigned long long)nid);
if (sock_count > 0) {
int rr = 0;
for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) {
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) continue;
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd] v6 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)nid); continue; }
int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; }
if (zero) continue;
if (zero) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[ncd] v6 skip zero-addr node=0x%016llx", (unsigned long long)nid); continue; }
struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6;
memcpy(&sin6.sin6_addr, a->addr, 16); sin6.sin6_port = htons(a->port);
struct ETCP_SOCKET* use_sock = socks[rr++ % sock_count];

Loading…
Cancel
Save