Smartest way to compare IP addresses quickly? - c

I have a list of IP addresses, stored like this:
char IP_addresses_list[] = {
"157.55.130", /* 157.55.130.0/24 */
"157.56.52", /* 157.56.52.0/24 */
"157.12.53", /* 157.12.53.0/24 */
...
};
I get the IP address from the sniffed packet (casting it to struct iphdr *iph = (struct iphdr *)(packet + sizeof(struct ether_header)); I convert it in a character string using inet_ntop; finally, I compare the IP address from the packet with the ones in the list with the following code:
/*
* input: IP address to search in the list
* output: 1 if IP address is found in the list, 0 otherwise
*/
int find_IP_addr(char *server) {
int ret = 0;
int i, string_size1, string_size2;
char *copied_server, *copied_const_char;
char *save_ptr1, *save_ptr2;
char dot[2] = ".";
/* Here I store the IP address from the packet */
char first_IPaddr_pkt[4], second_IPaddr_pkt[4], third_IPaddr_pkt[4];
/* Here I store the IP address from the list */
char first_IPaddr_list[4], second_IPaddr_list[4], third_IPaddr_list[4];
string_size1 = strlen(server)+1;
copied_server = (char *)malloc(string_size1 * sizeof(char));
strcpy(copied_server, server);
/* I store and compare the first three bits of the IP address */
strcpy(first_IPaddr_pkt, strtok_r(copied_server, dot, &save_ptr1));
strcpy(second_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1));
strcpy(third_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1));
printf("tokenized %s, %s and %s\n", first_IPaddr_pkt, second_IPaddr_pkt, third_IPaddr_pkt);
/* Now I scan the list */
for (i=0; i<LIST_LENGTH; i++) {
/* I copy an address from the list */
string_size2 = strlen(IP_addresses_list[i])+1; // +1 for null character
copied_const_char = (char *)malloc(string_size2 * sizeof(char));
strcpy(copied_const_char, IP_addresses_list[i]);
/* Let's split the address from the list */
strcpy(first_IPaddr_list, strtok_r(copied_const_char, dot, &save_ptr2));
strcpy(second_IPaddr_list, strtok_r(NULL, dot, &save_ptr2));
strcpy(third_IPaddr_list, strtok_r(NULL, dot, &save_ptr2));
printf("tokenized %s, %s and %s\n", first_IPaddr_list, second_IPaddr_list, third_IPaddr_list);
/* I compare the first byte of the address from the packet I got and
the first byte of the address from the list:
if they are different, there's no reason to continue comparing
the other bytes of the addresses */
if (strcmp(first_IPaddr_pkt, first_IPaddr_list) != 0) {
continue;
}
else {
if (strcmp(second_IPaddr_pkt, second_IPaddr_list) != 0) {
continue;
}
else {
if (strcmp(third_IPaddr_pkt, third_IPaddr_list) != 0) {
continue;
}
else
/* All the bytes are the same! */
ret = 1;
}
}
free(copied_const_char);
}
free(copied_server);
return ret;
}
I'd like to make this more fast, without using strtok, strcmp, malloc or free.
In /usr/include/netinet/ip.h I see that addresses are
u_int32_t saddr;
u_int32_t daddr;
is it possible to compare without even using inet_ntop first, maybe just comparing the two addresses while they still are u_int32_t?
EDIT: here's a solution example for whoever will read this question.
#include <stdio.h>
#include <sys/types.h>
int main() {
// In the list I have: 104.40.0.0./13
int cidr = 13;
u_int32_t ipaddr_from_pkt = 1747488105; // pkt coming from 104.40.141.105
u_int32_t ipaddr_from_list = 1747451904; // 104.40.0.0
int mask = (-1) << (32 - cidr);
if ((ipaddr_from_pkt & mask) == ipaddr_from_list)
printf("IP address belongs to the given range!!!\n");
else printf ("failure\n");
return 0;
}
Thanks to iharob too for the bsearch hint.

I would avoid converting the binary data to strings. If you keep them binary then it's quite easy to compare:
match = (ip & listed_mask) == listed_ip;
"/24" is a mask. Means inly 24 highest bits are relevant. You convert it to binary mask as follows:
listed_mask = (-1) << (32 - 24);

