Skip to content
Snippets Groups Projects

First unit tests

Merged Leon Dietrich requested to merge first_unit_tests into master
2 files
+ 245
0
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 196
0
#include "net/sock_address_factory.hpp"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <net/address.hpp>
namespace rmrf::net {
std::string format_network_error(int error) {
switch (error) {
case EAI_ADDRFAMILY:
return "There was no compatible address for the requested families. (EAI_ADDRFAMILY)";
case EAI_AGAIN:
return "The consulted DNS server reported a temporary lookup failure. Try again later. (EAI_AGAIN)";
case EAI_BADFLAGS:
return "The supplied host information did not suffice to identify a host. (EAI_BADFLAGS)";
case EAI_FAIL:
return "The DNS server crashed. (EAI_FAIL)";
case EAI_FAMILY:
return "The required protocol family is not supported by this host. (EAI_FAMILY)";
case EAI_MEMORY:
return "Out of Memory while performing DNS lookup. (EAI_MEMORY)";
case EAI_NODATA:
return "The requested DNS entry does not contain an A or AAAA entry. (EAI_NODATA)";
case EAI_NONAME:
return "There was no matching nodename, service tuple. (EAI_NONAME)";
case EAI_SERVICE:
return "The requested service entry is not avaiable with the requested socket type. (EAI_SERVICE)";
case EAI_SOCKTYPE:
return "There was a mismatch of the requested socket type (TCP and UDP are mutually exclusive). (EAI_SOCKTYPE)";
default:
return std::to_string(error);
}
}
bool decode_address(std::list<socketaddr> &l, addrinfo* looked_up_addrs, const int port) {
if (looked_up_addrs->ai_family == AF_UNIX) {
const sockaddr_un* unix_addr = (sockaddr_un*) looked_up_addrs->ai_addr;
const sockaddr_un addr{*unix_addr};
socketaddr sa{addr};
l.push_back(sa);
} else if (looked_up_addrs->ai_family == AF_INET6) {
const sockaddr_in6* ip6_addr = (sockaddr_in6*) looked_up_addrs->ai_addr;
sockaddr_in6 addr{*ip6_addr};
if (port != -1) {
addr.sin6_port = htons((uint16_t) port);
}
socketaddr sa{addr};
l.push_back(sa);
} else if (looked_up_addrs->ai_family == AF_INET) {
const sockaddr_in* ip6_addr = (sockaddr_in*) looked_up_addrs->ai_addr;
sockaddr_in addr{*ip6_addr};
if (port != -1) {
addr.sin_port = htons((uint16_t) port);
}
socketaddr sa{addr};
l.push_back(sa);
} else {
throw std::invalid_argument("The resulting address family should exist. Instead it is " + std::to_string(looked_up_addrs->ai_family));
}
return true;
}
bool is_plain_ip_address(const std::string &interface_description) {
if (
interface_description.starts_with("[") &&
interface_description.ends_with("]") &&
is_valid_ip6addr(
interface_description.substr(1, interface_description.length() - 2)
)
) {
return true;
}
return is_valid_ip4addr(interface_description) || is_valid_ip6addr(interface_description);
}
socketaddr parse_plain_ip_address(const std::string &interface_description, const uint16_t port, const socket_t socket_type) {
(void)socket_type;
if (interface_description.find(':') != std::string::npos) {
// Assume IPv6
struct in6_addr addr;
if (interface_description.starts_with("[") && interface_description.ends_with("]")) {
const std::string tmp = interface_description.substr(1, interface_description.length() - 2);
if (::inet_pton(AF_INET6, tmp.c_str(), &addr) != 1) {
return {};
}
} else {
if (::inet_pton(AF_INET6, interface_description.c_str(), &addr) != 1) {
return {};
}
}
sockaddr_in6 addr_ip6;
addr_ip6.sin6_family = AF_INET6;
addr_ip6.sin6_addr = addr;
addr_ip6.sin6_port = htons(port);
socketaddr sa{addr_ip6};
return sa;
} else {
// Assume IPv4
struct in_addr addr;
if (::inet_pton(AF_INET, interface_description.c_str(), &addr) != 1) {
return {};
}
sockaddr_in addr_ip4;
addr_ip4.sin_family = AF_INET;
addr_ip4.sin_addr = addr;
addr_ip4.sin_port = htons(port);
socketaddr sa{addr_ip4};
return sa;
}
}
std::list<socketaddr> get_socketaddr_list(const std::string &interface_description, const std::string &service_or_port, const socket_t socket_type) {
int port = -1;
try {
port = (uint16_t) std::stoi(service_or_port);
} catch (const std::invalid_argument &_) {
// Could not parse port. Continue as service
}
// TODO sort to prioritize local addresses over more remote ones
if (port != -1 && is_plain_ip_address(interface_description)) {
std::list<socketaddr> l = {parse_plain_ip_address(interface_description, (uint16_t) port, socket_type)};
return l;
}
struct addrinfo hints = {};
struct addrinfo* addrs;
hints.ai_family = AF_INET6;
hints.ai_socktype = socket_type == socket_t::TCP ? SOCK_STREAM : SOCK_DGRAM;
hints.ai_protocol = socket_type == socket_t::TCP ? IPPROTO_TCP : IPPROTO_UDP;
if (auto dns_error = getaddrinfo(interface_description.c_str(), port == -1 ? service_or_port.c_str() : NULL, &hints, &addrs); dns_error != 0) {
hints.ai_family = AF_UNSPEC;
dns_error = getaddrinfo(interface_description.c_str(), NULL, &hints, &addrs);
if (dns_error != 0) {
freeaddrinfo(addrs);
throw std::invalid_argument("Something went wrong with the DNS lookup. Error code: " + format_network_error(dns_error));
}
}
std::list<socketaddr> l;
for (auto result_ptr = addrs; result_ptr != NULL; result_ptr = result_ptr->ai_next) {
decode_address(l, result_ptr, port);
}
freeaddrinfo(addrs);
return l;
}
socketaddr get_first_general_socketaddr(const std::string &interface_description, const std::string &service, const socket_t socket_type) {
const auto l = get_socketaddr_list(interface_description, service, socket_type);
if (l.size() > 0) {
return l.front();
} else {
throw std::invalid_argument("No suitable socket address was found.");
}
}
socketaddr get_first_general_socketaddr(const std::string &interface_description, const uint16_t port, const socket_t socket_type) {
return get_first_general_socketaddr(interface_description, std::to_string(port), socket_type);
}
}
Loading