Line data Source code
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 <string>
32 : #include <sstream>
33 :
34 : #include "macros.hpp"
35 : #include "tcp_address.hpp"
36 : #include "stdint.hpp"
37 : #include "err.hpp"
38 : #include "ip.hpp"
39 :
40 : #ifndef ZMQ_HAVE_WINDOWS
41 : #include <sys/types.h>
42 : #include <arpa/inet.h>
43 : #include <netinet/tcp.h>
44 : #include <net/if.h>
45 : #include <netdb.h>
46 : #include <ctype.h>
47 : #include <unistd.h>
48 : #include <stdlib.h>
49 : #endif
50 :
51 : #ifdef ZMQ_HAVE_SOLARIS
52 : #include <sys/sockio.h>
53 :
54 : // On Solaris platform, network interface name can be queried by ioctl.
55 : int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_, bool is_src_)
56 : {
57 : // TODO: Unused parameter, IPv6 support not implemented for Solaris.
58 : LIBZMQ_UNUSED (ipv6_);
59 :
60 : // Create a socket.
61 : const int fd = open_socket (AF_INET, SOCK_DGRAM, 0);
62 : errno_assert (fd != -1);
63 :
64 : // Retrieve number of interfaces.
65 : lifnum ifn;
66 : ifn.lifn_family = AF_INET;
67 : ifn.lifn_flags = 0;
68 : int rc = ioctl (fd, SIOCGLIFNUM, (char*) &ifn);
69 : errno_assert (rc != -1);
70 :
71 : // Allocate memory to get interface names.
72 : const size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
73 : char *ifr = (char*) malloc (ifr_size);
74 : alloc_assert (ifr);
75 :
76 : // Retrieve interface names.
77 : lifconf ifc;
78 : ifc.lifc_family = AF_INET;
79 : ifc.lifc_flags = 0;
80 : ifc.lifc_len = ifr_size;
81 : ifc.lifc_buf = ifr;
82 : rc = ioctl (fd, SIOCGLIFCONF, (char*) &ifc);
83 : errno_assert (rc != -1);
84 :
85 : // Find the interface with the specified name and AF_INET family.
86 : bool found = false;
87 : lifreq *ifrp = ifc.lifc_req;
88 : for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq));
89 : n ++, ifrp ++) {
90 : if (!strcmp (nic_, ifrp->lifr_name)) {
91 : rc = ioctl (fd, SIOCGLIFADDR, (char*) ifrp);
92 : errno_assert (rc != -1);
93 : if (ifrp->lifr_addr.ss_family == AF_INET) {
94 : if (is_src_)
95 : source_address.ipv4 = *(sockaddr_in*) &ifrp->lifr_addr;
96 : else
97 : address.ipv4 = *(sockaddr_in*) &ifrp->lifr_addr;
98 : found = true;
99 : break;
100 : }
101 : }
102 : }
103 :
104 : // Clean-up.
105 : free (ifr);
106 : close (fd);
107 :
108 : if (!found) {
109 : errno = ENODEV;
110 : return -1;
111 : }
112 : return 0;
113 : }
114 :
115 : #elif defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_ANDROID
116 : #include <sys/ioctl.h>
117 :
118 : int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_, bool is_src_)
119 : {
120 : #if defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX
121 : // IPv6 support not implemented for AIX or HP/UX.
122 : if (ipv6_)
123 : {
124 : errno = ENODEV;
125 : return -1;
126 : }
127 : #endif
128 :
129 : // Create a socket.
130 : const int sd = open_socket (ipv6_ ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
131 : errno_assert (sd != -1);
132 :
133 : struct ifreq ifr;
134 :
135 : // Copy interface name for ioctl get.
136 : strncpy (ifr.ifr_name, nic_, sizeof (ifr.ifr_name) );
137 :
138 : // Fetch interface address.
139 : const int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (ifr) );
140 :
141 : // Clean up.
142 : close (sd);
143 :
144 : if (rc == -1) {
145 : errno = ENODEV;
146 : return -1;
147 : }
148 :
149 : const int family = ifr.ifr_addr.sa_family;
150 : if ((family == AF_INET || (ipv6_ && family == AF_INET6))
151 : && !strcmp (nic_, ifr.ifr_name))
152 : {
153 : if (is_src_)
154 : memcpy (&source_address, &ifr.ifr_addr,
155 : (family == AF_INET) ? sizeof (struct sockaddr_in)
156 : : sizeof (struct sockaddr_in6));
157 : else
158 : memcpy (&address, &ifr.ifr_addr,
159 : (family == AF_INET) ? sizeof (struct sockaddr_in)
160 : : sizeof (struct sockaddr_in6));
161 : }
162 : else
163 : {
164 : errno = ENODEV;
165 : return -1;
166 : }
167 :
168 : return 0;
169 : }
170 :
171 : #elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\
172 : defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD ||\
173 : defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD ||\
174 : defined ZMQ_HAVE_DRAGONFLY || defined ZMQ_HAVE_GNU)\
175 : && defined ZMQ_HAVE_IFADDRS)
176 :
177 : #include <ifaddrs.h>
178 :
179 : // On these platforms, network interface name can be queried
180 : // using getifaddrs function.
181 285 : int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_, bool is_src_)
182 : {
183 : // Get the addresses.
184 285 : ifaddrs *ifa = NULL;
185 285 : const int rc = getifaddrs (&ifa);
186 285 : errno_assert (rc == 0);
187 285 : zmq_assert (ifa != NULL);
188 :
189 : // Find the corresponding network interface.
190 285 : bool found = false;
191 2265 : for (ifaddrs *ifp = ifa; ifp != NULL; ifp = ifp->ifa_next) {
192 1983 : if (ifp->ifa_addr == NULL)
193 : continue;
194 :
195 1983 : const int family = ifp->ifa_addr->sa_family;
196 1983 : if ((family == AF_INET || (ipv6_ && family == AF_INET6))
197 603 : && !strcmp (nic_, ifp->ifa_name)) {
198 3 : if (is_src_)
199 : memcpy (&source_address, ifp->ifa_addr,
200 : (family == AF_INET) ? sizeof (struct sockaddr_in)
201 3 : : sizeof (struct sockaddr_in6));
202 : else
203 : memcpy (&address, ifp->ifa_addr,
204 : (family == AF_INET) ? sizeof (struct sockaddr_in)
205 0 : : sizeof (struct sockaddr_in6));
206 : found = true;
207 : break;
208 : }
209 : }
210 :
211 : // Clean-up;
212 285 : freeifaddrs (ifa);
213 :
214 285 : if (!found) {
215 282 : errno = ENODEV;
216 282 : return -1;
217 : }
218 : return 0;
219 : }
220 :
221 : #else
222 :
223 : // On other platforms we assume there are no sane interface names.
224 : // This is true especially of Windows.
225 : int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv6_, bool is_src_)
226 : {
227 : LIBZMQ_UNUSED (nic_);
228 : LIBZMQ_UNUSED (ipv6_);
229 :
230 : errno = ENODEV;
231 : return -1;
232 : }
233 :
234 : #endif
235 :
236 291 : int zmq::tcp_address_t::resolve_interface (const char *interface_, bool ipv6_, bool is_src_)
237 : {
238 : // Initialize temporary output pointers with storage address.
239 : sockaddr_storage ss;
240 291 : sockaddr *out_addr = (sockaddr*) &ss;
241 : size_t out_addrlen;
242 :
243 : // Initialise IP-format family/port and populate temporary output pointers
244 : // with the address.
245 291 : if (ipv6_) {
246 : sockaddr_in6 ip6_addr;
247 : memset (&ip6_addr, 0, sizeof (ip6_addr) );
248 15 : ip6_addr.sin6_family = AF_INET6;
249 : memcpy (&ip6_addr.sin6_addr, &in6addr_any, sizeof (in6addr_any) );
250 15 : out_addrlen = sizeof (ip6_addr);
251 : memcpy (out_addr, &ip6_addr, out_addrlen);
252 : }
253 : else {
254 : sockaddr_in ip4_addr;
255 : memset (&ip4_addr, 0, sizeof (ip4_addr) );
256 276 : ip4_addr.sin_family = AF_INET;
257 : ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY);
258 276 : out_addrlen = sizeof (ip4_addr);
259 : memcpy (out_addr, &ip4_addr, out_addrlen);
260 : }
261 : // "*" resolves to INADDR_ANY or in6addr_any.
262 291 : if (strcmp (interface_, "*") == 0) {
263 6 : zmq_assert (out_addrlen <= sizeof (address) );
264 6 : if (is_src_)
265 0 : memcpy (&source_address, out_addr, out_addrlen);
266 : else
267 6 : memcpy (&address, out_addr, out_addrlen);
268 : return 0;
269 : }
270 :
271 : // Try to resolve the string as a NIC name.
272 285 : int rc = resolve_nic_name (interface_, ipv6_, is_src_);
273 285 : if (rc == 0 || errno != ENODEV)
274 : return rc;
275 :
276 : // There's no such interface name. Assume literal address.
277 : #if defined ZMQ_HAVE_OPENVMS && defined __ia64
278 : __addrinfo64 *res = NULL;
279 : __addrinfo64 req;
280 : #else
281 282 : addrinfo *res = NULL;
282 : addrinfo req;
283 : #endif
284 : memset (&req, 0, sizeof (req) );
285 :
286 : // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
287 : // IPv4-in-IPv6 addresses.
288 282 : req.ai_family = ipv6_? AF_INET6: AF_INET;
289 :
290 : // Arbitrary, not used in the output, but avoids duplicate results.
291 282 : req.ai_socktype = SOCK_STREAM;
292 :
293 : // Restrict hostname/service to literals to avoid any DNS lookups or
294 : // service-name irregularity due to indeterminate socktype.
295 282 : req.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
296 :
297 : #if defined AI_V4MAPPED && !defined ZMQ_HAVE_FREEBSD && !defined ZMQ_HAVE_DRAGONFLY
298 : // In this API we only require IPv4-mapped addresses when
299 : // no native IPv6 interfaces are available (~AI_ALL).
300 : // This saves an additional DNS roundtrip for IPv4 addresses.
301 : // Note: While the AI_V4MAPPED flag is defined on FreeBSD system,
302 : // it is not supported here. See libzmq issue #331.
303 282 : if (req.ai_family == AF_INET6)
304 12 : req.ai_flags |= AI_V4MAPPED;
305 : #endif
306 :
307 : // Resolve the literal address. Some of the error info is lost in case
308 : // of error, however, there's no way to report EAI errors via errno.
309 282 : rc = getaddrinfo (interface_, NULL, &req, &res);
310 282 : if (rc) {
311 3 : errno = ENODEV;
312 3 : return -1;
313 : }
314 :
315 : // Use the first result.
316 279 : zmq_assert (res != NULL);
317 279 : zmq_assert ((size_t) res->ai_addrlen <= sizeof (address) );
318 279 : if (is_src_)
319 3 : memcpy (&source_address, res->ai_addr, res->ai_addrlen);
320 : else
321 276 : memcpy (&address, res->ai_addr, res->ai_addrlen);
322 :
323 : // Cleanup getaddrinfo after copying the possibly referenced result.
324 279 : freeaddrinfo (res);
325 :
326 279 : return 0;
327 : }
328 :
329 3797 : int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv6_, bool is_src_)
330 : {
331 : // Set up the query.
332 : #if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64
333 : __addrinfo64 req;
334 : #else
335 : addrinfo req;
336 : #endif
337 : memset (&req, 0, sizeof (req) );
338 :
339 : // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
340 : // IPv4-in-IPv6 addresses.
341 3797 : req.ai_family = ipv6_? AF_INET6: AF_INET;
342 :
343 : // Need to choose one to avoid duplicate results from getaddrinfo() - this
344 : // doesn't really matter, since it's not included in the addr-output.
345 3797 : req.ai_socktype = SOCK_STREAM;
346 :
347 : #if defined AI_V4MAPPED && !defined ZMQ_HAVE_FREEBSD && !defined ZMQ_HAVE_DRAGONFLY
348 : // In this API we only require IPv4-mapped addresses when
349 : // no native IPv6 interfaces are available.
350 : // This saves an additional DNS roundtrip for IPv4 addresses.
351 : // Note: While the AI_V4MAPPED flag is defined on FreeBSD system,
352 : // it is not supported here. See libzmq issue #331.
353 3797 : if (req.ai_family == AF_INET6)
354 18 : req.ai_flags |= AI_V4MAPPED;
355 : #endif
356 :
357 : // Resolve host name. Some of the error info is lost in case of error,
358 : // however, there's no way to report EAI errors via errno.
359 : #if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64
360 : __addrinfo64 *res;
361 : #else
362 : addrinfo *res;
363 : #endif
364 3797 : const int rc = getaddrinfo (hostname_, NULL, &req, &res);
365 3797 : if (rc) {
366 3 : switch (rc) {
367 : case EAI_MEMORY:
368 0 : errno = ENOMEM;
369 0 : break;
370 : default:
371 3 : errno = EINVAL;
372 3 : break;
373 : }
374 : return -1;
375 : }
376 :
377 : // Copy first result to output addr with hostname and service.
378 3794 : zmq_assert ((size_t) res->ai_addrlen <= sizeof (address) );
379 3794 : if (is_src_)
380 0 : memcpy (&source_address, res->ai_addr, res->ai_addrlen);
381 : else
382 3794 : memcpy (&address, res->ai_addr, res->ai_addrlen);
383 :
384 3794 : freeaddrinfo (res);
385 :
386 3794 : return 0;
387 : }
388 :
389 4080 : zmq::tcp_address_t::tcp_address_t () :
390 4080 : _has_src_addr (false)
391 : {
392 4080 : memset (&address, 0, sizeof (address) );
393 4080 : memset (&source_address, 0, sizeof (source_address) );
394 4080 : }
395 :
396 279 : zmq::tcp_address_t::tcp_address_t (const sockaddr *sa, socklen_t sa_len) :
397 279 : _has_src_addr (false)
398 : {
399 279 : zmq_assert (sa && sa_len > 0);
400 :
401 279 : memset (&address, 0, sizeof (address) );
402 279 : memset (&source_address, 0, sizeof (source_address) );
403 279 : if (sa->sa_family == AF_INET && sa_len >= (socklen_t) sizeof (address.ipv4) )
404 264 : memcpy (&address.ipv4, sa, sizeof (address.ipv4) );
405 : else
406 15 : if (sa->sa_family == AF_INET6 && sa_len >= (socklen_t) sizeof (address.ipv6) )
407 15 : memcpy (&address.ipv6, sa, sizeof (address.ipv6) );
408 279 : }
409 :
410 4358 : zmq::tcp_address_t::~tcp_address_t ()
411 : {
412 4358 : }
413 :
414 4092 : int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv6_, bool is_src_)
415 : {
416 4092 : if (!is_src_) {
417 : // Test the ';' to know if we have a source address in name_
418 4083 : const char *src_delimiter = strrchr (name_, ';');
419 4083 : if (src_delimiter) {
420 9 : std::string src_name (name_, src_delimiter - name_);
421 18 : const int rc = resolve (src_name.c_str (), local_, ipv6_, true);
422 9 : if (rc != 0)
423 3 : return -1;
424 6 : name_ = src_delimiter + 1;
425 6 : _has_src_addr = true;
426 : }
427 : }
428 :
429 : // Find the ':' at end that separates address from the port number.
430 4089 : const char *delimiter = strrchr (name_, ':');
431 4089 : if (!delimiter) {
432 0 : errno = EINVAL;
433 0 : return -1;
434 : }
435 :
436 : // Separate the address/port.
437 4089 : std::string addr_str (name_, delimiter - name_);
438 4089 : std::string port_str (delimiter + 1);
439 :
440 : // Remove square brackets around the address, if any, as used in IPv6
441 8193 : if (addr_str.size () >= 2 && addr_str [0] == '[' &&
442 42 : addr_str [addr_str.size () - 1] == ']')
443 42 : addr_str = addr_str.substr (1, addr_str.size () - 2);
444 :
445 : // Test the '%' to know if we have an interface name / zone_id in the address
446 : // Reference: https://tools.ietf.org/html/rfc4007
447 4089 : std::size_t pos = addr_str.rfind("%");
448 4089 : uint32_t zone_id = 0;
449 4089 : if (pos != std::string::npos) {
450 0 : std::string if_str = addr_str.substr(pos + 1);
451 0 : addr_str = addr_str.substr(0, pos);
452 0 : if (isalpha (if_str.at (0)))
453 : #if !defined ZMQ_HAVE_WINDOWS_TARGET_XP
454 0 : zone_id = if_nametoindex(if_str.c_str());
455 : #else
456 : // The function 'if_nametoindex' is not supported on Windows XP.
457 : // If we are targeting XP using a vxxx_xp toolset then fail.
458 : // This is brutal as this code could be run on later windows clients
459 : // meaning the IPv6 zone_id cannot have an interface name.
460 : // This could be fixed with a runtime check.
461 : zone_id = 0;
462 : #endif
463 : else
464 0 : zone_id = (uint32_t) atoi (if_str.c_str ());
465 0 : if (zone_id == 0) {
466 0 : errno = EINVAL;
467 0 : return -1;
468 : }
469 :
470 : }
471 :
472 : // Allow 0 specifically, to detect invalid port error in atoi if not
473 : uint16_t port;
474 8156 : if (port_str == "*" || port_str == "0")
475 : // Resolve wildcard to 0 to allow autoselection of port
476 : port = 0;
477 : else {
478 : // Parse the port number (0 is not a valid port).
479 8129 : port = (uint16_t) atoi (port_str.c_str ());
480 4064 : if (port == 0) {
481 0 : errno = EINVAL;
482 0 : return -1;
483 : }
484 : }
485 :
486 : // Resolve the IP address.
487 : int rc;
488 4088 : if (local_ || is_src_)
489 582 : rc = resolve_interface (addr_str.c_str (), ipv6_, is_src_);
490 : else
491 7594 : rc = resolve_hostname (addr_str.c_str (), ipv6_, is_src_);
492 4089 : if (rc != 0)
493 : return -1;
494 :
495 : // Set the port into the address structure.
496 4083 : if (is_src_) {
497 6 : if (source_address.generic.sa_family == AF_INET6) {
498 0 : source_address.ipv6.sin6_port = htons (port);
499 0 : source_address.ipv6.sin6_scope_id = zone_id;
500 : }
501 : else
502 6 : source_address.ipv4.sin_port = htons (port);
503 : }
504 : else {
505 4077 : if (address.generic.sa_family == AF_INET6) {
506 33 : address.ipv6.sin6_port = htons (port);
507 33 : address.ipv6.sin6_scope_id = zone_id;
508 : }
509 : else
510 4044 : address.ipv4.sin_port = htons (port);
511 : }
512 :
513 : return 0;
514 : }
515 :
516 766 : int zmq::tcp_address_t::to_string (std::string &addr_)
517 : {
518 766 : if (address.generic.sa_family != AF_INET
519 766 : && address.generic.sa_family != AF_INET6) {
520 : addr_.clear ();
521 0 : return -1;
522 : }
523 :
524 : // Not using service resolving because of
525 : // https://github.com/zeromq/libzmq/commit/1824574f9b5a8ce786853320e3ea09fe1f822bc4
526 : char hbuf [NI_MAXHOST];
527 766 : int rc = getnameinfo (addr (), addrlen (), hbuf, sizeof (hbuf), NULL, 0, NI_NUMERICHOST);
528 766 : if (rc != 0) {
529 : addr_.clear ();
530 0 : return rc;
531 : }
532 :
533 766 : if (address.generic.sa_family == AF_INET6) {
534 33 : std::stringstream s;
535 33 : s << "tcp://[" << hbuf << "]:" << ntohs (address.ipv6.sin6_port);
536 33 : addr_ = s.str ();
537 : }
538 : else {
539 733 : std::stringstream s;
540 733 : s << "tcp://" << hbuf << ":" << ntohs (address.ipv4.sin_port);
541 733 : addr_ = s.str ();
542 : }
543 : return 0;
544 : }
545 :
546 4056 : const sockaddr *zmq::tcp_address_t::addr () const
547 : {
548 4056 : return &address.generic;
549 : }
550 :
551 4056 : socklen_t zmq::tcp_address_t::addrlen () const
552 : {
553 4822 : if (address.generic.sa_family == AF_INET6)
554 : return (socklen_t) sizeof (address.ipv6);
555 : else
556 4026 : return (socklen_t) sizeof (address.ipv4);
557 : }
558 :
559 6 : const sockaddr *zmq::tcp_address_t::src_addr () const
560 : {
561 6 : return &source_address.generic;
562 : }
563 :
564 6 : socklen_t zmq::tcp_address_t::src_addrlen () const
565 : {
566 6 : if (address.generic.sa_family == AF_INET6)
567 : return (socklen_t) sizeof (source_address.ipv6);
568 : else
569 6 : return (socklen_t) sizeof (source_address.ipv4);
570 : }
571 :
572 3789 : bool zmq::tcp_address_t::has_src_addr () const
573 : {
574 3789 : return _has_src_addr;
575 : }
576 :
577 : #if defined ZMQ_HAVE_WINDOWS
578 : unsigned short zmq::tcp_address_t::family () const
579 : #else
580 8118 : sa_family_t zmq::tcp_address_t::family () const
581 : #endif
582 : {
583 8118 : return address.generic.sa_family;
584 : }
585 :
586 0 : zmq::tcp_address_mask_t::tcp_address_mask_t () :
587 : tcp_address_t (),
588 0 : address_mask (-1)
589 : {
590 0 : }
591 :
592 0 : int zmq::tcp_address_mask_t::mask () const
593 : {
594 0 : return address_mask;
595 : }
596 :
597 0 : int zmq::tcp_address_mask_t::resolve (const char *name_, bool ipv6_)
598 : {
599 : // Find '/' at the end that separates address from the cidr mask number.
600 : // Allow empty mask clause and treat it like '/32' for ipv4 or '/128' for ipv6.
601 : std::string addr_str, mask_str;
602 0 : const char *delimiter = strrchr (name_, '/');
603 0 : if (delimiter != NULL) {
604 0 : addr_str.assign (name_, delimiter - name_);
605 0 : mask_str.assign (delimiter + 1);
606 0 : if (mask_str.empty ()) {
607 0 : errno = EINVAL;
608 0 : return -1;
609 : }
610 : }
611 : else
612 0 : addr_str.assign (name_);
613 :
614 : // Parse address part using standard routines.
615 : const int rc =
616 0 : tcp_address_t::resolve_hostname (addr_str.c_str (), ipv6_);
617 0 : if (rc != 0)
618 : return rc;
619 :
620 : // Parse the cidr mask number.
621 0 : if (mask_str.empty ()) {
622 0 : if (address.generic.sa_family == AF_INET6)
623 0 : address_mask = 128;
624 : else
625 0 : address_mask = 32;
626 : }
627 : else
628 0 : if (mask_str == "0")
629 0 : address_mask = 0;
630 : else {
631 0 : const int mask = atoi (mask_str.c_str ());
632 0 : if (
633 0 : (mask < 1) ||
634 0 : (address.generic.sa_family == AF_INET6 && mask > 128) ||
635 0 : (address.generic.sa_family != AF_INET6 && mask > 32)
636 : ) {
637 0 : errno = EINVAL;
638 0 : return -1;
639 : }
640 0 : address_mask = mask;
641 : }
642 :
643 : return 0;
644 : }
645 :
646 0 : int zmq::tcp_address_mask_t::to_string (std::string &addr_)
647 : {
648 0 : if (address.generic.sa_family != AF_INET
649 0 : && address.generic.sa_family != AF_INET6) {
650 : addr_.clear ();
651 0 : return -1;
652 : }
653 0 : if (address_mask == -1) {
654 : addr_.clear ();
655 0 : return -1;
656 : }
657 :
658 : char hbuf [NI_MAXHOST];
659 0 : int rc = getnameinfo (addr (), addrlen (), hbuf, sizeof (hbuf), NULL, 0, NI_NUMERICHOST);
660 0 : if (rc != 0) {
661 : addr_.clear ();
662 0 : return rc;
663 : }
664 :
665 0 : if (address.generic.sa_family == AF_INET6) {
666 0 : std::stringstream s;
667 0 : s << "[" << hbuf << "]/" << address_mask;
668 0 : addr_ = s.str ();
669 : }
670 : else {
671 0 : std::stringstream s;
672 0 : s << hbuf << "/" << address_mask;
673 0 : addr_ = s.str ();
674 : }
675 : return 0;
676 : }
677 :
678 0 : bool zmq::tcp_address_mask_t::match_address (const struct sockaddr *ss, const socklen_t ss_len) const
679 : {
680 0 : zmq_assert (address_mask != -1
681 : && ss != NULL
682 : && ss_len >= (socklen_t) sizeof (struct sockaddr));
683 :
684 0 : if (ss->sa_family != address.generic.sa_family)
685 : return false;
686 :
687 0 : if (address_mask > 0) {
688 : int mask;
689 : const uint8_t *our_bytes, *their_bytes;
690 0 : if (ss->sa_family == AF_INET6) {
691 0 : zmq_assert (ss_len == sizeof (struct sockaddr_in6));
692 0 : their_bytes = (const uint8_t *) &(((const struct sockaddr_in6 *) ss)->sin6_addr);
693 0 : our_bytes = (const uint8_t *) &address.ipv6.sin6_addr;
694 0 : mask = sizeof (struct in6_addr) * 8;
695 : }
696 : else {
697 0 : zmq_assert (ss_len == sizeof (struct sockaddr_in));
698 0 : their_bytes = (const uint8_t *) &(((const struct sockaddr_in *) ss)->sin_addr);
699 0 : our_bytes = (const uint8_t *) &address.ipv4.sin_addr;
700 0 : mask = sizeof (struct in_addr) * 8;
701 : }
702 0 : if (address_mask < mask)
703 0 : mask = address_mask;
704 :
705 0 : const size_t full_bytes = mask / 8;
706 0 : if (memcmp (our_bytes, their_bytes, full_bytes))
707 : return false;
708 :
709 0 : const uint8_t last_byte_bits = 0xffU << (8 - mask % 8);
710 0 : if (last_byte_bits) {
711 0 : if ((their_bytes [full_bytes] & last_byte_bits) != (our_bytes [full_bytes] & last_byte_bits))
712 : return false;
713 : }
714 : }
715 :
716 : return true;
717 : }
|