The performance issues have nothing to do with strcmp(), malloc() is unnecessary though.
If you are only using IPv4 addresses you only need 16 characters to store it so you can remove malloc() and declare the temporary storage as an array.
But there is an important improvement if there are going to be many ip addresses in the list.
First you need to sort the list of IP addresses, and then use bsearch() to search for the right IP. This way the code will run in O(log(2n)) time which is a lot faster than O(N), specially for large N

My approach here would be:
simply use a strncat with ".0" to build valid IPv4 addresses.
use getaddrinfo with constant values for socket type etc to build a addrinfo struct
compare the relevant fields of the addrinfo.
Basically, the example from man getaddrinfo does all this.

The fastest way would be to store addresses in a dictionary, see this link

Related

Pass array that is part of struct as uint8_t pointer to function

I am working with the Renesas RA2A1 using their Flexible software package, trying to send data over a uart.
I am sending ints and floats over the uart, so I created a union of a float and a 4 byte uint8_t array, same for ints.
I put a few of these in a struct, and then put that in a union with an array that is the size of all the data contained in the struct.
I can't get it to work by passing the array in the struct to the function.. If I create an array of uint8_t, that passes in and works OK... I'm not sure what's wrong with trying to pass the array as I am.
It is failing an assert in R_SCI_UART_WRITE that checks the size, which is failing because it is 0.
typedef union{
float num_float;
uint32_t num_uint32;
int32_t num_int32;
uint8_t num_array[4];
} comms_data_t;
typedef struct{
comms_data_t a;
comms_data_t b;
comms_data_t c;
comms_data_t d;
comms_data_t e;
uint8_t lr[2];
} packet_data_t;
typedef union{
packet_data_t msg_packet_data;
uint8_t packet_array[22];
}msg_data_t;
/* Works */
uint8_t myData[10] = "Hi Dave!\r\n";
uart_print_main_processor_msg(myData);
/* Doesn't work */
msg_data_t msg_data;
/* code removed that puts data into msg_data,ex below */
msg_data.msg_packet_data.a.num_float = 1.2f;
uart_print_main_processor_msg(msg_data.packet_array);
// Functions below
/****************************************************************************************************************/
fsp_err_t uart_print_main_processor_msg(uint8_t *p_msg)
{
fsp_err_t err = FSP_SUCCESS;
uint8_t msg_len = RESET_VALUE;
uint32_t local_timeout = (DATA_LENGTH * UINT16_MAX);
char *p_temp_ptr = (char *)p_msg;
/* Calculate length of message received */
msg_len = ((uint8_t)(strlen(p_temp_ptr)));
/* Reset callback capture variable */
g_uart_event = RESET_VALUE;
/* Writing to terminal */
err = R_SCI_UART_Write (&g_uartMainProcessor_ctrl, p_msg, msg_len);
if (FSP_SUCCESS != err)
{
APP_ERR_PRINT ("\r\n** R_SCI_UART_Write API Failed **\r\n");
return err;
}
/* Check for event transfer complete */
while ((UART_EVENT_TX_COMPLETE != g_uart_event) && (--local_timeout))
{
/* Check if any error event occurred */
if (UART_ERROR_EVENTS == g_uart_event)
{
APP_ERR_PRINT ("\r\n** UART Error Event Received **\r\n");
return FSP_ERR_TRANSFER_ABORTED;
}
}
if(RESET_VALUE == local_timeout)
{
err = FSP_ERR_TIMEOUT;
}
return err;
}
fsp_err_t R_SCI_UART_Write (uart_ctrl_t * const p_api_ctrl, uint8_t const * const p_src, uint32_t const bytes)
{
#if (SCI_UART_CFG_TX_ENABLE)
sci_uart_instance_ctrl_t * p_ctrl = (sci_uart_instance_ctrl_t *) p_api_ctrl;
#if SCI_UART_CFG_PARAM_CHECKING_ENABLE || SCI_UART_CFG_DTC_SUPPORTED
fsp_err_t err = FSP_SUCCESS;
#endif
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
err = r_sci_read_write_param_check(p_ctrl, p_src, bytes);
FSP_ERROR_RETURN(FSP_SUCCESS == err, err);
FSP_ERROR_RETURN(0U == p_ctrl->tx_src_bytes, FSP_ERR_IN_USE);
#endif
/* Transmit interrupts must be disabled to start with. */
p_ctrl->p_reg->SCR &= (uint8_t) ~(SCI_SCR_TIE_MASK | SCI_SCR_TEIE_MASK);
/* If the fifo is not used the first write will be done from this function. Subsequent writes will be done
* from txi_isr. */
#if SCI_UART_CFG_FIFO_SUPPORT
if (p_ctrl->fifo_depth > 0U)
{
p_ctrl->tx_src_bytes = bytes;
p_ctrl->p_tx_src = p_src;
}
else
#endif
{
p_ctrl->tx_src_bytes = bytes - p_ctrl->data_bytes;
p_ctrl->p_tx_src = p_src + p_ctrl->data_bytes;
}
#if SCI_UART_CFG_DTC_SUPPORTED
/* If a transfer instance is used for transmission, reset the transfer instance to transmit the requested
* data. */
if ((NULL != p_ctrl->p_cfg->p_transfer_tx) && p_ctrl->tx_src_bytes)
{
uint32_t data_bytes = p_ctrl->data_bytes;
uint32_t num_transfers = p_ctrl->tx_src_bytes >> (data_bytes - 1);
p_ctrl->tx_src_bytes = 0U;
#if (SCI_UART_CFG_PARAM_CHECKING_ENABLE)
/* Check that the number of transfers is within the 16-bit limit. */
FSP_ASSERT(num_transfers <= SCI_UART_DTC_MAX_TRANSFER);
#endif
err = p_ctrl->p_cfg->p_transfer_tx->p_api->reset(p_ctrl->p_cfg->p_transfer_tx->p_ctrl,
(void const *) p_ctrl->p_tx_src,
NULL,
(uint16_t) num_transfers);
FSP_ERROR_RETURN(FSP_SUCCESS == err, err);
}
#endif
#if SCI_UART_CFG_FLOW_CONTROL_SUPPORT
if ((((sci_uart_extended_cfg_t *) p_ctrl->p_cfg->p_extend)->uart_mode == UART_MODE_RS485_HD) &&
(p_ctrl->flow_pin != SCI_UART_INVALID_16BIT_PARAM))
{
R_BSP_PinAccessEnable();
R_BSP_PinWrite(p_ctrl->flow_pin, BSP_IO_LEVEL_HIGH);
R_BSP_PinAccessDisable();
}
#endif
/* Trigger a TXI interrupt. This triggers the transfer instance or a TXI interrupt if the transfer instance is
* not used. */
p_ctrl->p_reg->SCR |= SCI_SCR_TIE_MASK;
#if SCI_UART_CFG_FIFO_SUPPORT
if (p_ctrl->fifo_depth == 0U)
#endif
{
/* On channels with no FIFO, the first byte is sent from this function to trigger the first TXI event. This
* method is used instead of setting TE and TIE at the same time as recommended in the hardware manual to avoid
* the one frame delay that occurs when the TE bit is set. */
if (2U == p_ctrl->data_bytes)
{
p_ctrl->p_reg->FTDRHL = *((uint16_t *) (p_src)) | (uint16_t) ~(SCI_UART_FIFO_DAT_MASK);
}
else
{
p_ctrl->p_reg->TDR = *(p_src);
}
}
return FSP_SUCCESS;
#else
FSP_PARAMETER_NOT_USED(p_api_ctrl);
FSP_PARAMETER_NOT_USED(p_src);
FSP_PARAMETER_NOT_USED(bytes);
return FSP_ERR_UNSUPPORTED;
#endif
}
There are several issues with this program. A large part of this code relies on undefined behavior. Unions are also UB if used for aliasing, even if pretty much all C compilers tend to allow it, but if you are using a union I would still prefer using a char[] for the array used for aliasing. As mentioned in the comments, "Hi Dave!\r\n"; actually takes up 11 bytes with the null-character. It's safer to use uint8_t myData[] = "Hi Dave!\r\n"; or const * uint8_t = "Hi Dave!\r\n"; and spare yourself the trouble.
Second problem is that strlen cannot work correctly for binary data. strlen works by searching for the first occurrence of the null-character in the string, so it's not applicable for binary data. If you pass a floating point value which has a single zero byte in its IEEE 754 representation, it will mark the end of this "string".
Plain and simple, your function should be declared as fsp_err_t uart_write(const char * msg, size_t msg_len); and be called using uart_write(data_array, sizeof data_array);. If you want to transmit messages of variable size over the UART, you will also have to define a certain communication protocol, i.e. create a message that can be unambiguously parsed. This will likely mean: 1) some cookie at the beginning, 2) length of the transmitted data, 3) actual data, 4) crc -- but this is outside the scope of this question.
So, strlen won't tell you the length of the data, you will pass it to the function yourself, and you don't need unions at all. If you choose not to properly serialize the data (e.g. using protobuf or some other protocol), you can simply pass the pointer to the struct to the function, i.e. call the above mentioned uart_write((char*)&some_struct, sizeof some_struct); and it will work as if you passed an array.
Note that char in this case doesn't mean "ascii character", or "character in a string". The point with using the char* is that it's the only pointer which is legally allowed to alias other pointers. So, you acquire a pointer to your struct (&str), cast it to a char*, and pass it to a function which can then read its representation in memory. I am aware that R_SCI_UART_Write is likely generated by your IDE, and unfortunately these blocks often use uint8_t* instead of char*, so you will probably have to cast to uint8_t* at some point.

