src/corosio/src/endpoint.cpp

92.7% Lines (76/82) 100.0% Functions (3/3)
src/corosio/src/endpoint.cpp
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #include <boost/corosio/endpoint.hpp>
11
12 #include <charconv>
13
14 namespace boost::corosio {
15
16 endpoint_format
17 52 detect_endpoint_format(std::string_view s) noexcept
18 {
19 52 if (s.empty())
20 return endpoint_format::ipv4_no_port;
21
22 // Bracketed IPv6
23 52 if (s[0] == '[')
24 20 return endpoint_format::ipv6_bracketed;
25
26 // Count colons
27 32 std::size_t colon_count = 0;
28 357 for (char c : s)
29 {
30 325 if (c == ':')
31 32 ++colon_count;
32 }
33
34 32 if (colon_count == 0)
35 9 return endpoint_format::ipv4_no_port;
36 23 if (colon_count == 1)
37 17 return endpoint_format::ipv4_with_port;
38 6 return endpoint_format::ipv6_no_port;
39 }
40
41 namespace {
42
43 // Parse port number from string
44 // Returns true on success
45 bool
46 26 parse_port(std::string_view s, std::uint16_t& port) noexcept
47 {
48 26 if (s.empty())
49 4 return false;
50
51 // No leading zeros allowed (except "0" itself)
52 22 if (s.size() > 1 && s[0] == '0')
53 2 return false;
54
55 20 unsigned long val = 0;
56 20 auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), val);
57 20 if (ec != std::errc{} || ptr != s.data() + s.size())
58 6 return false;
59 14 if (val > 65535)
60 4 return false;
61
62 10 port = static_cast<std::uint16_t>(val);
63 10 return true;
64 }
65
66 } // namespace
67
68 std::error_code
69 48 parse_endpoint(std::string_view s, endpoint& ep) noexcept
70 {
71 48 if (s.empty())
72 2 return std::make_error_code(std::errc::invalid_argument);
73
74 46 auto fmt = detect_endpoint_format(s);
75
76 46 switch (fmt)
77 {
78 8 case endpoint_format::ipv4_no_port:
79 {
80 8 ipv4_address addr;
81 8 auto ec = parse_ipv4_address(s, addr);
82 8 if (ec)
83 6 return ec;
84 2 ep = endpoint(addr, 0);
85 2 return {};
86 }
87
88 16 case endpoint_format::ipv4_with_port:
89 {
90 // Find the colon separating address and port
91 16 auto colon_pos = s.rfind(':');
92 16 if (colon_pos == std::string_view::npos)
93 return std::make_error_code(std::errc::invalid_argument);
94
95 16 auto addr_str = s.substr(0, colon_pos);
96 16 auto port_str = s.substr(colon_pos + 1);
97
98 16 ipv4_address addr;
99 16 auto ec = parse_ipv4_address(addr_str, addr);
100 16 if (ec)
101 return ec;
102
103 std::uint16_t port;
104 16 if (!parse_port(port_str, port))
105 10 return std::make_error_code(std::errc::invalid_argument);
106
107 6 ep = endpoint(addr, port);
108 6 return {};
109 }
110
111 4 case endpoint_format::ipv6_no_port:
112 {
113 4 ipv6_address addr;
114 4 auto ec = parse_ipv6_address(s, addr);
115 4 if (ec)
116 return ec;
117 4 ep = endpoint(addr, 0);
118 4 return {};
119 }
120
121 18 case endpoint_format::ipv6_bracketed:
122 {
123 // Must start with '[' and contain ']'
124 18 if (s.size() < 2 || s[0] != '[')
125 2 return std::make_error_code(std::errc::invalid_argument);
126
127 16 auto close_bracket = s.find(']');
128 16 if (close_bracket == std::string_view::npos)
129 2 return std::make_error_code(std::errc::invalid_argument);
130
131 14 auto addr_str = s.substr(1, close_bracket - 1);
132
133 14 ipv6_address addr;
134 14 auto ec = parse_ipv6_address(addr_str, addr);
135 14 if (ec)
136 2 return ec;
137
138 12 std::uint16_t port = 0;
139 12 if (close_bracket + 1 < s.size())
140 {
141 // There's something after ']'
142 10 if (s[close_bracket + 1] != ':')
143 6 return std::make_error_code(std::errc::invalid_argument);
144
145 10 auto port_str = s.substr(close_bracket + 2);
146 10 if (!parse_port(port_str, port))
147 6 return std::make_error_code(std::errc::invalid_argument);
148 }
149
150 6 ep = endpoint(addr, port);
151 6 return {};
152 }
153
154 default:
155 return std::make_error_code(std::errc::invalid_argument);
156 }
157 }
158
159 } // namespace boost::corosio
160