libzmq master
The Intelligent Transport Layer

tcp_address.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (c) 2009-2011 250bpm s.r.o.
00003     Copyright (c) 2007-2009 iMatix Corporation
00004     Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
00005 
00006     This file is part of 0MQ.
00007 
00008     0MQ is free software; you can redistribute it and/or modify it under
00009     the terms of the GNU Lesser General Public License as published by
00010     the Free Software Foundation; either version 3 of the License, or
00011     (at your option) any later version.
00012 
00013     0MQ is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU Lesser General Public License for more details.
00017 
00018     You should have received a copy of the GNU Lesser General Public License
00019     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 */
00021 
00022 #include <string.h>
00023 #include <string>
00024 
00025 #include "tcp_address.hpp"
00026 #include "platform.hpp"
00027 #include "stdint.hpp"
00028 #include "err.hpp"
00029 #include "ip.hpp"
00030 
00031 #ifdef ZMQ_HAVE_WINDOWS
00032 #include "windows.hpp"
00033 #else
00034 #include <sys/types.h>
00035 #include <arpa/inet.h>
00036 #include <netinet/tcp.h>
00037 #include <netdb.h>
00038 #endif
00039 
00040 //  Some platforms (notably Darwin/OSX and NetBSD) do not define all AI_
00041 //  flags for getaddrinfo(). This can be worked around safely by defining
00042 //  these to 0.
00043 #ifndef AI_ADDRCONFIG
00044 #define AI_ADDRCONFIG 0
00045 #endif
00046 
00047 #if defined ZMQ_HAVE_SOLARIS
00048 
00049 #include <sys/sockio.h>
00050 #include <net/if.h>
00051 #include <unistd.h>
00052 #include <stdlib.h>
00053 
00054 //  On Solaris platform, network interface name can be queried by ioctl.
00055 int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
00056 {
00057     //  TODO: Unused parameter, IPv6 support not implemented for Solaris.
00058     (void) ipv4only_;
00059 
00060     //  Create a socket.
00061     int fd = open_socket (AF_INET, SOCK_DGRAM, 0);
00062     zmq_assert (fd != -1);
00063 
00064     //  Retrieve number of interfaces.
00065     lifnum ifn;
00066     ifn.lifn_family = AF_INET;
00067     ifn.lifn_flags = 0;
00068     int rc = ioctl (fd, SIOCGLIFNUM, (char*) &ifn);
00069     zmq_assert (rc != -1);
00070 
00071     //  Allocate memory to get interface names.
00072     size_t ifr_size = sizeof (struct lifreq) * ifn.lifn_count;
00073     char *ifr = (char*) malloc (ifr_size);
00074     alloc_assert (ifr);
00075     
00076     //  Retrieve interface names.
00077     lifconf ifc;
00078     ifc.lifc_family = AF_INET;
00079     ifc.lifc_flags = 0;
00080     ifc.lifc_len = ifr_size;
00081     ifc.lifc_buf = ifr;
00082     rc = ioctl (fd, SIOCGLIFCONF, (char*) &ifc);
00083     zmq_assert (rc != -1);
00084 
00085     //  Find the interface with the specified name and AF_INET family.
00086     bool found = false;
00087     lifreq *ifrp = ifc.lifc_req;
00088     for (int n = 0; n < (int) (ifc.lifc_len / sizeof (lifreq));
00089           n ++, ifrp ++) {
00090         if (!strcmp (nic_, ifrp->lifr_name)) {
00091             rc = ioctl (fd, SIOCGLIFADDR, (char*) ifrp);
00092             zmq_assert (rc != -1);
00093             if (ifrp->lifr_addr.ss_family == AF_INET) {
00094                 address.ipv4 = *(sockaddr_in*) &ifrp->lifr_addr;
00095                 found = true;
00096                 break;
00097             }
00098         }
00099     }
00100 
00101     //  Clean-up.
00102     free (ifr);
00103     close (fd);
00104 
00105     if (!found) {
00106         errno = ENODEV;
00107         return -1;
00108     }
00109 
00110     return 0;
00111 }
00112 
00113 #elif defined ZMQ_HAVE_AIX || ZMQ_HAVE_HPUX || ZMQ_HAVE_ANDROID
00114 
00115 #include <sys/types.h>
00116 #include <unistd.h>
00117 #include <sys/ioctl.h>
00118 #include <net/if.h>
00119 
00120 int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
00121 {
00122     //  TODO: Unused parameter, IPv6 support not implemented for AIX or HP/UX.
00123     (void) ipv4only_;
00124 
00125     //  Create a socket.
00126     int sd = open_socket (AF_INET, SOCK_DGRAM, 0);
00127     zmq_assert (sd != -1);
00128 
00129     struct ifreq ifr; 
00130 
00131     //  Copy interface name for ioctl get.
00132     strncpy (ifr.ifr_name, nic_, sizeof (ifr.ifr_name));
00133 
00134     //  Fetch interface address.
00135     int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (struct ifreq));
00136 
00137     //  Clean up.
00138     close (sd);
00139 
00140     if (rc == -1) {
00141         errno = ENODEV;
00142         return -1;
00143     }
00144 
00145     memcpy (&address.ipv4.sin_addr, &((sockaddr_in*) &ifr.ifr_addr)->sin_addr,
00146         sizeof (in_addr));
00147 
00148     return 0;    
00149 }
00150 
00151 #elif ((defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\
00152     defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_OPENBSD ||\
00153     defined ZMQ_HAVE_QNXNTO || defined ZMQ_HAVE_NETBSD)\
00154     && defined ZMQ_HAVE_IFADDRS)
00155 
00156 #include <ifaddrs.h>
00157 
00158 //  On these platforms, network interface name can be queried
00159 //  using getifaddrs function.
00160 int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
00161 {
00162     //  Get the addresses.
00163     ifaddrs* ifa = NULL;
00164     int rc = getifaddrs (&ifa);
00165     zmq_assert (rc == 0);    
00166     zmq_assert (ifa != NULL);
00167 
00168     //  Find the corresponding network interface.
00169     bool found = false;
00170     for (ifaddrs *ifp = ifa; ifp != NULL ;ifp = ifp->ifa_next)
00171     {
00172         if (ifp->ifa_addr == NULL)
00173             continue;
00174 
00175         int family = ifp->ifa_addr->sa_family;
00176 
00177         if ((family == AF_INET
00178              || (!ipv4only_ && family == AF_INET6))
00179             && !strcmp (nic_, ifp->ifa_name))
00180         {
00181             memcpy (&address, ifp->ifa_addr,
00182                     (family == AF_INET) ? sizeof (struct sockaddr_in)
00183                                         : sizeof (struct sockaddr_in6));
00184             found = true;
00185             break;
00186         }
00187     }
00188 
00189     //  Clean-up;
00190     freeifaddrs (ifa);
00191 
00192     if (!found) {
00193         errno = ENODEV;
00194         return -1;
00195     }
00196 
00197     return 0;
00198 }
00199 
00200 #else
00201 
00202 //  On other platforms we assume there are no sane interface names.
00203 //  This is true especially of Windows.
00204 int zmq::tcp_address_t::resolve_nic_name (const char *nic_, bool ipv4only_)
00205 {
00206     //  All unused parameters.
00207     (void) nic_;
00208     (void) ipv4only_;
00209 
00210     errno = ENODEV;
00211     return -1;
00212 }
00213 
00214 #endif
00215 
00216 int zmq::tcp_address_t::resolve_interface (char const *interface_,
00217     bool ipv4only_)
00218 {
00219     //  Initialize temporary output pointers with storage address.
00220     sockaddr_storage ss;
00221     sockaddr *out_addr = (sockaddr *) &ss;
00222     socklen_t out_addrlen;
00223 
00224     //  Initialise IP-format family/port and populate temporary output pointers
00225     //  with the address.
00226     if (ipv4only_) {
00227         sockaddr_in ip4_addr;
00228         memset (&ip4_addr, 0, sizeof (ip4_addr));
00229         ip4_addr.sin_family = AF_INET;
00230         ip4_addr.sin_addr.s_addr = htonl (INADDR_ANY);
00231         out_addrlen = (socklen_t) sizeof (ip4_addr);
00232         memcpy (out_addr, &ip4_addr, out_addrlen);
00233     } else {
00234         sockaddr_in6 ip6_addr;
00235         memset (&ip6_addr, 0, sizeof (ip6_addr));
00236         ip6_addr.sin6_family = AF_INET6;
00237         memcpy (&ip6_addr.sin6_addr, &in6addr_any, sizeof (in6addr_any));
00238         out_addrlen = (socklen_t) sizeof (ip6_addr);
00239         memcpy (out_addr, &ip6_addr, out_addrlen);
00240     }
00241 
00242     //  * resolves to INADDR_ANY or in6addr_any.
00243     if (strcmp (interface_, "*") == 0) {
00244         zmq_assert (out_addrlen <= (socklen_t) sizeof (address));
00245         memcpy (&address, out_addr, out_addrlen);
00246         return 0;
00247     }
00248 
00249     //  Try to resolve the string as a NIC name.
00250     int rc = resolve_nic_name (interface_, ipv4only_);
00251     if (rc != 0 && errno != ENODEV)
00252         return rc;
00253     if (rc == 0) {
00254         zmq_assert (out_addrlen <= (socklen_t) sizeof (address));
00255         memcpy (&address, out_addr, out_addrlen);
00256         return 0;
00257     }
00258 
00259     //  There's no such interface name. Assume literal address.
00260 #if defined ZMQ_HAVE_OPENVMS && defined __ia64
00261     __addrinfo64 *res = NULL;
00262     __addrinfo64 req;
00263 #else
00264     addrinfo *res = NULL;
00265     addrinfo req;
00266 #endif
00267     memset (&req, 0, sizeof (req));
00268 
00269     //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
00270     //  IPv4-in-IPv6 addresses.
00271     req.ai_family = ipv4only_ ? AF_INET : AF_INET6;
00272 
00273     //  Arbitrary, not used in the output, but avoids duplicate results.
00274     req.ai_socktype = SOCK_STREAM;
00275 
00276     //  Restrict hostname/service to literals to avoid any DNS lookups or
00277     //  service-name irregularity due to indeterminate socktype.
00278     req.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
00279 
00280 #ifndef ZMQ_HAVE_WINDOWS
00281     //  Windows by default maps IPv4 addresses into IPv6. In this API we only
00282     //  require IPv4-mapped addresses when no native IPv6 interfaces are
00283     //  available (~AI_ALL).  This saves an additional DNS roundtrip for IPv4
00284     //  addresses.
00285     if (req.ai_family == AF_INET6)
00286         req.ai_flags |= AI_V4MAPPED;
00287 #endif
00288 
00289     //  Resolve the literal address. Some of the error info is lost in case
00290     //  of error, however, there's no way to report EAI errors via errno.
00291     rc = getaddrinfo (interface_, NULL, &req, &res);
00292     if (rc) {
00293         errno = ENODEV;
00294         return -1;
00295     }
00296 
00297     //  Use the first result.
00298     zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (address));
00299     memcpy (&address, res->ai_addr, res->ai_addrlen);
00300 
00301     //  Cleanup getaddrinfo after copying the possibly referenced result.
00302     if (res)
00303         freeaddrinfo (res);
00304 
00305     return 0;
00306 }
00307 
00308 int zmq::tcp_address_t::resolve_hostname (const char *hostname_, bool ipv4only_)
00309 {
00310     //  Set up the query.
00311 #if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64
00312     __addrinfo64 req;
00313 #else
00314     addrinfo req;
00315 #endif
00316     memset (&req, 0, sizeof (req));
00317 
00318     //  Choose IPv4 or IPv6 protocol family. Note that IPv6 allows for
00319     //  IPv4-in-IPv6 addresses.
00320     req.ai_family = ipv4only_ ? AF_INET : AF_INET6;
00321 
00322     //  Need to choose one to avoid duplicate results from getaddrinfo() - this
00323     //  doesn't really matter, since it's not included in the addr-output.
00324     req.ai_socktype = SOCK_STREAM;
00325     
00326 #ifndef ZMQ_HAVE_WINDOWS
00327     //  Windows by default maps IPv4 addresses into IPv6. In this API we only
00328     //  require IPv4-mapped addresses when no native IPv6 interfaces are
00329     //  available.  This saves an additional DNS roundtrip for IPv4 addresses.
00330     if (req.ai_family == AF_INET6)
00331         req.ai_flags |= AI_V4MAPPED;
00332 #endif
00333 
00334     //  Resolve host name. Some of the error info is lost in case of error,
00335     //  however, there's no way to report EAI errors via errno.
00336 #if defined ZMQ_HAVE_OPENVMS && defined __ia64 && __INITIAL_POINTER_SIZE == 64
00337     __addrinfo64 *res;
00338 #else
00339     addrinfo *res;
00340 #endif
00341     int rc = getaddrinfo (hostname_, NULL, &req, &res);
00342     if (rc) {
00343         switch (rc) {
00344         case EAI_MEMORY:
00345             errno = ENOMEM;
00346             break;
00347         default:
00348             errno = EINVAL;
00349             break;
00350         }
00351         return -1;
00352     }
00353 
00354     //  Copy first result to output addr with hostname and service.
00355     zmq_assert ((size_t) (res->ai_addrlen) <= sizeof (address));
00356     memcpy (&address, res->ai_addr, res->ai_addrlen);
00357  
00358     freeaddrinfo (res);
00359     
00360     return 0;
00361 }
00362 
00363 zmq::tcp_address_t::tcp_address_t ()
00364 {
00365     memset (&address, 0, sizeof (address));
00366 }
00367 
00368 zmq::tcp_address_t::~tcp_address_t ()
00369 {
00370 }
00371 
00372 int zmq::tcp_address_t::resolve (const char *name_, bool local_, bool ipv4only_)
00373 {
00374     //  Find the ':' at end that separates address from the port number.
00375     const char *delimiter = strrchr (name_, ':');
00376     if (!delimiter) {
00377         errno = EINVAL;
00378         return -1;
00379     }
00380 
00381     //  Separate the address/port.
00382     std::string addr_str (name_, delimiter - name_);
00383     std::string port_str (delimiter + 1);
00384 
00385     //  Remove square brackets around the address, if any.
00386     if (!addr_str.empty () && addr_str [0] == '[' &&
00387           addr_str [addr_str.size () - 1] == ']')
00388         addr_str = addr_str.substr (1, addr_str.size () - 2);
00389 
00390     //  Parse the port number (0 is not a valid port).
00391     uint16_t port = (uint16_t) atoi (port_str.c_str());
00392     if (port == 0) {
00393         errno = EINVAL;
00394         return -1;
00395     }
00396 
00397     //  Resolve the IP address.
00398     int rc;
00399     if (local_)
00400         rc = resolve_interface (addr_str.c_str (), ipv4only_);
00401     else
00402         rc = resolve_hostname (addr_str.c_str (), ipv4only_);
00403     if (rc != 0)
00404         return -1;
00405 
00406     //  Set the port into the address structure.
00407     if (address.generic.sa_family == AF_INET6)
00408         address.ipv6.sin6_port = htons (port);
00409     else
00410         address.ipv4.sin_port = htons (port);
00411 
00412     return 0;
00413 }
00414 
00415 sockaddr *zmq::tcp_address_t::addr ()
00416 {
00417     return &address.generic;
00418 }
00419 
00420 socklen_t zmq::tcp_address_t::addrlen ()
00421 {
00422     if (address.generic.sa_family == AF_INET6)
00423         return (socklen_t) sizeof (address.ipv6);
00424     else
00425         return (socklen_t) sizeof (address.ipv4);
00426 }
00427 
00428 #if defined ZMQ_HAVE_WINDOWS
00429 unsigned short zmq::tcp_address_t::family ()
00430 #else
00431 sa_family_t zmq::tcp_address_t::family ()
00432 #endif
00433 {
00434     return address.generic.sa_family;
00435 }
00436 
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines