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