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 "ipc_listener.hpp"
32
33#if defined ZMQ_HAVE_IPC
34
35#include <new>
36
37#include <string.h>
38
39#include "ipc_address.hpp"
40#include "io_thread.hpp"
41#include "config.hpp"
42#include "err.hpp"
43#include "ip.hpp"
44#include "socket_base.hpp"
45#include "address.hpp"
46
47#ifdef _MSC_VER
48#ifdef ZMQ_IOTHREAD_POLLER_USE_SELECT
49#error On Windows, IPC does not work with POLLER=select, use POLLER=epoll instead, or disable IPC transport
50#endif
51
52#include <afunix.h>
53#include <direct.h>
54
55#define rmdir _rmdir
56#define unlink _unlink
57
58#else
59#include <unistd.h>
60#include <sys/socket.h>
61#include <fcntl.h>
62#include <sys/un.h>
63#endif
64
65#ifdef ZMQ_HAVE_LOCAL_PEERCRED
66#include <sys/types.h>
67#include <sys/ucred.h>
68#endif
69#ifdef ZMQ_HAVE_SO_PEERCRED
70#include <sys/types.h>
71#include <pwd.h>
72#include <grp.h>
73#if defined ZMQ_HAVE_OPENBSD
74#define ucred sockpeercred
75#endif
76#endif
77
78zmq::ipc_listener_t::ipc_listener_t (io_thread_t *io_thread_,
79 socket_base_t *socket_,
80 const options_t &options_) :
81 stream_listener_base_t (io_thread_, socket_, options_),
82 _has_file (false)
83{
84}
85
86void zmq::ipc_listener_t::in_event ()
87{
88 fd_t fd = accept ();
89
90 // If connection was reset by the peer in the meantime, just ignore it.
91 // TODO: Handle specific errors like ENFILE/EMFILE etc.
92 if (fd == retired_fd) {
93 _socket->event_accept_failed (
94 make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
95 return;
96 }
97
98 // Create the engine object for this connection.
99 create_engine (fd);
100}
101
102std::string
103zmq::ipc_listener_t::get_socket_name (zmq::fd_t fd_,
104 socket_end_t socket_end_) const
105{
106 return zmq::get_socket_name<ipc_address_t> (fd_, socket_end_);
107}
108
109int zmq::ipc_listener_t::set_local_address (const char *addr_)
110{
111 // Create addr on stack for auto-cleanup
112 std::string addr (addr_);
113
114 // Allow wildcard file
115 if (options.use_fd == -1 && addr[0] == '*') {
116 if (create_ipc_wildcard_address (_tmp_socket_dirname, addr) < 0) {
117 return -1;
118 }
119 }
120
121 // Get rid of the file associated with the UNIX domain socket that
122 // may have been left behind by the previous run of the application.
123 // MUST NOT unlink if the FD is managed by the user, or it will stop
124 // working after the first client connects. The user will take care of
125 // cleaning up the file after the service is stopped.
126 if (options.use_fd == -1) {
127 ::unlink (addr.c_str ());
128 }
129 _filename.clear ();
130
131 // Initialise the address structure.
132 ipc_address_t address;
133 int rc = address.resolve (addr.c_str ());
134 if (rc != 0) {
135 if (!_tmp_socket_dirname.empty ()) {
136 // We need to preserve errno to return to the user
137 int tmp_errno = errno;
138 ::rmdir (_tmp_socket_dirname.c_str ());
139 _tmp_socket_dirname.clear ();
140 errno = tmp_errno;
141 }
142 return -1;
143 }
144
145 address.to_string (_endpoint);
146
147 if (options.use_fd != -1) {
148 _s = options.use_fd;
149 } else {
150 // Create a listening socket.
151 _s = open_socket (AF_UNIX, SOCK_STREAM, 0);
152 if (_s == retired_fd) {
153 if (!_tmp_socket_dirname.empty ()) {
154 // We need to preserve errno to return to the user
155 int tmp_errno = errno;
156 ::rmdir (_tmp_socket_dirname.c_str ());
157 _tmp_socket_dirname.clear ();
158 errno = tmp_errno;
159 }
160 return -1;
161 }
162
163 // Bind the socket to the file path.
164 rc = bind (_s, const_cast<sockaddr *> (address.addr ()),
165 address.addrlen ());
166 if (rc != 0)
167 goto error;
168
169 // Listen for incoming connections.
170 rc = listen (_s, options.backlog);
171 if (rc != 0)
172 goto error;
173 }
174
175 _filename = ZMQ_MOVE (addr);
176 _has_file = true;
177
178 _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint),
179 _s);
180 return 0;
181
182error:
183 int err = errno;
184 close ();
185 errno = err;
186 return -1;
187}
188
189int zmq::ipc_listener_t::close ()
190{
191 zmq_assert (_s != retired_fd);
192 fd_t fd_for_event = _s;
193#ifdef ZMQ_HAVE_WINDOWS
194 int rc = closesocket (_s);
195 wsa_assert (rc != SOCKET_ERROR);
196#else
197 int rc = ::close (_s);
198 errno_assert (rc == 0);
199#endif
200
201 _s = retired_fd;
202
203 if (_has_file && options.use_fd == -1) {
204 if (!_tmp_socket_dirname.empty ()) {
205 // TODO review this behaviour, it is inconsistent with the use of
206 // unlink in open since 656cdb959a7482c45db979c1d08ede585d12e315;
207 // however, we must at least remove the file before removing the
208 // directory, otherwise it will always fail
209 rc = ::unlink (_filename.c_str ());
210
211 if (rc == 0) {
212 rc = ::rmdir (_tmp_socket_dirname.c_str ());
213 _tmp_socket_dirname.clear ();
214 }
215 }
216
217 if (rc != 0) {
218 _socket->event_close_failed (
219 make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
220 return -1;
221 }
222 }
223
224 _socket->event_closed (make_unconnected_bind_endpoint_pair (_endpoint),
225 fd_for_event);
226 return 0;
227}
228
229#if defined ZMQ_HAVE_SO_PEERCRED
230
231bool zmq::ipc_listener_t::filter (fd_t sock_)
232{
233 if (options.ipc_uid_accept_filters.empty ()
234 && options.ipc_pid_accept_filters.empty ()
235 && options.ipc_gid_accept_filters.empty ())
236 return true;
237
238 struct ucred cred;
239 socklen_t size = sizeof (cred);
240
241 if (getsockopt (sock_, SOL_SOCKET, SO_PEERCRED, &cred, &size))
242 return false;
243 if (options.ipc_uid_accept_filters.find (cred.uid)
244 != options.ipc_uid_accept_filters.end ()
245 || options.ipc_gid_accept_filters.find (cred.gid)
246 != options.ipc_gid_accept_filters.end ()
247 || options.ipc_pid_accept_filters.find (cred.pid)
248 != options.ipc_pid_accept_filters.end ())
249 return true;
250
251 struct passwd *pw;
252 struct group *gr;
253
254 if (!(pw = getpwuid (cred.uid)))
255 return false;
256 for (options_t::ipc_gid_accept_filters_t::const_iterator it =
257 options.ipc_gid_accept_filters.begin ();
258 it != options.ipc_gid_accept_filters.end (); it++) {
259 if (!(gr = getgrgid (*it)))
260 continue;
261 for (char **mem = gr->gr_mem; *mem; mem++) {
262 if (!strcmp (*mem, pw->pw_name))
263 return true;
264 }
265 }
266 return false;
267}
268
269#elif defined ZMQ_HAVE_LOCAL_PEERCRED
270
271bool zmq::ipc_listener_t::filter (fd_t sock_)
272{
273 if (options.ipc_uid_accept_filters.empty ()
274 && options.ipc_gid_accept_filters.empty ())
275 return true;
276
277 struct xucred cred;
278 socklen_t size = sizeof (cred);
279
280 if (getsockopt (sock_, 0, LOCAL_PEERCRED, &cred, &size))
281 return false;
282 if (cred.cr_version != XUCRED_VERSION)
283 return false;
284 if (options.ipc_uid_accept_filters.find (cred.cr_uid)
285 != options.ipc_uid_accept_filters.end ())
286 return true;
287 for (int i = 0; i < cred.cr_ngroups; i++) {
288 if (options.ipc_gid_accept_filters.find (cred.cr_groups[i])
289 != options.ipc_gid_accept_filters.end ())
290 return true;
291 }
292
293 return false;
294}
295
296#endif
297
298zmq::fd_t zmq::ipc_listener_t::accept ()
299{
300 // Accept one connection and deal with different failure modes.
301 // The situation where connection cannot be accepted due to insufficient
302 // resources is considered valid and treated by ignoring the connection.
303 zmq_assert (_s != retired_fd);
304#if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4
305 fd_t sock = ::accept4 (_s, NULL, NULL, SOCK_CLOEXEC);
306#else
307 struct sockaddr_storage ss;
308 memset (&ss, 0, sizeof (ss));
309#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS
310 int ss_len = sizeof (ss);
311#else
312 socklen_t ss_len = sizeof (ss);
313#endif
314
315 fd_t sock =
316 ::accept (_s, reinterpret_cast<struct sockaddr *> (&ss), &ss_len);
317#endif
318 if (sock == retired_fd) {
319#if defined ZMQ_HAVE_WINDOWS
320 const int last_error = WSAGetLastError ();
321 wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET
322 || last_error == WSAEMFILE || last_error == WSAENOBUFS);
323#else
324 errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR
325 || errno == ECONNABORTED || errno == EPROTO
326 || errno == ENFILE);
327#endif
328 return retired_fd;
329 }
330
331 make_socket_noninheritable (sock);
332
333 // IPC accept() filters
334#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
335 if (!filter (sock)) {
336 int rc = ::close (sock);
337 errno_assert (rc == 0);
338 return retired_fd;
339 }
340#endif
341
342 if (zmq::set_nosigpipe (sock)) {
343#ifdef ZMQ_HAVE_WINDOWS
344 int rc = closesocket (sock);
345 wsa_assert (rc != SOCKET_ERROR);
346#else
347 int rc = ::close (sock);
348 errno_assert (rc == 0);
349#endif
350 return retired_fd;
351 }
352
353 return sock;
354}
355
356#endif
357