1//
2// detail/impl/socket_select_interrupter.ipp
3// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4//
5// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6//
7// Distributed under the Boost Software License, Version 1.0. (See accompanying
8// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9//
10
11#ifndef BOOST_ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
12#define BOOST_ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
13
14#if defined(_MSC_VER) && (_MSC_VER >= 1200)
15# pragma once
16#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17
18#include <boost/asio/detail/config.hpp>
19
20#if !defined(BOOST_ASIO_WINDOWS_RUNTIME)
21
22#if defined(BOOST_ASIO_WINDOWS) \
23  || defined(__CYGWIN__) \
24  || defined(__SYMBIAN32__)
25
26#include <cstdlib>
27#include <boost/asio/detail/socket_holder.hpp>
28#include <boost/asio/detail/socket_ops.hpp>
29#include <boost/asio/detail/socket_select_interrupter.hpp>
30#include <boost/asio/detail/throw_error.hpp>
31#include <boost/asio/error.hpp>
32
33#include <boost/asio/detail/push_options.hpp>
34
35namespace boost {
36namespace asio {
37namespace detail {
38
39socket_select_interrupter::socket_select_interrupter()
40{
41  open_descriptors();
42}
43
44void socket_select_interrupter::open_descriptors()
45{
46  boost::system::error_code ec;
47  socket_holder acceptor(socket_ops::socket(
48        AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
49  if (acceptor.get() == invalid_socket)
50    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
51
52  int opt = 1;
53  socket_ops::state_type acceptor_state = 0;
54  socket_ops::setsockopt(acceptor.get(), acceptor_state,
55      SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec);
56
57  using namespace std; // For memset.
58  sockaddr_in4_type addr;
59  std::size_t addr_len = sizeof(addr);
60  memset(&addr, 0, sizeof(addr));
61  addr.sin_family = AF_INET;
62  addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK);
63  addr.sin_port = 0;
64  if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr,
65        addr_len, ec) == socket_error_retval)
66    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
67
68  if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr,
69        &addr_len, ec) == socket_error_retval)
70    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
71
72  // Some broken firewalls on Windows will intermittently cause getsockname to
73  // return 0.0.0.0 when the socket is actually bound to 127.0.0.1. We
74  // explicitly specify the target address here to work around this problem.
75  if (addr.sin_addr.s_addr == socket_ops::host_to_network_long(INADDR_ANY))
76    addr.sin_addr.s_addr = socket_ops::host_to_network_long(INADDR_LOOPBACK);
77
78  if (socket_ops::listen(acceptor.get(),
79        SOMAXCONN, ec) == socket_error_retval)
80    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
81
82  socket_holder client(socket_ops::socket(
83        AF_INET, SOCK_STREAM, IPPROTO_TCP, ec));
84  if (client.get() == invalid_socket)
85    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
86
87  if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr,
88        addr_len, ec) == socket_error_retval)
89    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
90
91  socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec));
92  if (server.get() == invalid_socket)
93    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
94
95  ioctl_arg_type non_blocking = 1;
96  socket_ops::state_type client_state = 0;
97  if (socket_ops::ioctl(client.get(), client_state,
98        FIONBIO, &non_blocking, ec))
99    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
100
101  opt = 1;
102  socket_ops::setsockopt(client.get(), client_state,
103      IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
104
105  non_blocking = 1;
106  socket_ops::state_type server_state = 0;
107  if (socket_ops::ioctl(server.get(), server_state,
108        FIONBIO, &non_blocking, ec))
109    boost::asio::detail::throw_error(ec, "socket_select_interrupter");
110
111  opt = 1;
112  socket_ops::setsockopt(server.get(), server_state,
113      IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec);
114
115  read_descriptor_ = server.release();
116  write_descriptor_ = client.release();
117}
118
119socket_select_interrupter::~socket_select_interrupter()
120{
121  close_descriptors();
122}
123
124void socket_select_interrupter::close_descriptors()
125{
126  boost::system::error_code ec;
127  socket_ops::state_type state = socket_ops::internal_non_blocking;
128  if (read_descriptor_ != invalid_socket)
129    socket_ops::close(read_descriptor_, state, true, ec);
130  if (write_descriptor_ != invalid_socket)
131    socket_ops::close(write_descriptor_, state, true, ec);
132}
133
134void socket_select_interrupter::recreate()
135{
136  close_descriptors();
137
138  write_descriptor_ = invalid_socket;
139  read_descriptor_ = invalid_socket;
140
141  open_descriptors();
142}
143
144void socket_select_interrupter::interrupt()
145{
146  char byte = 0;
147  socket_ops::buf b;
148  socket_ops::init_buf(b, &byte, 1);
149  boost::system::error_code ec;
150  socket_ops::send(write_descriptor_, &b, 1, 0, ec);
151}
152
153bool socket_select_interrupter::reset()
154{
155  char data[1024];
156  socket_ops::buf b;
157  socket_ops::init_buf(b, data, sizeof(data));
158  boost::system::error_code ec;
159  for (;;)
160  {
161    int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec);
162    if (bytes_read == sizeof(data))
163      continue;
164    if (bytes_read > 0)
165      return true;
166    if (bytes_read == 0)
167      return false;
168    if (ec == boost::asio::error::would_block
169        || ec == boost::asio::error::try_again)
170      return true;
171    return false;
172  }
173}
174
175} // namespace detail
176} // namespace asio
177} // namespace boost
178
179#include <boost/asio/detail/pop_options.hpp>
180
181#endif // defined(BOOST_ASIO_WINDOWS)
182       // || defined(__CYGWIN__)
183       // || defined(__SYMBIAN32__)
184
185#endif // !defined(BOOST_ASIO_WINDOWS_RUNTIME)
186
187#endif // BOOST_ASIO_DETAIL_IMPL_SOCKET_SELECT_INTERRUPTER_IPP
188