tcp_address.cpp 11.3 KB
Newer Older
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 7 8
    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
9 10
    (at your option) any later version.

11 12 13 14 15 16 17 18 19 20 21 22 23 24
    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.
25 26 27 28 29

    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/>.
*/

30
#include "precompiled.hpp"
31 32
#include <string>

33
#include "macros.hpp"
34
#include "tcp_address.hpp"
Martin Sustrik's avatar
Martin Sustrik committed
35
#include "stdint.hpp"
36
#include "err.hpp"
37
#include "ip.hpp"
38

39
#ifndef ZMQ_HAVE_WINDOWS
40 41 42
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
43
#include <net/if.h>
44
#include <netdb.h>
45
#include <ctype.h>
46 47
#include <unistd.h>
#include <stdlib.h>
48 49
#endif

50 51
#include <limits.h>

52
zmq::tcp_address_t::tcp_address_t () : _has_src_addr (false)
53
{
54 55
    memset (&_address, 0, sizeof (_address));
    memset (&_source_address, 0, sizeof (_source_address));
56 57
}

58
zmq::tcp_address_t::tcp_address_t (const sockaddr *sa_, socklen_t sa_len_) :
59
    _has_src_addr (false)
60
{
61
    zmq_assert (sa_ && sa_len_ > 0);
Martin Hurton's avatar
Martin Hurton committed
62

63 64
    memset (&_address, 0, sizeof (_address));
    memset (&_source_address, 0, sizeof (_source_address));
65
    if (sa_->sa_family == AF_INET
66 67
        && sa_len_ >= static_cast<socklen_t> (sizeof (_address.ipv4)))
        memcpy (&_address.ipv4, sa_, sizeof (_address.ipv4));
68
    else if (sa_->sa_family == AF_INET6
69 70
             && sa_len_ >= static_cast<socklen_t> (sizeof (_address.ipv6)))
        memcpy (&_address.ipv6, sa_, sizeof (_address.ipv6));
71 72
}

73
int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_)
74
{
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    // Test the ';' to know if we have a source address in name_
    const char *src_delimiter = strrchr (name_, ';');
    if (src_delimiter) {
        std::string src_name (name_, src_delimiter - name_);

        ip_resolver_options_t src_resolver_opts;

        src_resolver_opts
          .bindable (true)
          //  Restrict hostname/service to literals to avoid any DNS
          //  lookups or service-name irregularity due to
          //  indeterminate socktype.
          .allow_dns (false)
          .allow_nic_name (true)
          .ipv6 (ipv6_)
          .expect_port (true);

        ip_resolver_t src_resolver (src_resolver_opts);

        const int rc =
95
          src_resolver.resolve (&_source_address, src_name.c_str ());
96
        if (rc != 0)
97
            return -1;
98 99
        name_ = src_delimiter + 1;
        _has_src_addr = true;
100 101
    }

102
    ip_resolver_options_t resolver_opts;
103

104 105 106 107 108
    resolver_opts.bindable (local_)
      .allow_dns (!local_)
      .allow_nic_name (local_)
      .ipv6 (ipv6_)
      .expect_port (true);
109

110
    ip_resolver_t resolver (resolver_opts);
111

112
    return resolver.resolve (&_address, name_);
113 114
}

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
template <size_t N1, size_t N2>
static std::string make_address_string (const char *hbuf,
                                        uint16_t port,
                                        const char (&ipv6_prefix)[N1],
                                        const char (&ipv6_suffix)[N2])
{
    const size_t max_port_str_length = 5;
    char buf[NI_MAXHOST + sizeof ipv6_prefix + sizeof ipv6_suffix
             + max_port_str_length];
    char *pos = buf;
    memcpy (pos, ipv6_prefix, sizeof ipv6_prefix - 1);
    pos += sizeof ipv6_prefix - 1;
    const int hbuf_len = strlen (hbuf);
    memcpy (pos, hbuf, hbuf_len);
    pos += hbuf_len;
    memcpy (pos, ipv6_suffix, sizeof ipv6_suffix - 1);
    pos += sizeof ipv6_suffix - 1;
    pos += sprintf (pos, "%d", ntohs (port));
    return std::string (buf, pos - buf);
}

