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
ipc_listener_t(io_thread_t * io_thread_,socket_base_t * socket_,const options_t & options_)78 zmq::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
in_event()86 void zmq::ipc_listener_t::in_event ()
87 {
88 const 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
102 std::string
get_socket_name(zmq::fd_t fd_,socket_end_t socket_end_) const103 zmq::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
set_local_address(const char * addr_)109 int 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 const 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 const 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
182 error:
183 const int err = errno;
184 close ();
185 errno = err;
186 return -1;
187 }
188
close()189 int zmq::ipc_listener_t::close ()
190 {
191 zmq_assert (_s != retired_fd);
192 const 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
filter(fd_t sock_)231 bool 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 const struct passwd *pw;
252 const struct group *gr;
253
254 if (!(pw = getpwuid (cred.uid)))
255 return false;
256 for (options_t::ipc_gid_accept_filters_t::const_iterator
257 it = options.ipc_gid_accept_filters.begin (),
258 end = options.ipc_gid_accept_filters.end ();
259 it != end; it++) {
260 if (!(gr = getgrgid (*it)))
261 continue;
262 for (char **mem = gr->gr_mem; *mem; mem++) {
263 if (!strcmp (*mem, pw->pw_name))
264 return true;
265 }
266 }
267 return false;
268 }
269
270 #elif defined ZMQ_HAVE_LOCAL_PEERCRED
271
filter(fd_t sock_)272 bool zmq::ipc_listener_t::filter (fd_t sock_)
273 {
274 if (options.ipc_uid_accept_filters.empty ()
275 && options.ipc_gid_accept_filters.empty ())
276 return true;
277
278 struct xucred cred;
279 socklen_t size = sizeof (cred);
280
281 if (getsockopt (sock_, 0, LOCAL_PEERCRED, &cred, &size))
282 return false;
283 if (cred.cr_version != XUCRED_VERSION)
284 return false;
285 if (options.ipc_uid_accept_filters.find (cred.cr_uid)
286 != options.ipc_uid_accept_filters.end ())
287 return true;
288 for (int i = 0; i < cred.cr_ngroups; i++) {
289 if (options.ipc_gid_accept_filters.find (cred.cr_groups[i])
290 != options.ipc_gid_accept_filters.end ())
291 return true;
292 }
293
294 return false;
295 }
296
297 #endif
298
accept()299 zmq::fd_t zmq::ipc_listener_t::accept ()
300 {
301 // Accept one connection and deal with different failure modes.
302 // The situation where connection cannot be accepted due to insufficient
303 // resources is considered valid and treated by ignoring the connection.
304 zmq_assert (_s != retired_fd);
305 #if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4
306 fd_t sock = ::accept4 (_s, NULL, NULL, SOCK_CLOEXEC);
307 #else
308 struct sockaddr_storage ss;
309 memset (&ss, 0, sizeof (ss));
310 #if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS
311 int ss_len = sizeof (ss);
312 #else
313 socklen_t ss_len = sizeof (ss);
314 #endif
315
316 const fd_t sock =
317 ::accept (_s, reinterpret_cast<struct sockaddr *> (&ss), &ss_len);
318 #endif
319 if (sock == retired_fd) {
320 #if defined ZMQ_HAVE_WINDOWS
321 const int last_error = WSAGetLastError ();
322 wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET
323 || last_error == WSAEMFILE || last_error == WSAENOBUFS);
324 #else
325 errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR
326 || errno == ECONNABORTED || errno == EPROTO
327 || errno == ENFILE);
328 #endif
329 return retired_fd;
330 }
331
332 make_socket_noninheritable (sock);
333
334 // IPC accept() filters
335 #if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
336 if (!filter (sock)) {
337 int rc = ::close (sock);
338 errno_assert (rc == 0);
339 return retired_fd;
340 }
341 #endif
342
343 if (zmq::set_nosigpipe (sock)) {
344 #ifdef ZMQ_HAVE_WINDOWS
345 const int rc = closesocket (sock);
346 wsa_assert (rc != SOCKET_ERROR);
347 #else
348 int rc = ::close (sock);
349 errno_assert (rc == 0);
350 #endif
351 return retired_fd;
352 }
353
354 return sock;
355 }
356
357 #endif
358