Search code examples
c++boostipc++17

Convert struct sockaddr_in6* to boost::asio::ip::address_v6


I am implementing a host name / ip address parser for a TCP/IP client / server program. I can successfully cast IPv4 addresses from sockaddr_in* to boost::asio::ip::address_v4 but I fail to get the IPv6 conversion from struct sockaddr_in6* to boost::asio::ip::address_v6 right:

#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);
        }
        /*
         * TODO: Implement IPv6 support.
        else {
            auto ipv6socket = reinterpret_cast<struct sockaddr_in6*>(info->ai_addr);
            auto ipv6base = reinterpret_cast<array<unsigned char, 16>>(ipv6socket->sin6_addr.__in6_u);
            auto ipv6addr = address_v6(ipv6base, ipv6socket->sin6_scope_id);
            addresses.emplace_back(ipv6addr);
        }
        */
    }

    return addresses;
}

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

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

    return 0;
}

Solution

  • Got it working:

    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);
                array<unsigned char, 16> bytes = {
                    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(bytes, ipv6socket->sin6_scope_id);
                addresses.emplace_back(ipv6addr);
            }
        }
    
        return addresses;
    }
    

    Next station: codereview