571 lines
15 KiB
C
571 lines
15 KiB
C
|
/*
|
||
|
get default route (gateway) IPv4 address of the named network
|
||
|
interface/adapter (like "eth0").
|
||
|
|
||
|
This works on both Linux and windows.
|
||
|
*/
|
||
|
#include "rawsock.h"
|
||
|
#include "util-safefunc.h"
|
||
|
#include "util-malloc.h"
|
||
|
#include "massip-parse.h"
|
||
|
#include "util-logger.h"
|
||
|
|
||
|
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun__)
|
||
|
#include <unistd.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <net/route.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <net/if_dl.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
#define ROUNDUP2(a, n) ((a) > 0 ? (1 + (((a) - 1U) | ((n) - 1))) : (n))
|
||
|
|
||
|
#if defined(__APPLE__)
|
||
|
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
|
||
|
#elif defined(__NetBSD__)
|
||
|
# define ROUNDUP(a) ROUNDUP2((a), sizeof(uint64_t))
|
||
|
#elif defined(__FreeBSD__)
|
||
|
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
|
||
|
#elif defined(__OpenBSD__)
|
||
|
# define ROUNDUP(a) ROUNDUP2((a), sizeof(int))
|
||
|
#else
|
||
|
# error unknown platform
|
||
|
#endif
|
||
|
|
||
|
static struct sockaddr *
|
||
|
get_rt_address(struct rt_msghdr *rtm, int desired)
|
||
|
{
|
||
|
int i;
|
||
|
int bitmask = rtm->rtm_addrs;
|
||
|
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
|
||
|
|
||
|
for (i = 0; i < RTAX_MAX; i++) {
|
||
|
if (bitmask & (1 << i)) {
|
||
|
if ((1<<i) == desired)
|
||
|
return sa;
|
||
|
sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
|
||
|
} else
|
||
|
;
|
||
|
}
|
||
|
return NULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
hexdump(const void *v, size_t len)
|
||
|
{
|
||
|
const unsigned char *p = (const unsigned char *)v;
|
||
|
size_t i;
|
||
|
|
||
|
|
||
|
for (i=0; i<len; i += 16) {
|
||
|
size_t j;
|
||
|
|
||
|
for (j=i; j<i+16 && j<len; j++)
|
||
|
printf("%02x ", p[j]);
|
||
|
for (;j<i+16; j++)
|
||
|
printf(" ");
|
||
|
printf(" ");
|
||
|
for (j=i; j<i+16 && j<len; j++)
|
||
|
if (isprint(p[j]) && !isspace(p[j]))
|
||
|
printf("%c", p[j]);
|
||
|
else
|
||
|
printf(".");
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
#define RTA_DST 0x1 /* destination sockaddr present */
|
||
|
#define RTA_GATEWAY 0x2 /* gateway sockaddr present */
|
||
|
#define RTA_NETMASK 0x4 /* netmask sockaddr present */
|
||
|
#define RTA_GENMASK 0x8 /* cloning mask sockaddr present */
|
||
|
#define RTA_IFP 0x10 /* interface name sockaddr present */
|
||
|
#define RTA_IFA 0x20 /* interface addr sockaddr present */
|
||
|
#define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */
|
||
|
#define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */
|
||
|
#endif
|
||
|
|
||
|
void
|
||
|
dump_rt_addresses(struct rt_msghdr *rtm);
|
||
|
|
||
|
void
|
||
|
dump_rt_addresses(struct rt_msghdr *rtm)
|
||
|
{
|
||
|
int i;
|
||
|
int bitmask = rtm->rtm_addrs;
|
||
|
struct sockaddr *sa = (struct sockaddr *)(rtm + 1);
|
||
|
|
||
|
for (i = 0; i < RTAX_MAX; i++) {
|
||
|
if (bitmask & (1 << i)) {
|
||
|
printf("b=%u fam=%u len=%u\n", (1<<i), sa->sa_family, sa->sa_len);
|
||
|
hexdump(sa, sa->sa_len + sizeof(sa->sa_family));
|
||
|
sa = (struct sockaddr *)(ROUNDUP(sa->sa_len) + (char *)sa);
|
||
|
} else
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
rawsock_get_default_gateway(const char *ifname, unsigned *ipv4)
|
||
|
{
|
||
|
int fd;
|
||
|
int seq = (int)time(0);
|
||
|
ssize_t err;
|
||
|
struct rt_msghdr *rtm;
|
||
|
size_t sizeof_buffer;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Requests/responses from the kernel are done with an "rt_msghdr"
|
||
|
* structure followed by an array of "sockaddr" structures.
|
||
|
*/
|
||
|
sizeof_buffer = sizeof(*rtm) + 512;
|
||
|
rtm = calloc(1, sizeof_buffer);
|
||
|
|
||
|
/*
|
||
|
* Create a socket for querying the kernel
|
||
|
*/
|
||
|
fd = socket(AF_ROUTE, SOCK_RAW, 0);
|
||
|
if (fd < 0) {
|
||
|
perror("socket(AF_ROUTE)");
|
||
|
free(rtm);
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
/* Needs a timeout. Sometimes it'll hang indefinitely waiting for a
|
||
|
* response that will never arrive */
|
||
|
{
|
||
|
struct timeval timeout;
|
||
|
timeout.tv_sec = 1;
|
||
|
timeout.tv_usec = 0;
|
||
|
|
||
|
err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
||
|
if (err < 0)
|
||
|
LOG(0, "[-] SO_RCVTIMEO: %d %s\n", errno, strerror(errno));
|
||
|
|
||
|
err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
|
||
|
if (err < 0)
|
||
|
LOG(0, "[-] SO_SNDTIMEO: %d %s\n", errno, strerror(errno));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Format and send request to kernel
|
||
|
*/
|
||
|
rtm->rtm_msglen = sizeof(*rtm) + sizeof(struct sockaddr_in);
|
||
|
rtm->rtm_version = RTM_VERSION;
|
||
|
rtm->rtm_flags = RTF_UP;
|
||
|
rtm->rtm_type = RTM_GET;
|
||
|
rtm->rtm_addrs = RTA_DST | RTA_IFP;
|
||
|
rtm->rtm_pid = getpid();
|
||
|
rtm->rtm_seq = seq;
|
||
|
|
||
|
/*
|
||
|
* Create an empty address of 0.0.0.0 to query the route
|
||
|
*/
|
||
|
{
|
||
|
struct sockaddr_in *sin;
|
||
|
sin = (struct sockaddr_in *)(rtm + 1);
|
||
|
sin->sin_len = sizeof(*sin);
|
||
|
sin->sin_family = AF_INET;
|
||
|
sin->sin_addr.s_addr = 0;
|
||
|
}
|
||
|
|
||
|
err = write(fd, (char *)rtm, rtm->rtm_msglen);
|
||
|
if (err <= 0) {
|
||
|
LOG(0, "[-] getroute: write(): returned %d %s\n", errno, strerror(errno));
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read responses until we find one that belongs to us
|
||
|
*/
|
||
|
for (;;) {
|
||
|
err = read(fd, (char *)rtm, sizeof_buffer);
|
||
|
if (err <= 0)
|
||
|
break;
|
||
|
if (rtm->rtm_seq != seq) {
|
||
|
printf("seq: %u %u\n", rtm->rtm_seq, seq);
|
||
|
continue;
|
||
|
}
|
||
|
if (rtm->rtm_pid != getpid()) {
|
||
|
printf("pid: %u %u\n", rtm->rtm_pid, getpid());
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
close(fd);
|
||
|
|
||
|
/*
|
||
|
* Parse our data
|
||
|
*/
|
||
|
{
|
||
|
struct sockaddr_in *sin;
|
||
|
struct sockaddr_dl *sdl;
|
||
|
|
||
|
sdl = (struct sockaddr_dl *)get_rt_address(rtm, RTA_IFP);
|
||
|
if (sdl) {
|
||
|
//hexdump(sdl, sdl->sdl_len);
|
||
|
//printf("%.*s\n", sdl->sdl_nlen, sdl->sdl_data);
|
||
|
if (memcmp(ifname, sdl->sdl_data, sdl->sdl_nlen) != 0) {
|
||
|
fprintf(stderr, "ERROR: ROUTE DOESN'T MATCH INTERFACE\n");
|
||
|
fprintf(stderr, "YOU'LL HAVE TO SET --router-mac MANUALLY\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sin = (struct sockaddr_in *)get_rt_address(rtm, RTA_GATEWAY);
|
||
|
if (sin) {
|
||
|
*ipv4 = ntohl(sin->sin_addr.s_addr);
|
||
|
free(rtm);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
fail:
|
||
|
free(rtm);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
#elif defined(__linux__)
|
||
|
#include <netinet/in.h>
|
||
|
#include <net/if.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <linux/netlink.h>
|
||
|
#include <linux/rtnetlink.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <arpa/inet.h>
|
||
|
|
||
|
|
||
|
|
||
|
struct route_info {
|
||
|
struct in_addr dstAddr;
|
||
|
struct in_addr srcAddr;
|
||
|
struct in_addr gateWay;
|
||
|
char ifName[IF_NAMESIZE];
|
||
|
};
|
||
|
|
||
|
static int
|
||
|
read_netlink(int fd, char *bufPtr, size_t sizeof_buffer, int seqNum, int pId)
|
||
|
{
|
||
|
struct nlmsghdr *nlHdr;
|
||
|
int readLen = 0, msgLen = 0;
|
||
|
|
||
|
do {
|
||
|
/* Receive response from the kernel */
|
||
|
if ((readLen = recv(fd, bufPtr, sizeof_buffer - msgLen, 0)) < 0) {
|
||
|
perror("SOCK READ: ");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
nlHdr = (struct nlmsghdr *) bufPtr;
|
||
|
|
||
|
/* Check if the header is valid */
|
||
|
if ((NLMSG_OK(nlHdr, readLen) == 0)
|
||
|
|| (nlHdr->nlmsg_type == NLMSG_ERROR)) {
|
||
|
perror("Error in received packet");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Check if the its the last message */
|
||
|
if (nlHdr->nlmsg_type == NLMSG_DONE) {
|
||
|
break;
|
||
|
} else {
|
||
|
/* Else move the pointer to buffer appropriately */
|
||
|
bufPtr += readLen;
|
||
|
msgLen += readLen;
|
||
|
}
|
||
|
|
||
|
/* Check if its a multi part message */
|
||
|
if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0) {
|
||
|
/* return if its not */
|
||
|
break;
|
||
|
}
|
||
|
} while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pId));
|
||
|
|
||
|
return msgLen;
|
||
|
}
|
||
|
|
||
|
/* For parsing the route info returned */
|
||
|
static int
|
||
|
parseRoutes(struct nlmsghdr *nlHdr, struct route_info *rtInfo)
|
||
|
{
|
||
|
struct rtmsg *rtMsg;
|
||
|
struct rtattr *rtAttr;
|
||
|
int rtLen = 0;
|
||
|
|
||
|
rtMsg = (struct rtmsg *) NLMSG_DATA(nlHdr);
|
||
|
|
||
|
/* This must be an IPv4 (AF_INET) route */
|
||
|
if (rtMsg->rtm_family != AF_INET)
|
||
|
return 1;
|
||
|
|
||
|
/* This must be in main routing table */
|
||
|
if (rtMsg->rtm_table != RT_TABLE_MAIN)
|
||
|
return 1;
|
||
|
|
||
|
/* Attributes field*/
|
||
|
rtAttr = (struct rtattr *)RTM_RTA(rtMsg);
|
||
|
rtLen = RTM_PAYLOAD(nlHdr);
|
||
|
for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen)) {
|
||
|
switch (rtAttr->rta_type) {
|
||
|
case RTA_OIF:
|
||
|
if_indextoname(*(int *) RTA_DATA(rtAttr), rtInfo->ifName);
|
||
|
break;
|
||
|
case RTA_GATEWAY:
|
||
|
rtInfo->gateWay.s_addr = *(u_int *)RTA_DATA(rtAttr);
|
||
|
break;
|
||
|
case RTA_PREFSRC:
|
||
|
rtInfo->srcAddr.s_addr = *(u_int *)RTA_DATA(rtAttr);
|
||
|
break;
|
||
|
case RTA_DST:
|
||
|
rtInfo->dstAddr .s_addr = *(u_int *)RTA_DATA(rtAttr);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4)
|
||
|
{
|
||
|
int fd;
|
||
|
struct nlmsghdr *nlMsg;
|
||
|
char msgBuf[16384];
|
||
|
int len;
|
||
|
int msgSeq = 0;
|
||
|
|
||
|
/*
|
||
|
* Set to zero, in case we cannot find it
|
||
|
*/
|
||
|
*ipv4 = 0;
|
||
|
|
||
|
/*
|
||
|
* Create 'netlink' socket to query kernel
|
||
|
*/
|
||
|
fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||
|
if (fd < 0) {
|
||
|
fprintf(stderr, "%s:%d: socket(NETLINK_ROUTE): %d\n",
|
||
|
__FILE__, __LINE__, errno);
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* format the netlink buffer
|
||
|
*/
|
||
|
memset(msgBuf, 0, sizeof(msgBuf));
|
||
|
nlMsg = (struct nlmsghdr *)msgBuf;
|
||
|
|
||
|
nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||
|
nlMsg->nlmsg_type = RTM_GETROUTE;
|
||
|
nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
|
||
|
nlMsg->nlmsg_seq = msgSeq++;
|
||
|
nlMsg->nlmsg_pid = getpid();
|
||
|
|
||
|
/*
|
||
|
* send first request to kernel
|
||
|
*/
|
||
|
if (send(fd, nlMsg, nlMsg->nlmsg_len, 0) < 0) {
|
||
|
fprintf(stderr, "%s:%d: send(NETLINK_ROUTE): %d\n",
|
||
|
__FILE__, __LINE__, errno);
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Now read all the responses
|
||
|
*/
|
||
|
len = read_netlink(fd, msgBuf, sizeof(msgBuf), msgSeq, getpid());
|
||
|
if (len <= 0) {
|
||
|
fprintf(stderr, "%s:%d: read_netlink: %d\n",
|
||
|
__FILE__, __LINE__, errno);
|
||
|
return errno;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Parse the response
|
||
|
*/
|
||
|
for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len)) {
|
||
|
struct route_info rtInfo[1];
|
||
|
int err;
|
||
|
|
||
|
memset(rtInfo, 0, sizeof(struct route_info));
|
||
|
|
||
|
err = parseRoutes(nlMsg, rtInfo);
|
||
|
if (err != 0)
|
||
|
continue;
|
||
|
|
||
|
/* make sure we match the desired network interface */
|
||
|
if (ifname && strcmp(rtInfo->ifName, ifname) != 0)
|
||
|
continue;
|
||
|
|
||
|
/* make sure destination = 0.0.0.0 for "default route" */
|
||
|
if (rtInfo->dstAddr.s_addr != 0)
|
||
|
continue;
|
||
|
|
||
|
/* found the gateway! */
|
||
|
*ipv4 = ntohl(rtInfo->gateWay.s_addr);
|
||
|
}
|
||
|
|
||
|
close(fd);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined(WIN32)
|
||
|
/* From:
|
||
|
* https://stackoverflow.com/questions/10972794/undefined-reference-to-getadaptersaddresses20-but-i-included-liphlpapi
|
||
|
* I think this fixes issue #734
|
||
|
*/
|
||
|
#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501
|
||
|
#undef _WIN32_WINNT
|
||
|
#define _WIN32_WINNT 0x501
|
||
|
#endif
|
||
|
|
||
|
#include <winsock2.h>
|
||
|
#include <iphlpapi.h>
|
||
|
#ifdef _MSC_VER
|
||
|
#pragma comment(lib, "IPHLPAPI.lib")
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
int rawsock_get_default_gateway(const char *ifname, unsigned *ipv4)
|
||
|
{
|
||
|
PIP_ADAPTER_INFO pAdapterInfo;
|
||
|
PIP_ADAPTER_INFO pAdapter = NULL;
|
||
|
DWORD err;
|
||
|
ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
|
||
|
|
||
|
/*
|
||
|
* Translate numeric index (if it exists) to real name
|
||
|
*/
|
||
|
ifname = rawsock_win_name(ifname);
|
||
|
//printf("------ %s -----\n", ifname);
|
||
|
|
||
|
/*
|
||
|
* Allocate a proper sized buffer
|
||
|
*/
|
||
|
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO));
|
||
|
if (pAdapterInfo == NULL) {
|
||
|
fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n");
|
||
|
return EFAULT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Query the adapter info. If the buffer is not big enough, loop around
|
||
|
* and try again
|
||
|
*/
|
||
|
again:
|
||
|
err = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
|
||
|
if (err == ERROR_BUFFER_OVERFLOW) {
|
||
|
free(pAdapterInfo);
|
||
|
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
|
||
|
if (pAdapterInfo == NULL) {
|
||
|
fprintf(stderr, "Error allocating memory needed to call GetAdaptersinfo\n");
|
||
|
return EFAULT;
|
||
|
}
|
||
|
goto again;
|
||
|
}
|
||
|
if (err != NO_ERROR) {
|
||
|
fprintf(stderr, "GetAdaptersInfo failed with error: %u\n",
|
||
|
(unsigned)err);
|
||
|
return EFAULT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* loop through all adapters looking for ours
|
||
|
*/
|
||
|
for ( pAdapter = pAdapterInfo;
|
||
|
pAdapter;
|
||
|
pAdapter = pAdapter->Next) {
|
||
|
if (rawsock_is_adapter_names_equal(pAdapter->AdapterName, ifname))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (pAdapter) {
|
||
|
//printf("\tComboIndex: \t%d\n", pAdapter->ComboIndex);
|
||
|
//printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName);
|
||
|
//printf("\tAdapter Desc: \t%s\n", pAdapter->Description);
|
||
|
|
||
|
|
||
|
//printf("\tAdapter Addr: \t");
|
||
|
/*for (i = 0; i < pAdapter->AddressLength; i++) {
|
||
|
if (i == (pAdapter->AddressLength - 1))
|
||
|
printf("%.2X\n", (int) pAdapter->Address[i]);
|
||
|
else
|
||
|
printf("%.2X-", (int) pAdapter->Address[i]);
|
||
|
}*/
|
||
|
//printf("\tIndex: \t%d\n", pAdapter->Index);
|
||
|
//printf("\tType: \t");
|
||
|
switch (pAdapter->Type) {
|
||
|
case MIB_IF_TYPE_OTHER:
|
||
|
//printf("Other\n");
|
||
|
break;
|
||
|
case MIB_IF_TYPE_ETHERNET:
|
||
|
//printf("Ethernet\n");
|
||
|
break;
|
||
|
case MIB_IF_TYPE_TOKENRING:
|
||
|
//printf("Token Ring\n");
|
||
|
break;
|
||
|
case MIB_IF_TYPE_FDDI:
|
||
|
//printf("FDDI\n");
|
||
|
break;
|
||
|
case MIB_IF_TYPE_PPP:
|
||
|
//printf("PPP\n");
|
||
|
break;
|
||
|
case MIB_IF_TYPE_LOOPBACK:
|
||
|
//printf("Lookback\n");
|
||
|
break;
|
||
|
case MIB_IF_TYPE_SLIP:
|
||
|
//printf("Slip\n");
|
||
|
break;
|
||
|
default:
|
||
|
//printf("Unknown type %ld\n", pAdapter->Type);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//printf("\tIP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String);
|
||
|
//printf("\tIP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String);
|
||
|
|
||
|
/*typedef struct _IP_ADDR_STRING {
|
||
|
struct _IP_ADDR_STRING* Next;
|
||
|
IP_ADDRESS_STRING IpAddress;
|
||
|
IP_MASK_STRING IpMask;
|
||
|
DWORD Context;
|
||
|
} IP_ADDR_STRING, *PIP_ADDR_STRING;*/
|
||
|
|
||
|
{
|
||
|
const IP_ADDR_STRING *addr;
|
||
|
|
||
|
for (addr = &pAdapter->GatewayList; addr; addr = addr->Next) {
|
||
|
unsigned x = massip_parse_ipv4(addr->IpAddress.String);
|
||
|
if (x != 0xFFFFFFFF) {
|
||
|
*ipv4 = x;
|
||
|
goto end;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//printf("\n");
|
||
|
}
|
||
|
end:
|
||
|
if (pAdapterInfo)
|
||
|
free(pAdapterInfo);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif
|