1 /*
2     Copyright (c) 2007-2019 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 "ws_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 #include "ws_engine.hpp"
45 #include "session_base.hpp"
46 
47 #ifdef ZMQ_HAVE_WSS
48 #include "wss_engine.hpp"
49 #include "wss_address.hpp"
50 #endif
51 
52 #ifndef ZMQ_HAVE_WINDOWS
53 #include <unistd.h>
54 #include <sys/socket.h>
55 #include <arpa/inet.h>
56 #include <netinet/tcp.h>
57 #include <netinet/in.h>
58 #include <netdb.h>
59 #include <fcntl.h>
60 #ifdef ZMQ_HAVE_VXWORKS
61 #include <sockLib.h>
62 #endif
63 #endif
64 
65 #ifdef ZMQ_HAVE_OPENVMS
66 #include <ioctl.h>
67 #endif
68 
ws_listener_t(io_thread_t * io_thread_,socket_base_t * socket_,const options_t & options_,bool wss_)69 zmq::ws_listener_t::ws_listener_t (io_thread_t *io_thread_,
70                                    socket_base_t *socket_,
71                                    const options_t &options_,
72                                    bool wss_) :
73     stream_listener_base_t (io_thread_, socket_, options_),
74     _wss (wss_)
75 {
76 #ifdef ZMQ_HAVE_WSS
77     if (_wss) {
78         int rc = gnutls_certificate_allocate_credentials (&_tls_cred);
79         zmq_assert (rc == GNUTLS_E_SUCCESS);
80 
81         gnutls_datum_t cert = {(unsigned char *) options_.wss_cert_pem.c_str (),
82                                (unsigned int) options_.wss_cert_pem.length ()};
83         gnutls_datum_t key = {(unsigned char *) options_.wss_key_pem.c_str (),
84                               (unsigned int) options_.wss_key_pem.length ()};
85         rc = gnutls_certificate_set_x509_key_mem (_tls_cred, &cert, &key,
86                                                   GNUTLS_X509_FMT_PEM);
87         zmq_assert (rc == GNUTLS_E_SUCCESS);
88     }
89 #endif
90 }
91 
~ws_listener_t()92 zmq::ws_listener_t::~ws_listener_t ()
93 {
94 #ifdef ZMQ_HAVE_WSS
95     if (_wss)
96         gnutls_certificate_free_credentials (_tls_cred);
97 #endif
98 }
99 
in_event()100 void zmq::ws_listener_t::in_event ()
101 {
102     const fd_t fd = accept ();
103 
104     //  If connection was reset by the peer in the meantime, just ignore it.
105     //  TODO: Handle specific errors like ENFILE/EMFILE etc.
106     if (fd == retired_fd) {
107         _socket->event_accept_failed (
108           make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
109         return;
110     }
111 
112     int rc = tune_tcp_socket (fd);
113     rc = rc | tune_tcp_maxrt (fd, options.tcp_maxrt);
114     if (rc != 0) {
115         _socket->event_accept_failed (
116           make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
117         return;
118     }
119 
120     //  Create the engine object for this connection.
121     create_engine (fd);
122 }
123 
get_socket_name(zmq::fd_t fd_,socket_end_t socket_end_) const124 std::string zmq::ws_listener_t::get_socket_name (zmq::fd_t fd_,
125                                                  socket_end_t socket_end_) const
126 {
127     std::string socket_name;
128 
129 #ifdef ZMQ_HAVE_WSS
130     if (_wss)
131         socket_name = zmq::get_socket_name<wss_address_t> (fd_, socket_end_);
132     else
133 #endif
134         socket_name = zmq::get_socket_name<ws_address_t> (fd_, socket_end_);
135 
136     return socket_name + _address.path ();
137 }
138 
create_socket(const char * addr_)139 int zmq::ws_listener_t::create_socket (const char *addr_)
140 {
141     tcp_address_t address;
142     _s = tcp_open_socket (addr_, options, true, true, &address);
143     if (_s == retired_fd) {
144         return -1;
145     }
146 
147     //  TODO why is this only done for the listener?
148     make_socket_noninheritable (_s);
149 
150     //  Allow reusing of the address.
151     int flag = 1;
152     int rc;
153 #ifdef ZMQ_HAVE_WINDOWS
154     //  TODO this was changed for Windows from SO_REUSEADDRE to
155     //  SE_EXCLUSIVEADDRUSE by 0ab65324195ad70205514d465b03d851a6de051c,
156     //  so the comment above is no longer correct; also, now the settings are
157     //  different between listener and connecter with a src address.
158     //  is this intentional?
159     rc = setsockopt (_s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
160                      reinterpret_cast<const char *> (&flag), sizeof (int));
161     wsa_assert (rc != SOCKET_ERROR);
162 #elif defined ZMQ_HAVE_VXWORKS
163     rc =
164       setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof (int));
165     errno_assert (rc == 0);
166 #else
167     rc = setsockopt (_s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof (int));
168     errno_assert (rc == 0);
169 #endif
170 
171     //  Bind the socket to the network interface and port.
172 #if defined ZMQ_HAVE_VXWORKS
173     rc = bind (_s, (sockaddr *) _address.addr (), _address.addrlen ());
174 #else
175     rc = bind (_s, address.addr (), address.addrlen ());
176 #endif
177 #ifdef ZMQ_HAVE_WINDOWS
178     if (rc == SOCKET_ERROR) {
179         errno = wsa_error_to_errno (WSAGetLastError ());
180         goto error;
181     }
182 #else
183     if (rc != 0)
184         goto error;
185 #endif
186 
187     //  Listen for incoming connections.
188     rc = listen (_s, options.backlog);
189 #ifdef ZMQ_HAVE_WINDOWS
190     if (rc == SOCKET_ERROR) {
191         errno = wsa_error_to_errno (WSAGetLastError ());
192         goto error;
193     }
194 #else
195     if (rc != 0)
196         goto error;
197 #endif
198 
199     return 0;
200 
201 error:
202     const int err = errno;
203     close ();
204     errno = err;
205     return -1;
206 }
207 
set_local_address(const char * addr_)208 int zmq::ws_listener_t::set_local_address (const char *addr_)
209 {
210     if (options.use_fd != -1) {
211         //  in this case, the addr_ passed is not used and ignored, since the
212         //  socket was already created by the application
213         _s = options.use_fd;
214     } else {
215         const int rc = _address.resolve (addr_, true, options.ipv6);
216         if (rc != 0)
217             return -1;
218 
219         //  remove the path, otherwise resolving the port will fail with wildcard
220         const char *delim = strrchr (addr_, '/');
221         std::string host_address;
222         if (delim) {
223             host_address = std::string (addr_, delim - addr_);
224         } else {
225             host_address = addr_;
226         }
227 
228         if (create_socket (host_address.c_str ()) == -1)
229             return -1;
230     }
231 
232     _endpoint = get_socket_name (_s, socket_end_local);
233 
234     _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint),
235                               _s);
236     return 0;
237 }
238 
accept()239 zmq::fd_t zmq::ws_listener_t::accept ()
240 {
241     //  The situation where connection cannot be accepted due to insufficient
242     //  resources is considered valid and treated by ignoring the connection.
243     //  Accept one connection and deal with different failure modes.
244     zmq_assert (_s != retired_fd);
245 
246     struct sockaddr_storage ss;
247     memset (&ss, 0, sizeof (ss));
248 #if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS
249     int ss_len = sizeof (ss);
250 #else
251     socklen_t ss_len = sizeof (ss);
252 #endif
253 #if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4
254     fd_t sock = ::accept4 (_s, reinterpret_cast<struct sockaddr *> (&ss),
255                            &ss_len, SOCK_CLOEXEC);
256 #else
257     const fd_t sock =
258       ::accept (_s, reinterpret_cast<struct sockaddr *> (&ss), &ss_len);
259 #endif
260 
261     if (sock == retired_fd) {
262 #if defined ZMQ_HAVE_WINDOWS
263         const int last_error = WSAGetLastError ();
264         wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET
265                     || last_error == WSAEMFILE || last_error == WSAENOBUFS);
266 #elif defined ZMQ_HAVE_ANDROID
267         errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR
268                       || errno == ECONNABORTED || errno == EPROTO
269                       || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE
270                       || errno == ENFILE || errno == EINVAL);
271 #else
272         errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR
273                       || errno == ECONNABORTED || errno == EPROTO
274                       || errno == ENOBUFS || errno == ENOMEM || errno == EMFILE
275                       || errno == ENFILE);
276 #endif
277         return retired_fd;
278     }
279 
280     make_socket_noninheritable (sock);
281 
282     if (zmq::set_nosigpipe (sock)) {
283 #ifdef ZMQ_HAVE_WINDOWS
284         const int rc = closesocket (sock);
285         wsa_assert (rc != SOCKET_ERROR);
286 #else
287         int rc = ::close (sock);
288         errno_assert (rc == 0);
289 #endif
290         return retired_fd;
291     }
292 
293     // Set the IP Type-Of-Service priority for this client socket
294     if (options.tos != 0)
295         set_ip_type_of_service (sock, options.tos);
296 
297     // Set the protocol-defined priority for this client socket
298     if (options.priority != 0)
299         set_socket_priority (sock, options.priority);
300 
301     return sock;
302 }
303 
create_engine(fd_t fd_)304 void zmq::ws_listener_t::create_engine (fd_t fd_)
305 {
306     const endpoint_uri_pair_t endpoint_pair (
307       get_socket_name (fd_, socket_end_local),
308       get_socket_name (fd_, socket_end_remote), endpoint_type_bind);
309 
310     i_engine *engine = NULL;
311     if (_wss)
312 #ifdef ZMQ_HAVE_WSS
313         engine = new (std::nothrow)
314           wss_engine_t (fd_, options, endpoint_pair, _address, false, _tls_cred,
315                         std::string ());
316 #else
317         zmq_assert (false);
318 #endif
319     else
320         engine = new (std::nothrow)
321           ws_engine_t (fd_, options, endpoint_pair, _address, false);
322 
323     alloc_assert (engine);
324 
325     //  Choose I/O thread to run connecter in. Given that we are already
326     //  running in an I/O thread, there must be at least one available.
327     io_thread_t *io_thread = choose_io_thread (options.affinity);
328     zmq_assert (io_thread);
329 
330     //  Create and launch a session object.
331     session_base_t *session =
332       session_base_t::create (io_thread, false, _socket, options, NULL);
333     errno_assert (session);
334     session->inc_seqnum ();
335     launch_child (session);
336     send_attach (session, engine, false);
337 
338     _socket->event_accepted (endpoint_pair, fd_);
339 }
340