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