You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

204 lines
5.3 KiB

/**
* getmyip.c - Get local IP for outgoing connections
* Определяет локальный IP адрес, который будет использоваться
* при отправке пакетов на заданный destination.
*/
#include "getmyip.h"
#include "platform_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
# pragma comment(lib, "ws2_32.lib")
typedef SOCKET socket_t;
# define SOCKET_INVALID INVALID_SOCKET
#else
# include <unistd.h>
# include <ifaddrs.h>
# include <net/if.h>
typedef int socket_t;
# define SOCKET_INVALID (-1)
#endif
static void socket_close(socket_t s)
{
#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)
return -1;
if (family != AF_INET && family != AF_INET6)
return -1;
// Create UDP socket
sock = socket(family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == SOCKET_INVALID)
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;
}
// 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));
if (family == AF_INET) {
((struct sockaddr_in*)result)->sin_port = 0;
} else {
((struct sockaddr_in6*)result)->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);
}
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)
return -1;
memset(result, 0, sizeof(*result));
if (getifaddrs(&ifaddr) == -1)
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;
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;
}
freeifaddrs(ifaddr);
return ret;
}
#else
int get_local_ip_by_index(uint32_t ifindex, struct sockaddr_storage* result)
{
(void)ifindex;
(void)result;
return -1;
}
#endif