Mismatch in assigning IP address to a buffer and printing contents of buffer

I am implementing raw sockets in C. In the code below, I am parsing the IP header I received from sender.
a. I will send back the ack as well so storing IP address received in a buffer(ret_ip).
b. I don't have another computer so using lo (local loop back) as my interface.
//first two printf statements are printing the right address, 10.100.207.74
//daddr SENT = 32.112.247.9saddr SENT = 36.112.247.9
How can I get it correct?
I think this problem is due to memcpy whose first argument is pointing to unsigned char while second argument is pointing to _be32.
What I actually want to do in my program is : ret_ip's first 4 bytes should contain the destination address and next 4 the source address. Then I will create IP header and make dest addr=source addr and source-addr=dest-addr. and send ACK to sender.
char* ParseIPHeader(unsigned char *packet,int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
char *ret_ip;
ethernet_header=(struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
printf("Dest IP address: %s\n",inet_ntoa(ip_header->daddr));
printf("Source IP address: %s\n",inet_ntoa(ip_header->saddr));
memcpy(ret_ip,&(ip_header->daddr),sizeof(ip_header->daddr));
memcpy(ret_ip+4,&(ip_header->saddr),4);
printf("daddr SENT = %s",inet_ntoa(ret_ip));
printf("saddr SENT = %s",inet_ntoa(ret_ip+4));
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return ret_ip;
}
Thanks :)
first problem is your memory allocation
ret_ip=malloc(2*(ip_header->daddr));
it should be
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
but why you are not using the ip_hdr struct again ? for example
struct iphdr *ret_ip = malloc(sizeof(iphdr));
ret_ip->daddr = ip_header->saddr;
ret_ip->saddr = ip_header->daddr;
i suggest this solution is much easier ;)

