Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2008, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "talk/base/nethelpers.h"
     29 
     30 #include "talk/base/byteorder.h"
     31 #include "talk/base/signalthread.h"
     32 
     33 namespace talk_base {
     34 
     35 #if defined(LINUX) || defined(ANDROID)
     36 static const size_t kInitHostentLen = 1024;
     37 static const size_t kMaxHostentLen = kInitHostentLen * 8;
     38 #endif
     39 
     40 // AsyncResolver
     41 
     42 AsyncResolver::AsyncResolver() : result_(NULL), error_(0) {
     43 }
     44 
     45 AsyncResolver::~AsyncResolver() {
     46   FreeHostEnt(result_);
     47 }
     48 
     49 void AsyncResolver::DoWork() {
     50   result_ = SafeGetHostByName(addr_.hostname().c_str(), &error_);
     51 }
     52 
     53 void AsyncResolver::OnWorkDone() {
     54   if (result_) {
     55     addr_.SetIP(NetworkToHost32(
     56         *reinterpret_cast<uint32*>(result_->h_addr_list[0])));
     57   }
     58 }
     59 
     60 // The functions below are used to do gethostbyname, but with an allocated
     61 // instead of a static buffer.
     62 hostent* SafeGetHostByName(const char* hostname, int* herrno) {
     63   if (NULL == hostname || NULL == herrno) {
     64     return NULL;
     65   }
     66   hostent* result = NULL;
     67 #if defined(WIN32)
     68   // On Windows we have to allocate a buffer, and manually copy the hostent,
     69   // along with its embedded pointers.
     70   hostent* ent = gethostbyname(hostname);
     71   if (!ent) {
     72     *herrno = WSAGetLastError();
     73     return NULL;
     74   }
     75 
     76   // Get the total number of bytes we need to copy, and allocate our buffer.
     77   int num_aliases = 0, num_addrs = 0;
     78   int total_len = sizeof(hostent);
     79   total_len += strlen(ent->h_name) + 1;
     80   while (ent->h_aliases[num_aliases]) {
     81     total_len += sizeof(char*) + strlen(ent->h_aliases[num_aliases]) + 1;
     82     ++num_aliases;
     83   }
     84   total_len += sizeof(char*);
     85   while (ent->h_addr_list[num_addrs]) {
     86     total_len += sizeof(char*) + ent->h_length;
     87     ++num_addrs;
     88   }
     89   total_len += sizeof(char*);
     90 
     91   result = static_cast<hostent*>(malloc(total_len));
     92   if (NULL == result) {
     93     return NULL;
     94   }
     95   char* p = reinterpret_cast<char*>(result) + sizeof(hostent);
     96 
     97   // Copy the hostent into it, along with its embedded pointers.
     98   result->h_name = p;
     99   memcpy(p, ent->h_name, strlen(ent->h_name) + 1);
    100   p += strlen(ent->h_name) + 1;
    101 
    102   result->h_aliases = reinterpret_cast<char**>(p);
    103   p += (num_aliases + 1) * sizeof(char*);
    104   for (int i = 0; i < num_aliases; ++i) {
    105     result->h_aliases[i] = p;
    106     memcpy(p, ent->h_aliases[i], strlen(ent->h_aliases[i]) + 1);
    107     p += strlen(ent->h_aliases[i]) + 1;
    108   }
    109   result->h_aliases[num_aliases] = NULL;
    110 
    111   result->h_addrtype = ent->h_addrtype;
    112   result->h_length = ent->h_length;
    113 
    114   result->h_addr_list = reinterpret_cast<char**>(p);
    115   p += (num_addrs + 1) * sizeof(char*);
    116   for (int i = 0; i < num_addrs; ++i) {
    117     result->h_addr_list[i] = p;
    118     memcpy(p, ent->h_addr_list[i], ent->h_length);
    119     p += ent->h_length;
    120   }
    121   result->h_addr_list[num_addrs] = NULL;
    122 
    123   *herrno = 0;
    124 #elif defined(LINUX) || defined(ANDROID)
    125   // gethostbyname() is not thread safe, so we need to call gethostbyname_r()
    126   // which is a reentrant version of gethostbyname().
    127   ASSERT(kInitHostentLen > sizeof(hostent));
    128   size_t size = kInitHostentLen;
    129   int ret;
    130   void* buf = malloc(size);
    131   if (NULL == buf) {
    132     return NULL;
    133   }
    134   char* aux = static_cast<char*>(buf) + sizeof(hostent);
    135   size_t aux_len = size - sizeof(hostent);
    136   while ((ret = gethostbyname_r(hostname, reinterpret_cast<hostent*>(buf), aux,
    137       aux_len, &result, herrno)) == ERANGE) {
    138     size *= 2;
    139     if (size > kMaxHostentLen) {
    140       break;  // Just to be safe.
    141     }
    142     buf = realloc(buf, size);
    143     if (NULL == buf) {
    144       return NULL;
    145     }
    146     aux = static_cast<char*>(buf) + sizeof(hostent);
    147     aux_len = size - sizeof(hostent);
    148   }
    149   if (ret != 0 || buf != result) {
    150     free(buf);
    151     return NULL;
    152   }
    153   *herrno = 0;
    154 #elif defined(OSX) || defined(IOS)
    155   // Mac OS returns an object with everything allocated.
    156   result = getipnodebyname(hostname, AF_INET, AI_DEFAULT, herrno);
    157 #else
    158 #error "I don't know how to do gethostbyname safely on your system."
    159 #endif
    160   return result;
    161 }
    162 
    163 // This function should mirror the above function, and free any resources
    164 // allocated by the above.
    165 void FreeHostEnt(hostent* host) {
    166 #if defined(OSX) || defined(IOS)
    167   freehostent(host);
    168 #elif defined(WIN32) || defined(POSIX)
    169   free(host);
    170 #else
    171 #error "I don't know how to free a hostent on your system."
    172 #endif
    173 }
    174 
    175 }  // namespace talk_base
    176