1  
//
1  
//
2  
// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_ENDPOINT_HPP
10  
#ifndef BOOST_COROSIO_ENDPOINT_HPP
11  
#define BOOST_COROSIO_ENDPOINT_HPP
11  
#define BOOST_COROSIO_ENDPOINT_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/except.hpp>
14  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/ipv4_address.hpp>
15  
#include <boost/corosio/ipv4_address.hpp>
16  
#include <boost/corosio/ipv6_address.hpp>
16  
#include <boost/corosio/ipv6_address.hpp>
17  

17  

18  
#include <cstdint>
18  
#include <cstdint>
19  
#include <string_view>
19  
#include <string_view>
20  
#include <system_error>
20  
#include <system_error>
21  

21  

22  
namespace boost::corosio {
22  
namespace boost::corosio {
23  

23  

24  
/** An IP endpoint (address + port) supporting both IPv4 and IPv6.
24  
/** An IP endpoint (address + port) supporting both IPv4 and IPv6.
25  

25  

26  
    This class represents an endpoint for IP communication,
26  
    This class represents an endpoint for IP communication,
27  
    consisting of either an IPv4 or IPv6 address and a port number.
27  
    consisting of either an IPv4 or IPv6 address and a port number.
28  
    Endpoints are used to specify connection targets and bind addresses.
28  
    Endpoints are used to specify connection targets and bind addresses.
29  

29  

30  
    The endpoint holds both address types as separate members (not a union),
30  
    The endpoint holds both address types as separate members (not a union),
31  
    with a discriminator to track which address type is active.
31  
    with a discriminator to track which address type is active.
32  

32  

33  
    @par Thread Safety
33  
    @par Thread Safety
34  
    Distinct objects: Safe.@n
34  
    Distinct objects: Safe.@n
35  
    Shared objects: Safe.
35  
    Shared objects: Safe.
36  

36  

37  
    @par Example
37  
    @par Example
38  
    @code
38  
    @code
39  
    // IPv4 endpoint
39  
    // IPv4 endpoint
40  
    endpoint ep4(ipv4_address::loopback(), 8080);
40  
    endpoint ep4(ipv4_address::loopback(), 8080);
41  

41  

42  
    // IPv6 endpoint
42  
    // IPv6 endpoint
43  
    endpoint ep6(ipv6_address::loopback(), 8080);
43  
    endpoint ep6(ipv6_address::loopback(), 8080);
44  

44  

45  
    // Port only (defaults to IPv4 any address)
45  
    // Port only (defaults to IPv4 any address)
46  
    endpoint bind_addr(8080);
46  
    endpoint bind_addr(8080);
47  

47  

48  
    // Parse from string
48  
    // Parse from string
49  
    endpoint ep;
49  
    endpoint ep;
50  
    if (auto ec = parse_endpoint("192.168.1.1:8080", ep); !ec) {
50  
    if (auto ec = parse_endpoint("192.168.1.1:8080", ep); !ec) {
51  
        // use ep
51  
        // use ep
52  
    }
52  
    }
53  
    @endcode
53  
    @endcode
54  
*/
54  
*/
55  
class endpoint
55  
class endpoint
56  
{
56  
{
57  
    ipv4_address v4_address_;
57  
    ipv4_address v4_address_;
58  
    ipv6_address v6_address_;
58  
    ipv6_address v6_address_;
59  
    std::uint16_t port_ = 0;
59  
    std::uint16_t port_ = 0;
60  
    bool is_v4_ = true;
60  
    bool is_v4_ = true;
61  

61  

62  
public:
62  
public:
63  
    /** Default constructor.
63  
    /** Default constructor.
64  

64  

65  
        Creates an endpoint with the IPv4 any address (0.0.0.0) and port 0.
65  
        Creates an endpoint with the IPv4 any address (0.0.0.0) and port 0.
66  
    */
66  
    */
67  
    endpoint() noexcept
67  
    endpoint() noexcept
68  
        : v4_address_(ipv4_address::any())
68  
        : v4_address_(ipv4_address::any())
69  
        , v6_address_{}
69  
        , v6_address_{}
70  
        , port_(0)
70  
        , port_(0)
71  
        , is_v4_(true)
71  
        , is_v4_(true)
72  
    {
72  
    {
73  
    }
73  
    }
74  

74  

75  
    /** Construct from IPv4 address and port.
75  
    /** Construct from IPv4 address and port.
76  

76  

77  
        @param addr The IPv4 address.
77  
        @param addr The IPv4 address.
78  
        @param p The port number in host byte order.
78  
        @param p The port number in host byte order.
79  
    */
79  
    */
80  
    endpoint(ipv4_address addr, std::uint16_t p) noexcept
80  
    endpoint(ipv4_address addr, std::uint16_t p) noexcept
81  
        : v4_address_(addr)
81  
        : v4_address_(addr)
82  
        , v6_address_{}
82  
        , v6_address_{}
83  
        , port_(p)
83  
        , port_(p)
84  
        , is_v4_(true)
84  
        , is_v4_(true)
85  
    {
85  
    {
86  
    }
86  
    }
87  

87  

88  
    /** Construct from IPv6 address and port.
88  
    /** Construct from IPv6 address and port.
89  

89  

90  
        @param addr The IPv6 address.
90  
        @param addr The IPv6 address.
91  
        @param p The port number in host byte order.
91  
        @param p The port number in host byte order.
92  
    */
92  
    */
93  
    endpoint(ipv6_address addr, std::uint16_t p) noexcept
93  
    endpoint(ipv6_address addr, std::uint16_t p) noexcept
94  
        : v4_address_(ipv4_address::any())
94  
        : v4_address_(ipv4_address::any())
95  
        , v6_address_(addr)
95  
        , v6_address_(addr)
96  
        , port_(p)
96  
        , port_(p)
97  
        , is_v4_(false)
97  
        , is_v4_(false)
98  
    {
98  
    {
99  
    }
99  
    }
100  

100  

101  
    /** Construct from port only.
101  
    /** Construct from port only.
102  

102  

103  
        Uses the IPv4 any address (0.0.0.0), which binds to all
103  
        Uses the IPv4 any address (0.0.0.0), which binds to all
104  
        available network interfaces.
104  
        available network interfaces.
105  

105  

106  
        @param p The port number in host byte order.
106  
        @param p The port number in host byte order.
107  
    */
107  
    */
108  
    explicit endpoint(std::uint16_t p) noexcept
108  
    explicit endpoint(std::uint16_t p) noexcept
109  
        : v4_address_(ipv4_address::any())
109  
        : v4_address_(ipv4_address::any())
110  
        , v6_address_{}
110  
        , v6_address_{}
111  
        , port_(p)
111  
        , port_(p)
112  
        , is_v4_(true)
112  
        , is_v4_(true)
113  
    {
113  
    {
114  
    }
114  
    }
115  

115  

116  
    /** Construct from an endpoint's address with a different port.
116  
    /** Construct from an endpoint's address with a different port.
117  

117  

118  
        Creates a new endpoint using the address from an existing
118  
        Creates a new endpoint using the address from an existing
119  
        endpoint but with a different port number.
119  
        endpoint but with a different port number.
120  

120  

121  
        @param ep The endpoint whose address to use.
121  
        @param ep The endpoint whose address to use.
122  
        @param p The port number in host byte order.
122  
        @param p The port number in host byte order.
123  
    */
123  
    */
124  
    endpoint(endpoint const& ep, std::uint16_t p) noexcept
124  
    endpoint(endpoint const& ep, std::uint16_t p) noexcept
125  
        : v4_address_(ep.v4_address_)
125  
        : v4_address_(ep.v4_address_)
126  
        , v6_address_(ep.v6_address_)
126  
        , v6_address_(ep.v6_address_)
127  
        , port_(p)
127  
        , port_(p)
128  
        , is_v4_(ep.is_v4_)
128  
        , is_v4_(ep.is_v4_)
129  
    {
129  
    {
130  
    }
130  
    }
131  

131  

132  
    /** Construct from a string.
132  
    /** Construct from a string.
133  

133  

134  
        Parses an endpoint string in one of the following formats:
134  
        Parses an endpoint string in one of the following formats:
135  
        @li IPv4 without port: `192.168.1.1`
135  
        @li IPv4 without port: `192.168.1.1`
136  
        @li IPv4 with port: `192.168.1.1:8080`
136  
        @li IPv4 with port: `192.168.1.1:8080`
137  
        @li IPv6 without port: `::1` or `2001:db8::1`
137  
        @li IPv6 without port: `::1` or `2001:db8::1`
138  
        @li IPv6 with port (bracketed): `[::1]:8080`
138  
        @li IPv6 with port (bracketed): `[::1]:8080`
139  

139  

140  
        @param s The string to parse.
140  
        @param s The string to parse.
141  

141  

142  
        @throws std::system_error on parse failure.
142  
        @throws std::system_error on parse failure.
143  
    */
143  
    */
144  
    explicit endpoint(std::string_view s);
144  
    explicit endpoint(std::string_view s);
145  

145  

146  
    /** Check if this endpoint uses an IPv4 address.
146  
    /** Check if this endpoint uses an IPv4 address.
147  

147  

148  
        @return `true` if the endpoint uses IPv4, `false` if IPv6.
148  
        @return `true` if the endpoint uses IPv4, `false` if IPv6.
149  
    */
149  
    */
150  
    bool is_v4() const noexcept
150  
    bool is_v4() const noexcept
151  
    {
151  
    {
152  
        return is_v4_;
152  
        return is_v4_;
153  
    }
153  
    }
154  

154  

155  
    /** Check if this endpoint uses an IPv6 address.
155  
    /** Check if this endpoint uses an IPv6 address.
156  

156  

157  
        @return `true` if the endpoint uses IPv6, `false` if IPv4.
157  
        @return `true` if the endpoint uses IPv6, `false` if IPv4.
158  
    */
158  
    */
159  
    bool is_v6() const noexcept
159  
    bool is_v6() const noexcept
160  
    {
160  
    {
161  
        return !is_v4_;
161  
        return !is_v4_;
162  
    }
162  
    }
163  

163  

164  
    /** Get the IPv4 address.
164  
    /** Get the IPv4 address.
165  

165  

166  
        @return The IPv4 address. The value is valid even if
166  
        @return The IPv4 address. The value is valid even if
167  
        the endpoint is using IPv6 (it will be the default any address).
167  
        the endpoint is using IPv6 (it will be the default any address).
168  
    */
168  
    */
169  
    ipv4_address v4_address() const noexcept
169  
    ipv4_address v4_address() const noexcept
170  
    {
170  
    {
171  
        return v4_address_;
171  
        return v4_address_;
172  
    }
172  
    }
173  

173  

174  
    /** Get the IPv6 address.
174  
    /** Get the IPv6 address.
175  

175  

176  
        @return The IPv6 address. The value is valid even if
176  
        @return The IPv6 address. The value is valid even if
177  
        the endpoint is using IPv4 (it will be the default any address).
177  
        the endpoint is using IPv4 (it will be the default any address).
178  
    */
178  
    */
179  
    ipv6_address v6_address() const noexcept
179  
    ipv6_address v6_address() const noexcept
180  
    {
180  
    {
181  
        return v6_address_;
181  
        return v6_address_;
182  
    }
182  
    }
183  

183  

184  
    /** Get the port number.
184  
    /** Get the port number.
185  

185  

186  
        @return The port number in host byte order.
186  
        @return The port number in host byte order.
187  
    */
187  
    */
188  
    std::uint16_t port() const noexcept
188  
    std::uint16_t port() const noexcept
189  
    {
189  
    {
190  
        return port_;
190  
        return port_;
191  
    }
191  
    }
192  

192  

193  
    /** Compare endpoints for equality.
193  
    /** Compare endpoints for equality.
194  

194  

195  
        Two endpoints are equal if they have the same address type,
195  
        Two endpoints are equal if they have the same address type,
196  
        the same address value, and the same port.
196  
        the same address value, and the same port.
197  

197  

198  
        @return `true` if both endpoints are equal.
198  
        @return `true` if both endpoints are equal.
199  
    */
199  
    */
200  
    friend bool operator==(endpoint const& a, endpoint const& b) noexcept
200  
    friend bool operator==(endpoint const& a, endpoint const& b) noexcept
201  
    {
201  
    {
202  
        if (a.is_v4_ != b.is_v4_)
202  
        if (a.is_v4_ != b.is_v4_)
203  
            return false;
203  
            return false;
204  
        if (a.port_ != b.port_)
204  
        if (a.port_ != b.port_)
205  
            return false;
205  
            return false;
206  
        if (a.is_v4_)
206  
        if (a.is_v4_)
207  
            return a.v4_address_ == b.v4_address_;
207  
            return a.v4_address_ == b.v4_address_;
208  
        else
208  
        else
209  
            return a.v6_address_ == b.v6_address_;
209  
            return a.v6_address_ == b.v6_address_;
210  
    }
210  
    }
211  

211  

212  
    /** Compare endpoints for inequality.
212  
    /** Compare endpoints for inequality.
213  

213  

214  
        @return `true` if endpoints differ.
214  
        @return `true` if endpoints differ.
215  
    */
215  
    */
216  
    friend bool operator!=(endpoint const& a, endpoint const& b) noexcept
216  
    friend bool operator!=(endpoint const& a, endpoint const& b) noexcept
217  
    {
217  
    {
218  
        return !(a == b);
218  
        return !(a == b);
219  
    }
219  
    }
220  
};
220  
};
221  

221  

222  
//------------------------------------------------
222  
//------------------------------------------------
223  

223  

224  
/** Endpoint format detection result.
224  
/** Endpoint format detection result.
225  

225  

226  
    Used internally by parse_endpoint to determine
226  
    Used internally by parse_endpoint to determine
227  
    the format of an endpoint string.
227  
    the format of an endpoint string.
228  
*/
228  
*/
229  
enum class endpoint_format
229  
enum class endpoint_format
230  
{
230  
{
231  
    ipv4_no_port,      ///< "192.168.1.1"
231  
    ipv4_no_port,      ///< "192.168.1.1"
232  
    ipv4_with_port,    ///< "192.168.1.1:8080"
232  
    ipv4_with_port,    ///< "192.168.1.1:8080"
233  
    ipv6_no_port,      ///< "::1" or "1:2:3:4:5:6:7:8"
233  
    ipv6_no_port,      ///< "::1" or "1:2:3:4:5:6:7:8"
234  
    ipv6_bracketed     ///< "[::1]" or "[::1]:8080"
234  
    ipv6_bracketed     ///< "[::1]" or "[::1]:8080"
235  
};
235  
};
236  

236  

237  
/** Detect the format of an endpoint string.
237  
/** Detect the format of an endpoint string.
238  

238  

239  
    This helper function determines the endpoint format
239  
    This helper function determines the endpoint format
240  
    based on simple rules:
240  
    based on simple rules:
241  
    1. Starts with `[` -> `ipv6_bracketed`
241  
    1. Starts with `[` -> `ipv6_bracketed`
242  
    2. Else count `:` characters:
242  
    2. Else count `:` characters:
243  
       - 0 colons -> `ipv4_no_port`
243  
       - 0 colons -> `ipv4_no_port`
244  
       - 1 colon -> `ipv4_with_port`
244  
       - 1 colon -> `ipv4_with_port`
245  
       - 2+ colons -> `ipv6_no_port`
245  
       - 2+ colons -> `ipv6_no_port`
246  

246  

247  
    @param s The string to analyze.
247  
    @param s The string to analyze.
248  
    @return The detected endpoint format.
248  
    @return The detected endpoint format.
249  
*/
249  
*/
250  
BOOST_COROSIO_DECL
250  
BOOST_COROSIO_DECL
251  
endpoint_format
251  
endpoint_format
252  
detect_endpoint_format(std::string_view s) noexcept;
252  
detect_endpoint_format(std::string_view s) noexcept;
253  

253  

254  
/** Parse an endpoint from a string.
254  
/** Parse an endpoint from a string.
255  

255  

256  
    This function parses an endpoint string in one of
256  
    This function parses an endpoint string in one of
257  
    the following formats:
257  
    the following formats:
258  

258  

259  
    @li IPv4 without port: `192.168.1.1`
259  
    @li IPv4 without port: `192.168.1.1`
260  
    @li IPv4 with port: `192.168.1.1:8080`
260  
    @li IPv4 with port: `192.168.1.1:8080`
261  
    @li IPv6 without port: `::1` or `2001:db8::1`
261  
    @li IPv6 without port: `::1` or `2001:db8::1`
262  
    @li IPv6 with port (bracketed): `[::1]:8080`
262  
    @li IPv6 with port (bracketed): `[::1]:8080`
263  

263  

264  
    @par Example
264  
    @par Example
265  
    @code
265  
    @code
266  
    endpoint ep;
266  
    endpoint ep;
267  
    if (auto ec = parse_endpoint("192.168.1.1:8080", ep); !ec) {
267  
    if (auto ec = parse_endpoint("192.168.1.1:8080", ep); !ec) {
268  
        // ep.is_v4() == true
268  
        // ep.is_v4() == true
269  
        // ep.port() == 8080
269  
        // ep.port() == 8080
270  
    }
270  
    }
271  

271  

272  
    if (auto ec = parse_endpoint("[::1]:443", ep); !ec) {
272  
    if (auto ec = parse_endpoint("[::1]:443", ep); !ec) {
273  
        // ep.is_v6() == true
273  
        // ep.is_v6() == true
274  
        // ep.port() == 443
274  
        // ep.port() == 443
275  
    }
275  
    }
276  
    @endcode
276  
    @endcode
277  

277  

278  
    @param s The string to parse.
278  
    @param s The string to parse.
279  
    @param ep The endpoint to store the result.
279  
    @param ep The endpoint to store the result.
280  
    @return An error code (empty on success).
280  
    @return An error code (empty on success).
281  
*/
281  
*/
282  
[[nodiscard]] BOOST_COROSIO_DECL
282  
[[nodiscard]] BOOST_COROSIO_DECL
283  
std::error_code
283  
std::error_code
284  
parse_endpoint(
284  
parse_endpoint(
285  
    std::string_view s,
285  
    std::string_view s,
286  
    endpoint& ep) noexcept;
286  
    endpoint& ep) noexcept;
287  

287  

288  
inline
288  
inline
289  
endpoint::endpoint(std::string_view s)
289  
endpoint::endpoint(std::string_view s)
290  
{
290  
{
291  
    auto ec = parse_endpoint(s, *this);
291  
    auto ec = parse_endpoint(s, *this);
292  
    if (ec)
292  
    if (ec)
293  
        detail::throw_system_error(ec);
293  
        detail::throw_system_error(ec);
294  
}
294  
}
295  

295  

296  
} // namespace boost::corosio
296  
} // namespace boost::corosio
297  

297  

298  
#endif
298  
#endif