136
int zmq::tcp_address_t::to_string (std::string &addr_) const
137
{
138
    if (_address.family () != AF_INET && _address.family () != AF_INET6) {
139 140 141 142
        addr_.clear ();
        return -1;
    }

143
    //  Not using service resolving because of
144
    //  https://github.com/zeromq/libzmq/commit/1824574f9b5a8ce786853320e3ea09fe1f822bc4
145
    char hbuf[NI_MAXHOST];
146 147
    const int rc = getnameinfo (addr (), addrlen (), hbuf, sizeof (hbuf), NULL,
                                0, NI_NUMERICHOST);
148 149 150 151 152
    if (rc != 0) {
        addr_.clear ();
        return rc;
    }

153 154 155 156
    const char ipv4_prefix[] = "tcp://";
    const char ipv4_suffix[] = ":";
    const char ipv6_prefix[] = "tcp://[";
    const char ipv6_suffix[] = "]:";
157
    if (_address.family () == AF_INET6) {
158 159
        addr_ = make_address_string (hbuf, _address.ipv6.sin6_port, ipv6_prefix,
                                     ipv6_suffix);
160
    } else {
161 162
        addr_ = make_address_string (hbuf, _address.ipv4.sin_port, ipv4_prefix,
                                     ipv4_suffix);
Martin Hurton's avatar
Martin Hurton committed
163
    }
164 165 166
    return 0;
}

167
const sockaddr *zmq::tcp_address_t::addr () const
168
{
169
    return _address.as_sockaddr ();
170 171
}

172
socklen_t zmq::tcp_address_t::addrlen () const
173
{
174
    return _address.sockaddr_len ();
175 176
}

177 178
const sockaddr *zmq::tcp_address_t::src_addr () const
{
179
    return _source_address.as_sockaddr ();
180 181 182 183
}

socklen_t zmq::tcp_address_t::src_addrlen () const
{
184
    return _source_address.sockaddr_len ();
185 186
}

Martin Hurton's avatar
Martin Hurton committed
187
bool zmq::tcp_address_t::has_src_addr () const
188 189 190 191
{
    return _has_src_addr;
}

Martin Sustrik's avatar
Martin Sustrik committed
192
#if defined ZMQ_HAVE_WINDOWS
193
unsigned short zmq::tcp_address_t::family () const
Martin Sustrik's avatar
Martin Sustrik committed
194
#else
195
sa_family_t zmq::tcp_address_t::family () const
Martin Sustrik's avatar
Martin Sustrik committed
196
#endif
197
{
198
    return _address.family ();
199 200
}

201
zmq::tcp_address_mask_t::tcp_address_mask_t () : _address_mask (-1)
202
{
203
    memset (&_network_address, 0, sizeof (_network_address));
204 205
}

206
int zmq::tcp_address_mask_t::mask () const
207
{
208
    return _address_mask;
209 210
}

Pieter Hintjens's avatar
Pieter Hintjens committed
211
int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv6_)
212 213
{
    // Find '/' at the end that separates address from the cidr mask number.
Martin Hurton's avatar
Martin Hurton committed
214
    // Allow empty mask clause and treat it like '/32' for ipv4 or '/128' for ipv6.
215
    std::string addr_str, mask_str;
216 217 218 219 220 221 222 223
    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;
        }
224
    } else
225 226 227
        addr_str.assign (name_);

    // Parse address part using standard routines.
228 229 230 231 232 233 234 235 236 237
    ip_resolver_options_t resolver_opts;

    resolver_opts.bindable (false)
      .allow_dns (false)
      .allow_nic_name (false)
      .ipv6 (ipv6_)
      .expect_port (false);

    ip_resolver_t resolver (resolver_opts);

238
    const int rc = resolver.resolve (&_network_address, addr_str.c_str ());
239 240 241 242
    if (rc != 0)
        return rc;

    // Parse the cidr mask number.
243 244 245 246
    const int full_mask_ipv4 =
      sizeof (_network_address.ipv4.sin_addr) * CHAR_BIT;
    const int full_mask_ipv6 =
      sizeof (_network_address.ipv6.sin6_addr) * CHAR_BIT;
247
    if (mask_str.empty ()) {
248 249
        _address_mask = _network_address.family () == AF_INET6 ? full_mask_ipv6
                                                               : full_mask_ipv4;
250
    } else if (mask_str == "0")
251
        _address_mask = 0;
252
    else {
253
        const long mask = strtol (mask_str.c_str (), NULL, 10);
254
        if ((mask < 1)
255 256 257
            || (_network_address.family () == AF_INET6 && mask > full_mask_ipv6)
            || (_network_address.family () != AF_INET6
                && mask > full_mask_ipv4)) {
258 259 260
            errno = EINVAL;
            return -1;
        }
261
        _address_mask = static_cast<int> (mask);
262 263 264 265 266
    }

    return 0;
}

