Search code examples
c++networkingendiannessreinterpret-cast

Effect of endianness on network byte order when doing reinterpret_cast


I am trying to convert a string representation of an IP address to its byte representation and back. For simplicity let's just talk about IPv4 addresses since endianness would affect both kinds of IP addresses in the same way.

Given a string in the form "168.212.226.204", I create an array of length 4, storing each byte of the IP address.

Here is the code:

std::array<uint8_t, 4> IpAddress;
const char* addr_str = "168.212.226.204";
struct in_addr in_addr = {};
if (inet_pton(AF_INET, addr_str , &in_addr) == 1) // successful conversion
{
    uint8_t* addr = reinterpret_cast<uint8_t*>(&(in_addr.s_addr));
    for (size_t i = 0; i < 4; i++)
    {
        IpAddress[i] = addr[i];
    }
  }

The inet_pton function returns its result in network byte order (big endian) and because I reinterpret the result as an array of bytes, I am sure that ipAdress[0] will always contain the MSB of the IP address. So in this regard, endianness should not be a problem at all.

For the conversion from byte array to string, I do the following:

char ip_str[INET_ADDRSTRLEN];
struct in_addr ip_addr = {};
ip_addr.s_addr = *(reinterpret_cast<uint32_t*>(IpAddress.data()));

if (inet_ntop(AF_INET, &ip_addr, ip_str, INET_ADDRSTRLEN) != NULL)
{
  return std::string(ip_str);
}

Now, reinterpret-casting the data of IpAddress will result in a different uint32_t depending on the endianness of the machine, but since I assign the value to an in_addr, just to call inet_ntop, I still hope that this code will work on any platform, even if inet_ntop would require network byte order. Is my code correct?

Edit: I have a little-endian machine and there it works fine. In the end, the returned string is equal to what I started with ("168.212.226.204").


Solution

  • Is my code correct?

    No, but the problem is not the endianness.

    First, with regard to endianness, the 32-bit value you load through the casted pointer will be in network byte order. Since you don't interpret or convert it and simply store it to another location, that stored value will still be in network byte order suitable for inet_ntop. This will be true on any platform. Note that the fact that you're using an unsigned integer to transfer the value is essential here, as signed integers are allowed to have trap representations, and loading an arbitrary value from memory could lead to undefined behavior in that case.

    The problem is that dereferencing the casted pointer requires the memory to be suitably aligned, which your code does not guarantee. You need to either enforce alignment of IpAddress to 4 bytes (e.g. using alignas) or use a different method of transferring the value that does not require alignment, such as memcpy. The latter is more preferable as it expresses your intention more clearly.

    std::memcpy(&ip_addr.s_addr, IpAddress.data(), IpAddress.size());