src/corosio/src/endpoint.cpp

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