267
int zmq::tcp_address_mask_t::to_string (std::string &addr_) const
268
{
269 270
    if (_network_address.family () != AF_INET
        && _network_address.family () != AF_INET6) {
271 272 273
        addr_.clear ();
        return -1;
    }
274
    if (_address_mask == -1) {
275 276 277 278
        addr_.clear ();
        return -1;
    }

279
    char hbuf[NI_MAXHOST];
280 281 282
    const int rc = getnameinfo (_network_address.as_sockaddr (),
                                _network_address.sockaddr_len (), hbuf,
                                sizeof (hbuf), NULL, 0, NI_NUMERICHOST);
283 284 285 286 287
    if (rc != 0) {
        addr_.clear ();
        return rc;
    }

288 289 290 291 292 293 294 295 296 297 298 299 300 301
    const size_t max_mask_len = 4;
    const char ipv6_prefix[] = "[";
    const char ipv6_suffix[] = "]/";
    const char ipv4_suffix[] = "/";
    char
      buf[NI_MAXHOST + sizeof ipv6_prefix + sizeof ipv6_suffix + max_mask_len];
    char *pos = buf;
    if (_network_address.family () == AF_INET6) {
        memcpy (pos, ipv6_prefix, sizeof ipv6_prefix - 1);
        pos += sizeof ipv6_prefix - 1;
    }
    const int hbuf_len = strlen (hbuf);
    memcpy (pos, hbuf, hbuf_len);
    pos += hbuf_len;
302
    if (_network_address.family () == AF_INET6) {
303 304
        memcpy (pos, ipv6_suffix, sizeof ipv6_suffix - 1);
        pos += sizeof ipv6_suffix - 1;
305
    } else {
306 307
        memcpy (pos, ipv4_suffix, sizeof ipv4_suffix - 1);
        pos += sizeof ipv4_suffix - 1;
Martin Hurton's avatar
Martin Hurton committed
308
    }
309 310
    pos += sprintf (pos, "%d", _address_mask);
    addr_.assign (buf, pos - buf);
311 312 313
    return 0;
}

314 315
bool zmq::tcp_address_mask_t::match_address (const struct sockaddr *ss_,
                                             const socklen_t ss_len_) const
316
{
317
    zmq_assert (_address_mask != -1 && ss_ != NULL
318 319
                && ss_len_
                     >= static_cast<socklen_t> (sizeof (struct sockaddr)));
320

321
    if (ss_->sa_family != _network_address.generic.sa_family)
322 323
        return false;

324
    if (_address_mask > 0) {
325
        int mask;
326
        const uint8_t *our_bytes, *their_bytes;
327 328 329 330 331
        if (ss_->sa_family == AF_INET6) {
            zmq_assert (ss_len_ == sizeof (struct sockaddr_in6));
            their_bytes = reinterpret_cast<const uint8_t *> (
              &((reinterpret_cast<const struct sockaddr_in6 *> (ss_))
                  ->sin6_addr));
332 333
            our_bytes = reinterpret_cast<const uint8_t *> (
              &_network_address.ipv6.sin6_addr);
334
            mask = sizeof (struct in6_addr) * 8;
335
        } else {
336 337 338
            zmq_assert (ss_len_ == sizeof (struct sockaddr_in));
            their_bytes = reinterpret_cast<const uint8_t *> (&(
              (reinterpret_cast<const struct sockaddr_in *> (ss_))->sin_addr));
339 340
            our_bytes = reinterpret_cast<const uint8_t *> (
              &_network_address.ipv4.sin_addr);
341 342
            mask = sizeof (struct in_addr) * 8;
        }
343 344
        if (_address_mask < mask)
            mask = _address_mask;
345

Martin Hurton's avatar
Martin Hurton committed
346
        const size_t full_bytes = mask / 8;
347
        if (memcmp (our_bytes, their_bytes, full_bytes) != 0)
348
            return false;
349

Martin Hurton's avatar
Martin Hurton committed
350
        const uint8_t last_byte_bits = 0xffU << (8 - mask % 8);
351
        if (last_byte_bits) {
352 353
            if ((their_bytes[full_bytes] & last_byte_bits)
                != (our_bytes[full_bytes] & last_byte_bits))
354 355 356 357 358 359
                return false;
        }
    }

    return true;
}