/* * libjingle * Copyright 2008, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "talk/base/nethelpers.h" #include "talk/base/byteorder.h" #include "talk/base/signalthread.h" namespace talk_base { #if defined(LINUX) || defined(ANDROID) static const size_t kInitHostentLen = 1024; static const size_t kMaxHostentLen = kInitHostentLen * 8; #endif // AsyncResolver AsyncResolver::AsyncResolver() : result_(NULL), error_(0) { } AsyncResolver::~AsyncResolver() { FreeHostEnt(result_); } void AsyncResolver::DoWork() { result_ = SafeGetHostByName(addr_.hostname().c_str(), &error_); } void AsyncResolver::OnWorkDone() { if (result_) { addr_.SetIP(NetworkToHost32( *reinterpret_cast<uint32*>(result_->h_addr_list[0]))); } } // The functions below are used to do gethostbyname, but with an allocated // instead of a static buffer. hostent* SafeGetHostByName(const char* hostname, int* herrno) { if (NULL == hostname || NULL == herrno) { return NULL; } hostent* result = NULL; #if defined(WIN32) // On Windows we have to allocate a buffer, and manually copy the hostent, // along with its embedded pointers. hostent* ent = gethostbyname(hostname); if (!ent) { *herrno = WSAGetLastError(); return NULL; } // Get the total number of bytes we need to copy, and allocate our buffer. int num_aliases = 0, num_addrs = 0; int total_len = sizeof(hostent); total_len += strlen(ent->h_name) + 1; while (ent->h_aliases[num_aliases]) { total_len += sizeof(char*) + strlen(ent->h_aliases[num_aliases]) + 1; ++num_aliases; } total_len += sizeof(char*); while (ent->h_addr_list[num_addrs]) { total_len += sizeof(char*) + ent->h_length; ++num_addrs; } total_len += sizeof(char*); result = static_cast<hostent*>(malloc(total_len)); if (NULL == result) { return NULL; } char* p = reinterpret_cast<char*>(result) + sizeof(hostent); // Copy the hostent into it, along with its embedded pointers. result->h_name = p; memcpy(p, ent->h_name, strlen(ent->h_name) + 1); p += strlen(ent->h_name) + 1; result->h_aliases = reinterpret_cast<char**>(p); p += (num_aliases + 1) * sizeof(char*); for (int i = 0; i < num_aliases; ++i) { result->h_aliases[i] = p; memcpy(p, ent->h_aliases[i], strlen(ent->h_aliases[i]) + 1); p += strlen(ent->h_aliases[i]) + 1; } result->h_aliases[num_aliases] = NULL; result->h_addrtype = ent->h_addrtype; result->h_length = ent->h_length; result->h_addr_list = reinterpret_cast<char**>(p); p += (num_addrs + 1) * sizeof(char*); for (int i = 0; i < num_addrs; ++i) { result->h_addr_list[i] = p; memcpy(p, ent->h_addr_list[i], ent->h_length); p += ent->h_length; } result->h_addr_list[num_addrs] = NULL; *herrno = 0; #elif defined(LINUX) || defined(ANDROID) // gethostbyname() is not thread safe, so we need to call gethostbyname_r() // which is a reentrant version of gethostbyname(). ASSERT(kInitHostentLen > sizeof(hostent)); size_t size = kInitHostentLen; int ret; void* buf = malloc(size); if (NULL == buf) { return NULL; } char* aux = static_cast<char*>(buf) + sizeof(hostent); size_t aux_len = size - sizeof(hostent); while ((ret = gethostbyname_r(hostname, reinterpret_cast<hostent*>(buf), aux, aux_len, &result, herrno)) == ERANGE) { size *= 2; if (size > kMaxHostentLen) { break; // Just to be safe. } buf = realloc(buf, size); if (NULL == buf) { return NULL; } aux = static_cast<char*>(buf) + sizeof(hostent); aux_len = size - sizeof(hostent); } if (ret != 0 || buf != result) { free(buf); return NULL; } *herrno = 0; #elif defined(OSX) || defined(IOS) // Mac OS returns an object with everything allocated. result = getipnodebyname(hostname, AF_INET, AI_DEFAULT, herrno); #else #error "I don't know how to do gethostbyname safely on your system." #endif return result; } // This function should mirror the above function, and free any resources // allocated by the above. void FreeHostEnt(hostent* host) { #if defined(OSX) || defined(IOS) freehostent(host); #elif defined(WIN32) || defined(POSIX) free(host); #else #error "I don't know how to free a hostent on your system." #endif } } // namespace talk_base