- Address Conversion Functions
- Allocating IP Addresses
- Manipulating IP Numbers
- What's Next
Manipulating IP Numbers
To ease the programming burden of turning IP numbers in string form into usable socket addresses, a number of conversion functions have been provided. These and other useful functions will be presented in the following sections.
Using the inet_addr(3) Function
The first function that you will learn about is an older function, which should probably no longer be used in new code. However, you will find it in a lot of existing network code, and so you should become familiar with it and know its limitations.
The synopsis for inet_addr(3) is as follows:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> unsigned long inet_addr(const char *string);
This function accepts an input C string argument string and parses the dotted-quad notation into a 32-bit Internet address value. The 32-bit value returned is in network byte order.
If the input argument string does not represent a valid dotted-quad address, the value INADDR_NONE is returned. Any other returned value represents the converted value.
Note
The 32-bit value returned by inet_addr(3) is in network byte order. Do not use htonl(3) on the returned value, because it is already in network byte order.
Caution
The inet_addr(3) does not establish a reason code in errno when INADDR_NONE is returned. So, do not test errno or report it when an error indication is returned by this function.
The program shown in Listing 3.3 is an example of how you would use this function. To compile and run the program, perform the following:
$ make inetaddr gcc -c -D_GNU_SOURCE -Wall inetaddr.c gcc inetaddr.o -o inetaddr $ ./inetaddr
The program in Listing 3.3, when it is run, converts a C string constant containing an IP number into a network sequenced 32-bit IP address. This value is then placed into an AF_INET socket address and bound to the socket.
Listing 3.3: inetaddr.c--Example Program Using inet_addr(3)
1: /* inetaddr.c: 2: * 3: * Example using inet_addr(3): 4: */ 5: #include <stdio.h> 6: #include <unistd.h> 7: #include <stdlib.h> 8: #include <errno.h> 9: #include <string.h> 10: #include <sys/types.h> 11: #include <sys/socket.h> 12: #include <netinet/in.h> 13: #include <arpa/inet.h> 14: 15: /* 16: * This function reports the error and 17: * exits back to the shell: 18: */ 19: static void 20: bail(const char *on_what) { 21: fputs(on_what,stderr); 22: fputc('\n',stderr); 23: exit(1); 24: } 25: 26: int 27: main(int argc,char **argv) { 28: int z; 29: struct sockaddr_in adr_inet;/* AF_INET */ 30: int len_inet; /* length */ 31: int sck_inet; /* Socket */ 32: 33: /* Create a Socket */ 34: sck_inet = socket(AF_INET,SOCK_STREAM,0); 35: 36: if ( sck_inet == -1 ) 37: bail("socket()"); 38: 39: /* Establish address */ 40: memset(&adr_inet,0,sizeof adr_inet); 41: 42: adr_inet.sin_family = AF_INET; 43: adr_inet.sin_port = htons(9000); 44: 45: adr_inet.sin_addr.s_addr = 46: inet_addr("127.0.0.95"); 47: 48: if ( adr_inet.sin_addr.s_addr == INADDR_NONE ) 49: bail("bad address."); 50: 51: len_inet = sizeof adr_inet; 52: 53: /* Bind it to the socket */ 54: z = bind(sck_inet, 55: (struct sockaddr *)&adr_inet, 56: len_inet); 57: 58: if ( z == -1 ) 59: bail("bind()"); 60: 61: /* Display our socket address */ 62: system("netstat -pa --tcp 2>/dev/null" 63: "| grep inetaddr"); 64: 65: return 0; 66: }
Note
If netstat(1) command on your system does not support the options used in lines 62 and 63 of Listing 3.3, substitute the following call if you have lsof installed:
system("lsof -i tcp | grep inetaddr");
The general program structure is very similar to the ones used in the previous chapter. However, the steps used to set up the socket address are a bit different. They are
-
The socket address structure is zeroed out in line 40. This is an optional step, but many find that this helps debugging should it become necessary.
-
The address family is established as AF_INET in line 42.
-
The port number has been established as port 9000 in this example (line 43).
-
The function inet_addr(3) is called in line 46 to convert the string constant "127.0.0.95" into a network 32-bit address. This value is stored into the socket address member adr_inet.sin_addr.s_addr.
-
The value returned from the conversion in step 4 is tested to see whether it matches the value INADDR_NONE in line 48. If it does, this indicates that the value provided in the C string was not a good Internet IP number (the program bails out in line 49 if this happens).
-
Finally, the length of the socket address is established in line 51 as before.
The above procedure has established the socket address in the variable adr_inet. This is later passed to bind(2) in line 54, which has not been covered yet. The bind(3) call just applies the address to the socket (the full discussion will otherwise be deferred for now).
The important thing that you accomplish with the use of the inet_addr(3) function is that you are spared from performing all parsing and testing of the input IP number.
Tip
Avoid using inet_addr(3) in new programs. Use the function inet_aton(3) instead.
The inet_addr(3) function has the limitation that it returns the value INADDR_NONE if the input argument is an invalid IP number. The limitation is that it also returns the value INADDR_NONE if you pass it the valid IP address of 255.255.255.255.
Tip
This creates a problem for programs like the ping(8) command where this is a valid broadcast address to use.
Running the program yields the following output:
$ ./inetaddr tcp 0 0 127.0.0.95:9000 *:* CLOSE 992/inetaddr $
The program invokes netstat(1) using grep to look for the program name inetaddr. Consequently, you see one output line showing the address established for the socket as 127.0.0.95:9000. You'll remember that the program arbitrarily chose the port number 9000 for this experiment.
The inet_aton(3) Function
The inet_aton(3) is an improved way to convert a string IP number into a 32-bit networked sequenced IP number. The synopsis of the function is given as follows:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *string, struct in_addr *addr);
The inet_aton(3) function accepts two arguments. They are
-
The input argument string, which contains the ASCII representation of the dotted-quad IP number.
-
The output argument addr is the structure that will be updated with the new IP address.
The return value of the function is nonzero (true) if the conversion succeeds. The value zero (false) is returned if the input address is incorrect. There is no error code established in errno, so its value should be ignored.
What is a little bit confusing about this function is the pointer required for argument two of this function call. If you define an AF_INET socket address as
struct sockaddr_in adr_inet; /* AF_INET */
the pointer that should be supplied as argument two of the inet_aton(3) function is the following:
&adr_inet.sin_addr
Review Listing 2.7 in Chapter 2, "Domains and Address Families," page 48, if this does
not seem clear to you. It will make more sense when you review the definition of the
sockaddr_in structure.
Listing 3.4 shows a program that calls upon inet_aton(3) instead of the older inet_addr(3) function that you learned about in the previous section. This program operates the same way, except that it is compiled and executed as follows:
$ make inetaton gcc -c -D_GNU_SOURCE -Wall inetaton.c gcc inetaton.o -o inetaton $ ./inetaton
Now, spend a few moments examining Listing 3.4. You'll find that the new function is invoked in lines 45 to 47.
Listing 3.4: inetaton.c--Using inet_aton(3)
1: /* inetaton.c: 2: * 3: * Example using inet_aton(3) : 4: */ 5: #include <stdio.h> 6: #include <unistd.h> 7: #include <stdlib.h> 8: #include <errno.h> 9: #include <string.h> 10: #include <sys/types.h> 11: #include <sys/socket.h> 12: #include <netinet/in.h> 13: #include <arpa/inet.h> 14: 15: /* 16: * This function reports the error and 17: * exits back to the shell: 18: */ 19: static void 20: bail(const char *on_what) { 21: fputs(on_what,stderr); 22: fputc('\n',stderr); 23: exit(1); 24: } 25: 26: int 27: main(int argc,char **argv) { 28: int z; 29: struct sockaddr_in adr_inet;/* AF_INET */ 30: int len_inet; /* length */ 31: int sck_inet; /* Socket */ 32: 33: /* Create a Socket */ 34: sck_inet = socket(AF_INET,SOCK_STREAM,0); 35: 36: if ( sck_inet == -1 ) 37: bail("socket()"); 38: 39: /* Establish address */ 40: memset(&adr_inet,0,sizeof adr_inet); 41: 42: adr_inet.sin_family = AF_INET; 43: adr_inet.sin_port = htons(9000); 44: 45: if ( !inet_aton("127.0.0.23", 46: &adr_inet.sin_addr) ) 47: bail("bad address."); 48: 49: len_inet = sizeof adr_inet; 50: 51: /* Bind it to the socket */ 52: z = bind(sck_inet, 53: (struct sockaddr *)&adr_inet, 54: len_inet); 55: 56: if ( z == -1 ) 57: bail("bind()"); 58: 59: /* Display our socket address */ 60: system("netstat -pa --tcp 2>/dev/null" 61: "| grep inetaton"); 62: 63: return 0; 64: }
Running this program yields the following results:
$ ./inetaton tcp 0 0 127.0.0.23:9000 *:* CLOSE 1007/inetaton >$
Note
If netstat(1) command on your system does not support the options used in lines 60 and 61 of Listing 3.4, substitute the following call if you have lsof installed:
system("lsof -i tcp | grep inetaton");
While the bulk of the program was the same as the previous one in Listing 3.3, the following steps are of particular importance:
-
The new function inet_aton(3) is invoked from within the if statement in line 45.
-
Note the second argument in line 46, given as the value &adr_inet.sin_addr. This is the required pointer for argument two.
-
If the return value of the function in line 45 is zero, this indicates the conversion failed, and line 47 is executed.
The program in Listing 3.4 shows you how easily the newer function inet_aton(3) can be put to work in place of the older function inet_addr(3). There are perhaps three things that you need to remember about this function:
The pointer in argument two always refers to the sockaddr_in member sin_addr (&adr_inet.sin_addr in the example program).
The return value indicates a Boolean success value. A return value of true (nonzero) means that the call succeeded, whereas false (zero) means that it failed.
Do not consult the value in errno. No meaningful code is established by inet_aton(3) for errno.
In the next section, you'll see how you can take a socket IP address and convert it back to a string for reporting purposes.
Using the inet_ntoa(3) Function
There are times when a socket address represents the address of a user that has connected to your server, or represents the sender of a UDP packet. The job of converting a network sequenced 32-bit value into dotted-quad notation is inconvenient. Hence, the inet_ntoa(3) function has been provided. The synopsis of the function is as follows:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> char *inet_ntoa(struct in_addr addr);
The function requires only one input argument addr. Note that the struct in_addr is an internal part of the Internet socket address. The address is converted into a static buffer, which is internal to the function. This character array pointer is returned as the return value. The results will be valid only until the next call to this function.
4 Review the sockaddr_in structure in Figure 2.3 of Chapter 2, "Domains and
Address Families," page 48. This will help you visualize the physical address structure that
you are working with.
If a socket address addr exists in your program as a sockaddr_in structure, then the following code shows how to use inet_ntoa(3) to perform the conversion. The IP number is converted to a string and reported, using the printf(3) function:
struct sockaddr_in addr; /* Socket Address */ printf("IP ADDR: %s\n", inet_ntoa(addr.sin_addr));
A complete example program is provided in Listing 3.5. To compile and run this program, the following steps are required:
$ make inetntoa gcc -c -D_GNU_SOURCE -Wall inetntoa.c gcc inetntoa.o -o inetntoa $ ./inetntoa
The program in Listing 3.5 uses the same steps to set up the address as did the previous example program. The function inet_ntoa(3) is called upon to allow the IP number to be displayed.
Listing 3.5: inetntoa.c--Demonstration of inet_ntoa(3) Function
1: /* inetntoa.c: 2: * 3: * Example using inet_ntoa(3): 4: */ 5: #include <stdio.h> 6: #include <unistd.h> 7: #include <stdlib.h> 8: #include <sys/types.h> 9: #include <sys/socket.h> 10: #include <netinet/in.h> 11: #include <arpa/inet.h> 12: 13: int 14: main(int argc,char **argv) { 15: struct sockaddr_in adr_inet;/* AF_INET */ 16: int len_inet; /* length */ 17: 18: /* 19: * Establish address (pretend we got 20: * this address from a connecting 21: * client): 22: */ 23: memset(&adr_inet,0,sizeof adr_inet); 24: 25: adr_inet.sin_family = AF_INET; 26: adr_inet.sin_port = htons(9000); 27: 28: if ( !inet_aton("127.0.0.23", 29: &adr_inet.sin_addr) ) 30: puts("bad address."); 31: 32: len_inet = sizeof adr_inet; 33: 34: /* 35: * Demonstrate use of inet_ntoa(3): 36: */ 37: printf("The IP Address is %s\n", 38: inet_ntoa(adr_inet.sin_addr)); 39: 40: return 0; 41: }
Now, review the steps that were taken in the program:
-
The structure adr_inet is declared as a sockaddr_in type in line 15. This is the form of the address that you'll work with most of the time.
-
Lines 23 to 32 set up the address, just as before. In this example, the socket(2) and bind(2) calls were omitted because they don't help in this illustration.
-
Line 37 shows a call to printf(3). Here, the statement calls upon inet_ntoa(3) in line 38 to convert the IP address in adr_inet to string form so that it can be printed.
The results returned from inet_ntoa(3) are valid only until the next call to this function.
Caution
Due to the limitation given in the previous note, if you use this function in threaded code, you must make certain that only one thread at a time calls this function. Failure to heed this advice will result in returned results being overwritten by other threads.
The program's output is shown as follows:
$ ./inetntoa The IP Address is 127.0.0.23 $
You know, because of the initialization in line 28, that this is the correct result. Line 38 converts this value back into a string.
Using inet_network(3)
There might be occasions in which it is more convenient to have the dotted-quad IP number converted into a 32-bit host-ordered value. This is more convenient when you are applying mask values to extract host or network bits from the addresses.
The function synopsis for inet_network(3) is as follows:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> unsigned long inet_network(const char *addr);
This function takes one input string containing a dotted quad address in argument addr. The return value is the 32-bit value of the IP address, but in host-order format. However, if the input value is malformed, the returned result will be 0xFFFFFFFF (all 1 bits).
Having the returned value in host-endian order means that you can safely assume constants for mask values and bit positions. If the returned value were in network-endian order, the constants and code would then be different for different CPU platforms.
An example of how inet_network(3) might be used is shown next. The following shows how to extract the network address from a class C address:
unsigned long net_addr; net_addr = inet_network("192.168.9.1") & 0xFFFFFF00;
The value assigned to net_addr would be the value 0xC0A80900 (or 192.168.9.0 in dotted-quad notation). The logical and operation masked out the low-order eight bits to arrive at the network ID without the host ID.
The example shown in Listing 3.6 illustrates how the inet_network(3) function can be used. The program also calls upon the htonl(3) function to display how the value looks in network-endian order.
Listing 3.6: network.c--Demonstration of the inet_network(3) Function
1: /* network.c: 2: * 3: * Example using inet_network(3): 4: */ 5: #include <stdio.h> 6: #include <unistd.h> 7: #include <stdlib.h> 8: #include <sys/types.h> 9: #include <sys/socket.h> 10: #include <netinet/in.h> 11: #include <arpa/inet.h> 12: 13: int 14: main(int argc,char **argv) { 15: int x; 16: const char *addr[] = { 17: "44.135.86.12", 18: "127.0.0.1", 19: "172.16.23.95", 20: "192.168.9.1" 21: }; 22: unsigned long net_addr; 23: 24: for ( x=0; x<4; ++x ) { 25: net_addr = inet_network(addr[x]); 26: printf("%14s = 0x%08lX net 0x%08lX\n", 27: addr[x],net_addr, 28: (unsigned long)htonl(net_addr)); 29: } 30: 31: return 0; 32: }
This program is compiled and run as follows:
$ make network gcc -c -D_GNU_SOURCE -Wall network.c gcc network.o -o network $
The steps used in the program shown in Listing 3.6 are as follows:
-
Four arbitrarily picked IP numbers are declared in lines 17 to 20 to initialize the array addr[].
-
Lines 24 to 29 loop through each of the four strings in the addr[] array, starting with the first.
-
The inet_network(3) function is called in line 25 to convert the string into a host-endian ordered 32-bit value representing the IP number given.
-
The printf(3) function is called in line 26 to illustrate the output values. The first is the original string that was given to inet_network(3) in line 25. The second value printed is the value returned by inet_network(3), which is in host-endian form. The last value displayed on the line is the network-endian ordered value. For Intel CPU platforms, the last two columns will display differently.
The program is run as follows:
$ ./network 44.135.86.12 = 0x2C87560C net 0x0C56872C 127.0.0.1 = 0x7F000001 net 0x0100007F 172.16.23.95 = 0xAC10175F net 0x5F1710AC 192.168.9.1 = 0xC0A80901 net 0x0109A8C0 $
This program was run on an Intel CPU running Linux. Consequently, because an Intel CPU is little-endian by design, its host-ordered value (second column) and the networked-ordered value (last column) appear different. If you run this same program on a big-endian machine, the last two columns will be identical.
Using the inet_lnaof(3) Function
The inet_lnaof(3) function converts the IP number contained in a socket address, which is in network byte order, to a host ID number with the network ID removed. The return value is in host-endian order.
This function saves you from having to determine the class of the IP number and then extracting the host ID portion. The function synopsis for inet_lnaof(3) is given as follows:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> unsigned long inet_lnaof(struct in_addr addr);
The input argument addr must be the struct in_addr member of the socket address that you will normally be working with. This value will be in network byte sequence, which is what the function expects. An example of how to invoke the function using a sockaddr_in address is given as follows:
struct sockaddr_in addr; /* Socket Address */ unsigned long host_id; /* Host ID number */ host_id = inet_lnaof(addr.sin_addr);
Table 3.5 shows some example values that can be supplied to the input of inet_lnaof(3) and the values that result. To make the reasons for the results clearer, the class of each example address is included in the table.
Table 3.5: Example Values Returned from inet_lnaof(3) (the Hexadecimal Values Are Host-Endian Ordered)
IP Number |
Class |
Hexadecimal |
Dotted-Quad |
44.135.86.12 |
A |
0087560C |
0.135.86.12 |
127.0.0.1 |
A |
00000001 |
0.0.0.1 |
172.16.23.95 |
B |
0000175F |
0.0.23.95 |
192.168.9.1 |
C |
00000001 |
0.0.0.1 |
You should notice in the table's class A examples only the first byte is zeroed in the returned result (review Figure 3.1 to visualize the class boundaries again if you need to). In the class B example, the upper 16 bits are zeroed in the returned result. Finally, the class C example in Table 3.5 zeroes out the upper 3 bytes of the address, leaving the host ID behind in the last byte.
Note
The input is in network-endian sequence. The returned value is in host-endian sequence.
Using the inet_netof(3) Function
The inet_netof(3) function is the companion to the inet_lnaof(3) function. The inet_netof(3) function returns the network ID instead of the host ID value. In all other respects, these functions are the same. The following lists the function synopsis:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> unsigned long inet_netof(struct in_addr addr);
Again, the input is the struct in_addr member of the socket address sockaddr_in structure that you'll normally be working with. An example of its use is given as follows:
struct sockaddr_in addr; /* Socket Address */ unsigned long net_id; /* Network ID number */ net_id = inet_netof(addr.sin_addr);
Table 3.6 shows the same example IP numbers used in Table 3.5. Table 3.6 shows the values returned for the function inet_netof(3) function, however.
Table 3.6: Example Values Returned from inet_netof(3) (the Hexadecimal Values Are Host-Endian Ordered)
IP Number |
Class |
Hexadecimal |
Dotted-Quad |
44.135.86.12 |
A |
0000002C |
0.0.0.44 |
127.0.0.1 |
A |
0000007F |
0.0.0.127 |
172.16.23.95 |
B |
0000AC10 |
0.0.172.16 |
192.168.9.1 |
C |
00C0A809 |
0.192.168.9 |
You might find the values in Table 3.6 to be a bit of a surprise. These return values are the network bits shifted right, in order to eliminate the host ID bits. What you are left with is the right-justified network ID number.
Note
The return values from inet_netof(3) are right-justified. The host ID bits are shifted out.
Using the inet_makeaddr(3) Function
With the functions inet_lnaof(3) and inet_netof(3), you have the ability to extract host and network ID values. To re-create a consolidated IP address with the network and host ID values combined, you need to use the inet_makeaddr(3) function. Its function synopsis is as follows:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> struct in_addr inet_makeaddr(int net,int host);
The arguments to the inet_makeaddr(3) function are described as follows:
-
The net argument is the network ID, right-justified and in host-endian order. This same value is returned from the function inet_netof(3).
-
The host argument is the host ID, which is in host-endian order. This same value is returned from the function inet_lnaof(3).
The value returned in the struct in_addr member of the sockaddr_in socket address. This value is in network-endian sequence, which is correct for the socket address.
A program has been provided in Listing 3.7 that uses the three functions inet_lnaof(3), inet_netof(3), and inet_makeaddr(3). The IP address is split apart from a sockaddr_in structure into its network and host ID parts. Then, the socket address is zeroed out and reconstructed from just the network and host ID parts.
Listing 3.7: makeaddr.c--Demonstration of inet_netof(3), inet_lnaof(3), and inet_makeaddr(3)
1: /* makeaddr.c: 2: * 3: * Demonstrate inet_lnaof, inet_netof 4: * and inet_makeaddr(3) functions; 5: */ 6: #include <stdio.h> 7: #include <unistd.h> 8: #include <stdlib.h> 9: #include <sys/types.h> 10: #include <sys/socket.h> 11: #include <netinet/in.h> 12: #include <arpa/inet.h> 13: 14: int 15: main(int argc,char **argv) { 16: int x; 17: struct sockaddr_in adr_inet;/* AF_INET */ 18: const char *addr[] = { 19: "44.135.86.12", 20: "127.0.0.1", 21: "172.16.23.95", 22: "192.168.9.1" 23: }; 24: unsigned long net, hst; 25: 26: for ( x=0; x<4; ++x ) { 27: /* 28: * Create a socket address: 29: */ 30: memset(&adr_inet,0,sizeof adr_inet); 31: adr_inet.sin_family = AF_INET; 32: adr_inet.sin_port = htons(9000); 33: if ( !inet_aton(addr[x], 34: &adr_inet.sin_addr) ) 35: puts("bad address."); 36: 37: /* 38: * Split address into Host & Net ID 39: */ 40: hst = inet_lnaof(adr_inet.sin_addr); 41: net = inet_netof(adr_inet.sin_addr); 42: 43: printf("%14s : net=0x%08lX host=0x%08lX\n", 44: inet_ntoa(adr_inet.sin_addr),net,hst); 45: 46: /* 47: * Zero the address to prove later that 48: * we can reconstruct this value: 49: */ 50: memset(&adr_inet,0,sizeof adr_inet); 51: adr_inet.sin_family = AF_INET; 52: adr_inet.sin_port = htons(9000); 53: 54: adr_inet.sin_addr = 55: inet_makeaddr(net,hst); 56: 57: /* 58: * Now display the reconstructed 59: * address: 60: */ 61: printf("%14s : %s\n\n", 62: "inet_makeaddr", 63: inet_ntoa(adr_inet.sin_addr)); 64: } 65: 66: return 0; 67: }
To compile the program in Listing 3.7, perform the following:
$ make makeaddr gcc -c -D_GNU_SOURCE -Wall makeaddr.c gcc makeaddr.o -o makeaddr $
The procedure used in Listing 3.7 is as follows:
-
The socket address is declared in line 17 as a sockaddr_in structure. This is the form of the socket address that you'll normally work with.
-
Four example addresses are declared in lines 18 to 23.
-
The loop starts in line 26, and iterates through each of the four example IP numbers declared in step 2.
-
Lines 30 to 35 set up a sockaddr_in socket address. Port 9000 was just an arbitrary port number used as an example.
-
The host ID is extracted from the socket address adr_inet in line 40.
-
The network ID is extracted from the socket address adr_inet in line 41.
-
The example IP number, network ID, and host ID numbers are reported in lines 43 and 44.
-
The entire socket address adr_inet is zeroed out in line 50. This was done to eliminate any doubt that the socket address is re-created later.
-
The address family and example port number are re-established in lines 51 and 52.
-
The IP number is reconstructed by calling inet_makeaddr(3) in lines 54 and 55. The input values are the extracted values net and hst from steps 5 and 6.
-
The reconstructed address is reported in lines 61 to 63.
Running the program yields the following results:
$ ./makeaddr 44.135.86.12 : net=0x0000002C host=0x0087560C inet_makeaddr : 44.135.86.12 127.0.0.1 : net=0x0000007F host=0x00000001 inet_makeaddr : 127.0.0.1 172.16.23.95 : net=0x0000AC10 host=0x0000175F inet_makeaddr : 172.16.23.95 192.168.9.1 : net=0x00C0A809 host=0x00000001 inet_makeaddr : 192.168.9.1 $
In each case, you can see for yourself that the program did indeed re-create the final IP numbers from the extracted host and network ID numbers.