src/corosio/src/endpoint.cpp

92.7% Lines (76/82) 100.0% Functions (3/3)
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 52x detect_endpoint_format(std::string_view s) noexcept
18 {
19 52x if (s.empty())
20 return endpoint_format::ipv4_no_port;
21
22 // Bracketed IPv6
23 52x if (s[0] == '[')
24 20x return endpoint_format::ipv6_bracketed;
25
26 // Count colons
27 32x std::size_t colon_count = 0;
28 357x for (char c : s)
29 {
30 325x if (c == ':')
31 32x ++colon_count;
32 }
33
34 32x if (colon_count == 0)
35 9x return endpoint_format::ipv4_no_port;
36 23x if (colon_count == 1)
37 17x return endpoint_format::ipv4_with_port;
38 6x 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 26x parse_port(std::string_view s, std::uint16_t& port) noexcept
47 {
48 26x if (s.empty())
49 4x return false;
50
51 // No leading zeros allowed (except "0" itself)
52 22x if (s.size() > 1 && s[0] == '0')
53 2x return false;
54
55 20x unsigned long val = 0;
56 20x auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), val);
57 20x if (ec != std::errc{} || ptr != s.data() + s.size())
58 6x return false;
59 14x if (val > 65535)
60 4x return false;
61
62 10x port = static_cast<std::uint16_t>(val);
63 10x return true;
64 }
65
66 } // namespace
67
68 std::error_code
69 48x parse_endpoint(std::string_view s, endpoint& ep) noexcept
70 {
71 48x if (s.empty())
72 2x return std::make_error_code(std::errc::invalid_argument);
73
74 46x auto fmt = detect_endpoint_format(s);
75
76 46x switch (fmt)
77 {
78 8x case endpoint_format::ipv4_no_port:
79 {
80 8x ipv4_address addr;
81 8x auto ec = parse_ipv4_address(s, addr);
82 8x if (ec)
83 6x return ec;
84 2x ep = endpoint(addr, 0);
85 2x return {};
86 }
87
88 16x case endpoint_format::ipv4_with_port:
89 {
90 // Find the colon separating address and port
91 16x auto colon_pos = s.rfind(':');
92 16x if (colon_pos == std::string_view::npos)
93 return std::make_error_code(std::errc::invalid_argument);
94
95 16x auto addr_str = s.substr(0, colon_pos);
96 16x auto port_str = s.substr(colon_pos + 1);
97
98 16x ipv4_address addr;
99 16x auto ec = parse_ipv4_address(addr_str, addr);
100 16x if (ec)
101 return ec;
102
103 std::uint16_t port;
104 16x if (!parse_port(port_str, port))
105 10x return std::make_error_code(std::errc::invalid_argument);
106
107 6x ep = endpoint(addr, port);
108 6x return {};
109 }
110
111 4x case endpoint_format::ipv6_no_port:
112 {
113 4x ipv6_address addr;
114 4x auto ec = parse_ipv6_address(s, addr);
115 4x if (ec)
116 return ec;
117 4x ep = endpoint(addr, 0);
118 4x return {};
119 }
120
121 18x case endpoint_format::ipv6_bracketed:
122 {
123 // Must start with '[' and contain ']'
124 18x if (s.size() < 2 || s[0] != '[')
125 2x return std::make_error_code(std::errc::invalid_argument);
126
127 16x auto close_bracket = s.find(']');
128 16x if (close_bracket == std::string_view::npos)
129 2x return std::make_error_code(std::errc::invalid_argument);
130
131 14x auto addr_str = s.substr(1, close_bracket - 1);
132
133 14x ipv6_address addr;
134 14x auto ec = parse_ipv6_address(addr_str, addr);
135 14x if (ec)
136 2x return ec;
137
138 12x std::uint16_t port = 0;
139 12x if (close_bracket + 1 < s.size())
140 {
141 // There's something after ']'
142 10x if (s[close_bracket + 1] != ':')
143 6x return std::make_error_code(std::errc::invalid_argument);
144
145 10x auto port_str = s.substr(close_bracket + 2);
146 10x if (!parse_port(port_str, port))
147 6x return std::make_error_code(std::errc::invalid_argument);
148 }
149
150 6x ep = endpoint(addr, port);
151 6x return {};
152 }
153
154 default:
155 return std::make_error_code(std::errc::invalid_argument);
156 }
157 }
158
159 } // namespace boost::corosio
160