mirror of
https://github.com/zeromq/libzmq.git
synced 2024-12-13 18:55:10 +01:00
Merge pull request #311 from shripchenko/master
Implement ZMQ_TCP_ACCEPT_FILTER setsockopt() for listening TCP sockets.
This commit is contained in:
commit
525be51812
@ -415,6 +415,22 @@ Default value:: -1 (leave to OS default)
|
||||
Applicable socket types:: all, when using TCP transports.
|
||||
|
||||
|
||||
ZMQ_TCP_ACCEPT_FILTER: Assign filters to allow new TCP connections
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Assign arbitrary number of filters that will be applied for each new TCP transport
|
||||
connection on a listening socket.
|
||||
If no filters applied, then TCP transport allows connections from any ip.
|
||||
If at least one filter is applied then new connection source ip should be matched.
|
||||
To clear all filters call zmq_setsockopt(socket, ZMQ_TCP_ACCEPT_FILTER, NULL, 0).
|
||||
Filter is a null-terminated string with ipv6 or ipv4 CIDR.
|
||||
|
||||
[horizontal]
|
||||
Option value type:: binary data
|
||||
Option value unit:: N/A
|
||||
Default value:: no filters (allow from all)
|
||||
Applicable socket types:: all listening sockets, when using TCP transports.
|
||||
|
||||
|
||||
RETURN VALUE
|
||||
------------
|
||||
The _zmq_setsockopt()_ function shall return zero if successful. Otherwise it
|
||||
|
@ -226,6 +226,7 @@ ZMQ_EXPORT int zmq_msg_set (zmq_msg_t *msg, int option, int optval);
|
||||
#define ZMQ_TCP_KEEPALIVE_CNT 35
|
||||
#define ZMQ_TCP_KEEPALIVE_IDLE 36
|
||||
#define ZMQ_TCP_KEEPALIVE_INTVL 37
|
||||
#define ZMQ_TCP_ACCEPT_FILTER 38
|
||||
|
||||
|
||||
/* Message options */
|
||||
|
@ -287,6 +287,32 @@ int zmq::options_t::setsockopt (int option_, const void *optval_,
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
case ZMQ_TCP_ACCEPT_FILTER:
|
||||
{
|
||||
if (optvallen_ == 0 && optval_ == NULL) {
|
||||
tcp_accept_filters.clear ();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
if (optvallen_ < 1 || optvallen_ > 255 || optval_ == NULL || *((const char*) optval_) == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
std::string filter_str ((const char*) optval_, optvallen_);
|
||||
|
||||
tcp_address_mask_t filter;
|
||||
int rc = filter.resolve (filter_str.c_str (), ipv4only ? true : false);
|
||||
if (rc != 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
tcp_accept_filters.push_back(filter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -24,9 +24,11 @@
|
||||
#define __ZMQ_OPTIONS_HPP_INCLUDED__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.hpp"
|
||||
#include "tcp_address.hpp"
|
||||
|
||||
namespace zmq
|
||||
{
|
||||
@ -118,6 +120,10 @@ namespace zmq
|
||||
int tcp_keepalive_idle;
|
||||
int tcp_keepalive_intvl;
|
||||
|
||||
// TCP accept() filters
|
||||
typedef std::vector <tcp_address_mask_t> tcp_accept_filters_t;
|
||||
tcp_accept_filters_t tcp_accept_filters;
|
||||
|
||||
// ID of the socket.
|
||||
int socket_id;
|
||||
};
|
||||
|
@ -445,3 +445,105 @@ sa_family_t zmq::tcp_address_t::family () const
|
||||
return address.generic.sa_family;
|
||||
}
|
||||
|
||||
zmq::tcp_address_mask_t::tcp_address_mask_t () :
|
||||
tcp_address_t ()
|
||||
{
|
||||
address_mask = -1;
|
||||
}
|
||||
|
||||
const int zmq::tcp_address_mask_t::mask () const
|
||||
{
|
||||
return address_mask;
|
||||
}
|
||||
|
||||
int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv4only_)
|
||||
{
|
||||
std::string addr_str;
|
||||
std::string mask_str;
|
||||
|
||||
// Find '/' at the end that separates address from the cidr mask number.
|
||||
// Allow empty mask clause and threat it like '/32' for ipv4 or '/128' for ipv6.
|
||||
const char *delimiter = strrchr (name_, '/');
|
||||
if (delimiter != NULL) {
|
||||
addr_str.assign (name_, delimiter - name_);
|
||||
mask_str.assign (delimiter + 1);
|
||||
if (mask_str.empty ()) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
addr_str.assign (name_);
|
||||
}
|
||||
|
||||
// Parse address part using standard routines.
|
||||
int rc = tcp_address_t::resolve_hostname (addr_str.c_str (), ipv4only_);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
// Parse the cidr mask number.
|
||||
if (mask_str.empty ()) {
|
||||
if (address.generic.sa_family == AF_INET6)
|
||||
address_mask = 128;
|
||||
else
|
||||
address_mask = 32;
|
||||
}
|
||||
else
|
||||
if (mask_str == "0") {
|
||||
address_mask = 0;
|
||||
}
|
||||
else {
|
||||
int mask = atoi (mask_str.c_str ());
|
||||
if (
|
||||
(mask < 1) ||
|
||||
(address.generic.sa_family == AF_INET6 && mask > 128) ||
|
||||
(address.generic.sa_family != AF_INET6 && mask > 32)
|
||||
) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
address_mask = mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bool zmq::tcp_address_mask_t::match_address (const struct sockaddr *ss, socklen_t ss_len) const
|
||||
{
|
||||
zmq_assert (ss != NULL && ss_len >= sizeof(struct sockaddr));
|
||||
|
||||
if (ss->sa_family != address.generic.sa_family)
|
||||
return false;
|
||||
|
||||
if (address_mask > 0) {
|
||||
int mask;
|
||||
const int8_t *our_bytes, *their_bytes;
|
||||
if (ss->sa_family == AF_INET6) {
|
||||
zmq_assert (ss_len == sizeof (struct sockaddr_in6));
|
||||
their_bytes = (const int8_t *) &(((const struct sockaddr_in6 *) ss)->sin6_addr);
|
||||
our_bytes = (const int8_t *) &address.ipv6.sin6_addr;
|
||||
mask = sizeof (struct in6_addr) * 8;
|
||||
}
|
||||
else {
|
||||
zmq_assert (ss_len == sizeof (struct sockaddr_in));
|
||||
their_bytes = (const int8_t *) &(((const struct sockaddr_in *) ss)->sin_addr);
|
||||
our_bytes = (const int8_t *) &address.ipv4.sin_addr;
|
||||
mask = sizeof (struct in_addr) * 8;
|
||||
}
|
||||
if (address_mask < mask) mask = address_mask;
|
||||
|
||||
int full_bytes = mask / 8;
|
||||
if (full_bytes) {
|
||||
if (memcmp(our_bytes, their_bytes, full_bytes))
|
||||
return false;
|
||||
}
|
||||
|
||||
int last_byte_bits = (0xffU << (8 - (mask % 8))) & 0xffU;
|
||||
if (last_byte_bits) {
|
||||
if ((their_bytes[full_bytes] & last_byte_bits) != (our_bytes[full_bytes] & last_byte_bits))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ namespace zmq
|
||||
const sockaddr *addr () const;
|
||||
socklen_t addrlen () const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
|
||||
int resolve_nic_name (const char *nic_, bool ipv4only_);
|
||||
int resolve_interface (const char *interface_, bool ipv4only_);
|
||||
@ -66,12 +66,28 @@ namespace zmq
|
||||
sockaddr_in ipv4;
|
||||
sockaddr_in6 ipv6;
|
||||
} address;
|
||||
};
|
||||
|
||||
tcp_address_t (const tcp_address_t&);
|
||||
const tcp_address_t &operator = (const tcp_address_t&);
|
||||
class tcp_address_mask_t : public tcp_address_t
|
||||
{
|
||||
public:
|
||||
|
||||
tcp_address_mask_t ();
|
||||
|
||||
// This function enhances tcp_address_t::resolve() with ability to parse
|
||||
// additional cidr-like(/xx) mask value at the end of the name string.
|
||||
// Works only with remote hostnames.
|
||||
int resolve (const char* name_, bool ipv4only_);
|
||||
|
||||
const int mask () const;
|
||||
|
||||
const bool match_address (const struct sockaddr *sa, socklen_t ss_len) const;
|
||||
|
||||
private:
|
||||
|
||||
int address_mask;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -235,7 +235,11 @@ zmq::fd_t zmq::tcp_listener_t::accept ()
|
||||
{
|
||||
// Accept one connection and deal with different failure modes.
|
||||
zmq_assert (s != retired_fd);
|
||||
fd_t sock = ::accept (s, NULL, NULL);
|
||||
|
||||
struct sockaddr_storage ss = {0};
|
||||
socklen_t ss_len = sizeof (ss);
|
||||
fd_t sock = ::accept (s, (struct sockaddr *) &ss, &ss_len);
|
||||
|
||||
#ifdef ZMQ_HAVE_WINDOWS
|
||||
if (sock == INVALID_SOCKET) {
|
||||
wsa_assert (WSAGetLastError () == WSAEWOULDBLOCK ||
|
||||
@ -250,6 +254,28 @@ zmq::fd_t zmq::tcp_listener_t::accept ()
|
||||
return retired_fd;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!options.tcp_accept_filters.empty ()) {
|
||||
bool matched = false;
|
||||
//ss_len = 1;
|
||||
for (options_t::tcp_accept_filters_t::size_type i = 0; i != options.tcp_accept_filters.size (); ++i) {
|
||||
if (options.tcp_accept_filters[i].match_address ((struct sockaddr *) &ss, ss_len)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
#ifdef ZMQ_HAVE_WINDOWS
|
||||
int rc = closesocket (sock);
|
||||
wsa_assert (rc != SOCKET_ERROR);
|
||||
#else
|
||||
int rc = ::close (sock);
|
||||
errno_assert (rc == 0);
|
||||
#endif
|
||||
return retired_fd;
|
||||
}
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,8 @@ namespace zmq
|
||||
|
||||
// Accept the new connection. Returns the file descriptor of the
|
||||
// newly created connection. The function may return retired_fd
|
||||
// if the connection was dropped while waiting in the listen backlog.
|
||||
// if the connection was dropped while waiting in the listen backlog
|
||||
// or was denied because of accept filters.
|
||||
fd_t accept ();
|
||||
|
||||
// Address to listen on.
|
||||
|
Loading…
Reference in New Issue
Block a user