2011-07-28 13:19:55 +02:00
|
|
|
/*
|
2016-01-28 15:07:31 +01:00
|
|
|
Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2015-06-02 22:33:55 +02:00
|
|
|
This file is part of libzmq, the ZeroMQ core engine in C++.
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2015-06-02 22:33:55 +02:00
|
|
|
libzmq is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU Lesser General Public License (LGPL) as published
|
|
|
|
by the Free Software Foundation; either version 3 of the License, or
|
2011-07-28 13:19:55 +02:00
|
|
|
(at your option) any later version.
|
|
|
|
|
2015-06-02 22:33:55 +02:00
|
|
|
As a special exception, the Contributors give you permission to link
|
|
|
|
this library with independent modules to produce an executable,
|
|
|
|
regardless of the license terms of these independent modules, and to
|
|
|
|
copy and distribute the resulting executable under terms of your choice,
|
|
|
|
provided that you also meet, for each linked independent module, the
|
|
|
|
terms and conditions of the license of that module. An independent
|
|
|
|
module is a module which is not derived from or based on this library.
|
|
|
|
If you modify this library, you must extend this exception to your
|
|
|
|
version of the library.
|
|
|
|
|
|
|
|
libzmq is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
2011-07-28 13:19:55 +02:00
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2016-02-18 10:56:52 -06:00
|
|
|
#include "precompiled.hpp"
|
2011-07-28 13:46:16 +02:00
|
|
|
#include "ipc_listener.hpp"
|
|
|
|
|
2019-10-19 14:46:53 +02:00
|
|
|
#if defined ZMQ_HAVE_IPC
|
2011-07-28 13:46:16 +02:00
|
|
|
|
2011-07-28 13:19:55 +02:00
|
|
|
#include <new>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
2011-08-18 11:08:22 +02:00
|
|
|
#include "ipc_address.hpp"
|
2011-07-28 13:19:55 +02:00
|
|
|
#include "io_thread.hpp"
|
|
|
|
#include "config.hpp"
|
|
|
|
#include "err.hpp"
|
2011-09-02 15:34:12 +02:00
|
|
|
#include "ip.hpp"
|
2012-05-04 02:32:46 +01:00
|
|
|
#include "socket_base.hpp"
|
2019-02-01 10:47:31 -05:00
|
|
|
#include "address.hpp"
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2019-10-19 14:46:53 +02:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#ifdef ZMQ_IOTHREAD_POLLER_USE_SELECT
|
|
|
|
#error On Windows, IPC does not work with POLLER=select, use POLLER=epoll instead, or disable IPC transport
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <afunix.h>
|
|
|
|
#include <direct.h>
|
|
|
|
|
|
|
|
#define rmdir _rmdir
|
|
|
|
#define unlink _unlink
|
|
|
|
|
|
|
|
#else
|
2011-07-28 13:19:55 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/un.h>
|
2019-10-19 14:46:53 +02:00
|
|
|
#endif
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2016-02-22 02:21:34 +00:00
|
|
|
#ifdef ZMQ_HAVE_LOCAL_PEERCRED
|
2013-12-04 13:56:12 -08:00
|
|
|
#include <sys/types.h>
|
2016-02-22 02:21:34 +00:00
|
|
|
#include <sys/ucred.h>
|
2013-12-04 13:56:12 -08:00
|
|
|
#endif
|
|
|
|
#ifdef ZMQ_HAVE_SO_PEERCRED
|
2016-02-22 02:21:34 +00:00
|
|
|
#include <sys/types.h>
|
2013-12-04 13:56:12 -08:00
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
2014-07-02 21:06:33 -03:00
|
|
|
#if defined ZMQ_HAVE_OPENBSD
|
|
|
|
#define ucred sockpeercred
|
|
|
|
#endif
|
2013-12-04 13:56:12 -08:00
|
|
|
#endif
|
|
|
|
|
2011-07-28 13:19:55 +02:00
|
|
|
zmq::ipc_listener_t::ipc_listener_t (io_thread_t *io_thread_,
|
|
|
|
socket_base_t *socket_,
|
|
|
|
const options_t &options_) :
|
2021-09-21 19:11:11 +01:00
|
|
|
stream_listener_base_t (io_thread_, socket_, options_), _has_file (false)
|
2011-07-28 13:19:55 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void zmq::ipc_listener_t::in_event ()
|
|
|
|
{
|
2019-12-25 13:51:21 +01:00
|
|
|
const fd_t fd = accept ();
|
2011-07-28 13:19:55 +02:00
|
|
|
|
|
|
|
// If connection was reset by the peer in the meantime, just ignore it.
|
|
|
|
// TODO: Handle specific errors like ENFILE/EMFILE etc.
|
2012-05-04 02:32:46 +01:00
|
|
|
if (fd == retired_fd) {
|
2019-02-01 05:43:45 -05:00
|
|
|
_socket->event_accept_failed (
|
|
|
|
make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
|
2011-07-28 13:19:55 +02:00
|
|
|
return;
|
2012-05-04 02:32:46 +01:00
|
|
|
}
|
2011-07-28 13:19:55 +02:00
|
|
|
|
|
|
|
// Create the engine object for this connection.
|
2019-01-31 11:06:50 -05:00
|
|
|
create_engine (fd);
|
2011-07-28 13:19:55 +02:00
|
|
|
}
|
|
|
|
|
2019-02-02 16:36:32 +01:00
|
|
|
std::string
|
|
|
|
zmq::ipc_listener_t::get_socket_name (zmq::fd_t fd_,
|
|
|
|
socket_end_t socket_end_) const
|
2012-02-08 22:06:46 +00:00
|
|
|
{
|
2019-02-02 16:36:32 +01:00
|
|
|
return zmq::get_socket_name<ipc_address_t> (fd_, socket_end_);
|
2012-02-08 22:06:46 +00:00
|
|
|
}
|
|
|
|
|
2019-02-01 10:47:31 -05:00
|
|
|
int zmq::ipc_listener_t::set_local_address (const char *addr_)
|
2011-07-28 13:19:55 +02:00
|
|
|
{
|
2013-06-15 16:11:50 +08:00
|
|
|
// Create addr on stack for auto-cleanup
|
|
|
|
std::string addr (addr_);
|
|
|
|
|
|
|
|
// Allow wildcard file
|
2016-04-10 23:58:33 +01:00
|
|
|
if (options.use_fd == -1 && addr[0] == '*') {
|
2019-12-06 18:17:28 +01:00
|
|
|
if (create_ipc_wildcard_address (_tmp_socket_dirname, addr) < 0) {
|
2013-12-19 21:06:22 +01:00
|
|
|
return -1;
|
2016-03-14 08:49:11 -07:00
|
|
|
}
|
2012-02-08 22:06:46 +00:00
|
|
|
}
|
2012-02-18 20:44:41 +00:00
|
|
|
|
2011-07-28 13:46:16 +02:00
|
|
|
// Get rid of the file associated with the UNIX domain socket that
|
|
|
|
// may have been left behind by the previous run of the application.
|
2016-02-01 12:33:17 +00:00
|
|
|
// MUST NOT unlink if the FD is managed by the user, or it will stop
|
|
|
|
// working after the first client connects. The user will take care of
|
|
|
|
// cleaning up the file after the service is stopped.
|
2016-02-09 09:36:14 +00:00
|
|
|
if (options.use_fd == -1) {
|
2016-02-01 12:33:17 +00:00
|
|
|
::unlink (addr.c_str ());
|
|
|
|
}
|
2019-01-31 10:01:23 -05:00
|
|
|
_filename.clear ();
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2011-08-18 11:08:22 +02:00
|
|
|
// Initialise the address structure.
|
|
|
|
ipc_address_t address;
|
2013-06-15 16:11:50 +08:00
|
|
|
int rc = address.resolve (addr.c_str ());
|
2016-03-14 08:49:11 -07:00
|
|
|
if (rc != 0) {
|
2019-01-31 10:01:23 -05:00
|
|
|
if (!_tmp_socket_dirname.empty ()) {
|
2016-03-14 08:49:11 -07:00
|
|
|
// We need to preserve errno to return to the user
|
2019-12-25 13:51:21 +01:00
|
|
|
const int tmp_errno = errno;
|
2019-01-31 10:01:23 -05:00
|
|
|
::rmdir (_tmp_socket_dirname.c_str ());
|
|
|
|
_tmp_socket_dirname.clear ();
|
2018-08-09 16:15:24 +02:00
|
|
|
errno = tmp_errno;
|
2016-03-14 08:49:11 -07:00
|
|
|
}
|
2011-07-28 13:46:16 +02:00
|
|
|
return -1;
|
2016-03-14 08:49:11 -07:00
|
|
|
}
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2019-01-31 10:01:23 -05:00
|
|
|
address.to_string (_endpoint);
|
2012-05-04 02:32:46 +01:00
|
|
|
|
2016-02-09 09:36:14 +00:00
|
|
|
if (options.use_fd != -1) {
|
2019-01-31 10:01:23 -05:00
|
|
|
_s = options.use_fd;
|
2016-02-01 12:33:17 +00:00
|
|
|
} else {
|
|
|
|
// Create a listening socket.
|
2019-01-31 10:01:23 -05:00
|
|
|
_s = open_socket (AF_UNIX, SOCK_STREAM, 0);
|
2019-10-19 14:46:53 +02:00
|
|
|
if (_s == retired_fd) {
|
2019-01-31 10:01:23 -05:00
|
|
|
if (!_tmp_socket_dirname.empty ()) {
|
2016-03-14 08:49:11 -07:00
|
|
|
// We need to preserve errno to return to the user
|
2019-12-25 13:51:21 +01:00
|
|
|
const int tmp_errno = errno;
|
2019-01-31 10:01:23 -05:00
|
|
|
::rmdir (_tmp_socket_dirname.c_str ());
|
|
|
|
_tmp_socket_dirname.clear ();
|
2018-08-09 16:15:24 +02:00
|
|
|
errno = tmp_errno;
|
2016-03-14 08:49:11 -07:00
|
|
|
}
|
2016-02-01 12:33:17 +00:00
|
|
|
return -1;
|
2016-03-14 08:49:11 -07:00
|
|
|
}
|
2016-02-01 12:33:17 +00:00
|
|
|
|
|
|
|
// Bind the socket to the file path.
|
2019-01-31 10:01:23 -05:00
|
|
|
rc = bind (_s, const_cast<sockaddr *> (address.addr ()),
|
2018-05-27 06:48:49 -04:00
|
|
|
address.addrlen ());
|
2016-02-01 12:33:17 +00:00
|
|
|
if (rc != 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
// Listen for incoming connections.
|
2019-01-31 10:01:23 -05:00
|
|
|
rc = listen (_s, options.backlog);
|
2016-02-01 12:33:17 +00:00
|
|
|
if (rc != 0)
|
|
|
|
goto error;
|
|
|
|
}
|
2011-07-28 16:32:08 +02:00
|
|
|
|
2019-01-31 10:01:23 -05:00
|
|
|
_filename = ZMQ_MOVE (addr);
|
|
|
|
_has_file = true;
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2019-02-01 05:43:45 -05:00
|
|
|
_socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint),
|
|
|
|
_s);
|
2012-02-18 20:44:41 +00:00
|
|
|
return 0;
|
2012-06-13 14:48:27 +02:00
|
|
|
|
|
|
|
error:
|
2019-12-25 13:51:21 +01:00
|
|
|
const int err = errno;
|
2012-06-13 14:48:27 +02:00
|
|
|
close ();
|
|
|
|
errno = err;
|
|
|
|
return -1;
|
2011-07-28 13:19:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int zmq::ipc_listener_t::close ()
|
|
|
|
{
|
2019-01-31 10:01:23 -05:00
|
|
|
zmq_assert (_s != retired_fd);
|
2019-12-25 13:51:21 +01:00
|
|
|
const fd_t fd_for_event = _s;
|
2019-10-19 14:46:53 +02:00
|
|
|
#ifdef ZMQ_HAVE_WINDOWS
|
|
|
|
int rc = closesocket (_s);
|
|
|
|
wsa_assert (rc != SOCKET_ERROR);
|
|
|
|
#else
|
2019-01-31 10:01:23 -05:00
|
|
|
int rc = ::close (_s);
|
2012-07-11 17:59:22 +02:00
|
|
|
errno_assert (rc == 0);
|
2019-10-19 14:46:53 +02:00
|
|
|
#endif
|
2011-07-28 13:19:55 +02:00
|
|
|
|
2019-01-31 10:01:23 -05:00
|
|
|
_s = retired_fd;
|
2012-07-12 01:47:36 +02:00
|
|
|
|
2019-01-31 10:01:23 -05:00
|
|
|
if (_has_file && options.use_fd == -1) {
|
2019-02-04 05:33:28 -05:00
|
|
|
if (!_tmp_socket_dirname.empty ()) {
|
|
|
|
// TODO review this behaviour, it is inconsistent with the use of
|
|
|
|
// unlink in open since 656cdb959a7482c45db979c1d08ede585d12e315;
|
|
|
|
// however, we must at least remove the file before removing the
|
|
|
|
// directory, otherwise it will always fail
|
|
|
|
rc = ::unlink (_filename.c_str ());
|
|
|
|
|
|
|
|
if (rc == 0) {
|
|
|
|
rc = ::rmdir (_tmp_socket_dirname.c_str ());
|
|
|
|
_tmp_socket_dirname.clear ();
|
|
|
|
}
|
2016-03-14 08:49:11 -07:00
|
|
|
}
|
|
|
|
|
2012-05-04 02:32:46 +01:00
|
|
|
if (rc != 0) {
|
2019-02-01 05:43:45 -05:00
|
|
|
_socket->event_close_failed (
|
|
|
|
make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ());
|
2011-07-28 13:19:55 +02:00
|
|
|
return -1;
|
2012-05-04 02:32:46 +01:00
|
|
|
}
|
2011-07-28 13:19:55 +02:00
|
|
|
}
|
|
|
|
|
2019-02-01 05:43:45 -05:00
|
|
|
_socket->event_closed (make_unconnected_bind_endpoint_pair (_endpoint),
|
|
|
|
fd_for_event);
|
2011-07-28 13:19:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-04 13:56:12 -08:00
|
|
|
#if defined ZMQ_HAVE_SO_PEERCRED
|
|
|
|
|
2018-08-09 16:15:24 +02:00
|
|
|
bool zmq::ipc_listener_t::filter (fd_t sock_)
|
2013-12-04 13:56:12 -08:00
|
|
|
{
|
|
|
|
if (options.ipc_uid_accept_filters.empty ()
|
|
|
|
&& options.ipc_pid_accept_filters.empty ()
|
|
|
|
&& options.ipc_gid_accept_filters.empty ())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
struct ucred cred;
|
|
|
|
socklen_t size = sizeof (cred);
|
|
|
|
|
2018-08-09 16:15:24 +02:00
|
|
|
if (getsockopt (sock_, SOL_SOCKET, SO_PEERCRED, &cred, &size))
|
2013-12-04 13:56:12 -08:00
|
|
|
return false;
|
|
|
|
if (options.ipc_uid_accept_filters.find (cred.uid)
|
|
|
|
!= options.ipc_uid_accept_filters.end ()
|
2013-12-06 00:46:14 -08:00
|
|
|
|| options.ipc_gid_accept_filters.find (cred.gid)
|
|
|
|
!= options.ipc_gid_accept_filters.end ()
|
2013-12-04 13:56:12 -08:00
|
|
|
|| options.ipc_pid_accept_filters.find (cred.pid)
|
|
|
|
!= options.ipc_pid_accept_filters.end ())
|
|
|
|
return true;
|
|
|
|
|
2020-02-04 12:37:31 +01:00
|
|
|
const struct passwd *pw;
|
|
|
|
const struct group *gr;
|
2013-12-04 13:56:12 -08:00
|
|
|
|
|
|
|
if (!(pw = getpwuid (cred.uid)))
|
|
|
|
return false;
|
2019-12-24 16:39:04 +01:00
|
|
|
for (options_t::ipc_gid_accept_filters_t::const_iterator
|
|
|
|
it = options.ipc_gid_accept_filters.begin (),
|
|
|
|
end = options.ipc_gid_accept_filters.end ();
|
|
|
|
it != end; it++) {
|
2013-12-04 13:56:12 -08:00
|
|
|
if (!(gr = getgrgid (*it)))
|
|
|
|
continue;
|
2013-12-06 00:46:14 -08:00
|
|
|
for (char **mem = gr->gr_mem; *mem; mem++) {
|
2013-12-04 13:56:12 -08:00
|
|
|
if (!strcmp (*mem, pw->pw_name))
|
|
|
|
return true;
|
2013-12-06 00:46:14 -08:00
|
|
|
}
|
2013-12-04 13:56:12 -08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#elif defined ZMQ_HAVE_LOCAL_PEERCRED
|
|
|
|
|
2018-08-09 16:15:24 +02:00
|
|
|
bool zmq::ipc_listener_t::filter (fd_t sock_)
|
2013-12-04 13:56:12 -08:00
|
|
|
{
|
|
|
|
if (options.ipc_uid_accept_filters.empty ()
|
|
|
|
&& options.ipc_gid_accept_filters.empty ())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
struct xucred cred;
|
|
|
|
socklen_t size = sizeof (cred);
|
|
|
|
|
2018-08-09 16:15:24 +02:00
|
|
|
if (getsockopt (sock_, 0, LOCAL_PEERCRED, &cred, &size))
|
2013-12-04 13:56:12 -08:00
|
|
|
return false;
|
|
|
|
if (cred.cr_version != XUCRED_VERSION)
|
|
|
|
return false;
|
|
|
|
if (options.ipc_uid_accept_filters.find (cred.cr_uid)
|
|
|
|
!= options.ipc_uid_accept_filters.end ())
|
|
|
|
return true;
|
|
|
|
for (int i = 0; i < cred.cr_ngroups; i++) {
|
|
|
|
if (options.ipc_gid_accept_filters.find (cred.cr_groups[i])
|
|
|
|
!= options.ipc_gid_accept_filters.end ())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2011-07-28 13:19:55 +02:00
|
|
|
zmq::fd_t zmq::ipc_listener_t::accept ()
|
|
|
|
{
|
2011-07-28 16:32:08 +02:00
|
|
|
// Accept one connection and deal with different failure modes.
|
2012-09-30 11:52:43 -07:00
|
|
|
// The situation where connection cannot be accepted due to insufficient
|
|
|
|
// resources is considered valid and treated by ignoring the connection.
|
2019-01-31 10:01:23 -05:00
|
|
|
zmq_assert (_s != retired_fd);
|
2017-11-17 18:40:53 +00:00
|
|
|
#if defined ZMQ_HAVE_SOCK_CLOEXEC && defined HAVE_ACCEPT4
|
2019-01-31 10:01:23 -05:00
|
|
|
fd_t sock = ::accept4 (_s, NULL, NULL, SOCK_CLOEXEC);
|
2016-07-30 21:22:50 +02:00
|
|
|
#else
|
2019-10-19 14:46:53 +02:00
|
|
|
struct sockaddr_storage ss;
|
|
|
|
memset (&ss, 0, sizeof (ss));
|
|
|
|
#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS
|
|
|
|
int ss_len = sizeof (ss);
|
|
|
|
#else
|
|
|
|
socklen_t ss_len = sizeof (ss);
|
|
|
|
#endif
|
|
|
|
|
2019-12-25 13:51:21 +01:00
|
|
|
const fd_t sock =
|
2019-10-19 14:46:53 +02:00
|
|
|
::accept (_s, reinterpret_cast<struct sockaddr *> (&ss), &ss_len);
|
2016-07-30 21:22:50 +02:00
|
|
|
#endif
|
2019-10-19 14:46:53 +02:00
|
|
|
if (sock == retired_fd) {
|
|
|
|
#if defined ZMQ_HAVE_WINDOWS
|
|
|
|
const int last_error = WSAGetLastError ();
|
|
|
|
wsa_assert (last_error == WSAEWOULDBLOCK || last_error == WSAECONNRESET
|
|
|
|
|| last_error == WSAEMFILE || last_error == WSAENOBUFS);
|
|
|
|
#else
|
2011-07-28 16:32:08 +02:00
|
|
|
errno_assert (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR
|
2013-01-21 17:14:26 -07:00
|
|
|
|| errno == ECONNABORTED || errno == EPROTO
|
2012-09-30 11:52:43 -07:00
|
|
|
|| errno == ENFILE);
|
2019-10-19 14:46:53 +02:00
|
|
|
#endif
|
2011-07-28 13:19:55 +02:00
|
|
|
return retired_fd;
|
2011-07-28 16:32:08 +02:00
|
|
|
}
|
2013-12-04 13:56:12 -08:00
|
|
|
|
2018-05-23 10:19:34 +02:00
|
|
|
make_socket_noninheritable (sock);
|
2014-04-02 12:25:40 -04:00
|
|
|
|
2013-12-04 13:56:12 -08:00
|
|
|
// IPC accept() filters
|
|
|
|
#if defined ZMQ_HAVE_SO_PEERCRED || defined ZMQ_HAVE_LOCAL_PEERCRED
|
|
|
|
if (!filter (sock)) {
|
|
|
|
int rc = ::close (sock);
|
|
|
|
errno_assert (rc == 0);
|
|
|
|
return retired_fd;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-01-03 23:55:57 +01:00
|
|
|
if (zmq::set_nosigpipe (sock)) {
|
|
|
|
#ifdef ZMQ_HAVE_WINDOWS
|
2019-12-25 13:51:21 +01:00
|
|
|
const int rc = closesocket (sock);
|
2017-01-03 23:55:57 +01:00
|
|
|
wsa_assert (rc != SOCKET_ERROR);
|
|
|
|
#else
|
|
|
|
int rc = ::close (sock);
|
|
|
|
errno_assert (rc == 0);
|
|
|
|
#endif
|
|
|
|
return retired_fd;
|
|
|
|
}
|
|
|
|
|
2011-07-28 13:19:55 +02:00
|
|
|
return sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|