1 | /* |
2 | Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file |
3 | |
4 | This file is part of libzmq, the ZeroMQ core engine in C++. |
5 | |
6 | libzmq is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU Lesser General Public License (LGPL) as published |
8 | by the Free Software Foundation; either version 3 of the License, or |
9 | (at your option) any later version. |
10 | |
11 | As a special exception, the Contributors give you permission to link |
12 | this library with independent modules to produce an executable, |
13 | regardless of the license terms of these independent modules, and to |
14 | copy and distribute the resulting executable under terms of your choice, |
15 | provided that you also meet, for each linked independent module, the |
16 | terms and conditions of the license of that module. An independent |
17 | module is a module which is not derived from or based on this library. |
18 | If you modify this library, you must extend this exception to your |
19 | version of the library. |
20 | |
21 | libzmq is distributed in the hope that it will be useful, but WITHOUT |
22 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
23 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
24 | License for more details. |
25 | |
26 | You should have received a copy of the GNU Lesser General Public License |
27 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
28 | */ |
29 | |
30 | #include "precompiled.hpp" |
31 | #include <new> |
32 | |
33 | #include <string> |
34 | #include <stdio.h> |
35 | |
36 | #include "tcp_listener.hpp" |
37 | #include "io_thread.hpp" |
38 | #include "config.hpp" |
39 | #include "err.hpp" |
40 | #include "ip.hpp" |
41 | #include "tcp.hpp" |
42 | #include "socket_base.hpp" |
43 | #include "address.hpp" |
44 | |
45 | #ifndef ZMQ_HAVE_WINDOWS |
46 | #include <unistd.h> |
47 | #include <sys/socket.h> |
48 | #include <arpa/inet.h> |
49 | #include <netinet/tcp.h> |
50 | #include <netinet/in.h> |
51 | #include <netdb.h> |
52 | #include <fcntl.h> |
53 | #ifdef ZMQ_HAVE_VXWORKS |
54 | #include <sockLib.h> |
55 | #endif |
56 | #endif |
57 | |
58 | #ifdef ZMQ_HAVE_OPENVMS |
59 | #include <ioctl.h> |
60 | #endif |
61 | |
62 | zmq::tcp_listener_t::tcp_listener_t (io_thread_t *io_thread_, |
63 | socket_base_t *socket_, |
64 | const options_t &options_) : |
65 | stream_listener_base_t (io_thread_, socket_, options_) |
66 | { |
67 | } |
68 | |
69 | void zmq::tcp_listener_t::in_event () |
70 | { |
71 | fd_t fd = accept (); |
72 | |
73 | // If connection was reset by the peer in the meantime, just ignore it. |
74 | // TODO: Handle specific errors like ENFILE/EMFILE etc. |
75 | if (fd == retired_fd) { |
76 | _socket->event_accept_failed ( |
77 | make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); |
78 | return; |
79 | } |
80 | |
81 | int rc = tune_tcp_socket (fd); |
82 | rc = rc |
83 | | tune_tcp_keepalives ( |
84 | fd, options.tcp_keepalive, options.tcp_keepalive_cnt, |
85 | options.tcp_keepalive_idle, options.tcp_keepalive_intvl); |
86 | rc = rc | tune_tcp_maxrt (fd, options.tcp_maxrt); |
87 | if (rc != 0) { |
88 | _socket->event_accept_failed ( |
89 | make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); |
90 | return; |
91 | } |
92 | |
93 | // Create the engine object for this connection. |
94 | create_engine (fd); |
95 | } |
96 | |
97 | std::string |
98 | zmq::tcp_listener_t::get_socket_name (zmq::fd_t fd_, |
99 | socket_end_t socket_end_) const |
100 | { |
101 | return zmq::get_socket_name<tcp_address_t> (fd_, socket_end_); |
102 | } |
103 | |
104 | int zmq::tcp_listener_t::create_socket (const char *addr_) |
105 | { |
106 | _s = tcp_open_socket (addr_, options, true, true, &_address); |
107 | if (_s == retired_fd) { |
108 | return -1; |
109 | } |
110 | |
111 | // TODO why is this only done for the listener? |
112 | make_socket_noninheritable (_s); |
113 | |
114 | // Allow reusing of the address. |
115 | int flag = 1; |
116 | int rc; |
117 | #ifdef ZMQ_HAVE_WINDOWS |
118 | // TODO this was changed for Windows from SO_REUSEADDRE to |
119 | // SE_EXCLUSIVEADDRUSE by 0ab65324195ad70205514d465b03d851a6de051c, |
120 | // so the comment above is no longer correct; also, now the settings are |
121 | // different between listener and connecter with a src address. |
122 | // is this intentional? |
123 | rc = setsockopt (_s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, |
124 | reinterpret_cast<const char *> (&flag), sizeof (int)); |
125 | wsa_assert (rc != SOCKET_ERROR); |
126 | #elif defined ZMQ_HAVE_VXWORKS |
127 | rc = |
128 | setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof (int)); |
129 | errno_assert (rc == 0); |
130 | #else |
131 | rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int)); |
132 | errno_assert (rc == 0); |
133 | #endif |
134 | |
135 | // Bind the socket to the network interface and port. |
136 | #if defined ZMQ_HAVE_VXWORKS |
137 | rc = bind (_s, (sockaddr *) _address.addr (), _address.addrlen ()); |
138 | #else |
139 | rc = bind (_s, _address.addr (), _address.addrlen ()); |
140 | #endif |
141 | #ifdef ZMQ_HAVE_WINDOWS |
142 | if (rc == SOCKET_ERROR) { |
143 | errno = wsa_error_to_errno (WSAGetLastError ()); |
144 | goto error; |
145 | } |
146 | #else |
147 | if (rc != 0) |
148 | goto error; |
149 | #endif |
150 | |
151 | // Listen for incoming connections. |
152 | rc = listen (_s, options.backlog); |
153 | #ifdef ZMQ_HAVE_WINDOWS |
154 | if (rc == SOCKET_ERROR) { |
155 | errno = wsa_error_to_errno (WSAGetLastError ()); |
156 | goto error; |
157 | } |
158 | #else |
159 | if (rc != 0) |
160 | goto error; |
161 | #endif |
162 | |
163 | return 0; |
164 | |
165 | error: |
166 | int err = errno; |
167 | close (); |
168 | errno = err; |
169 | return -1; |
170 | } |
171 | |
172 | int zmq::tcp_listener_t::set_local_address (const char *addr_) |
173 | { |
174 | if (options.use_fd != -1) { |
175 | // in this case, the addr_ passed is not used and ignored, since the |
176 | // socket was already created by the application |
177 | _s = options.use_fd; |
178 | } else { |
179 | if (create_socket (addr_) == -1) |
180 | return -1; |
181 | } |
182 | |
183 | _endpoint = get_socket_name (_s, socket_end_local); |
184 | |
185 | _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint), |
186 | _s); |
187 | return 0; |
188 | } |
189 | |
190 | zmq::fd_t zmq::tcp_listener_t::accept () |
191 | { |
192 | // The situation where connection cannot be accepted due to insufficient |
193 | // resources is considered valid and treated by ignoring the connection. |
194 | // Accept one connection and deal with different failure modes. |
195 | zmq_assert (_s != retired_fd); |
196 | |
197 | struct sockaddr_storage ss; |
198 | memset (&ss, 0, sizeof (ss)); |
199 | #if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS |
200 | int ss_len = sizeof (ss); |
201 | #else |
202 | socklen_t ss_len = sizeof (ss); |
203 | #endif |
204 | #if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4 |
205 | fd_t sock = ::accept4 (_s, reinterpret_cast<struct sockaddr *> (&ss), |
206 | &ss_len, SOCK_CLOEXEC); |
207 | #else |
208 | fd_t sock = |
209 | ::accept (_s, reinterpret_cast<struct sockaddr *> (&ss), &ss_len); |
210 | #endif |
211 | |
212 | if (sock == retired_fd) { |
213 | #if defined ZMQ_HAVE_WINDOWS |
214 | const int last_error = WSAGetLastError (); |
215 | wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET |
216 | || last_error == WSAEMFILE || last_error == WSAENOBUFS); |
217 | #elif defined ZMQ_HAVE_ANDROID |
218 | errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR |
219 | || errno == ECONNABORTED || errno == EPROTO |
220 | || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE |
221 | || errno == ENFILE || errno == EINVAL); |
222 | #else |
223 | errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR |
224 | || errno == ECONNABORTED || errno == EPROTO |
225 | || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE |
226 | || errno == ENFILE); |
227 | #endif |
228 | return retired_fd; |
229 | } |
230 | |
231 | make_socket_noninheritable (sock); |
232 | |
233 | if (!options.tcp_accept_filters.empty ()) { |
234 | bool matched = false; |
235 | for (options_t::tcp_accept_filters_t::size_type i = 0; |
236 | i != options.tcp_accept_filters.size (); ++i) { |
237 | if (options.tcp_accept_filters[i].match_address ( |
238 | reinterpret_cast<struct sockaddr *> (&ss), ss_len)) { |
239 | matched = true; |
240 | break; |
241 | } |
242 | } |
243 | if (!matched) { |
244 | #ifdef ZMQ_HAVE_WINDOWS |
245 | int rc = closesocket (sock); |
246 | wsa_assert (rc != SOCKET_ERROR); |
247 | #else |
248 | int rc = ::close (sock); |
249 | errno_assert (rc == 0); |
250 | #endif |
251 | return retired_fd; |
252 | } |
253 | } |
254 | |
255 | if (zmq::set_nosigpipe (sock)) { |
256 | #ifdef ZMQ_HAVE_WINDOWS |
257 | int rc = closesocket (sock); |
258 | wsa_assert (rc != SOCKET_ERROR); |
259 | #else |
260 | int rc = ::close (sock); |
261 | errno_assert (rc == 0); |
262 | #endif |
263 | return retired_fd; |
264 | } |
265 | |
266 | // Set the IP Type-Of-Service priority for this client socket |
267 | if (options.tos != 0) |
268 | set_ip_type_of_service (sock, options.tos); |
269 | |
270 | return sock; |
271 | } |
272 | |