Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions doc/design/physical-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,13 @@ public:
virtual native_handle_type native_handle() const noexcept = 0;
virtual void cancel() noexcept = 0;

// Protocol-specific socket options
virtual std::error_code set_no_delay( bool ) noexcept = 0;
// ...
// Generic socket options (level/name passed through from option type)
virtual std::error_code set_option(
int level, int optname,
void const* data, std::size_t size ) noexcept = 0;
virtual std::error_code get_option(
int level, int optname,
void* data, std::size_t* size ) const noexcept = 0;
};

private:
Expand Down
35 changes: 29 additions & 6 deletions include/boost/corosio/detail/acceptor_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,41 @@ class BOOST_COROSIO_DECL acceptor_service
/// Identifies this service for `execution_context` lookup.
using key_type = acceptor_service;

/** Open an acceptor.
/** Create the acceptor socket without binding or listening.

Creates an IPv4 TCP socket, binds it to the specified endpoint,
and begins listening for incoming connections.
Creates a socket with dual-stack enabled for IPv6 but does
not bind or listen. Does not set SO_REUSEADDR.

@param impl The acceptor implementation to open.
@param family Address family (e.g. `AF_INET`, `AF_INET6`).
@param type Socket type (e.g. `SOCK_STREAM`).
@param protocol Protocol number (e.g. `IPPROTO_TCP`).
@return Error code on failure, empty on success.
*/
virtual std::error_code open_acceptor_socket(
tcp_acceptor::implementation& impl,
int family, int type, int protocol) = 0;

/** Bind an open acceptor to a local endpoint.

@param impl The acceptor implementation to bind.
@param ep The local endpoint to bind to.
@param backlog The maximum length of the queue of pending connections.
@return Error code on failure, empty on success.
*/
virtual std::error_code open_acceptor(
tcp_acceptor::implementation& impl, endpoint ep, int backlog) = 0;
virtual std::error_code bind_acceptor(
tcp_acceptor::implementation& impl, endpoint ep) = 0;

/** Start listening for incoming connections.

Registers the acceptor with the platform reactor after
calling `::listen()`.

@param impl The acceptor implementation to listen on.
@param backlog The maximum length of the pending connection queue.
@return Error code on failure, empty on success.
*/
virtual std::error_code listen_acceptor(
tcp_acceptor::implementation& impl, int backlog) = 0;

protected:
/// Construct the acceptor service.
Expand Down
139 changes: 139 additions & 0 deletions include/boost/corosio/detail/endpoint_convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,145 @@ from_sockaddr_in6(sockaddr_in6 const& sa) noexcept
return endpoint(ipv6_address(bytes), ntohs(sa.sin6_port));
}

/** Convert an IPv4 endpoint to an IPv4-mapped IPv6 sockaddr_in6.

Produces a `sockaddr_in6` with the `::ffff:` prefix, suitable
for passing an IPv4 destination to a dual-stack IPv6 socket.

@param ep The endpoint to convert. Must be IPv4 (is_v4() == true).
@return A sockaddr_in6 with the IPv4-mapped address.
*/
inline sockaddr_in6
to_v4_mapped_sockaddr_in6(endpoint const& ep) noexcept
{
sockaddr_in6 sa{};
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(ep.port());
// ::ffff:0:0/96 prefix
sa.sin6_addr.s6_addr[10] = 0xff;
sa.sin6_addr.s6_addr[11] = 0xff;
auto bytes = ep.v4_address().to_bytes();
std::memcpy(&sa.sin6_addr.s6_addr[12], bytes.data(), 4);
return sa;
}

/** Convert endpoint to sockaddr_storage.

Dispatches to @ref to_sockaddr_in or @ref to_sockaddr_in6
based on the endpoint's address family.

@param ep The endpoint to convert.
@param storage Output parameter filled with the sockaddr.
@return The length of the filled sockaddr structure.
*/
inline socklen_t
to_sockaddr( endpoint const& ep, sockaddr_storage& storage ) noexcept
{
std::memset( &storage, 0, sizeof( storage ) );
if( ep.is_v4() )
{
auto sa = to_sockaddr_in( ep );
std::memcpy( &storage, &sa, sizeof( sa ) );
return sizeof( sa );
}
auto sa6 = to_sockaddr_in6( ep );
std::memcpy( &storage, &sa6, sizeof( sa6 ) );
return sizeof( sa6 );
}

/** Convert endpoint to sockaddr_storage for a specific socket family.

When the socket is AF_INET6 and the endpoint is IPv4, the address
is converted to an IPv4-mapped IPv6 address (`::ffff:x.x.x.x`) so
dual-stack sockets can connect to IPv4 destinations.

@param ep The endpoint to convert.
@param socket_family The address family of the socket (AF_INET or
AF_INET6).
@param storage Output parameter filled with the sockaddr.
@return The length of the filled sockaddr structure.
*/
inline socklen_t
to_sockaddr(
endpoint const& ep,
int socket_family,
sockaddr_storage& storage) noexcept
{
// IPv4 endpoint on IPv6 socket: use IPv4-mapped address
if (ep.is_v4() && socket_family == AF_INET6)
{
std::memset(&storage, 0, sizeof(storage));
auto sa6 = to_v4_mapped_sockaddr_in6(ep);
std::memcpy(&storage, &sa6, sizeof(sa6));
return sizeof(sa6);
}
return to_sockaddr(ep, storage);
}

