1 +
//
 
2 +
// Copyright (c) 2026 Michael Vandeberg
 
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_NATIVE_DETAIL_SELECT_SELECT_TRAITS_HPP
 
11 +
#define BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TRAITS_HPP
 
12 +

 
13 +
#include <boost/corosio/detail/platform.hpp>
 
14 +

 
15 +
#if BOOST_COROSIO_HAS_SELECT
 
16 +

 
17 +
#include <boost/corosio/native/detail/make_err.hpp>
 
18 +
#include <boost/corosio/native/detail/reactor/reactor_descriptor_state.hpp>
 
19 +

 
20 +
#include <system_error>
 
21 +

 
22 +
#include <errno.h>
 
23 +
#include <fcntl.h>
 
24 +
#include <netinet/in.h>
 
25 +
#include <sys/select.h>
 
26 +
#include <sys/socket.h>
 
27 +
#include <unistd.h>
 
28 +

 
29 +
/* select backend traits.
 
30 +

 
31 +
   Captures the platform-specific behavior of the portable select() backend:
 
32 +
   manual fcntl for O_NONBLOCK/FD_CLOEXEC, FD_SETSIZE validation,
 
33 +
   conditional SO_NOSIGPIPE, sendmsg(MSG_NOSIGNAL) where available,
 
34 +
   and accept()+fcntl for accepted connections.
 
35 +
*/
 
