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


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:54