Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C - inet_pton() not producing network byte order

I got confused with the inet_pton() function. According to the man page

This function converts the character string src into a network address structure in the af address family, then copies the network address structure to dst. The af argument must be either AF_INET or AF_INET6. dst is written in network byte order.

So the function produces a network byte order value. But I got this code:

 struct sockaddr_in a;
 char sip[20];
 inet_pton(AF_INET, "192.168.0.182", (void *)&a.sin_addr);
 inet_ntop(AF_INET, (void *)&a.sin_addr, sip, 20);

 printf("htonl:%08x\n", htonl(a.sin_addr.s_addr));
 printf("inet_pton:%08x\n", a.sin_addr.s_addr);
 printf("inet_ntop:%s\n", sip);

output:

htonl:c0a800b6
inet_pton:b600a8c0
inet_ntop:192.168.0.182

the output of inet_pton is b6.00.a8.c0, which converts to 182.0.168.192, and it's also different from the output of htonl.

Since htonl converts host byte order to network byte order, so if inet_pton produces network byte order, I suppose their outputs should be the same? Does it mean that inet_pton actually produces host byte order?

if inet_pton already produces network byte order, why do I need htonl to get the right value?

like image 833
27Blend Avatar asked Sep 02 '25 09:09

27Blend


1 Answers

Yes, inet_pton puts the address bytes into the destination buffer in network order. Let's go through an example to see what happens. Using your address of "192.168.0.182", inet_pton produces these four bytes:

c0   a8  00  b6    (hex)
192  168 0   182   (dec)

That is network byte order. When you then call htonl (which is not actually correct -- you should be calling ntohl to convert from network order to host order but as @ZanLynx pointed out, the two functions are identical on x86), you re-order the bytes to:

b6 00 a8 c0

But then you pass that form to printf with %x as the format. That tells printf to interpret the four bytes as a single 32-bit integer, but x86 is a little endian machine, so when it loads the four bytes as an integer, the b6 is the lowest order byte and the c0 is the highest, which produces what you saw:

htonl:c0a800b6

So, in general, if you have an IPv4 address in network form, and you want to quickly display it (for debugging or whatever) in an order that "makes sense" to you (as a programmer) you would use:

printf("%x\n", ntohl(a.sin_addr.s_addr));

You could also display the single bytes as they reside in network order (which is really the exact same thing but may be easier to wrap your head around), just use an unsigned char * (or equivalently uint8_t * from <stdint.h>) to print the individual bytes:

uint8_t *ipp = (void *)&a.sin_addr.s_addr;
printf("%02x %02x %02x %02x\n", ipp[0], ipp[1], ipp[2], ipp[3]);

(You need to use an unsigned type here to avoid sign extension. In the above statement, each char will be promoted to an int in the call to printf. If you have a signed char containing, say, 0xc0, it will typically be sign-extended into a 32-bit int: 0xffffffc0. As an alternative, you can use the format specification "%02hhx" which explicitly tells printf that you are really passing it a char; then it will only look at the lowest order byte of each promoted int.)

like image 200
Gil Hamilton Avatar answered Sep 05 '25 01:09

Gil Hamilton