Skip to content
Snippets Groups Projects
socketaddress.hpp 6.02 KiB
Newer Older
Benny Baumann's avatar
Benny Baumann committed
#pragma once

Benny Baumann's avatar
Benny Baumann committed
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>

#include <functional>
#include <string>
#include <sstream>
#include "macros.hpp"
Benny Baumann's avatar
Benny Baumann committed
#include "net/netio_exception.hpp"

#ifdef __linux__
#include <linux/netlink.h>
#endif

namespace rmrf::net {

template <typename T>
struct family_map {};

template <>
struct family_map<sockaddr_in> {
    static constexpr int sa_family = AF_INET;
    static constexpr auto sa_family_field = &sockaddr_in::sin_family;
};

template <>
struct family_map<sockaddr_in6> {
    static constexpr int sa_family = AF_INET6;
    static constexpr auto sa_family_field = &sockaddr_in6::sin6_family;
};

template <>
struct family_map<sockaddr_un> {
    static constexpr int sa_family = AF_UNIX;
    static constexpr auto sa_family_field = &sockaddr_un::sun_family;
};

template <>
struct family_map<sockaddr_nl> {
    static constexpr int sa_family = AF_NETLINK;
    static constexpr auto sa_family_field = &sockaddr_nl::nl_family;
};

template <typename, typename = void>
struct has_field : std::false_type {};

template <typename T>
struct has_field<T, std::void_t<decltype(family_map<T>::sa_family)>> : std::is_convertible<decltype(family_map<T>::sa_family), int> {};

class socketaddr {
private:
    sockaddr_storage addr;
    socklen_t len;

public:
    socketaddr() = default;
Benny Baumann's avatar
Benny Baumann committed

    template <typename T, typename std::enable_if<has_field<T>::value, T>::type * = nullptr>
    explicit socketaddr(T *other) : addr{}, len{} {
        if (other->*(family_map<T>::sa_family_field) != family_map<T>::sa_family) {
            throw netio_exception("Address family mismatch in sockaddr structure.");
        }

        memcpy(&addr, other, sizeof(T));
        len = sizeof(T);
    }

    explicit socketaddr(const sockaddr_storage *other) : addr{}, len{} {
        *this = other;
    }

    explicit socketaddr(const sockaddr_storage &other) : addr{}, len{} {
        *this = &other;
    }

Benny Baumann's avatar
Benny Baumann committed
    template <typename T, typename std::enable_if<has_field<T>::value, T>::type * = nullptr>
    explicit socketaddr(const T& other) : addr{}, len{} {
        if (other.*(family_map<T>::sa_family_field) != family_map<T>::sa_family) {
            throw netio_exception("Address family mismatch in sockaddr structure.");
        }

        memcpy(&addr, &other, sizeof(T));
        len = sizeof(T);
    }

    template <typename T>
    socketaddr& operator=(const T *rhs) {
Benny Baumann's avatar
Benny Baumann committed
        if (rhs->*(family_map<T>::sa_family_field) != family_map<T>::sa_family) {
            throw netio_exception("Address family mismatch in sockaddr structure.");
        }

        memcpy(&addr, rhs, sizeof(T));
        len = sizeof(T);

COMPILER_SUPRESS("-Weffc++");
        return *this;
COMPILER_RESTORE("-Weffc++");
    socketaddr& operator=(const sockaddr_storage *rhs) {
        *this = (sockaddr*)rhs;
COMPILER_SUPRESS("-Weffc++");
        return *this;
COMPILER_RESTORE("-Weffc++");
Benny Baumann's avatar
Benny Baumann committed
    }

    socketaddr& operator=(sockaddr *rhs) {
Benny Baumann's avatar
Benny Baumann committed
        switch (rhs->sa_family) {
Benny Baumann's avatar
Benny Baumann committed
        case AF_INET:
            return *this = (sockaddr_in *)rhs;
        case AF_INET6:
            return *this = (sockaddr_in6 *)rhs;
        case AF_UNIX:
            return *this = (sockaddr_un *)rhs;
        case AF_NETLINK:
            return *this = (sockaddr_nl *)rhs;
        default:
            throw netio_exception("Trying to assign unknown address family");
        }
    };

    int family() const {
        return addr.ss_family;
    }

    template <typename T>
    operator T*() const {
        if (addr.ss_family != family_map<T>::sa_family) {
            throw netio_exception("Cannot convert address family of stored sockaddr structure.");
        }

        return (T*)&addr;
    }

    sockaddr* ptr() const {
        return (sockaddr*)&addr;
    }

    socklen_t size() const {
        return len;
    }
    inline bool operator==(const socketaddr& _other) const {
        if (!(this->family() == _other.family())) {
            return false;
        }

        switch (_other.family()) {
        case AF_INET:
            return (((sockaddr_in*) &this->addr)->sin_addr.s_addr == ((sockaddr_in*) &_other.addr)->sin_addr.s_addr)
                    && (((sockaddr_in*) &this->addr)->sin_port == ((sockaddr_in*) &_other.addr)->sin_port);
        case AF_INET6:
            return 0 == memcmp(&((sockaddr_in6*) &this->addr)->sin6_addr, &((sockaddr_in6*) &_other.addr)->sin6_addr, sizeof(in6_addr))
                    && (((sockaddr_in6*) &this->addr)->sin6_port == ((sockaddr_in6*) &_other.addr)->sin6_port);
        default:
            return
                this->len == _other.len &&
                0 == memcmp(&this->addr, &_other.addr, this->len);
        }

        return true;
    }

    inline bool operator!=(const socketaddr& _other) const {
        return !(*this == _other);
    }

    std::string str() const {
        std::ostringstream oss;
        oss << "SocketAddress: ";
        const int buffer_size = 1024;
        char buffer[buffer_size];

        switch (this->family()) {
        case AF_INET:
            inet_ntop(AF_INET, &((sockaddr_in*)&addr)->sin_addr, buffer, buffer_size);
            oss << "IPv4 " << buffer << ":" << ntohs(((sockaddr_in*)&addr)->sin_port);
            break;
        case AF_INET6:
            inet_ntop(AF_INET6, &((sockaddr_in6*)&addr)->sin6_addr, buffer, buffer_size);
            oss << "IPv6 ["<< buffer << "]:" << ntohs(((sockaddr_in6*)&addr)->sin6_port);
            break;
        case AF_UNIX:
            oss << "FileSocket " << ((sockaddr_un*)&addr)->sun_path;
            break;
        case AF_NETLINK: {
            const auto nl_sock_ptr = (sockaddr_nl*) &addr;
            oss << "Netlink g:" << nl_sock_ptr->nl_groups << " p:" << nl_sock_ptr->nl_pad << " pid:" << nl_sock_ptr->nl_pid;
            break;
        }
        default:
            oss << "Unknown Socket Address Type";
            break;
        }

        return oss.str();
    }

static inline std::ostream& operator<<(std::ostream& os, const socketaddr& sockaddr) {
    return os << sockaddr.str();
}