• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

ink_inet.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
00022  */
00023 
00024 #include "libts.h"
00025 #include "ts/TestBox.h"
00026 
00027 #if defined(darwin)
00028 extern "C"
00029 {
00030   struct hostent *gethostbyname_r(const char *name, struct hostent *result, char *buffer, int buflen, int *h_errnop);
00031   struct hostent *gethostbyaddr_r(const char *name, size_t size, int type,
00032                                   struct hostent *result, char *buffer, int buflen, int *h_errnop);
00033 }
00034 #endif
00035 
00036 IpAddr const IpAddr::INVALID;
00037 
00038 struct hostent *
00039 ink_gethostbyname_r(char *hostname, ink_gethostbyname_r_data * data)
00040 {
00041 #ifdef RENTRENT_GETHOSTBYNAME
00042   struct hostent *r = gethostbyname(hostname);
00043   if (r)
00044     data->ent = *r;
00045   data->herrno = errno;
00046 
00047 #else //RENTRENT_GETHOSTBYNAME
00048 #if GETHOSTBYNAME_R_GLIBC2
00049 
00050   struct hostent *addrp = NULL;
00051   int res = gethostbyname_r(hostname, &data->ent, data->buf,
00052                             INK_GETHOSTBYNAME_R_DATA_SIZE, &addrp,
00053                             &data->herrno);
00054   struct hostent *r = NULL;
00055   if (!res && addrp)
00056     r = addrp;
00057 
00058 #else
00059   struct hostent *r = gethostbyname_r(hostname, &data->ent, data->buf,
00060                                       INK_GETHOSTBYNAME_R_DATA_SIZE,
00061                                       &data->herrno);
00062 #endif
00063 #endif
00064   return r;
00065 }
00066 
00067 struct hostent *
00068 ink_gethostbyaddr_r(char *ip, int len, int type, ink_gethostbyaddr_r_data * data)
00069 {
00070 #if GETHOSTBYNAME_R_GLIBC2
00071   struct hostent *r = NULL;
00072   struct hostent *addrp = NULL;
00073   int res = gethostbyaddr_r((char *) ip, len, type, &data->ent, data->buf,
00074                             INK_GETHOSTBYNAME_R_DATA_SIZE, &addrp,
00075                             &data->herrno);
00076   if (!res && addrp)
00077     r = addrp;
00078 #else
00079 #ifdef RENTRENT_GETHOSTBYADDR
00080   struct hostent *r = gethostbyaddr((const void *) ip, len, type);
00081 
00082 #else
00083   struct hostent *r = gethostbyaddr_r((char *) ip, len, type, &data->ent,
00084                                       data->buf,
00085                                       INK_GETHOSTBYNAME_R_DATA_SIZE,
00086                                       &data->herrno);
00087 #endif
00088 #endif //LINUX
00089   return r;
00090 }
00091 
00092 uint32_t
00093 ink_inet_addr(const char *s)
00094 {
00095   uint32_t u[4];
00096   uint8_t *pc = (uint8_t *) s;
00097   int n = 0;
00098   uint32_t base = 10;
00099 
00100   while (n < 4) {
00101 
00102     u[n] = 0;
00103     base = 10;
00104 
00105     // handle hex, octal
00106 
00107     if (*pc == '0') {
00108       if (*++pc == 'x' || *pc == 'X')
00109         base = 16, pc++;
00110       else
00111         base = 8;
00112     }
00113     // handle hex, octal, decimal
00114 
00115     while (*pc) {
00116       if (ParseRules::is_digit(*pc)) {
00117         u[n] = u[n] * base + (*pc++ - '0');
00118         continue;
00119       }
00120       if (base == 16 && ParseRules::is_hex(*pc)) {
00121         u[n] = u[n] * 16 + ParseRules::ink_tolower(*pc++) - 'a' + 10;
00122         continue;
00123       }
00124       break;
00125     }
00126 
00127     n++;
00128     if (*pc == '.')
00129       pc++;
00130     else
00131       break;
00132   }
00133 
00134   if (*pc && !ParseRules::is_wslfcr(*pc))
00135     return htonl((uint32_t) - 1);
00136 
00137   switch (n) {
00138   case 1:
00139     return htonl(u[0]);
00140   case 2:
00141     if (u[0] > 0xff || u[1] > 0xffffff)
00142       return htonl((uint32_t) - 1);
00143     return htonl((u[0] << 24) | u[1]);
00144   case 3:
00145     if (u[0] > 0xff || u[1] > 0xff || u[2] > 0xffff)
00146       return htonl((uint32_t) - 1);
00147     return htonl((u[0] << 24) | (u[1] << 16) | u[2]);
00148   case 4:
00149     if (u[0] > 0xff || u[1] > 0xff || u[2] > 0xff || u[3] > 0xff)
00150       return htonl((uint32_t) - 1);
00151     return htonl((u[0] << 24) | (u[1] << 16) | (u[2] << 8) | u[3]);
00152   }
00153   return htonl((uint32_t) - 1);
00154 }
00155 
00156 const char *ats_ip_ntop(const struct sockaddr *addr, char *dst, size_t size)
00157 {
00158   char const* zret = 0;
00159 
00160   switch (addr->sa_family) {
00161   case AF_INET:
00162     zret = inet_ntop(AF_INET, &ats_ip4_addr_cast(addr), dst, size);
00163     break;
00164   case AF_INET6:
00165     zret = inet_ntop(AF_INET6, &ats_ip6_addr_cast(addr), dst, size);
00166     break;
00167   default:
00168     zret = dst;
00169     snprintf(dst, size, "*Not IP address [%u]*", addr->sa_family);
00170     break;
00171   }
00172   return zret;
00173 }
00174 
00175 char const*
00176 ats_ip_family_name(int family) {
00177   return AF_INET == family ? "IPv4"
00178     : AF_INET6 == family ? "IPv6"
00179     : "Unspec"
00180     ;
00181 }
00182 
00183 char const* ats_ip_nptop(
00184   sockaddr const* addr,
00185   char* dst, size_t size
00186 ) {
00187   char buff[INET6_ADDRSTRLEN];
00188   snprintf(dst, size, "%s:%u",
00189     ats_ip_ntop(addr, buff, sizeof(buff)),
00190     ats_ip_port_host_order(addr)
00191   );
00192   return dst;
00193 }
00194 
00195 int
00196 ats_ip_parse(ts::ConstBuffer src, ts::ConstBuffer* addr, ts::ConstBuffer* port) {
00197   // In case the incoming arguments are null.
00198   ts::ConstBuffer localAddr, localPort;
00199   if (!addr) addr = &localAddr;
00200   if (!port) port = &localPort;
00201   addr->reset();
00202   port->reset();
00203 
00204   // Let's see if we can find out what's in the address string.
00205   if (src) {
00206     while (src && isspace(*src)) ++src;
00207     // Check for brackets.
00208     if ('[' == *src) {
00209       /* Ugly. In a number of places we must use bracket notation
00210          to support port numbers. Rather than mucking with that
00211          everywhere, we'll tweak it here. Experimentally we can't
00212          depend on getaddrinfo to handle it. Note that the text
00213          buffer size includes space for the nul, so a bracketed
00214          address is at most that size - 1 + 2 -> size+1.
00215 
00216          It just gets better. In order to bind link local addresses
00217          the scope_id must be set to the interface index. That's
00218          most easily done by appending a %intf (where "intf" is the
00219          name of the interface) to the address. Which makes
00220          the address potentially larger than the standard maximum.
00221          So we can't depend on that sizing.
00222       */
00223       ++src; // skip bracket.
00224       *addr = src.splitOn(']');
00225       if (*addr && ':' == *src) { // found the closing bracket and port colon
00226         ++src; // skip colon.
00227         *port = src;
00228       } // else it's a fail for unclosed brackets.
00229     } else {
00230       // See if there's exactly 1 colon
00231       ts::ConstBuffer tmp = src.after(':');
00232       if (tmp && ! tmp.find(':')) { // 1 colon and no others
00233         src.clip(tmp.data() - 1); // drop port from address.
00234         *port = tmp;
00235       } // else 0 or > 1 colon and no brackets means no port.
00236       *addr = src;
00237     }
00238     // clip port down to digits.
00239     if (*port) {
00240       char const* spot = port->data();
00241       while (isdigit(*spot)) ++spot;
00242       port->clip(spot);
00243     }
00244   }
00245   return *addr ? 0 : -1; // true if we found an address.
00246 }
00247 
00248 int
00249 ats_ip_pton(const ts::ConstBuffer& src, sockaddr* ip)
00250 {
00251   int zret = -1;
00252   ts::ConstBuffer addr, port;
00253 
00254   ats_ip_invalidate(ip);
00255   if (0 == ats_ip_parse(src, &addr, &port)) {
00256     // Copy if not terminated.
00257     if (0 != addr[addr.size()-1]) {
00258       char* tmp = static_cast<char*>(alloca(addr.size()+1));
00259       memcpy(tmp, addr.data(), addr.size());
00260       tmp[addr.size()] = 0;
00261       addr.set(tmp, addr.size());
00262     }
00263     if (addr.find(':')) { // colon -> IPv6
00264       in6_addr addr6;
00265       if (inet_pton(AF_INET6, addr.data(), &addr6)) {
00266         zret = 0;
00267         ats_ip6_set(ip, addr6);
00268       }
00269     } else { // no colon -> must be IPv4
00270       in_addr addr4;
00271       if (inet_aton(addr.data(), &addr4)) {
00272         zret = 0;
00273         ats_ip4_set(ip, addr4.s_addr);
00274       }
00275     }
00276     // If we had a successful conversion, set the port.
00277     if (ats_is_ip(ip))
00278       ats_ip_port_cast(ip) = port ? htons(atoi(port.data())) : 0;
00279   }
00280 
00281   return zret;
00282 }
00283 
00284 uint32_t
00285 ats_ip_hash(sockaddr const* addr) {
00286   union md5sum {
00287     unsigned char c[16];
00288     uint32_t i;
00289   } zret;
00290   zret.i = 0;
00291 
00292   if (ats_is_ip4(addr)) {
00293     zret.i = ats_ip4_addr_cast(addr);
00294   } else if (ats_is_ip6(addr)) {
00295     ink_code_md5(const_cast<uint8_t*>(ats_ip_addr8_cast(addr)), TS_IP6_SIZE, zret.c);
00296   }
00297   return zret.i;
00298 }
00299 
00300 int
00301 ats_ip_to_hex(sockaddr const* src, char* dst, size_t len) {
00302   int zret = 0;
00303   ink_assert(len);
00304   char const* dst_limit = dst + len - 1; // reserve null space.
00305   if (ats_is_ip(src)) {
00306     uint8_t const* data = ats_ip_addr8_cast(src);
00307     for ( uint8_t const* src_limit = data + ats_ip_addr_size(src)
00308         ; data < src_limit && dst+1 < dst_limit
00309         ; ++data, zret += 2
00310     ) {
00311     uint8_t n1 = (*data >> 4) & 0xF; // high nybble.
00312     uint8_t n0 = *data & 0xF; // low nybble.
00313 
00314     *dst++ = n1 > 9 ? n1 + 'A' - 10 : n1 + '0';
00315     *dst++ = n0 > 9 ? n0 + 'A' - 10 : n0 + '0';
00316     }
00317   }
00318   *dst = 0; // terminate but don't include that in the length.
00319   return zret;
00320 }
00321 
00322 sockaddr* ats_ip_set(
00323   sockaddr* dst,
00324   IpAddr const& addr,
00325   uint16_t port
00326 ) {
00327   if (AF_INET == addr._family) ats_ip4_set(dst, addr._addr._ip4, port);
00328   else if (AF_INET6 == addr._family) ats_ip6_set(dst, addr._addr._ip6, port);
00329   else ats_ip_invalidate(dst);
00330   return dst;
00331 }
00332 
00333 int
00334 IpAddr::load(char const* text) {
00335   IpEndpoint ip;
00336   int zret = ats_ip_pton(text, &ip);
00337   *this = ip;
00338   return zret;
00339 }
00340 
00341 char*
00342 IpAddr::toString(char* dest, size_t len) const {
00343   IpEndpoint ip;
00344   ip.assign(*this);
00345   ats_ip_ntop(&ip, dest, len);
00346   return dest;
00347 }
00348 
00349 bool
00350 IpAddr::isMulticast() const {
00351   return (AF_INET == _family && 0xe == (_addr._byte[0] >> 4)) ||
00352     (AF_INET6 == _family && IN6_IS_ADDR_MULTICAST(&_addr._ip6))
00353     ;
00354 }
00355 
00356 bool
00357 operator == (IpAddr const& lhs, sockaddr const* rhs) {
00358   bool zret = false;
00359   if (lhs._family == rhs->sa_family) {
00360     if (AF_INET == lhs._family) {
00361       zret = lhs._addr._ip4 == ats_ip4_addr_cast(rhs);
00362     } else if (AF_INET6 == lhs._family) {
00363       zret = 0 == memcmp(&lhs._addr._ip6, &ats_ip6_addr_cast(rhs), sizeof(in6_addr));
00364     } else { // map all non-IP to the same thing.
00365       zret = true;
00366     }
00367   } // else different families, not equal.
00368   return zret;
00369 }
00370 
00371 int
00372 ats_ip_getbestaddrinfo(char const* host,
00373   IpEndpoint* ip4,
00374   IpEndpoint* ip6
00375 ) {
00376   int zret = -1;
00377   int port = 0; // port value to assign if we find an address.
00378   addrinfo ai_hints;
00379   addrinfo* ai_result;
00380   ts::ConstBuffer addr_text, port_text;
00381   ts::ConstBuffer src(host, strlen(host)+1);
00382 
00383   if (ip4) ats_ip_invalidate(ip4);
00384   if (ip6) ats_ip_invalidate(ip6);
00385 
00386   if (0 == ats_ip_parse(src, &addr_text, &port_text)) {
00387     // Copy if not terminated.
00388     if (0 != addr_text[addr_text.size()-1]) {
00389       char* tmp = static_cast<char*>(alloca(addr_text.size()+1));
00390       memcpy(tmp, addr_text.data(), addr_text.size());
00391       tmp[addr_text.size()] = 0;
00392       addr_text.set(tmp, addr_text.size());
00393     }
00394     ink_zero(ai_hints);
00395     ai_hints.ai_family = AF_UNSPEC;
00396     ai_hints.ai_flags = AI_ADDRCONFIG;
00397     zret = getaddrinfo(addr_text.data(), 0, &ai_hints, &ai_result);
00398   
00399     if (0 == zret) {
00400       // Walk the returned addresses and pick the "best".
00401       enum {
00402         NA, // Not an (IP) Address.
00403         LO, // Loopback.
00404         LL, // Link Local.
00405         PR, // Private.
00406         MC, // Multicast.
00407         GL  // Global.
00408       } spot_type = NA, ip4_type = NA, ip6_type = NA;
00409       sockaddr const* ip4_src = 0;
00410       sockaddr const* ip6_src = 0;
00411 
00412       for ( addrinfo* ai_spot = ai_result
00413           ; ai_spot
00414           ; ai_spot = ai_spot->ai_next
00415       ) {
00416         sockaddr const* ai_ip = ai_spot->ai_addr;
00417         if (!ats_is_ip(ai_ip)) spot_type = NA;
00418         else if (ats_is_ip_loopback(ai_ip)) spot_type = LO;
00419         else if (ats_is_ip_linklocal(ai_ip)) spot_type = LL;
00420         else if (ats_is_ip_private(ai_ip)) spot_type = PR;
00421         else if (ats_is_ip_multicast(ai_ip)) spot_type = MC;
00422         else spot_type = GL;
00423         
00424         if (spot_type == NA) continue; // Next!
00425 
00426         if (ats_is_ip4(ai_ip)) {
00427           if (spot_type > ip4_type) {
00428             ip4_src = ai_ip;
00429             ip4_type = spot_type;
00430           }
00431         } else if (ats_is_ip6(ai_ip)) {
00432           if (spot_type > ip6_type) {
00433             ip6_src = ai_ip;
00434             ip6_type = spot_type;
00435           }
00436         }
00437       }
00438       if (ip4_type > NA) ats_ip_copy(ip4, ip4_src);
00439       if (ip6_type > NA) ats_ip_copy(ip6, ip6_src);
00440       freeaddrinfo(ai_result); // free *after* the copy.
00441 
00442     }
00443   }
00444 
00445   // We don't really care if the port is null terminated - the parser
00446   // would get all the digits so the next character is a non-digit (null or
00447   // not) and atoi will do the right thing in either case.
00448   if (port_text.size()) port = htons(atoi(port_text.data()));
00449   if (ats_is_ip(ip4)) ats_ip_port_cast(ip4) = port;
00450   if (ats_is_ip(ip6)) ats_ip_port_cast(ip6) = port;
00451 
00452   if (!ats_is_ip(ip4) && !ats_is_ip(ip6)) zret = -1;
00453 
00454   return zret;
00455 }
00456 
00457 int
00458 ats_ip_check_characters(ts::ConstBuffer text) {
00459   bool found_colon = false;
00460   bool found_hex = false;
00461   for ( char const *p = text.data(), *limit = p + text.size()
00462       ; p < limit
00463       ; ++p
00464   )
00465     if (':' == *p) found_colon = true;
00466     else if ('.' == *p || isdigit(*p)) /* empty */;
00467     else if (isxdigit(*p)) found_hex = true;
00468     else return AF_UNSPEC;
00469 
00470   return found_hex && !found_colon ? AF_UNSPEC
00471     : found_colon ? AF_INET6
00472     : AF_INET
00473     ;
00474 }
00475 
00476 // Need to declare this type globally so gcc 4.4 can use it in the countof() template ...
00477 struct ip_parse_spec { const char * hostspec; const char * host; const char * port; };
00478 
00479 REGRESSION_TEST(Ink_Inet) (RegressionTest * t, int /* atype */, int * pstatus) {
00480   TestBox box(t, pstatus);
00481   IpEndpoint  ep;
00482   IpAddr      addr;
00483 
00484   box = REGRESSION_TEST_PASSED;
00485 
00486   // Test ats_ip_parse() ...
00487   {
00488     struct ip_parse_spec names[] = {
00489       { "::", "::", NULL },
00490       { "[::1]:99", "::1", "99" },
00491       { "127.0.0.1:8080", "127.0.0.1", "8080" },
00492       { "foo.example.com", "foo.example.com", NULL },
00493       { "foo.example.com:99", "foo.example.com", "99" },
00494     };
00495 
00496     for (unsigned i = 0; i < countof(names); ++i) {
00497       ts::ConstBuffer addr, port;
00498 
00499       box.check(ats_ip_parse(ts::ConstBuffer(names[i].hostspec, strlen(names[i].hostspec)), &addr, &port) == 0,
00500           "ats_ip_parse(%s)", names[i].hostspec);
00501       box.check(strncmp(addr.data(), names[i].host, addr.size()) ==  0,
00502           "ats_ip_parse(%s) gave addr '%.*s'", names[i].hostspec, (int)addr.size(), addr.data());
00503       if (names[i].port) {
00504         box.check(strncmp(port.data(), names[i].port, port.size()) ==  0,
00505           "ats_ip_parse(%s) gave port '%.*s'", names[i].hostspec, (int)port.size(), port.data());
00506       } else {
00507         box.check(port.size() == 0,
00508           "ats_ip_parse(%s) gave port '%.*s'", names[i].hostspec, (int)port.size(), port.data());
00509       }
00510 
00511     }
00512   }
00513 
00514   // Test ats_ip_pton() ...
00515   {
00516     box.check(ats_ip_pton("76.14.64.156", &ep.sa) == 0, "ats_ip_pton()");
00517     box.check(addr.load("76.14.64.156") == 0, "IpAddr::load()");
00518     box.check(addr.family() == ep.family(), "mismatched address family");
00519 
00520     switch (addr.family()) {
00521     case AF_INET:
00522       box.check(ep.sin.sin_addr.s_addr == addr._addr._ip4, "IPv4 address mismatch");
00523       break;
00524     case AF_INET6:
00525       box.check(memcmp(&ep.sin6.sin6_addr, &addr._addr._ip6, sizeof(in6_addr)) == 0, "IPv6 address mismatch");
00526       break;
00527     default:
00528       ;
00529     }
00530   }
00531 
00532 
00533 }

Generated by  doxygen 1.7.1