1
\$\begingroup\$

I have the following working code to get IPv4/v6 addresses from host names:

#include <array>
using std::array;

#include <iostream>
using std::cout;

#include <netdb.h>

#include <stdexcept>
using std::domain_error;

#include <sys/socket.h>

#include <string>
using std::string;

#include <vector>
using std::vector;

#include <boost/asio.hpp>
using boost::asio::ip::address;
using boost::asio::ip::address_v4;
using boost::asio::ip::address_v6;

vector<address> getAddresses(string const &hostname)
{
    struct addrinfo req = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_STREAM};
    struct addrinfo *pai;
    int error = getaddrinfo(hostname.c_str(), nullptr, &req, &pai);
    if (error)
        throw domain_error("Could not resolve host name.");

    vector<address> addresses;

    for(struct addrinfo *info = pai; info != nullptr; info = info->ai_next) {
        if (info->ai_family == AF_INET) {
            auto ipv4socket = reinterpret_cast<struct sockaddr_in*>(info->ai_addr);
            auto ipv4addr = address_v4(htonl(ipv4socket->sin_addr.s_addr));
            addresses.emplace_back(ipv4addr);
        } else {
            auto ipv6socket = reinterpret_cast<struct sockaddr_in6*>(info->ai_addr);
            auto ipv6base = reinterpret_cast<unsigned char*>(ipv6socket->sin6_addr.s6_addr);
            array<unsigned char, 16> bytearray = {
                ipv6socket->sin6_addr.s6_addr[0],
                ipv6socket->sin6_addr.s6_addr[1],
                ipv6socket->sin6_addr.s6_addr[2],
                ipv6socket->sin6_addr.s6_addr[3],
                ipv6socket->sin6_addr.s6_addr[4],
                ipv6socket->sin6_addr.s6_addr[5],
                ipv6socket->sin6_addr.s6_addr[6],
                ipv6socket->sin6_addr.s6_addr[7],
                ipv6socket->sin6_addr.s6_addr[8],
                ipv6socket->sin6_addr.s6_addr[9],
                ipv6socket->sin6_addr.s6_addr[10],
                ipv6socket->sin6_addr.s6_addr[11],
                ipv6socket->sin6_addr.s6_addr[12],
                ipv6socket->sin6_addr.s6_addr[13],
                ipv6socket->sin6_addr.s6_addr[14],
                ipv6socket->sin6_addr.s6_addr[15]
            };
            auto ipv6addr = address_v6(bytearray, ipv6socket->sin6_scope_id);
            addresses.emplace_back(ipv6addr);
        }
    }

    return addresses;
}

int main()
{
    auto addresses = getAddresses("heise.de");

    for (auto ipa : addresses)
        cout << "Address: " << ipa << "\n";

    return 0;
}

I find the conversion fom uint8_t[16] (ipv6socket->sin6_addr.s6_addr) to std::array<unsigned char, 16> a bit verbose. I also considered doing it in a for-loop, but this would still seem cumbersome to me, somehow. I tried static_cast and reinterpret_cast to no avail. Is there a possibility to do this conversion in a simpler way, preferably a one-liner?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You object to a for loop, would you object to std::copy or something similar? \$\endgroup\$
    – harold
    May 20 at 11:29
  • \$\begingroup\$ I would not. I am explicitely looking for alternatives. \$\endgroup\$ May 20 at 11:31
1
\$\begingroup\$

I agree with you that the copying of data from s6_addr seems cumbersome. I would instead just cast without copying:

auto& bytearray = reinterpret_cast<std::array<unsigned char,16>&>(ipv6socket->sin6_addr.s6_addr);

(If we had needed to copy, we could change auto& to plain auto).

Apart from this, the variable ipv6base is never used, so should just be removed.

\$\endgroup\$
5
  • \$\begingroup\$ Thank you so much. Such a noob mistake from me. I forgot the ampersands. :facepalm: \$\endgroup\$ May 20 at 13:50
  • \$\begingroup\$ Oh, I've done the same myself from time to time. Easy to get wrong, and very hard to spot what's incorrect! \$\endgroup\$ May 20 at 14:08
  • \$\begingroup\$ Using a type-change cast like that, with modern compilers you have to beware of undefined behavior. The old tricks using a union or type puns are now technically illegal. How is this not falling into that trap? \$\endgroup\$
    – JDługosz
    May 20 at 14:11
  • \$\begingroup\$ Good point @JDługosz. I can't prove that this is legal. I think it's likely to be safe in this context. \$\endgroup\$ May 20 at 14:14
  • \$\begingroup\$ en.cppreference.com/w/cpp/language/reinterpret_cast "Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true: ... AliasedType is std::byte, char, or unsigned char: this permits examination of the object representation of any object as an array of bytes." \$\endgroup\$
    – JDługosz
    May 20 at 14:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.