print strings from inside linux ntworking driver

I have written a linux networking driver.
This is my "hard_header" function:
int snull_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr,
unsigned int len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
pr_err("inside snull_header\n");
pr_err("THE DATA TO SEND BEFORE ADDITION IS:%s\n", skb->data);
pr_err("THE SOURCE IS:%s\n", (char*)saddr);
pr_err("THE DEST IS:%s\n", (char*)daddr);
eth->h_proto = htons(type);
memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len);
eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */
pr_err("THE DATA TO SEND AFTER ADDITION IS:%s\n", skb->data);
return (dev->hard_header_len);
}
This is the definition of pr_err (from printk.h):
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
When I run load this driver and try to send packet, I see all the prints, but instead of the strings of skb->data, source and destination I see gibberish.
My guess is that it's related somehow to the fact I'm referring to kernel memory, but on the other hand, this is what printk is for.
How can I print correctly these strings?
For the saddr and daddr, you can not print them in this way. You can print them with:
pr_err("THE SOURCE IS:%d.%d.%d.%d\n",
(0xff & saddr),
(0xff00 & saddr) >> 8,
(0xff000000 & saddr) >> 16
(0xff000000 & saddr) >>24);
For the skb->data, it's not null('\0') terminated so you can not printed as string with the format "%s". the limit of the skb->data determined by skb->len. You can print the content of the skb->data in this way.
int i;
for (i=0; i<skb->len; i++)
printk("%c", skb->data+i );
The contents of skb->data vary depending on which networking layer you are printing it. It can contain the header of previous layers even. So get the length of the data. And then print the data bytewise for the length. Then analyse what it contains. skb->data_len gives the size of data.

