1#include "precompiled.hpp"
2#include <string>
3#include <cstring>
4
5#include "macros.hpp"
6#include "stdint.hpp"
7#include "err.hpp"
8#include "ip.hpp"
9
10#ifndef ZMQ_HAVE_WINDOWS
11#include <sys/types.h>
12#include <arpa/inet.h>
13#include <netinet/tcp.h>
14#include <net/if.h>
15#include <netdb.h>
16#include <ctype.h>
17#include <unistd.h>
18#include <stdlib.h>
19#endif
20
21#include "ip_resolver.hpp"
22
23int zmq::ip_addr_t::family () const
24{
25 return generic.sa_family;
26}
27
28bool zmq::ip_addr_t::is_multicast () const
29{
30 if (family () == AF_INET) {
31 // IPv4 Multicast: address MSBs are 1110
32 // Range: 224.0.0.0 - 239.255.255.255
33 return IN_MULTICAST (ntohl (ipv4.sin_addr.s_addr));
34 }
35 // IPv6 Multicast: ff00::/8
36 return IN6_IS_ADDR_MULTICAST (&ipv6.sin6_addr) != 0;
37}
38
39uint16_t zmq::ip_addr_t::port () const
40{
41 if (family () == AF_INET6) {
42 return ntohs (ipv6.sin6_port);
43 }
44 return ntohs (ipv4.sin_port);
45}
46
47const struct sockaddr *zmq::ip_addr_t::as_sockaddr () const
48{
49 return &generic;
50}
51
52zmq::zmq_socklen_t zmq::ip_addr_t::sockaddr_len () const
53{
54 return static_cast<zmq_socklen_t> (family () == AF_INET6 ? sizeof (ipv6)
55 : sizeof (ipv4));
56}
57
58void zmq::ip_addr_t::set_port (uint16_t port_)
59{
60 if (family () == AF_INET6) {
61 ipv6.sin6_port = htons (port_);
62 } else {
63 ipv4.sin_port = htons (port_);
64 }
65}
66
67// Construct an "ANY" address for the given family
68zmq::ip_addr_t zmq::ip_addr_t::any (int family_)
69{
70 ip_addr_t addr;
71
72 if (family_ == AF_INET) {
73 sockaddr_in *ip4_addr = &addr.ipv4;
74 memset (ip4_addr, 0, sizeof (*ip4_addr));
75 ip4_addr->sin_family = AF_INET;
76 ip4_addr->sin_addr.s_addr = htonl (INADDR_ANY);
77 } else if (family_ == AF_INET6) {
78 sockaddr_in6 *ip6_addr = &addr.ipv6;
79
80 memset (ip6_addr, 0, sizeof (*ip6_addr));
81 ip6_addr->sin6_family = AF_INET6;
82#ifdef ZMQ_HAVE_VXWORKS
83 struct in6_addr newaddr = IN6ADDR_ANY_INIT;
84 memcpy (&ip6_addr->sin6_addr, &newaddr, sizeof (in6_addr));
85#else
86 memcpy (&ip6_addr->sin6_addr, &in6addr_any, sizeof (in6addr_any));
87#endif
88 } else {
89 assert (0 == "unsupported address family");
90 }
91
92 return addr;
93}
94
95zmq::ip_resolver_options_t::ip_resolver_options_t () :
96 _bindable_wanted (false),
97 _nic_name_allowed (false),
98 _ipv6_wanted (false),
99 _port_expected (false),
100 _dns_allowed (false),
101 _path_allowed (false)
102{
103}
104
105zmq::ip_resolver_options_t &
106zmq::ip_resolver_options_t::bindable (bool bindable_)
107{
108 _bindable_wanted = bindable_;
109
110 return *this;
111}
112
113zmq::ip_resolver_options_t &
114zmq::ip_resolver_options_t::allow_nic_name (bool allow_)
115{
116 _nic_name_allowed = allow_;
117
118 return *this;
119}
120
121zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::ipv6 (bool ipv6_)
122{
123 _ipv6_wanted = ipv6_;
124
125 return *this;
126}
127
128// If true we expect that the host will be followed by a colon and a port
129// number or service name
130zmq::ip_resolver_options_t &
131zmq::ip_resolver_options_t::expect_port (bool expect_)
132{
133 _port_expected = expect_;
134
135 return *this;
136}
137
138zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::allow_dns (bool allow_)
139{
140 _dns_allowed = allow_;
141
142 return *this;
143}
144
145zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::allow_path (bool allow_)
146{
147 _path_allowed = allow_;
148
149 return *this;
150}
151
152bool zmq::ip_resolver_options_t::bindable ()
153{
154 return _bindable_wanted;
155}
156
157bool zmq::ip_resolver_options_t::allow_nic_name ()
158{
159 return _nic_name_allowed;
160}
161
162bool zmq::ip_resolver_options_t::ipv6 ()
163{
164 return _ipv6_wanted;
165}
166
167bool zmq::ip_resolver_options_t::expect_port ()
168{
169 return _port_expected;
170}
171
172bool zmq::ip_resolver_options_t::allow_dns ()
173{
174 return _dns_allowed;
175}
176
177bool zmq::ip_resolver_options_t::allow_path ()
178{
179 return _path_allowed;
180}
181
182zmq::ip_resolver_t::ip_resolver_t (ip_resolver_options_t opts_) :
183 _options (opts_)
184{
185}
186
187int zmq::ip_resolver_t::resolve (ip_addr_t *ip_addr_, const char *name_)
188{
189 std::string addr;
190 uint16_t port;
191
192 if (_options.expect_port ()) {
193 // We expect 'addr:port'. It's important to use str*r*chr to only get
194 // the latest colon since IPv6 addresses use colons as delemiters.
195 const char *delim = strrchr (name_, ':');
196
197 if (delim == NULL) {
198 errno = EINVAL;
199 return -1;
200 }
201
202 addr = std::string (name_, delim - name_);
203 std::string port_str = std::string (delim + 1);
204
205 if (port_str == "*") {
206 if (_options.bindable ()) {
207 // Resolve wildcard to 0 to allow autoselection of port
208 port = 0;
209 } else {
210 errno = EINVAL;
211 return -1;
212 }
213 } else if (port_str == "0") {
214 // Using "0" for a bind address is equivalent to using "*". For a
215 // connectable address it could be used to connect to port 0.
216 port = 0;
217 } else {
218 // Parse the port number (0 is not a valid port).
219 port = static_cast<uint16_t> (atoi (port_str.c_str ()));
220 if (port == 0) {
221 errno = EINVAL;
222 return -1;
223 }
224 }
225 } else {
226 addr = std::string (name_);
227 port = 0;
228 }
229
230 // Check if path is allowed in ip address, if allowed it must be truncated
231 if (_options.allow_path ()) {
232 size_t pos = addr.find ('/');
233 if (pos != std::string::npos)
234 addr = addr.substr (0, pos);
235 }
236
237 // Trim any square brackets surrounding the address. Used for
238 // IPv6 addresses to remove the confusion with the port
239 // delimiter.
240 // TODO Should we validate that the brackets are present if
241 // 'addr' contains ':' ?
242 const size_t brackets_length = 2;
243 if (addr.size () >= brackets_length && addr[0] == '['
244 && addr[addr.size () - 1] == ']') {
245 addr = addr.substr (1, addr.size () - brackets_length);
246 }
247
248 // Look for an interface name / zone_id in the address
249 // Reference: https://tools.ietf.org/html/rfc4007
250 std::size_t pos = addr.rfind ('%');
251 uint32_t zone_id = 0;
252
253 if (pos != std::string::npos) {
254 std::string if_str = addr.substr (pos + 1);
255 addr = addr.substr (0, pos);
256
257 if (isalpha (if_str.at (0))) {
258 zone_id = do_if_nametoindex (if_str.c_str ());
259 } else {
260 zone_id = static_cast<uint32_t> (atoi (if_str.c_str ()));
261 }
262
263 if (zone_id == 0) {
264 errno = EINVAL;
265 return -1;
266 }
267 }
268
269 bool resolved = false;
270 const char *addr_str = addr.c_str ();
271
272 if (_options.bindable () && addr == "*") {
273 // Return an ANY address
274 *ip_addr_ = ip_addr_t::any (_options.ipv6 () ? AF_INET6 : AF_INET);
275 resolved = true;
276 }
277
278 if (!resolved && _options.allow_nic_name ()) {
279 // Try to resolve the string as a NIC name.
280 int rc = resolve_nic_name (ip_addr_, addr_str);
281
282 if (rc == 0) {
283 resolved = true;
284 } else if (errno != ENODEV) {
285 return rc;
286 }
287 }
288
289 if (!resolved) {
290 int rc = resolve_getaddrinfo (ip_addr_, addr_str);
291
292 if (rc != 0) {
293 return rc;
294 }
295 resolved = true;
296 }
297
298 // Store the port into the structure. We could get 'getaddrinfo' to do it
299 // for us but since we don't resolve service names it's a bit overkill and
300 // we'd still have to do it manually when the address is resolved by
301 // 'resolve_nic_name'
302 ip_addr_->set_port (port);
303
304 if (ip_addr_->family () == AF_INET6) {
305 ip_addr_->ipv6.sin6_scope_id = zone_id;
306 }
307
308 assert (resolved == true);
309 return 0;
310}
311
312int zmq::ip_resolver_t::resolve_getaddrinfo (ip_addr_t *ip_addr_,
313 const char *addr_)
314{
315#if defined ZMQ_HAVE_OPENVMS && defined __ia64
316 __addrinfo64 *res = NULL;
317 __addrinfo64 req;
318#else
319 addrinfo *res = NULL;
320 addrinfo req;
321#endif
322
323 memset (&req, 0, sizeof (req));
324
325 // Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
326 // IPv4-in-IPv6 addresses.
327 req.ai_family = _options.ipv6 () ? AF_INET6 : AF_INET;
328
329 // Arbitrary, not used in the output, but avoids duplicate results.
330 req.ai_socktype = SOCK_STREAM;
331
332 req.ai_flags = 0;
333
334 if (_options.bindable ()) {
335 req.ai_flags |= AI_PASSIVE;
336 }
337
338 if (!_options.allow_dns ()) {
339 req.ai_flags |= AI_NUMERICHOST;
340 }
341
342#if defined AI_V4MAPPED
343 // In this API we only require IPv4-mapped addresses when
344 // no native IPv6 interfaces are available (~AI_ALL).
345 // This saves an additional DNS roundtrip for IPv4 addresses.
346 if (req.ai_family == AF_INET6) {
347 req.ai_flags |= AI_V4MAPPED;
348 }
349#endif
350
351 // Resolve the literal address. Some of the error info is lost in case
352 // of error, however, there's no way to report EAI errors via errno.
353 int rc = do_getaddrinfo (addr_, NULL, &req, &res);
354
355#if defined AI_V4MAPPED
356 // Some OS do have AI_V4MAPPED defined but it is not supported in getaddrinfo()
357 // returning EAI_BADFLAGS. Detect this and retry
358 if (rc == EAI_BADFLAGS && (req.ai_flags & AI_V4MAPPED)) {
359 req.ai_flags &= ~AI_V4MAPPED;
360 rc = do_getaddrinfo (addr_, NULL, &req, &res);
361 }
362#endif
363
364#if defined ZMQ_HAVE_WINDOWS
365 // Resolve specific case on Windows platform when using IPv4 address
366 // with ZMQ_IPv6 socket option.
367 if ((req.ai_family == AF_INET6) && (rc == WSAHOST_NOT_FOUND)) {
368 req.ai_family = AF_INET;
369 rc = do_getaddrinfo (addr_, NULL, &req, &res);
370 }
371#endif
372
373 if (rc) {
374 switch (rc) {
375 case EAI_MEMORY:
376 errno = ENOMEM;
377 break;
378 default:
379 if (_options.bindable ()) {
380 errno = ENODEV;
381 } else {
382 errno = EINVAL;
383 }
384 break;
385 }
386 return -1;
387 }
388
389 // Use the first result.
390 zmq_assert (res != NULL);
391 zmq_assert ((size_t) res->ai_addrlen <= sizeof (*ip_addr_));
392 memcpy (ip_addr_, res->ai_addr, res->ai_addrlen);
393
394 // Cleanup getaddrinfo after copying the possibly referenced result.
395 do_freeaddrinfo (res);
396
397 return 0;
398}
399
400#ifdef ZMQ_HAVE_SOLARIS
401#include <sys/sockio.h>
402
403// On Solaris platform, network interface name can be queried by ioctl.
404int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
405{
406 // Create a socket.
407 const int fd = open_socket (AF_INET, SOCK_DGRAM, 0);
408 errno_assert (fd != -1);
409
410 // Retrieve number of interfaces.
411 lifnum ifn;
412 ifn.lifn_family = AF_INET;
413 ifn.lifn_flags = 0;
414 int rc = ioctl (fd, SIOCGLIFNUM, (char *) &ifn);
415 errno_assert (rc != -1);
416
417 // Allocate memory to get interface names.
418 const size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
419 char *ifr = (char *) malloc (ifr_size);
420 alloc_assert (ifr);
421
422 // Retrieve interface names.
423 lifconf ifc;
424 ifc.lifc_family = AF_INET;
425 ifc.lifc_flags = 0;
426 ifc.lifc_len = ifr_size;
427 ifc.lifc_buf = ifr;
428 rc = ioctl (fd, SIOCGLIFCONF, (char *) &ifc);
429 errno_assert (rc != -1);
430
431 // Find the interface with the specified name and AF_INET family.
432 bool found = false;
433 lifreq *ifrp = ifc.lifc_req;
434 for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq)); n++, ifrp++) {
435 if (!strcmp (nic_, ifrp->lifr_name)) {
436 rc = ioctl (fd, SIOCGLIFADDR, (char *) ifrp);
437 errno_assert (rc != -1);
438 if (ifrp->lifr_addr.ss_family == AF_INET) {
439 ip_addr_->ipv4 = *(sockaddr_in *) &ifrp->lifr_addr;
440 found = true;
441 break;
442 }
443 }
444 }
445
446 // Clean-up.
447 free (ifr);
448 close (fd);
449
450 if (!found) {
451 errno = ENODEV;
452 return -1;
453 }
454 return 0;
455}
456
457#elif defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX \
458 || defined ZMQ_HAVE_ANDROID || defined ZMQ_HAVE_VXWORKS
459#include <sys/ioctl.h>
460#ifdef ZMQ_HAVE_VXWORKS
461#include <ioLib.h>
462#endif
463
464int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
465{
466#if defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX
467 // IPv6 support not implemented for AIX or HP/UX.
468 if (_options.ipv6 ()) {
469 errno = ENODEV;
470 return -1;
471 }
472#endif
473
474 // Create a socket.
475 const int sd =
476 open_socket (_options.ipv6 () ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
477 errno_assert (sd != -1);
478
479 struct ifreq ifr;
480
481 // Copy interface name for ioctl get.
482 strncpy (ifr.ifr_name, nic_, sizeof (ifr.ifr_name));
483
484 // Fetch interface address.
485 const int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (ifr));
486
487 // Clean up.
488 close (sd);
489
490 if (rc == -1) {
491 errno = ENODEV;
492 return -1;
493 }
494
495 const int family = ifr.ifr_addr.sa_family;
496 if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)
497 && !strcmp (nic_, ifr.ifr_name)) {
498 memcpy (ip_addr_, &ifr.ifr_addr,
499 (family == AF_INET) ? sizeof (struct sockaddr_in)
500 : sizeof (struct sockaddr_in6));
501 } else {
502 errno = ENODEV;
503 return -1;
504 }
505
506 return 0;
507}
508
509#elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD \
510 || defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD \
511 || defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD \
512 || defined ZMQ_HAVE_DRAGONFLY || defined ZMQ_HAVE_GNU) \
513 && defined ZMQ_HAVE_IFADDRS)
514
515#include <ifaddrs.h>
516
517// On these platforms, network interface name can be queried
518// using getifaddrs function.
519int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
520{
521 // Get the addresses.
522 ifaddrs *ifa = NULL;
523 int rc = 0;
524 const int max_attempts = 10;
525 const int backoff_msec = 1;
526 for (int i = 0; i < max_attempts; i++) {
527 rc = getifaddrs (&ifa);
528 if (rc == 0 || (rc < 0 && errno != ECONNREFUSED))
529 break;
530 usleep ((backoff_msec << i) * 1000);
531 }
532
533 if (rc != 0 && ((errno == EINVAL) || (errno == EOPNOTSUPP))) {
534 // Windows Subsystem for Linux compatibility
535 errno = ENODEV;
536 return -1;
537 }
538 errno_assert (rc == 0);
539 zmq_assert (ifa != NULL);
540
541 // Find the corresponding network interface.
542 bool found = false;
543 for (ifaddrs *ifp = ifa; ifp != NULL; ifp = ifp->ifa_next) {
544 if (ifp->ifa_addr == NULL)
545 continue;
546
547 const int family = ifp->ifa_addr->sa_family;
548 if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)
549 && !strcmp (nic_, ifp->ifa_name)) {
550 memcpy (ip_addr_, ifp->ifa_addr,
551 (family == AF_INET) ? sizeof (struct sockaddr_in)
552 : sizeof (struct sockaddr_in6));
553 found = true;
554 break;
555 }
556 }
557
558 // Clean-up;
559 freeifaddrs (ifa);
560
561 if (!found) {
562 errno = ENODEV;
563 return -1;
564 }
565 return 0;
566}
567
568#elif (defined ZMQ_HAVE_WINDOWS)
569
570#include <netioapi.h>
571
572int zmq::ip_resolver_t::get_interface_name (unsigned long index_,
573 char **dest_) const
574{
575#ifdef ZMQ_HAVE_WINDOWS_UWP
576 char *buffer = (char *) malloc (1024);
577#else
578 char *buffer = static_cast<char *> (malloc (IF_MAX_STRING_SIZE));
579#endif
580 alloc_assert (buffer);
581
582 char *if_name_result = NULL;
583
584#if _WIN32_WINNT > _WIN32_WINNT_WINXP && !defined ZMQ_HAVE_WINDOWS_UWP
585 if_name_result = if_indextoname (index_, buffer);
586#endif
587
588 if (if_name_result == NULL) {
589 free (buffer);
590 return -1;
591 }
592
593 *dest_ = buffer;
594 return 0;
595}
596
597int zmq::ip_resolver_t::wchar_to_utf8 (const WCHAR *src_, char **dest_) const
598{
599 int rc;
600 int buffer_len =
601 WideCharToMultiByte (CP_UTF8, 0, src_, -1, NULL, 0, NULL, 0);
602
603 char *buffer = static_cast<char *> (malloc (buffer_len));
604 alloc_assert (buffer);
605
606 rc =
607 WideCharToMultiByte (CP_UTF8, 0, src_, -1, buffer, buffer_len, NULL, 0);
608
609 if (rc == 0) {
610 free (buffer);
611 return -1;
612 }
613
614 *dest_ = buffer;
615 return 0;
616}
617
618int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
619{
620 int rc;
621 bool found = false;
622 const int max_attempts = 10;
623
624 int iterations = 0;
625 IP_ADAPTER_ADDRESSES *addresses;
626 unsigned long out_buf_len = sizeof (IP_ADAPTER_ADDRESSES);
627
628 do {
629 addresses = static_cast<IP_ADAPTER_ADDRESSES *> (malloc (out_buf_len));
630 alloc_assert (addresses);
631
632 rc =
633 GetAdaptersAddresses (AF_UNSPEC,
634 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
635 | GAA_FLAG_SKIP_DNS_SERVER,
636 NULL, addresses, &out_buf_len);
637 if (rc == ERROR_BUFFER_OVERFLOW) {
638 free (addresses);
639 addresses = NULL;
640 } else {
641 break;
642 }
643 iterations++;
644 } while ((rc == ERROR_BUFFER_OVERFLOW) && (iterations < max_attempts));
645
646 if (rc == 0) {
647 for (const IP_ADAPTER_ADDRESSES *current_addresses = addresses;
648 current_addresses; current_addresses = current_addresses->Next) {
649 char *if_name = NULL;
650 char *if_friendly_name = NULL;
651
652 const int str_rc1 =
653 get_interface_name (current_addresses->IfIndex, &if_name);
654 const int str_rc2 = wchar_to_utf8 (current_addresses->FriendlyName,
655 &if_friendly_name);
656
657 // Find a network adapter by its "name" or "friendly name"
658 if (((str_rc1 == 0) && (!strcmp (nic_, if_name)))
659 || ((str_rc2 == 0) && (!strcmp (nic_, if_friendly_name)))) {
660 // Iterate over all unicast addresses bound to the current network interface
661 for (const IP_ADAPTER_UNICAST_ADDRESS *current_unicast_address =
662 current_addresses->FirstUnicastAddress;
663 current_unicast_address;
664 current_unicast_address = current_unicast_address->Next) {
665 const ADDRESS_FAMILY family =
666 current_unicast_address->Address.lpSockaddr->sa_family;
667
668 if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)) {
669 memcpy (
670 ip_addr_, current_unicast_address->Address.lpSockaddr,
671 (family == AF_INET) ? sizeof (struct sockaddr_in)
672 : sizeof (struct sockaddr_in6));
673 found = true;
674 break;
675 }
676 }
677
678 if (found)
679 break;
680 }
681
682 if (str_rc1 == 0)
683 free (if_name);
684 if (str_rc2 == 0)
685 free (if_friendly_name);
686 }
687
688 free (addresses);
689 }
690
691 if (!found) {
692 errno = ENODEV;
693 return -1;
694 }
695 return 0;
696}
697
698#else
699
700// On other platforms we assume there are no sane interface names.
701int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
702{
703 LIBZMQ_UNUSED (ip_addr_);
704 LIBZMQ_UNUSED (nic_);
705
706 errno = ENODEV;
707 return -1;
708}
709
710#endif
711
712int zmq::ip_resolver_t::do_getaddrinfo (const char *node_,
713 const char *service_,
714 const struct addrinfo *hints_,
715 struct addrinfo **res_)
716{
717 return getaddrinfo (node_, service_, hints_, res_);
718}
719
720void zmq::ip_resolver_t::do_freeaddrinfo (struct addrinfo *res_)
721{
722 freeaddrinfo (res_);
723}
724
725unsigned int zmq::ip_resolver_t::do_if_nametoindex (const char *ifname_)
726{
727#if _WIN32_WINNT > _WIN32_WINNT_WINXP && !defined ZMQ_HAVE_WINDOWS_UWP \
728 && !defined ZMQ_HAVE_VXWORKS
729 return if_nametoindex (ifname_);
730#else
731 // The function 'if_nametoindex' is not supported on Windows XP.
732 // If we are targeting XP using a vxxx_xp toolset then fail.
733 // This is brutal as this code could be run on later windows clients
734 // meaning the IPv6 zone_id cannot have an interface name.
735 // This could be fixed with a runtime check.
736 return 0;
737#endif
738}
739