Bug #34030 inet_ntoa/inet_nton do not convert from/to network byte order
Submitted: 24 Jan 2008 9:45 Modified: 18 Nov 2008 20:31
Reporter: Erik Romijn Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Documentation Severity:S3 (Non-critical)
Version:5.0.45 OS:Any
Assigned to: Paul DuBois CPU Architecture:Any
Tags: byte order, documentation, inet_aton, inet_ntoa

[24 Jan 2008 9:45] Erik Romijn
Description:
The functions for INET_NTOA() and INET_ATON() do not take byte order conversion into account.
This means that data generated with, for example, inet_aton or inet_pton from libc libraries can not be decoded with the built-in MySQL functions.

The manual explicitly states network byte order is used, although the example it gives actually shows host byte order as well.

The functions are consistent when used in a pair, e.g.: `select INET_NTOA(INET_ATON('192.0.2.1'));` works as expected, returning the same address.

I have only tried this on Linux, but the code gave no indication that this may be different on other platforms, except perhaps those where network byte order is the same as host byte order, as there conversion is not technically needed.

How to repeat:
An example:

4.23.113.0 fed through inet_ntoa(3) from my libc results in 7411460. When read by INET_NTOA, I get 0.113.23.4. When read by inet_aton(3) I get 4.23.113.0.

I can confirm that 7411460 is indeed in network byte order, by converting it back to host byte order with htonl(). This results in 68645120.
When divided by 255^3, this results in a bit more than 4. This confirms that 68645120 is the correct host byte order address, which confirms that 7411460 is the correct network byte order address.

Therefore, repeat by checking the output of `SELECT INET_NTOA(7411460);`
If 0.113.23.4, INET_NTOA() assumed host byte order.

Suggested fix:
Add appropriate htonl() / ntohl() statements to these statements.

The complexity of the implementation of these functions is sufficiently complex that I have not written a patch.
The reasons for not simply using inet_pton or inet_aton & friends in this code are unclear to me here - they would fix this issue as well, as they already do byte order conversion.
[24 Jan 2008 13:14] Erik Romijn
I wrote:
4.23.113.0 fed through inet_ntoa(3) from my libc results in 7411460. When read by
INET_NTOA, I get 0.113.23.4. When read by inet_aton(3) I get 4.23.113.0.

This should however say:
4.23.113.0 fed through inet_aton(3) from my libc results in 7411460. When read by
INET_NTOA, I get 0.113.23.4. When read by inet_ntoa(3) I get 4.23.113.0.
Whereas inet_aton/ntoa(3) refer to libc, and INET_NTOA refers to the MySQL built-in function.
[24 Jan 2008 15:10] Domas Mituzas
Taking endianness into account would produce different output on different platforms - what would limit platform portability. MySQL is known to have platform-independent data storage and behavior, so imposing such endianness would be harming overall model.
[24 Jan 2008 15:27] Erik Romijn
In that case this would be a bug in the documentation :-)
[24 Jan 2008 17:41] Valeriy Kravchuk
We should clearly describe intended behaviour and possible outcomes in the manual.
[25 Jan 2008 14:42] Erik Romijn
I'm afraid I've mixed up host and network byte order here: inet_ntoa & friends from libc give host byte order, MySQL INET_NTOA() & friend are always network byte order.

So, I would conclude that the manual is correct here.
[25 Jan 2008 14:52] Erik Romijn
(apologies for adding another comment)

However, I would suggest that the manual is clarified in this sense: that INET_NTOA from MySQL is not directly compatible with inet_ntoa from libc - the latter should be converted to network byte order before being placed in the database, if using INET_NTOA from MySQL is desirable.
[13 Nov 2008 17:06] Paul DuBois
I don't see the issue here. The bug report says:

inet_ntoa & friends from libc give host byte order

But the man page for these functions says:

inet_aton()  converts  the  Internet host address cp from the IPv4 numbers-and-dots notation into binary form (in network byte order) and stores it in the structure that inp points to.  inet_aton() returns non-zero if the address is valid, zero if not.  The address  supplied in cp can have one of the following forms:

The inet_ntoa() function converts the Internet host address in, given in network byte order, to a string in IPv4 dotted-decimal notation. The string is returned in a statically allocated buffer, which subsequent calls will overwrite.

In other words, both sets of functions use network byte order.
[18 Nov 2008 20:31] Paul DuBois
Thank you for your bug report. This issue has been addressed in the documentation. The updated documentation will appear on our website shortly, and will be included in the next release of the relevant products.

I've added a minor clarification that the argument to INET_NTOA() should be in network byte order. Otherwise, I see no issues to be addressed.
[21 Nov 2008 17:02] Clay McClure
IMHO, the MySQL documentation is still misleading. The INET_ATON() documentation states:

    mysql> SELECT INET_ATON('209.207.224.40');
            -> 3520061480

    The generated number is always in network byte order. For the example just shown, the number is
    calculated as 209×256^3 + 207×256^2 + 224×256 + 40.

The math listed in the documentation is irrelevant because computers don't treat IP addresses as sequences of bytes; they treat them as 32-bit integers. Any platform (little- or big-endian) will correctly represent an IP address internally when constructed as a sequence of bytes, as in the documentation, but the in-memory representation of the 32-bit integer will differ between little- and big-endian machines.

In C and other languages, inet_aton() does not return a sequence of bytes; it returns an unsigned 32-bit integer in network byte order (big endian). From the libc documentation:

     inet_aton(const char *cp, struct in_addr *pin);

    All Internet addresses are returned in network order (bytes ordered from left to right).

struct in_addr is defined as:

struct in_addr {
        in_addr_t s_addr;
};

and in_addr_t is defined as:

typedef __uint32_t      in_addr_t;      /* base type for internet address */

So we can see that in C, an IP address is just an unsigned 32-bit integer in network byte order. When we get a big-endian integer from inet_aton() and print it on a little-endian machine, the resulting decimal is not what we expect. On little-endian machines, we first need to swap the integer to host byte order with ntohl() before we can print it. This is because computers interpret integers as atomic 32-bit entities instead of as a sequence of bytes.

Contrast that with MySQL, which performs byte order translation behind the scenes. The sequence of bytes produced by INET_NTOA() may very well be in network byte order, but the decimal number that is returned by the SELECT is represented as if it were in host byte order. It is this behind-the-scenes byte order translation that makes MySQL database files portable between little- and big-endian machines, and I think that point should be spelled out in the documentation.