Iterate range of addresses between two IPV6 addresses

I need some way to iterate over the range of addresses between two IPv6 addresses. i.e. if the first IP is 2a03:6300:1:103:219:5bff:fe31:13e1 and the second one is 2a03:6300:1:103:219:5bff:fe31:13f4, I would like to visit the 19 addresses in that range.
With IPv4 I just do inet_aton for string representation and get htonl of s_addr in the resulting struct, but how can I do that for IPv6?
For simplify:
struct in6_addr sn,en;
long i;
s="2a03:6300:1:103:219:5bff:fe31:13e1";
e="2a03:6300:1:103:219:5bff:fe31:13f4";
inet_pton(AF_INET6,s,&sn);
inet_pton(AF_INET6,e,&en);
[..]
for (i = _first_ipv6_representation; i<=_second_ipv6_representation; i++){
/* stuck here */
}
Old answer stricken per your comments, updated to iterate a range of addresses:
char output[64];
struct in6_addr sn, en;
int octet;
s="2a03:6300:1:103:219:5bff:fe31:13e1";
e="2a03:6300:1:103:219:5bff:fe31:13f4";
inet_pton(AF_INET6,s,&sn);
inet_pton(AF_INET6,e,&en);
for ( ; ; ) {
/* print the address */
if (!inet_ntop(AF_INET6, &sn, output, sizeof(output))) {
perror("inet_ntop");
break;
}
printf("%s\n", output);
/* break if we hit the last address or (sn > en) */
if (memcmp(sn.s6_addr, en.s6_addr, 16) >= 0) break;
/* increment sn, and move towards en */
for (octet = 15; octet >= 0; --octet) {
if (sn.s6_addr[octet] < 255) {
sn.s6_addr[octet]++;
break;
} else sn.s6_addr[octet] = 0;
}
if (octet < 0) break; /* top of logical address range */
}
It's tricky indeed (I like this question). Basically you need to increment and compare integers that are stored like this: uint8_t s6_addr[16].
Find a cool way to convert those arrays to 128b integers and work from that
Define two functions inc_s6 and cmp_s6 that increment / compare such arrays
Here is an attempt at inc_s6:
void inc_s6(uint8_t *addr)
{
int i = 0;
for (i = 15; i >= 0; i--) {
if (++addr[i])
break;
}
}
The compare function is a lot easier.
For clarification:
I'm using that for some proxy-server that have a lot of binded IPv6 and to delegate new IP for each request.
My increment function with some additional explanation:
const char *s="2a03:6300:2:200:0:0:0:1"; // first ip in range
struct in6_addr sn;
inet_pton(AF_INET6,s,&sn);
static struct in6_addr cn = sn; //current ip in6_addr struct
unsigned int skipBits=126;
unsigned __int128 icn,skip; // works only with gcc
if (skipBits!=0){ // now we need to skip netmask bits to get next ip
skip=pow(2,(128-skipBits))-2;
u_int32_t swap;
swap=ntohl(cn.s6_addr32[3]);
cn.s6_addr32[3]=ntohl(cn.s6_addr32[0]);
cn.s6_addr32[0]=swap;
swap=ntohl(cn.s6_addr32[2]);
cn.s6_addr32[2]=ntohl(cn.s6_addr32[1]);
cn.s6_addr32[1]=swap;
memcpy(&icn,&cn,sizeof icn);
// increment, works very fast because gcc will compile it into sse2 intrinsic (double int64 operations)
icn+=skip;
memcpy(&cn,&icn,sizeof icn);
swap=ntohl(cn.s6_addr32[3]);
cn.s6_addr32[3]=ntohl(cn.s6_addr32[0]);
cn.s6_addr32[0]=swap;
swap=ntohl(cn.s6_addr32[2]);
cn.s6_addr32[2]=ntohl(cn.s6_addr32[1]);
cn.s6_addr32[1]=swap;
}
I don't show compare function because #sixlettervariables solution works good enough.
Python3:
import ipaddress
st_add=int(ips.ip_address(st_add).packed.hex(),16)
end_add=int(ips.ip_address(end_add).packed.hex(),16)
Ips=set(ips.ip_address(ip).compressed for ip in range(st_add,end_add))

Get an IP_ADDRESS_STRING from GetAdaptersAddresses()?

