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 : #ifndef BOOST_COROSIO_ENDPOINT_HPP
11 : #define BOOST_COROSIO_ENDPOINT_HPP
12 :
13 : #include <boost/corosio/detail/config.hpp>
14 : #include <boost/corosio/detail/except.hpp>
15 : #include <boost/corosio/ipv4_address.hpp>
16 : #include <boost/corosio/ipv6_address.hpp>
17 :
18 : #include <cstdint>
19 : #include <string_view>
20 : #include <system_error>
21 :
22 : namespace boost::corosio {
23 :
24 : /** An IP endpoint (address + port) supporting both IPv4 and IPv6.
25 :
26 : This class represents an endpoint for IP communication,
27 : consisting of either an IPv4 or IPv6 address and a port number.
28 : Endpoints are used to specify connection targets and bind addresses.
29 :
30 : The endpoint holds both address types as separate members (not a union),
31 : with a discriminator to track which address type is active.
32 :
33 : @par Thread Safety
34 : Distinct objects: Safe.@n
35 : Shared objects: Safe.
36 :
37 : @par Example
38 : @code
39 : // IPv4 endpoint
40 : endpoint ep4(ipv4_address::loopback(), 8080);
41 :
42 : // IPv6 endpoint
43 : endpoint ep6(ipv6_address::loopback(), 8080);
44 :
45 : // Port only (defaults to IPv4 any address)
46 : endpoint bind_addr(8080);
47 :
48 : // Parse from string
49 : endpoint ep;
50 : if (auto ec = parse_endpoint("192.168.1.1:8080", ep); !ec) {
51 : // use ep
52 : }
53 : @endcode
54 : */
55 : class endpoint
56 : {
57 : ipv4_address v4_address_;
58 : ipv6_address v6_address_;
59 : std::uint16_t port_ = 0;
60 : bool is_v4_ = true;
61 :
62 : public:
63 : /** Default constructor.
64 :
65 : Creates an endpoint with the IPv4 any address (0.0.0.0) and port 0.
66 : */
67 76045 : endpoint() noexcept
68 76045 : : v4_address_(ipv4_address::any())
69 76045 : , v6_address_{}
70 76045 : , port_(0)
71 76045 : , is_v4_(true)
72 : {
73 76045 : }
74 :
75 : /** Construct from IPv4 address and port.
76 :
77 : @param addr The IPv4 address.
78 : @param p The port number in host byte order.
79 : */
80 19089 : endpoint(ipv4_address addr, std::uint16_t p) noexcept
81 19089 : : v4_address_(addr)
82 19089 : , v6_address_{}
83 19089 : , port_(p)
84 19089 : , is_v4_(true)
85 : {
86 19089 : }
87 :
88 : /** Construct from IPv6 address and port.
89 :
90 : @param addr The IPv6 address.
91 : @param p The port number in host byte order.
92 : */
93 15 : endpoint(ipv6_address addr, std::uint16_t p) noexcept
94 15 : : v4_address_(ipv4_address::any())
95 15 : , v6_address_(addr)
96 15 : , port_(p)
97 15 : , is_v4_(false)
98 : {
99 15 : }
100 :
101 : /** Construct from port only.
102 :
103 : Uses the IPv4 any address (0.0.0.0), which binds to all
104 : available network interfaces.
105 :
106 : @param p The port number in host byte order.
107 : */
108 10 : explicit endpoint(std::uint16_t p) noexcept
109 10 : : v4_address_(ipv4_address::any())
110 10 : , v6_address_{}
111 10 : , port_(p)
112 10 : , is_v4_(true)
113 : {
114 10 : }
115 :
116 : /** Construct from an endpoint's address with a different port.
117 :
118 : Creates a new endpoint using the address from an existing
119 : endpoint but with a different port number.
120 :
121 : @param ep The endpoint whose address to use.
122 : @param p The port number in host byte order.
123 : */
124 2 : endpoint(endpoint const& ep, std::uint16_t p) noexcept
125 2 : : v4_address_(ep.v4_address_)
126 2 : , v6_address_(ep.v6_address_)
127 2 : , port_(p)
128 2 : , is_v4_(ep.is_v4_)
129 : {
130 2 : }
131 :
132 : /** Construct from a string.
133 :
134 : Parses an endpoint string in one of the following formats:
135 : @li IPv4 without port: `192.168.1.1`
136 : @li IPv4 with port: `192.168.1.1:8080`
137 : @li IPv6 without port: `::1` or `2001:db8::1`
138 : @li IPv6 with port (bracketed): `[::1]:8080`
139 :
140 : @param s The string to parse.
141 :
142 : @throws std::system_error on parse failure.
143 : */
144 : explicit endpoint(std::string_view s);
145 :
146 : /** Check if this endpoint uses an IPv4 address.
147 :
148 : @return `true` if the endpoint uses IPv4, `false` if IPv6.
149 : */
150 21 : bool is_v4() const noexcept
151 : {
152 21 : return is_v4_;
153 : }
154 :
155 : /** Check if this endpoint uses an IPv6 address.
156 :
157 : @return `true` if the endpoint uses IPv6, `false` if IPv4.
158 : */
159 12 : bool is_v6() const noexcept
160 : {
161 12 : return !is_v4_;
162 : }
163 :
164 : /** Get the IPv4 address.
165 :
166 : @return The IPv4 address. The value is valid even if
167 : the endpoint is using IPv6 (it will be the default any address).
168 : */
169 4844 : ipv4_address v4_address() const noexcept
170 : {
171 4844 : return v4_address_;
172 : }
173 :
174 : /** Get the IPv6 address.
175 :
176 : @return The IPv6 address. The value is valid even if
177 : the endpoint is using IPv4 (it will be the default any address).
178 : */
179 10 : ipv6_address v6_address() const noexcept
180 : {
181 10 : return v6_address_;
182 : }
183 :
184 : /** Get the port number.
185 :
186 : @return The port number in host byte order.
187 : */
188 4891 : std::uint16_t port() const noexcept
189 : {
190 4891 : return port_;
191 : }
192 :
193 : /** Compare endpoints for equality.
194 :
195 : Two endpoints are equal if they have the same address type,
196 : the same address value, and the same port.
197 :
198 : @return `true` if both endpoints are equal.
199 : */
200 54 : friend bool operator==(endpoint const& a, endpoint const& b) noexcept
201 : {
202 54 : if (a.is_v4_ != b.is_v4_)
203 0 : return false;
204 54 : if (a.port_ != b.port_)
205 0 : return false;
206 54 : if (a.is_v4_)
207 54 : return a.v4_address_ == b.v4_address_;
208 : else
209 0 : return a.v6_address_ == b.v6_address_;
210 : }
211 :
212 : /** Compare endpoints for inequality.
213 :
214 : @return `true` if endpoints differ.
215 : */
216 : friend bool operator!=(endpoint const& a, endpoint const& b) noexcept
217 : {
218 : return !(a == b);
219 : }
220 : };
221 :
222 : //------------------------------------------------
223 :
224 : /** Endpoint format detection result.
225 :
226 : Used internally by parse_endpoint to determine
227 : the format of an endpoint string.
228 : */
229 : enum class endpoint_format
230 : {
231 : ipv4_no_port, ///< "192.168.1.1"
232 : ipv4_with_port, ///< "192.168.1.1:8080"
233 : ipv6_no_port, ///< "::1" or "1:2:3:4:5:6:7:8"
234 : ipv6_bracketed ///< "[::1]" or "[::1]:8080"
235 : };
236 :
237 : /** Detect the format of an endpoint string.
238 :
239 : This helper function determines the endpoint format
240 : based on simple rules:
241 : 1. Starts with `[` -> `ipv6_bracketed`
242 : 2. Else count `:` characters:
243 : - 0 colons -> `ipv4_no_port`
244 : - 1 colon -> `ipv4_with_port`
245 : - 2+ colons -> `ipv6_no_port`
246 :
247 : @param s The string to analyze.
248 : @return The detected endpoint format.
249 : */
250 : BOOST_COROSIO_DECL
251 : endpoint_format
252 : detect_endpoint_format(std::string_view s) noexcept;
253 :
254 : /** Parse an endpoint from a string.
255 :
256 : This function parses an endpoint string in one of
257 : the following formats:
258 :
259 : @li IPv4 without port: `192.168.1.1`
260 : @li IPv4 with port: `192.168.1.1:8080`
261 : @li IPv6 without port: `::1` or `2001:db8::1`
262 : @li IPv6 with port (bracketed): `[::1]:8080`
263 :
264 : @par Example
265 : @code
266 : endpoint ep;
267 : if (auto ec = parse_endpoint("192.168.1.1:8080", ep); !ec) {
268 : // ep.is_v4() == true
269 : // ep.port() == 8080
270 : }
271 :
272 : if (auto ec = parse_endpoint("[::1]:443", ep); !ec) {
273 : // ep.is_v6() == true
274 : // ep.port() == 443
275 : }
276 : @endcode
277 :
278 : @param s The string to parse.
279 : @param ep The endpoint to store the result.
280 : @return An error code (empty on success).
281 : */
282 : [[nodiscard]] BOOST_COROSIO_DECL
283 : std::error_code
284 : parse_endpoint(
285 : std::string_view s,
286 : endpoint& ep) noexcept;
287 :
288 : inline
289 24 : endpoint::endpoint(std::string_view s)
290 : {
291 24 : auto ec = parse_endpoint(s, *this);
292 24 : if (ec)
293 15 : detail::throw_system_error(ec);
294 9 : }
295 :
296 : } // namespace boost::corosio
297 :
298 : #endif
|