Program Listing for File Netio3Backend.cpp

Return to documentation for file (Netio3Backend.cpp)

#include "netio3-backend/Netio3Backend.hpp"

#include <cstddef>
#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include "BackendAsyncmsg/BackendAsyncmsg.hpp"
#include "BackendLibfabric/BackendLibfabric.hpp"
#include "BackendLibfabric/BackendLibfabricConnectionless.hpp"

netio3::EndPointAddress::EndPointAddress(const std::string& ip, unsigned short port) :
  m_ip(ip), m_ip_numeric{convert_ip(m_ip)}, m_port(port)
{}

netio3::EndPointAddress::EndPointAddress(const std::string& str)
{
  const auto cpos = str.find(':');
  m_ip = str.substr(0, cpos);
  if (cpos != std::string::npos) {
    m_port = std::stoi(str.substr(cpos + 1));
  } else {
    m_port = 0;
  }
  m_ip_numeric = convert_ip(m_ip);
}

netio3::EndPointAddress::EndPointAddress(const sockaddr* addr, std::size_t addrlen)
{
  std::array<char, NI_MAXHOST> namebuf{};
  std::array<char, NI_MAXSERV> servbuf{};
  const int errcode = getnameinfo(addr,
                                  addrlen,
                                  namebuf.data(),
                                  namebuf.size(),
                                  servbuf.data(),
                                  servbuf.size(),
                                  NI_NUMERICHOST | NI_NUMERICSERV);
  if (errcode != 0) {
    if (errcode != EAI_SYSTEM) {
      throw std::invalid_argument(std::format("getnameinfo failed: {}", gai_strerror(errcode)));
    } else {
      throw std::invalid_argument(std::format("getnameinfo for {} failed: {}",
                                               namebuf.data(),
                                               utility::error_message(errno)));
    }
  }
  m_ip = namebuf.data();
  m_port = static_cast<unsigned short>(std::stoi(servbuf.data()));
  m_ip_numeric = convert_ip(m_ip);
}

sockaddr_storage netio3::EndPointAddress::to_sockaddr_storage() const
{
  // Try IPv4 first
  auto addr4 = sockaddr_in{};
  addr4.sin_family = AF_INET;
  addr4.sin_port = htons(m_port);

  if (inet_pton(AF_INET, m_ip.c_str(), &addr4.sin_addr) == 1) {
    // Successfully parsed as IPv4
    sockaddr_storage storage{};
    std::memcpy(&storage, &addr4, sizeof(addr4));
    return storage;
  }

  // Try IPv6
  auto addr6 = sockaddr_in6{};
  addr6.sin6_family = AF_INET6;
  addr6.sin6_port = htons(m_port);

  if (inet_pton(AF_INET6, m_ip.c_str(), &addr6.sin6_addr) == 1) {
    // Successfully parsed as IPv6
    sockaddr_storage storage{};
    std::memcpy(&storage, &addr6, sizeof(addr6));
    return storage;
  }

  // Neither IPv4 nor IPv6 worked
  throw std::invalid_argument(
    std::format("Invalid IP address format (neither IPv4 nor IPv6): {}", m_ip));
}

bool netio3::EndPointAddress::is_ipv4() const
{
  struct in_addr addr;
  return inet_pton(AF_INET, m_ip.c_str(), &addr) == 1;
}

bool netio3::EndPointAddress::is_ipv6() const
{
  struct in6_addr addr;
  return inet_pton(AF_INET6, m_ip.c_str(), &addr) == 1;
}

std::uint64_t netio3::EndPointAddress::convert_ip(const std::string& ip)
{
  std::uint32_t ip_numeric_v4{};
  if (inet_pton(AF_INET, ip.data(), &ip_numeric_v4) == 1) {
    return static_cast<std::uint64_t>(ip_numeric_v4);
  }

  struct in6_addr ipv6_addr;
  if (inet_pton(AF_INET6, ip.data(), &ipv6_addr) == 1) {
    std::hash<std::string> hasher;
    return hasher(ip);
  }

  throw std::invalid_argument(std::format("Invalid IP address format: {}", ip));
}

netio3::NetworkBackend::NetworkBackend(NetworkConfig config,
                                       std::shared_ptr<BaseEventLoop> evloop) :
  m_config{std::move(config)}, m_evloop{std::move(evloop)}
{}

std::unique_ptr<netio3::NetworkBackend> netio3::NetworkBackend::create(
  NetworkType type,
  const NetworkConfig& config,
  const std::shared_ptr<BaseEventLoop>& evloop)
{
  switch (type) {
  case NetworkType::LIBFABRIC:
    switch (config.mode) {
    case NetworkMode::RDM:
      return std::make_unique<libfabric::BackendLibfabricConnectionless>(config, evloop);
    default:
      return std::make_unique<libfabric::BackendLibfabric>(config, evloop);
    }
  case NetworkType::ASYNCMSG:
    return std::make_unique<asyncmsg::BackendAsyncmsg>(config, evloop);
  default:
    throw std::invalid_argument("Error: unknown network type.");
  }
}
namespace netio3 {
  std::ostream& operator<<(std::ostream& stream, const EndPointAddress& val)
  {
    return stream << val.m_ip << ":" << val.m_port;
  }

  std::ostream& operator<<(std::ostream& stream, const NetworkConfig& cfg)
  {
    return stream << "{mode=" << static_cast<int>(cfg.mode) << "}";
  }
}  // namespace netio3