/** Create endpoint from sockaddr_storage.

Dispatches on `ss_family` to reconstruct the appropriate
IPv4 or IPv6 endpoint.

@param storage The sockaddr_storage with fields in network byte order.
@return An endpoint with address and port extracted from storage.
*/
inline endpoint
from_sockaddr( sockaddr_storage const& storage ) noexcept
{
if( storage.ss_family == AF_INET )
{
sockaddr_in sa;
std::memcpy( &sa, &storage, sizeof( sa ) );
return from_sockaddr_in( sa );
}
if( storage.ss_family == AF_INET6 )
{
sockaddr_in6 sa6;
std::memcpy( &sa6, &storage, sizeof( sa6 ) );
return from_sockaddr_in6( sa6 );
}
return endpoint{};
}

/** Return the native address family for an endpoint.

@param ep The endpoint to query.
@return `AF_INET` for IPv4, `AF_INET6` for IPv6.
*/
inline int
endpoint_family( endpoint const& ep ) noexcept
{
return ep.is_v6() ? AF_INET6 : AF_INET;
}

/** Return the address family of a socket descriptor.

@param fd The socket file descriptor.
@return AF_INET, AF_INET6, or AF_UNSPEC on failure.
*/
inline int
socket_family(
#if BOOST_COROSIO_POSIX
int fd
#else
std::uintptr_t fd
#endif
) noexcept
{
sockaddr_storage storage{};
socklen_t len = sizeof(storage);
if (getsockname(
#if BOOST_COROSIO_POSIX
fd,
#else
static_cast<SOCKET>(fd),
#endif
reinterpret_cast<sockaddr*>(&storage), &len) != 0)
return AF_UNSPEC;
return storage.ss_family;
}

} // namespace boost::corosio::detail

#endif
9 changes: 7 additions & 2 deletions include/boost/corosio/detail/socket_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ class BOOST_COROSIO_DECL socket_service

/** Open a socket.

Creates an IPv4 TCP socket and associates it with the platform reactor.
Creates a socket and associates it with the platform reactor.

@param impl The socket implementation to open.
@param family Address family (e.g. `AF_INET`, `AF_INET6`).
@param type Socket type (e.g. `SOCK_STREAM`).
@param protocol Protocol number (e.g. `IPPROTO_TCP`).
@return Error code on failure, empty on success.
*/
virtual std::error_code open_socket(tcp_socket::implementation& impl) = 0;
virtual std::error_code
open_socket( tcp_socket::implementation& impl,
int family, int type, int protocol ) = 0;

protected:
/// Construct the socket service.
Expand Down
5 changes: 5 additions & 0 deletions include/boost/corosio/io/io_read_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ class BOOST_COROSIO_DECL io_read_stream : virtual public io_object
/// Construct from a handle.
explicit io_read_stream(handle h) noexcept : io_object(std::move(h)) {}

io_read_stream(io_read_stream&&) noexcept = default;
io_read_stream& operator=(io_read_stream&&) noexcept = delete;
io_read_stream(io_read_stream const&) = delete;
io_read_stream& operator=(io_read_stream const&) = delete;

public:
/** Asynchronously read data from the stream.

Expand Down
5 changes: 5 additions & 0 deletions include/boost/corosio/io/io_write_stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ class BOOST_COROSIO_DECL io_write_stream : virtual public io_object
/// Construct from a handle.
explicit io_write_stream(handle h) noexcept : io_object(std::move(h)) {}

io_write_stream(io_write_stream&&) noexcept = default;
io_write_stream& operator=(io_write_stream&&) noexcept = delete;
io_write_stream(io_write_stream const&) = delete;
io_write_stream& operator=(io_write_stream const&) = delete;

public:
/** Asynchronously write data to the stream.

Expand Down
12 changes: 12 additions & 0 deletions include/boost/corosio/ipv6_address.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@ class BOOST_COROSIO_DECL ipv6_address
return a1.addr_ != a2.addr_;
}

/** Return an address object that represents the unspecified address.

The address 0:0:0:0:0:0:0:0 (::) may be used to bind a socket
to all available interfaces.

@return The unspecified address (::).
*/
static ipv6_address any() noexcept
{
return ipv6_address();
}

/** Return an address object that represents the loopback address.

The unicast address 0:0:0:0:0:0:0:1 is called
Expand Down
7 changes: 7 additions & 0 deletions include/boost/corosio/native/detail/epoll/epoll_acceptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ class epoll_acceptor final
return fd_ >= 0;
}
void cancel() noexcept override;

std::error_code set_option(
int level, int optname,
void const* data, std::size_t size) noexcept override;
std::error_code get_option(
int level, int optname,
void* data, std::size_t* size) const noexcept override;
void cancel_single_op(epoll_op& op) noexcept;
void close_socket() noexcept;
void set_local_endpoint(endpoint ep) noexcept
Expand Down
Loading
Loading