36 +

 
37 +
namespace boost::corosio::detail {
 
38 +

 
39 +
class select_scheduler;
 
40 +
struct select_descriptor_state;
 
41 +

 
42 +
struct select_traits
 
43 +
{
 
44 +
    using scheduler_type    = select_scheduler;
 
45 +
    using desc_state_type   = select_descriptor_state;
 
46 +

 
47 +
    static constexpr bool needs_write_notification = true;
 
48 +

 
49 +
    /// No extra per-socket state or lifecycle hooks needed for select.
 
50 +
    struct stream_socket_hook
 
51 +
    {
 
52 +
        std::error_code on_set_option(
 
53 +
            int fd, int level, int optname,
 
54 +
            void const* data, std::size_t size) noexcept
 
55 +
        {
 
56 +
            if (::setsockopt(
 
57 +
                    fd, level, optname, data,
 
58 +
                    static_cast<socklen_t>(size)) != 0)
 
59 +
                return make_err(errno);
 
60 +
            return {};
 
61 +
        }
 
62 +
        static void pre_shutdown(int) noexcept {}
 
63 +
        static void pre_destroy(int) noexcept {}
 
64 +
    };
 
65 +

 
66 +
    struct write_policy
 
67 +
    {
 
68 +
        static ssize_t write(int fd, iovec* iovecs, int count) noexcept
 
69 +
        {
 
70 +
            msghdr msg{};
 
71 +
            msg.msg_iov    = iovecs;
 
72 +
            msg.msg_iovlen = static_cast<std::size_t>(count);
 
73 +

 
74 +
#ifdef MSG_NOSIGNAL
 
75 +
            constexpr int send_flags = MSG_NOSIGNAL;
 
76 +
#else
 
77 +
            constexpr int send_flags = 0;
 
78 +
#endif
 
79 +

 
80 +
            ssize_t n;
 
81 +
            do
 
82 +
            {
 
83 +
                n = ::sendmsg(fd, &msg, send_flags);
 
84 +
            }
 
85 +
            while (n < 0 && errno == EINTR);
 
86 +
            return n;
 
87 +
        }
 
88 +
    };
 
89 +

 
90 +
    struct accept_policy
 
91 +
    {
 
92 +
        static int do_accept(
 
93 +
            int fd, sockaddr_storage& peer, socklen_t& addrlen) noexcept
 
94 +
        {
 
95 +
            addrlen = sizeof(peer);
 
96 +
            int new_fd;
 
97 +
            do
 
98 +
            {
 
99 +
                new_fd = ::accept(
 
100 +
                    fd, reinterpret_cast<sockaddr*>(&peer), &addrlen);
 
101 +
            }
 
102 +
            while (new_fd < 0 && errno == EINTR);
 
103 +

 
104 +
            if (new_fd < 0)
 
105 +
                return new_fd;
 
106 +

 
107 +
            if (new_fd >= FD_SETSIZE)
 
108 +
            {
 
109 +
                ::close(new_fd);
 
110 +
                errno = EINVAL;
 
111 +
                return -1;
 
112 +
            }
 
113 +

 
114 +
            int flags = ::fcntl(new_fd, F_GETFL, 0);
 
115 +
            if (flags == -1)
 
116 +
            {
 
117 +
                int err = errno;
 
118 +
                ::close(new_fd);
 
119 +
                errno = err;
 
120 +
                return -1;
 
121 +
            }
 
122 +

 
123 +
            if (::fcntl(new_fd, F_SETFL, flags | O_NONBLOCK) == -1)
 
124 +
            {
 
125 +
                int err = errno;
 
126 +
                ::close(new_fd);
 
127 +
                errno = err;
 
128 +
                return -1;
 
129 +
            }
 
130 +

 
131 +
            if (::fcntl(new_fd, F_SETFD, FD_CLOEXEC) == -1)
 
132 +
            {
 
133 +
                int err = errno;
 
134 +
                ::close(new_fd);
 
135 +
                errno = err;
 
136 +
                return -1;
 
137 +
            }
 
138 +

 
139 +
#ifdef SO_NOSIGPIPE
 
140 +
            int one = 1;
 
141 +
            if (::setsockopt(
 
142 +
                    new_fd, SOL_SOCKET, SO_NOSIGPIPE,
 
143 +
                    &one, sizeof(one)) == -1)
 
144 +
            {
 
145 +
                int err = errno;
 
146 +
                ::close(new_fd);
 
147 +
                errno = err;
 
148 +
                return -1;
 
149 +
            }
 
150 +
#endif
 
151 +

 
152 +
            return new_fd;
 
153 +
        }
 
154 +
    };
 
155 +

 
156 +
    /// Create a plain socket (no atomic flags — select is POSIX-portable).
 
157 +
    static int create_socket(int family, int type, int protocol) noexcept
 
158 +
    {
 
159 +
        return ::socket(family, type, protocol);
 
160 +
    }
 
161 +

 
162 +
    /// Set O_NONBLOCK, FD_CLOEXEC; check FD_SETSIZE; optionally SO_NOSIGPIPE.
 
163 +
    /// Caller is responsible for closing fd on error.
 
164 +
    static std::error_code set_fd_options(int fd) noexcept
 
165 +
    {
 
166 +
        int flags = ::fcntl(fd, F_GETFL, 0);
 
167 +
        if (flags == -1)
 
168 +
            return make_err(errno);
 
169 +
        if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
 
170 +
            return make_err(errno);
 
171 +
        if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
 
172 +
            return make_err(errno);
 
173 +

 
174 +
        if (fd >= FD_SETSIZE)
 
175 +
            return make_err(EMFILE);
 
176 +

 
177 +
#ifdef SO_NOSIGPIPE
 
178 +
        // SO_NOSIGPIPE is the primary defense against SIGPIPE on
 
179 +
        // platforms that don't support MSG_NOSIGNAL. A silent failure
 
180 +
        // here would leave the fd vulnerable to crashing the process
 
181 +
        // on a closed-peer write, so propagate the error.
 
182 +
        {
 
183 +
            int one = 1;
 
184 +
            if (::setsockopt(
 
185 +
                    fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)) != 0)
 
186 +
                return make_err(errno);
 
187 +
        }
 
188 +
#endif
 
189 +

 
190 +
        return {};
 
191 +
    }
 
192 +

 
193 +
    /// Apply protocol-specific options after socket creation.
 
194 +
    /// For IP sockets, sets IPV6_V6ONLY on AF_INET6.
 
195 +
    static std::error_code
 
196 +
    configure_ip_socket(int fd, int family) noexcept
 
197 +
    {
 
198 +
        if (family == AF_INET6)
 
199 +
        {
 
200 +
            int one = 1;
 
201 +
            if (::setsockopt(
 
202 +
                    fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) != 0)
 
203 +
                return make_err(errno);
 
204 +
        }
 
205 +

 
206 +
        return set_fd_options(fd);
 
207 +
    }
 
208 +

 
209 +
    /// Apply protocol-specific options for acceptor sockets.
 
210 +
    /// For IP acceptors, sets IPV6_V6ONLY=0 (dual-stack).
 
211 +
    static std::error_code
 
212 +
    configure_ip_acceptor(int fd, int family) noexcept
 
213 +
    {
 
214 +
        if (family == AF_INET6)
 
215 +
        {
 
216 +
            int val = 0;
 
217 +
            if (::setsockopt(
 
218 +
                    fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) != 0)
 
219 +
                return make_err(errno);
 
220 +
        }
 
221 +

 
222 +
        return set_fd_options(fd);
 
223 +
    }
 
224 +

 
225 +
    /// Apply options for local (unix) sockets.
 
226 +
    static std::error_code
 
227 +
    configure_local_socket(int fd) noexcept
 
228 +
    {
 
229 +
        return set_fd_options(fd);
 
230 +
    }
 
231 +
};
 
232 +

 
233 +
} // namespace boost::corosio::detail
 
234 +

 
235 +
#endif // BOOST_COROSIO_HAS_SELECT
 
236 +

 
237 +
#endif // BOOST_COROSIO_NATIVE_DETAIL_SELECT_SELECT_TRAITS_HPP