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