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
|