diff --git a/lib/getmyip.c b/lib/getmyip.c index 9e73bac8..c3ee7535 100644 --- a/lib/getmyip.c +++ b/lib/getmyip.c @@ -1,204 +1,102 @@ /** - * getmyip.c - Get local IP for outgoing connections - * Определяет локальный IP адрес, который будет использоваться - * при отправке пакетов на заданный destination. + * getmyip.c - Определение локального IP-адреса для исходящих соединений + * + * Использует временный UDP-сокет с connect() для определения + * локального IP, который ОС выберет для заданного маршрута. */ #include "getmyip.h" -#include "platform_compat.h" -#include -#include -#include +#include "socket_compat.h" +#include "debug_config.h" #ifdef _WIN32 -# include -# include -# pragma comment(lib, "ws2_32.lib") - typedef SOCKET socket_t; -# define SOCKET_INVALID INVALID_SOCKET + #include + #include #else -# include -# include -# include - typedef int socket_t; -# define SOCKET_INVALID (-1) + #include + #include + #include #endif -static void socket_close(socket_t s) +int get_outgoing_local_ip(const struct sockaddr_storage *local_addr, + const struct sockaddr_storage *remote_addr, + struct sockaddr_storage *result) { -#ifdef _WIN32 - if (s != SOCKET_INVALID) - closesocket(s); -#else - if (s >= 0) - close(s); -#endif -} - -static int net_init(void) -{ -#ifdef _WIN32 - static int done = 0; - if (!done) { - WSADATA wsa; - if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) - return -1; - done = 1; - } -#endif - return 0; -} - -int get_outgoing_ip(const char* dst_ip, int dst_port, int family, - const struct sockaddr_storage* local_ip, - struct sockaddr_storage* result) -{ - socket_t sock = SOCKET_INVALID; - int ret = -1; - struct sockaddr_storage src; - socklen_t src_len = sizeof(src); - - if (!dst_ip || !result) - return -1; - - memset(result, 0, sizeof(*result)); - memset(&src, 0, sizeof(src)); - - if (net_init() != 0) + if (!local_addr || !remote_addr || !result) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: NULL argument"); return -1; + } - if (family != AF_INET && family != AF_INET6) + int family = local_addr->ss_family; + if (family != AF_INET && family != AF_INET6) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: unsupported family %d", family); return -1; + } - // Create UDP socket - sock = socket(family, SOCK_DGRAM, IPPROTO_UDP); - if (sock == SOCKET_INVALID) + if (remote_addr->ss_family != family) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: family mismatch"); return -1; - - // Bind to local address if specified - if (local_ip && local_ip->ss_family != 0) { - socklen_t local_len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - if (bind(sock, (struct sockaddr*)local_ip, local_len) != 0) { - socket_close(sock); - return -1; - } - } else { - // Bind to INADDR_ANY - struct sockaddr_in any4; - struct sockaddr_in6 any6; - if (family == AF_INET) { - memset(&any4, 0, sizeof(any4)); - any4.sin_family = AF_INET; - any4.sin_addr.s_addr = INADDR_ANY; - any4.sin_port = 0; - if (bind(sock, (struct sockaddr*)&any4, sizeof(any4)) != 0) { - socket_close(sock); - return -1; - } - } else { - memset(&any6, 0, sizeof(any6)); - any6.sin6_family = AF_INET6; - any6.sin6_addr = in6addr_any; - any6.sin6_port = 0; - if (bind(sock, (struct sockaddr*)&any6, sizeof(any6)) != 0) { - socket_close(sock); - return -1; - } - } } - // Connect to destination (kernel calculates route) - if (family == AF_INET) { - struct sockaddr_in dst; - memset(&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; - dst.sin_port = htons((uint16_t)dst_port); - if (inet_pton(AF_INET, dst_ip, &dst.sin_addr) != 1) - goto end; - if (connect(sock, (struct sockaddr*)&dst, sizeof(dst)) != 0) - goto end; - } else { - struct sockaddr_in6 dst; - memset(&dst, 0, sizeof(dst)); - dst.sin6_family = AF_INET6; - dst.sin6_port = htons((uint16_t)dst_port); - if (inet_pton(AF_INET6, dst_ip, &dst.sin6_addr) != 1) - goto end; - if (connect(sock, (struct sockaddr*)&dst, sizeof(dst)) != 0) - goto end; + socket_t sock = socket_create_udp(family); + if (sock == SOCKET_INVALID) { + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: socket_create_udp failed"); + return -1; } - // Get local address - if (getsockname(sock, (struct sockaddr*)&src, &src_len) != 0) - goto end; - - // Copy result (only IP, port = 0) - memcpy(result, &src, sizeof(src)); + // Bind на local_addr, но порт ставим 0 чтобы не конфликтовать с основным сокетом + struct sockaddr_storage bind_addr; + memcpy(&bind_addr, local_addr, sizeof(bind_addr)); if (family == AF_INET) { - ((struct sockaddr_in*)result)->sin_port = 0; + ((struct sockaddr_in *)&bind_addr)->sin_port = 0; } else { - ((struct sockaddr_in6*)result)->sin6_port = 0; + ((struct sockaddr_in6 *)&bind_addr)->sin6_port = 0; } - ret = 0; - -end: - socket_close(sock); - return ret; -} - -int get_default_local_ip(int family, struct sockaddr_storage* result) -{ - if (family == AF_INET) { - return get_outgoing_ip("8.8.8.8", 53, family, NULL, result); - } else if (family == AF_INET6) { - return get_outgoing_ip("2001:4860:4860::8888", 53, family, NULL, result); + socklen_t bind_len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + if (bind(sock, (struct sockaddr *)&bind_addr, bind_len) != 0) { + int err = socket_get_error(); + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: bind failed: %s", socket_strerror(err)); + socket_close_wrapper(sock); + return -1; } - return -1; -} - -#ifndef _WIN32 -int get_local_ip_by_index(uint32_t ifindex, struct sockaddr_storage* result) -{ - struct ifaddrs *ifaddr, *ifa; - int ret = -1; - if (!result) + // Connect к remote_addr — ядро вычислит маршрут и выберет source IP + socklen_t remote_len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + if (connect(sock, (struct sockaddr *)remote_addr, remote_len) != 0) { + int err = socket_get_error(); + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: connect failed: %s", socket_strerror(err)); + socket_close_wrapper(sock); return -1; + } - memset(result, 0, sizeof(*result)); - - if (getifaddrs(&ifaddr) == -1) + // Получаем выбранный локальный адрес + struct sockaddr_storage local; + socklen_t local_len = sizeof(local); + if (getsockname(sock, (struct sockaddr *)&local, &local_len) != 0) { + int err = socket_get_error(); + DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "get_outgoing_local_ip: getsockname failed: %s", socket_strerror(err)); + socket_close_wrapper(sock); return -1; + } - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) - continue; - if (ifa->ifa_name == NULL) - continue; - unsigned int idx = if_nametoindex(ifa->ifa_name); - if (idx != ifindex) - continue; - if (ifa->ifa_addr->sa_family != AF_INET) - continue; + socket_close_wrapper(sock); - struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr; - struct sockaddr_in *res = (struct sockaddr_in*)result; - res->sin_family = AF_INET; - res->sin_addr.s_addr = addr->sin_addr.s_addr; - res->sin_port = 0; - ret = 0; - break; + // Копируем только IP (без порта) + memset(result, 0, sizeof(*result)); + if (family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)result; + struct sockaddr_in *src = (struct sockaddr_in *)&local; + sin->sin_family = AF_INET; + sin->sin_addr = src->sin_addr; + sin->sin_port = 0; + } else { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)result; + struct sockaddr_in6 *src6 = (struct sockaddr_in6 *)&local; + sin6->sin6_family = AF_INET6; + sin6->sin6_addr = src6->sin6_addr; + sin6->sin6_port = 0; } - freeifaddrs(ifaddr); - return ret; -} -#else -int get_local_ip_by_index(uint32_t ifindex, struct sockaddr_storage* result) -{ - (void)ifindex; - (void)result; - return -1; + return 0; } -#endif \ No newline at end of file diff --git a/lib/getmyip.h b/lib/getmyip.h index 9f5e19b7..2978775a 100644 --- a/lib/getmyip.h +++ b/lib/getmyip.h @@ -1,14 +1,14 @@ /** - * getmyip.h - Get local IP for outgoing connections - * Определяет локальный IP адрес, который будет использоваться - * при отправке пакетов на заданный destination. + * getmyip.h - Определение локального IP-адреса для исходящих соединений + * + * Определяет локальный IP, который ОС выберет для отправки + * на заданный удаленный адрес через заданный локальный адрес. */ #ifndef GETMYIP_H #define GETMYIP_H #include -#include #include #include @@ -17,49 +17,23 @@ extern "C" { #endif /** - * Get local IPv4 or IPv6 address for outgoing connection - * - * @param dst_ip - Target IP (e.g., "8.8.8.8" or "2001:4860:4860::8888") - * @param dst_port - Target port (any open port, e.g., 53) - * @param family - AF_INET or AF_INET6 - * @param local_ip - If not NULL and ss_family != 0, bind to this local address first - * @param result - Output: local IP address (port will be 0) - * - * Returns 0 on success, -1 on error - * - * Algorithm: - * 1. Create UDP socket - * 2. If local_ip specified, bind to it (for binding to specific interface) - * 3. Connect to dst_ip:dst_port (kernel calculates route, no packets sent) - * 4. Use getsockname() to get actual local IP - * 5. Return result (only IP, port = 0) - */ -int get_outgoing_ip(const char* dst_ip, int dst_port, int family, - const struct sockaddr_storage* local_ip, - struct sockaddr_storage* result); - -/** - * Simplified version - get local IP for any destination (default route) + * Определяет локальный IP-адрес, который ОС выберет для отправки + * на заданный удаленный адрес через заданный локальный адрес. * - * @param family - AF_INET or AF_INET6 - * @param result - Output: local IP address + * Создает временный UDP-сокет, выполняет bind на local_addr, + * connect к remote_addr и получает выбранный локальный IP через getsockname. * - * Returns 0 on success, -1 on error + * @param local_addr Локальный адрес (0.0.0.0 / :: для автоопределения) + * @param remote_addr Удаленный адрес (для определения маршрута) + * @param result Заполняемый локальный IP (порт = 0) + * @return 0 при успехе, -1 при ошибке */ -int get_default_local_ip(int family, struct sockaddr_storage* result); - -/** - * Get local IPv4 address by interface index - * - * @param ifindex - Interface index (from if_nametoindex) - * @param result - Output: local IPv4 address - * - * Returns 0 on success, -1 on error - */ -int get_local_ip_by_index(uint32_t ifindex, struct sockaddr_storage* result); +int get_outgoing_local_ip(const struct sockaddr_storage *local_addr, + const struct sockaddr_storage *remote_addr, + struct sockaddr_storage *result); #ifdef __cplusplus } #endif -#endif /* GETMYIP_H */ \ No newline at end of file +#endif diff --git a/lib/platform_compat.c b/lib/platform_compat.c index fbfa3063..ea1a0d2c 100644 --- a/lib/platform_compat.c +++ b/lib/platform_compat.c @@ -47,96 +47,6 @@ int random_bytes(uint8_t *buffer, size_t len) { #endif } -#ifndef _WIN32 -static uint32_t get_default_route_ip_linux(void) { - FILE *f = fopen("/proc/net/route", "r"); - if (!f) return 0; - - char line[256]; - char *ifname = NULL; - unsigned int dest, gw; - - if (!fgets(line, sizeof(line), f)) { - fclose(f); - return 0; - } - - while (fgets(line, sizeof(line), f)) { - int items = sscanf(line, "%ms %x %x", &ifname, &dest, &gw); - if (items != 3) { - free(ifname); - continue; - } - - if (dest == 0) { - free(ifname); - break; - } - free(ifname); - ifname = NULL; - } - - fclose(f); - - if (!ifname) return 0; - - struct ifaddrs *ifaddr, *ifa; - if (getifaddrs(&ifaddr) == -1) { - free(ifname); - return 0; - } - - uint32_t result = 0; - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) continue; - if (ifa->ifa_name == NULL) continue; - if (strcmp(ifa->ifa_name, ifname) != 0) continue; - if (ifa->ifa_addr->sa_family != AF_INET) continue; - - struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr; - result = addr->sin_addr.s_addr; - break; - } - - freeifaddrs(ifaddr); - free(ifname); - return result; -} -#else -static uint32_t get_default_route_ip_windows(void) { - SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); - if (s == INVALID_SOCKET) return 0; - - struct sockaddr_in remote; - remote.sin_family = AF_INET; - remote.sin_port = htons(53); - remote.sin_addr.s_addr = inet_addr("8.8.8.8"); - - if (connect(s, (struct sockaddr *)&remote, sizeof(remote)) == SOCKET_ERROR) { - closesocket(s); - return 0; - } - - struct sockaddr_in local; - int len = sizeof(local); - if (getsockname(s, (struct sockaddr *)&local, &len) == SOCKET_ERROR) { - closesocket(s); - return 0; - } - - closesocket(s); - return local.sin_addr.s_addr; -} -#endif - -uint32_t get_default_route_ip(void) { -#ifdef _WIN32 - return get_default_route_ip_windows(); -#else - return get_default_route_ip_linux(); -#endif -} - #ifndef _WIN32 uint32_t get_interface_ip_by_index(uint32_t netif_index) { struct ifaddrs *ifaddr, *ifa; diff --git a/lib/platform_compat.h b/lib/platform_compat.h index ecc9e10d..8aeb2c3e 100644 --- a/lib/platform_compat.h +++ b/lib/platform_compat.h @@ -155,10 +155,6 @@ // Returns 0 on success, -1 on error int random_bytes(uint8_t *buffer, size_t len); -// Get IPv4 address of interface with default route (0.0.0.0 destination) -// Returns IPv4 address in network byte order, 0 on error -uint32_t get_default_route_ip(void); - // Get IPv4 address of interface by its index (ifindex) // Returns IPv4 address in network byte order, 0 on error uint32_t get_interface_ip_by_index(uint32_t netif_index); diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 508b4439..c5e23bbf 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -563,37 +563,55 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S } // Определяем interface_addr: IP интерфейса или из конфига memset(&e_sock->interface_addr, 0, sizeof(e_sock->interface_addr)); - if (netif_index > 0) { - struct sockaddr_storage if_addr; - if (get_local_ip_by_index(netif_index, &if_addr) == 0) { - memcpy(&e_sock->interface_addr, &if_addr, sizeof(struct sockaddr_storage)); + if (netif_index > 0 && ip && ip->ss_family == AF_INET) { + uint32_t if_ip = get_interface_ip_by_index(netif_index); + if (if_ip != 0) { + struct sockaddr_in* sin = (struct sockaddr_in*)&e_sock->interface_addr; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = if_ip; } - } else if (ip && ip->ss_family == AF_INET) { - struct sockaddr_in* sin = (struct sockaddr_in*)ip; - if (sin->sin_addr.s_addr == 0) { - struct sockaddr_storage def_addr; - if (get_default_local_ip(AF_INET, &def_addr) == 0) { - memcpy(&e_sock->interface_addr, &def_addr, sizeof(struct sockaddr_storage)); + } else if (ip) { + 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; + rem_sin->sin_family = AF_INET; + rem_sin->sin_port = htons(53); + inet_pton(AF_INET, "8.8.8.8", &rem_sin->sin_addr); + if (get_outgoing_local_ip(ip, &remote, &e_sock->interface_addr) != 0) { + DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "Failed to detect outgoing local IP for socket %s", e_sock->name); + } } - } - } else if (ip && ip->ss_family == AF_INET6) { - struct sockaddr_in6* sin6 = (struct sockaddr_in6*)ip; - if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { - struct sockaddr_storage def_addr; - if (get_default_local_ip(AF_INET6, &def_addr) == 0) { - memcpy(&e_sock->interface_addr, &def_addr, sizeof(struct sockaddr_storage)); + } 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) { + // :: — определяем через default route + struct sockaddr_storage remote; + memset(&remote, 0, sizeof(remote)); + struct sockaddr_in6* rem_sin6 = (struct sockaddr_in6*)&remote; + 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 (e_sock->interface_addr.ss_family == 0) { + // Не удалось определить автоматически — используем адрес из конфига if (ip) memcpy(&e_sock->interface_addr, ip, sizeof(struct sockaddr_storage)); - } else { - // скопируем порт из конфига в interface_addr - if (ip && ip->ss_family == AF_INET) { + } + // Копируем порт из конфига в interface_addr + if (ip && e_sock->interface_addr.ss_family == ip->ss_family) { + if (ip->ss_family == AF_INET) { struct sockaddr_in* sin_cfg = (struct sockaddr_in*)ip; struct sockaddr_in* sin_if = (struct sockaddr_in*)&e_sock->interface_addr; sin_if->sin_port = sin_cfg->sin_port; - } else if (ip && ip->ss_family == AF_INET6) { + } else if (ip->ss_family == AF_INET6) { struct sockaddr_in6* sin6_cfg = (struct sockaddr_in6*)ip; struct sockaddr_in6* sin6_if = (struct sockaddr_in6*)&e_sock->interface_addr; sin6_if->sin6_port = sin6_cfg->sin6_port; @@ -1722,10 +1740,18 @@ int init_sockets(struct UTUN_INSTANCE* instance) { if (server->type == CFG_SERVER_TYPE_PUBLIC) { struct sockaddr_in* sin = (struct sockaddr_in*)&server->ip; if (sin->sin_addr.s_addr == 0) { - struct sockaddr_storage def_addr; - if (get_default_local_ip(AF_INET, &def_addr) == 0) { - default_ip = ((struct sockaddr_in*)&def_addr)->sin_addr.s_addr; - } else { + struct sockaddr_storage remote; + memset(&remote, 0, sizeof(remote)); + struct sockaddr_in* rem_sin = (struct sockaddr_in*)&remote; + rem_sin->sin_family = AF_INET; + rem_sin->sin_port = htons(53); + inet_pton(AF_INET, "8.8.8.8", &rem_sin->sin_addr); + struct sockaddr_storage local; + if (get_outgoing_local_ip(&server->ip, &remote, &local) == 0) { + struct sockaddr_in* local_sin = (struct sockaddr_in*)&local; + default_ip = local_sin->sin_addr.s_addr; + } + if (default_ip == 0) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Failed to detect default route IP for server %s", server->name); } }