LCOV - code coverage report
Current view: top level - src/net - endpoint.cpp (source / functions) Coverage Total Hit
Test: HPActor Coverage Lines: 78.3 % 60 47
Test Date: 2026-05-20 02:24:49 Functions: 100.0 % 4 4
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : // Copyright 2026 HPActor Contributors
       2                 :             : //
       3                 :             : // Licensed under the Apache License, Version 2.0 (the "License");
       4                 :             : // you may not use this file except in compliance with the License.
       5                 :             : // You may obtain a copy of the License at
       6                 :             : //
       7                 :             : //     http://www.apache.org/licenses/LICENSE-2.0
       8                 :             : //
       9                 :             : // Unless required by applicable law or agreed to in writing, software
      10                 :             : // distributed under the License is distributed on an "AS IS" BASIS,
      11                 :             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :             : // See the License for the specific language governing permissions and
      13                 :             : // limitations under the License.
      14                 :             : 
      15                 :             : #include <arpa/inet.h>
      16                 :             : #include <cstdio>
      17                 :             : #include <hpactor/types/types.hpp>
      18                 :             : #include <netdb.h>
      19                 :             : #include <sys/socket.h>
      20                 :             : 
      21                 :             : namespace hpactor {
      22                 :             : namespace endpoint_ops {
      23                 :             : 
      24                 :           2 : Protocol protocol(const EndPoint& ep) {
      25                 :           2 :     if (std::holds_alternative<Ipv4Endpoint>(ep))
      26                 :           1 :         return Protocol::IPv4;
      27                 :           1 :     return Protocol::IPv6;
      28                 :             : }
      29                 :             : 
      30                 :           2 : int address_family(const EndPoint& ep) {
      31                 :           2 :     return std::holds_alternative<Ipv4Endpoint>(ep) ? AF_INET : AF_INET6;
      32                 :             : }
      33                 :             : 
      34                 :          15 : std::string to_string(const EndPoint& ep) {
      35                 :             :     char buf[64];
      36                 :          15 :     if (auto* ipv4 = std::get_if<Ipv4Endpoint>(&ep)) {
      37                 :             :         // addr is in network byte order, pass directly to inet_ntoa
      38                 :             :         struct in_addr addr;
      39                 :          14 :         addr.s_addr = ipv4->addr;
      40                 :          14 :         snprintf(buf, sizeof(buf), "%s:%u", inet_ntoa(addr), ipv4->port());
      41                 :          28 :         return std::string(buf);
      42                 :             :     }
      43                 :           1 :     if (auto* ipv6 = std::get_if<Ipv6Endpoint>(&ep)) {
      44                 :             :         struct in6_addr addr;
      45                 :           1 :         std::memcpy(addr.s6_addr, ipv6->addr.data(), 16);
      46                 :             :         char addrbuf[INET6_ADDRSTRLEN];
      47                 :           1 :         inet_ntop(AF_INET6, &addr, addrbuf, sizeof(addrbuf));
      48                 :           1 :         snprintf(buf, sizeof(buf), "[%s]:%u", addrbuf, ipv6->port());
      49                 :           2 :         return std::string(buf);
      50                 :             :     }
      51                 :           0 :     return "<invalid>";
      52                 :             : }
      53                 :             : 
      54                 :         136 : EndPoint parse_endpoint(std::string_view node_id) {
      55                 :             :     // Empty node_id means local node - return loopback endpoint
      56                 :         136 :     if (node_id.empty()) {
      57                 :           2 :         return LocalEndpoint;
      58                 :             :     }
      59                 :             : 
      60                 :             :     // Parse "host:port" format
      61                 :         134 :     auto colon_pos = node_id.find(':');
      62                 :         134 :     if (colon_pos == std::string_view::npos) {
      63                 :           0 :         return Ipv4Endpoint{0, 0}; // Unspecified on failure
      64                 :             :     }
      65                 :         134 :     std::string_view host = node_id.substr(0, colon_pos);
      66                 :         134 :     std::string_view port_str = node_id.substr(colon_pos + 1);
      67                 :             : 
      68                 :             :     // Parse port manually (no exceptions)
      69                 :         134 :     uint32_t port = 0;
      70                 :         652 :     for (char c : port_str) {
      71                 :         518 :         if (c >= '0' && c <= '9') {
      72                 :         518 :             port = port * 10 + static_cast<uint32_t>(c - '0');
      73                 :         518 :             if (port > 65535) {
      74                 :           0 :                 return Ipv4Endpoint{0, 0};
      75                 :             :             }
      76                 :             :         } else {
      77                 :           0 :             return Ipv4Endpoint{0, 0};
      78                 :             :         }
      79                 :             :     }
      80                 :             : 
      81                 :             :     // Try numeric IPv4 address first
      82                 :             :     struct in_addr addr;
      83                 :         268 :     if (inet_pton(AF_INET, std::string(host).c_str(), &addr) == 1) {
      84                 :             :         // inet_pton returns network byte order in s_addr
      85                 :          70 :         return Ipv4Endpoint{addr.s_addr, htons(static_cast<uint16_t>(port))};
      86                 :             :     }
      87                 :             : 
      88                 :             :     // Try numeric IPv6 address
      89                 :             :     struct in6_addr addr6;
      90                 :         128 :     if (inet_pton(AF_INET6, std::string(host).c_str(), &addr6) == 1) {
      91                 :             :         std::array<uint8_t, 16> StreamBuffer;
      92                 :           0 :         std::memcpy(StreamBuffer.data(), addr6.s6_addr, 16);
      93                 :           0 :         return Ipv6Endpoint{StreamBuffer, htons(static_cast<uint16_t>(port))};
      94                 :             :     }
      95                 :             : 
      96                 :             :     // Fallback: try DNS resolution via getaddrinfo for IPv4
      97                 :          64 :     struct addrinfo hints{};
      98                 :          64 :     hints.ai_family = AF_INET;
      99                 :          64 :     hints.ai_socktype = 0;
     100                 :          64 :     struct addrinfo* result = nullptr;
     101                 :          64 :     int ret = getaddrinfo(std::string(host).c_str(), nullptr, &hints, &result);
     102                 :          64 :     if (ret == 0 && result != nullptr) {
     103                 :          44 :         auto* ai = reinterpret_cast<struct sockaddr_in*>(result->ai_addr);
     104                 :             :         // sin_addr.s_addr is in network byte order
     105                 :          44 :         uint32_t addr_bytes = ai->sin_addr.s_addr;
     106                 :          44 :         freeaddrinfo(result);
     107                 :          44 :         return Ipv4Endpoint{addr_bytes, htons(static_cast<uint16_t>(port))};
     108                 :             :     }
     109                 :             : 
     110                 :             :     // Try IPv6 via DNS
     111                 :          20 :     if (result) {
     112                 :           0 :         freeaddrinfo(result);
     113                 :           0 :         result = nullptr;
     114                 :             :     }
     115                 :          20 :     hints.ai_family = AF_INET6;
     116                 :          20 :     ret = getaddrinfo(std::string(host).c_str(), nullptr, &hints, &result);
     117                 :          20 :     if (ret == 0 && result != nullptr) {
     118                 :           0 :         auto* ai = reinterpret_cast<struct sockaddr_in6*>(result->ai_addr);
     119                 :             :         std::array<uint8_t, 16> StreamBuffer;
     120                 :           0 :         std::memcpy(StreamBuffer.data(), ai->sin6_addr.s6_addr, 16);
     121                 :           0 :         freeaddrinfo(result);
     122                 :           0 :         return Ipv6Endpoint{StreamBuffer, htons(static_cast<uint16_t>(port))};
     123                 :             :     }
     124                 :          20 :     if (result) {
     125                 :           0 :         freeaddrinfo(result);
     126                 :             :     }
     127                 :             : 
     128                 :          20 :     return Ipv4Endpoint{0, 0}; // Unspecified on failure
     129                 :             :     if (result) {
     130                 :             :         freeaddrinfo(result);
     131                 :             :         result = nullptr;
     132                 :             :     }
     133                 :             :     hints.ai_family = AF_INET6;
     134                 :             :     ret = getaddrinfo(std::string(host).c_str(), nullptr, &hints, &result);
     135                 :             :     if (ret == 0 && result != nullptr) {
     136                 :             :         auto* ai = reinterpret_cast<struct sockaddr_in6*>(result->ai_addr);
     137                 :             :         std::array<uint8_t, 16> StreamBuffer;
     138                 :             :         std::memcpy(StreamBuffer.data(), ai->sin6_addr.s6_addr, 16);
     139                 :             :         freeaddrinfo(result);
     140                 :             :         return Ipv6Endpoint{StreamBuffer, htons(static_cast<uint16_t>(port))};
     141                 :             :     }
     142                 :             :     if (result) {
     143                 :             :         freeaddrinfo(result);
     144                 :             :     }
     145                 :             : 
     146                 :             :     return Ipv4Endpoint{0, 0}; // Unspecified on failure
     147                 :             : }
     148                 :             : 
     149                 :             : } // namespace endpoint_ops
     150                 :             : } // namespace hpactor
        

Generated by: LCOV version 2.0-1