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 
family() const23 int zmq::ip_addr_t::family () const
24 {
25     return generic.sa_family;
26 }
27 
is_multicast() const28 bool 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 
port() const39 uint16_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 
as_sockaddr() const47 const struct sockaddr *zmq::ip_addr_t::as_sockaddr () const
48 {
49     return &generic;
50 }
51 
sockaddr_len() const52 zmq::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 
set_port(uint16_t port_)58 void 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
any(int family_)68 zmq::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 
ip_resolver_options_t()95 zmq::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 
105 zmq::ip_resolver_options_t &
bindable(bool bindable_)106 zmq::ip_resolver_options_t::bindable (bool bindable_)
107 {
108     _bindable_wanted = bindable_;
109 
110     return *this;
111 }
112 
113 zmq::ip_resolver_options_t &
allow_nic_name(bool allow_)114 zmq::ip_resolver_options_t::allow_nic_name (bool allow_)
115 {
116     _nic_name_allowed = allow_;
117 
118     return *this;
119 }
120 
ipv6(bool ipv6_)121 zmq::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
130 zmq::ip_resolver_options_t &
expect_port(bool expect_)131 zmq::ip_resolver_options_t::expect_port (bool expect_)
132 {
133     _port_expected = expect_;
134 
135     return *this;
136 }
137 
allow_dns(bool allow_)138 zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::allow_dns (bool allow_)
139 {
140     _dns_allowed = allow_;
141 
142     return *this;
143 }
144 
allow_path(bool allow_)145 zmq::ip_resolver_options_t &zmq::ip_resolver_options_t::allow_path (bool allow_)
146 {
147     _path_allowed = allow_;
148 
149     return *this;
150 }
151 
bindable()152 bool zmq::ip_resolver_options_t::bindable ()
153 {
154     return _bindable_wanted;
155 }
156 
allow_nic_name()157 bool zmq::ip_resolver_options_t::allow_nic_name ()
158 {
159     return _nic_name_allowed;
160 }
161 
ipv6()162 bool zmq::ip_resolver_options_t::ipv6 ()
163 {
164     return _ipv6_wanted;
165 }
166 
expect_port()167 bool zmq::ip_resolver_options_t::expect_port ()
168 {
169     return _port_expected;
170 }
171 
allow_dns()172 bool zmq::ip_resolver_options_t::allow_dns ()
173 {
174     return _dns_allowed;
175 }
176 
allow_path()177 bool zmq::ip_resolver_options_t::allow_path ()
178 {
179     return _path_allowed;
180 }
181 
ip_resolver_t(ip_resolver_options_t opts_)182 zmq::ip_resolver_t::ip_resolver_t (ip_resolver_options_t opts_) :
183     _options (opts_)
184 {
185 }
186 
resolve(ip_addr_t * ip_addr_,const char * name_)187 int 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         const 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         const 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     const 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         if (if_str.empty ()) {
256             errno = EINVAL;
257             return -1;
258         }
259         addr = addr.substr (0, pos);
260 
261         if (isalpha (if_str.at (0))) {
262             zone_id = do_if_nametoindex (if_str.c_str ());
263         } else {
264             zone_id = static_cast<uint32_t> (atoi (if_str.c_str ()));
265         }
266 
267         if (zone_id == 0) {
268             errno = EINVAL;
269             return -1;
270         }
271     }
272 
273     bool resolved = false;
274     const char *addr_str = addr.c_str ();
275 
276     if (_options.bindable () && addr == "*") {
277         //  Return an ANY address
278         *ip_addr_ = ip_addr_t::any (_options.ipv6 () ? AF_INET6 : AF_INET);
279         resolved = true;
280     }
281 
282     if (!resolved && _options.allow_nic_name ()) {
283         //  Try to resolve the string as a NIC name.
284         const int rc = resolve_nic_name (ip_addr_, addr_str);
285 
286         if (rc == 0) {
287             resolved = true;
288         } else if (errno != ENODEV) {
289             return rc;
290         }
291     }
292 
293     if (!resolved) {
294         const int rc = resolve_getaddrinfo (ip_addr_, addr_str);
295 
296         if (rc != 0) {
297             return rc;
298         }
299         resolved = true;
300     }
301 
302     //  Store the port into the structure. We could get 'getaddrinfo' to do it
303     //  for us but since we don't resolve service names it's a bit overkill and
304     //  we'd still have to do it manually when the address is resolved by
305     //  'resolve_nic_name'
306     ip_addr_->set_port (port);
307 
308     if (ip_addr_->family () == AF_INET6) {
309         ip_addr_->ipv6.sin6_scope_id = zone_id;
310     }
311 
312     assert (resolved == true);
313     return 0;
314 }
315 
resolve_getaddrinfo(ip_addr_t * ip_addr_,const char * addr_)316 int zmq::ip_resolver_t::resolve_getaddrinfo (ip_addr_t *ip_addr_,
317                                              const char *addr_)
318 {
319 #if defined ZMQ_HAVE_OPENVMS && defined __ia64
320     __addrinfo64 *res = NULL;
321     __addrinfo64 req;
322 #else
323     addrinfo *res = NULL;
324     addrinfo req;
325 #endif
326 
327     memset (&req, 0, sizeof (req));
328 
329     //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
330     //  IPv4-in-IPv6 addresses.
331     req.ai_family = _options.ipv6 () ? AF_INET6 : AF_INET;
332 
333     //  Arbitrary, not used in the output, but avoids duplicate results.
334     req.ai_socktype = SOCK_STREAM;
335 
336     req.ai_flags = 0;
337 
338     if (_options.bindable ()) {
339         req.ai_flags |= AI_PASSIVE;
340     }
341 
342     if (!_options.allow_dns ()) {
343         req.ai_flags |= AI_NUMERICHOST;
344     }
345 
346 #if defined AI_V4MAPPED
347     //  In this API we only require IPv4-mapped addresses when
348     //  no native IPv6 interfaces are available (~AI_ALL).
349     //  This saves an additional DNS roundtrip for IPv4 addresses.
350     if (req.ai_family == AF_INET6) {
351         req.ai_flags |= AI_V4MAPPED;
352     }
353 #endif
354 
355     //  Resolve the literal address. Some of the error info is lost in case
356     //  of error, however, there's no way to report EAI errors via errno.
357     int rc = do_getaddrinfo (addr_, NULL, &req, &res);
358 
359 #if defined AI_V4MAPPED
360     // Some OS do have AI_V4MAPPED defined but it is not supported in getaddrinfo()
361     // returning EAI_BADFLAGS. Detect this and retry
362     if (rc == EAI_BADFLAGS && (req.ai_flags & AI_V4MAPPED)) {
363         req.ai_flags &= ~AI_V4MAPPED;
364         rc = do_getaddrinfo (addr_, NULL, &req, &res);
365     }
366 #endif
367 
368 #if defined ZMQ_HAVE_WINDOWS
369     //  Resolve specific case on Windows platform when using IPv4 address
370     //  with ZMQ_IPv6 socket option.
371     if ((req.ai_family == AF_INET6) && (rc == WSAHOST_NOT_FOUND)) {
372         req.ai_family = AF_INET;
373         rc = do_getaddrinfo (addr_, NULL, &req, &res);
374     }
375 #endif
376 
377     if (rc) {
378         switch (rc) {
379             case EAI_MEMORY:
380                 errno = ENOMEM;
381                 break;
382             default:
383                 if (_options.bindable ()) {
384                     errno = ENODEV;
385                 } else {
386                     errno = EINVAL;
387                 }
388                 break;
389         }
390         return -1;
391     }
392 
393     //  Use the first result.
394     zmq_assert (res != NULL);
395     zmq_assert (static_cast<size_t> (res->ai_addrlen) <= sizeof (*ip_addr_));
396     memcpy (ip_addr_, res->ai_addr, res->ai_addrlen);
397 
398     //  Cleanup getaddrinfo after copying the possibly referenced result.
399     do_freeaddrinfo (res);
400 
401     return 0;
402 }
403 
404 #ifdef ZMQ_HAVE_SOLARIS
405 #include <sys/sockio.h>
406 
407 //  On Solaris platform, network interface name can be queried by ioctl.
resolve_nic_name(ip_addr_t * ip_addr_,const char * nic_)408 int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
409 {
410     //  Create a socket.
411     const int fd = open_socket (AF_INET, SOCK_DGRAM, 0);
412     errno_assert (fd != -1);
413 
414     //  Retrieve number of interfaces.
415     lifnum ifn;
416     ifn.lifn_family = AF_INET;
417     ifn.lifn_flags = 0;
418     int rc = ioctl (fd, SIOCGLIFNUM, (char *) &ifn);
419     errno_assert (rc != -1);
420 
421     //  Allocate memory to get interface names.
422     const size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
423     char *ifr = (char *) malloc (ifr_size);
424     alloc_assert (ifr);
425 
426     //  Retrieve interface names.
427     lifconf ifc;
428     ifc.lifc_family = AF_INET;
429     ifc.lifc_flags = 0;
430     ifc.lifc_len = ifr_size;
431     ifc.lifc_buf = ifr;
432     rc = ioctl (fd, SIOCGLIFCONF, (char *) &ifc);
433     errno_assert (rc != -1);
434 
435     //  Find the interface with the specified name and AF_INET family.
436     bool found = false;
437     lifreq *ifrp = ifc.lifc_req;
438     for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq)); n++, ifrp++) {
439         if (!strcmp (nic_, ifrp->lifr_name)) {
440             rc = ioctl (fd, SIOCGLIFADDR, (char *) ifrp);
441             errno_assert (rc != -1);
442             if (ifrp->lifr_addr.ss_family == AF_INET) {
443                 ip_addr_->ipv4 = *(sockaddr_in *) &ifrp->lifr_addr;
444                 found = true;
445                 break;
446             }
447         }
448     }
449 
450     //  Clean-up.
451     free (ifr);
452     close (fd);
453 
454     if (!found) {
455         errno = ENODEV;
456         return -1;
457     }
458     return 0;
459 }
460 
461 #elif defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX                            \
462   || defined ZMQ_HAVE_ANDROID || defined ZMQ_HAVE_VXWORKS
463 #include <sys/ioctl.h>
464 #ifdef ZMQ_HAVE_VXWORKS
465 #include <ioLib.h>
466 #endif
467 
resolve_nic_name(ip_addr_t * ip_addr_,const char * nic_)468 int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
469 {
470 #if defined ZMQ_HAVE_AIX || defined ZMQ_HAVE_HPUX
471     // IPv6 support not implemented for AIX or HP/UX.
472     if (_options.ipv6 ()) {
473         errno = ENODEV;
474         return -1;
475     }
476 #endif
477 
478     //  Create a socket.
479     const int sd =
480       open_socket (_options.ipv6 () ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
481     errno_assert (sd != -1);
482 
483     struct ifreq ifr;
484 
485     //  Copy interface name for ioctl get.
486     strncpy (ifr.ifr_name, nic_, sizeof (ifr.ifr_name));
487 
488     //  Fetch interface address.
489     const int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (ifr));
490 
491     //  Clean up.
492     close (sd);
493 
494     if (rc == -1) {
495         errno = ENODEV;
496         return -1;
497     }
498 
499     const int family = ifr.ifr_addr.sa_family;
500     if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)
501         && !strcmp (nic_, ifr.ifr_name)) {
502         memcpy (ip_addr_, &ifr.ifr_addr,
503                 (family == AF_INET) ? sizeof (struct sockaddr_in)
504                                     : sizeof (struct sockaddr_in6));
505     } else {
506         errno = ENODEV;
507         return -1;
508     }
509 
510     return 0;
511 }
512 
513 #elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD                     \
514         || defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD                    \
515         || defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD                  \
516         || defined ZMQ_HAVE_DRAGONFLY || defined ZMQ_HAVE_GNU)                 \
517        && defined ZMQ_HAVE_IFADDRS)
518 
519 #include <ifaddrs.h>
520 
521 //  On these platforms, network interface name can be queried
522 //  using getifaddrs function.
resolve_nic_name(ip_addr_t * ip_addr_,const char * nic_)523 int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
524 {
525     //  Get the addresses.
526     ifaddrs *ifa = NULL;
527     int rc = 0;
528     const int max_attempts = 10;
529     const int backoff_msec = 1;
530     for (int i = 0; i < max_attempts; i++) {
531         rc = getifaddrs (&ifa);
532         if (rc == 0 || (rc < 0 && errno != ECONNREFUSED))
533             break;
534         usleep ((backoff_msec << i) * 1000);
535     }
536 
537     if (rc != 0 && ((errno == EINVAL) || (errno == EOPNOTSUPP))) {
538         // Windows Subsystem for Linux compatibility
539         errno = ENODEV;
540         return -1;
541     }
542     errno_assert (rc == 0);
543     zmq_assert (ifa != NULL);
544 
545     //  Find the corresponding network interface.
546     bool found = false;
547     for (const ifaddrs *ifp = ifa; ifp != NULL; ifp = ifp->ifa_next) {
548         if (ifp->ifa_addr == NULL)
549             continue;
550 
551         const int family = ifp->ifa_addr->sa_family;
552         if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)
553             && !strcmp (nic_, ifp->ifa_name)) {
554             memcpy (ip_addr_, ifp->ifa_addr,
555                     (family == AF_INET) ? sizeof (struct sockaddr_in)
556                                         : sizeof (struct sockaddr_in6));
557             found = true;
558             break;
559         }
560     }
561 
562     //  Clean-up;
563     freeifaddrs (ifa);
564 
565     if (!found) {
566         errno = ENODEV;
567         return -1;
568     }
569     return 0;
570 }
571 
572 #elif (defined ZMQ_HAVE_WINDOWS)
573 
574 #include <netioapi.h>
575 
get_interface_name(unsigned long index_,char ** dest_) const576 int zmq::ip_resolver_t::get_interface_name (unsigned long index_,
577                                             char **dest_) const
578 {
579 #ifdef ZMQ_HAVE_WINDOWS_UWP
580     char *buffer = (char *) malloc (1024);
581 #else
582     char *buffer = static_cast<char *> (malloc (IF_MAX_STRING_SIZE));
583 #endif
584     alloc_assert (buffer);
585 
586     char *if_name_result = NULL;
587 
588 #if _WIN32_WINNT > _WIN32_WINNT_WINXP && !defined ZMQ_HAVE_WINDOWS_UWP
589     if_name_result = if_indextoname (index_, buffer);
590 #endif
591 
592     if (if_name_result == NULL) {
593         free (buffer);
594         return -1;
595     }
596 
597     *dest_ = buffer;
598     return 0;
599 }
600 
wchar_to_utf8(const WCHAR * src_,char ** dest_) const601 int zmq::ip_resolver_t::wchar_to_utf8 (const WCHAR *src_, char **dest_) const
602 {
603     int rc;
604     const int buffer_len =
605       WideCharToMultiByte (CP_UTF8, 0, src_, -1, NULL, 0, NULL, 0);
606 
607     char *buffer = static_cast<char *> (malloc (buffer_len));
608     alloc_assert (buffer);
609 
610     rc =
611       WideCharToMultiByte (CP_UTF8, 0, src_, -1, buffer, buffer_len, NULL, 0);
612 
613     if (rc == 0) {
614         free (buffer);
615         return -1;
616     }
617 
618     *dest_ = buffer;
619     return 0;
620 }
621 
resolve_nic_name(ip_addr_t * ip_addr_,const char * nic_)622 int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
623 {
624     int rc;
625     bool found = false;
626     const int max_attempts = 10;
627 
628     int iterations = 0;
629     IP_ADAPTER_ADDRESSES *addresses;
630     unsigned long out_buf_len = sizeof (IP_ADAPTER_ADDRESSES);
631 
632     do {
633         addresses = static_cast<IP_ADAPTER_ADDRESSES *> (malloc (out_buf_len));
634         alloc_assert (addresses);
635 
636         rc =
637           GetAdaptersAddresses (AF_UNSPEC,
638                                 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
639                                   | GAA_FLAG_SKIP_DNS_SERVER,
640                                 NULL, addresses, &out_buf_len);
641         if (rc == ERROR_BUFFER_OVERFLOW) {
642             free (addresses);
643             addresses = NULL;
644         } else {
645             break;
646         }
647         iterations++;
648     } while ((rc == ERROR_BUFFER_OVERFLOW) && (iterations < max_attempts));
649 
650     if (rc == 0) {
651         for (const IP_ADAPTER_ADDRESSES *current_addresses = addresses;
652              current_addresses; current_addresses = current_addresses->Next) {
653             char *if_name = NULL;
654             char *if_friendly_name = NULL;
655 
656             const int str_rc1 =
657               get_interface_name (current_addresses->IfIndex, &if_name);
658             const int str_rc2 = wchar_to_utf8 (current_addresses->FriendlyName,
659                                                &if_friendly_name);
660 
661             //  Find a network adapter by its "name" or "friendly name"
662             if (((str_rc1 == 0) && (!strcmp (nic_, if_name)))
663                 || ((str_rc2 == 0) && (!strcmp (nic_, if_friendly_name)))) {
664                 //  Iterate over all unicast addresses bound to the current network interface
665                 for (const IP_ADAPTER_UNICAST_ADDRESS *current_unicast_address =
666                        current_addresses->FirstUnicastAddress;
667                      current_unicast_address;
668                      current_unicast_address = current_unicast_address->Next) {
669                     const ADDRESS_FAMILY family =
670                       current_unicast_address->Address.lpSockaddr->sa_family;
671 
672                     if (family == (_options.ipv6 () ? AF_INET6 : AF_INET)) {
673                         memcpy (
674                           ip_addr_, current_unicast_address->Address.lpSockaddr,
675                           (family == AF_INET) ? sizeof (struct sockaddr_in)
676                                               : sizeof (struct sockaddr_in6));
677                         found = true;
678                         break;
679                     }
680                 }
681 
682                 if (found)
683                     break;
684             }
685 
686             if (str_rc1 == 0)
687                 free (if_name);
688             if (str_rc2 == 0)
689                 free (if_friendly_name);
690         }
691 
692         free (addresses);
693     }
694 
695     if (!found) {
696         errno = ENODEV;
697         return -1;
698     }
699     return 0;
700 }
701 
702 #else
703 
704 //  On other platforms we assume there are no sane interface names.
resolve_nic_name(ip_addr_t * ip_addr_,const char * nic_)705 int zmq::ip_resolver_t::resolve_nic_name (ip_addr_t *ip_addr_, const char *nic_)
706 {
707     LIBZMQ_UNUSED (ip_addr_);
708     LIBZMQ_UNUSED (nic_);
709 
710     errno = ENODEV;
711     return -1;
712 }
713 
714 #endif
715 
do_getaddrinfo(const char * node_,const char * service_,const struct addrinfo * hints_,struct addrinfo ** res_)716 int zmq::ip_resolver_t::do_getaddrinfo (const char *node_,
717                                         const char *service_,
718                                         const struct addrinfo *hints_,
719                                         struct addrinfo **res_)
720 {
721     return getaddrinfo (node_, service_, hints_, res_);
722 }
723 
do_freeaddrinfo(struct addrinfo * res_)724 void zmq::ip_resolver_t::do_freeaddrinfo (struct addrinfo *res_)
725 {
726     freeaddrinfo (res_);
727 }
728 
729 
do_if_nametoindex(const char * ifname_)730 unsigned int zmq::ip_resolver_t::do_if_nametoindex (const char *ifname_)
731 {
732 #ifdef HAVE_IF_NAMETOINDEX
733     return if_nametoindex (ifname_);
734 #else
735     LIBZMQ_UNUSED (ifname_);
736     // The function 'if_nametoindex' is not supported on Windows XP.
737     // If we are targeting XP using a vxxx_xp toolset then fail.
738     // This is brutal as this code could be run on later windows clients
739     // meaning the IPv6 zone_id cannot have an interface name.
740     // This could be fixed with a runtime check.
741     return 0;
742 #endif
743 }
744