GetAdaptersAddresses() will get you addresses in IP_ADAPTER_UNICAST_ADDRESS format, which is defined as:
typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
union {
struct {
ULONG Length;
DWORD Flags;
} ;
} ;
struct _IP_ADAPTER_UNICAST_ADDRESS *Next;
SOCKET_ADDRESS Address;
IP_PREFIX_ORIGIN PrefixOrigin;
IP_SUFFIX_ORIGIN SuffixOrigin;
IP_DAD_STATE DadState;
ULONG ValidLifetime;
ULONG PreferredLifetime;
ULONG LeaseLifetime;
UINT8 OnLinkPrefixLength;
} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
The only field that seems to suggest the human-readable IP address string is Address, which is a SOCKET_ADDRESS structure defined as:
typedef struct _SOCKET_ADDRESS {
LPSOCKADDR lpSockaddr;
INT iSockaddrLength;
} SOCKET_ADDRESS, *PSOCKET_ADDRESS;
Which, in turn, uses another structure, SOCKADDR, defined as:
Sorry, it's way to complex to post here, as it varies depending on IPv4 vs. IPv6 and the Windows edition... so here is a link to the definition:
http://msdn.microsoft.com/en-us/library/ms740496%28v=VS.85%29.aspx
If you haven't gotten dizzy yet like I did and followed through this maze of definitions, you probably noticed that it's a nightmare to retrieve the good old dotted string style of an IP address, as it used to be much easier using GetAdaptersInfo().
My question is: Is there a truly IP Helper function that can convert IP_ADAPTER_UNICAST_ADDRESS to an IPv4 dotted string (or an IPv6 string)?
You can use GetIpAddrTable - the returned data structure contains a DWORD dwAddr that is the IPv4 address. The sample code on that first link should show you what you want. Brief excerpt to show you what I mean:
if ( (dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 )) != NO_ERROR ) {
printf("GetIpAddrTable failed with error %d\n", dwRetVal);
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) & lpMsgBuf, 0, NULL)) {
printf("\tError: %s", lpMsgBuf);
LocalFree(lpMsgBuf);
}
exit(1);
}
printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
The IP_ADAPTER_UNICAST_ADDRESS contains a SOCKET_ADDRESS in Address, which in turn contains a LPSOCKADDR in lpSockAddr - you can convert this to the ipv4 string form using WSAAddressToString.
Take a look at the documentation for SOCKADDR. That leads us to the documentation for SOCKADDR_STORAGE, which is a helper struct for both IPv4 and IPv6.
Quote from the sockaddr documentation:
Winsock functions using sockaddr are not strictly interpreted to be
pointers to a sockaddr structure. The structure is interpreted
differently in the context of different address families.
For ipv4, you can cast a sockaddr pointer to a sockaddr_in pointer and then access the IPv4 address information from there. Then you can use your favorite string builder to produce a dotted-quad formatted string.
sockaddr_in* address = (sockaddr_in*) temp->Address.lpSockaddr;
uint32_t ipv4 = address->sin_addr.S_un.S_addr;
// First octet: address->sin_addr.S_un.S_un_b.s_b1
// Second octet: address->sin_addr.S_un.S_un_b.s_b2
// Third octet: address->sin_addr.S_un.S_un_b.s_b3
// Fourth octet: address->sin_addr.S_un.S_un_b.s_b4
I would imagine that you can also cast the address for ipv6 in a similar way given the struct definitions (copied below).
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};
You can simply pass _SOCKET_ADDRESS.lpSockaddr and _SOCKET_ADDRESS.iSockaddrLength as lpsaAddress and dwAddressLength arguments to WSAAddressToString function. WSAAddressToString will do necessary conversion for you, no need to dig deeper.
function SOCKET_ADDRESS_ToString(const Addr: SOCKET_ADDRESS): String;
var
Len: DWORD;
begin
if (Addr.lpSockaddr = nil) or (Addr.iSockaddrLength <= 0) then
begin
Result := '';
Exit;
end;
Len := 0;
WSAAddressToString(Addr.lpSockaddr, Addr.iSockaddrLength, nil, nil, Len);
SetLength(Result, Len);
if WSAAddressToString(Addr.lpSockaddr, Addr.iSockaddrLength, nil, PChar(Result), Len) = 0 then
SetLength(Result, Len - 1)
else
Result := '';
end;

Resources