OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/sync/notifier/base/async_dns_lookup.h" |
| 6 |
| 7 #ifdef POSIX |
| 8 #include <arpa/inet.h> |
| 9 #include <netinet/in.h> |
| 10 #include <netinet/ip.h> |
| 11 #include <netdb.h> |
| 12 #include <sys/socket.h> |
| 13 #include <sys/types.h> |
| 14 #endif // POSIX |
| 15 |
| 16 #include <vector> |
| 17 |
| 18 #include "chrome/browser/sync/notifier/base/nethelpers.h" |
| 19 #include "chrome/browser/sync/notifier/gaia_auth/inet_aton.h" |
| 20 #include "talk/base/byteorder.h" |
| 21 #include "talk/base/common.h" |
| 22 #include "talk/base/logging.h" |
| 23 #include "talk/base/socketaddress.h" |
| 24 #include "talk/base/thread.h" |
| 25 |
| 26 enum { MSG_TIMEOUT = talk_base::SignalThread::ST_MSG_FIRST_AVAILABLE }; |
| 27 |
| 28 #ifndef WIN32 |
| 29 const int WSAHOST_NOT_FOUND = 11001; // follows the format in winsock2.h |
| 30 #endif // WIN32 |
| 31 |
| 32 namespace notifier { |
| 33 |
| 34 AsyncDNSLookup::AsyncDNSLookup(const talk_base::SocketAddress& server) |
| 35 : server_(new talk_base::SocketAddress(server)), |
| 36 error_(0) { |
| 37 // Timeout after 5 seconds. |
| 38 talk_base::Thread::Current()->PostDelayed(5000, this, MSG_TIMEOUT); |
| 39 } |
| 40 |
| 41 AsyncDNSLookup::~AsyncDNSLookup() { |
| 42 } |
| 43 |
| 44 void AsyncDNSLookup::DoWork() { |
| 45 std::string hostname(server_->IPAsString()); |
| 46 |
| 47 in_addr addr; |
| 48 if (inet_aton(hostname.c_str(), &addr)) { |
| 49 talk_base::CritScope scope(&cs_); |
| 50 ip_list_.push_back(talk_base::NetworkToHost32(addr.s_addr)); |
| 51 } else { |
| 52 LOG_F(LS_VERBOSE) << "(" << hostname << ")"; |
| 53 hostent ent; |
| 54 char buffer[8192]; |
| 55 int errcode = 0; |
| 56 hostent* host = SafeGetHostByName(hostname.c_str(), &ent, |
| 57 buffer, sizeof(buffer), |
| 58 &errcode); |
| 59 talk_base::Thread::Current()->Clear(this, MSG_TIMEOUT); |
| 60 if (host) { |
| 61 talk_base::CritScope scope(&cs_); |
| 62 |
| 63 // Check to see if this already timed out. |
| 64 if (error_ == 0) { |
| 65 for (int index = 0; true; ++index) { |
| 66 uint32* addr = reinterpret_cast<uint32*>(host->h_addr_list[index]); |
| 67 if (addr == 0) { // 0 = end of list |
| 68 break; |
| 69 } |
| 70 uint32 ip = talk_base::NetworkToHost32(*addr); |
| 71 LOG_F(LS_VERBOSE) << "(" << hostname << ") resolved to: " |
| 72 << talk_base::SocketAddress::IPToString(ip); |
| 73 ip_list_.push_back(ip); |
| 74 } |
| 75 // Maintain the invariant that either the list is not empty |
| 76 // or the error is non zero when we are done with processing |
| 77 // the dnslookup. |
| 78 if (ip_list_.empty() && error_ == 0) { |
| 79 error_ = WSAHOST_NOT_FOUND; |
| 80 } |
| 81 } |
| 82 FreeHostEnt(host); |
| 83 } else { |
| 84 { // Scoping for the critical section. |
| 85 talk_base::CritScope scope(&cs_); |
| 86 |
| 87 // Check to see if this already timed out. |
| 88 if (error_ == 0) { |
| 89 error_ = errcode; |
| 90 } |
| 91 } |
| 92 LOG_F(LS_ERROR) << "(" << hostname << ") error: " << error_; |
| 93 } |
| 94 } |
| 95 } |
| 96 |
| 97 void AsyncDNSLookup::OnMessage(talk_base::Message* message) { |
| 98 ASSERT(message); |
| 99 if (message->message_id == MSG_TIMEOUT) { |
| 100 OnTimeout(); |
| 101 } else { |
| 102 talk_base::SignalThread::OnMessage(message); |
| 103 } |
| 104 } |
| 105 |
| 106 void AsyncDNSLookup::OnTimeout() { |
| 107 // Allow the scope for the critical section to be the whole |
| 108 // method, just to be sure that the worker thread can't exit |
| 109 // while we are doing SignalWorkDone (because that could possibly |
| 110 // cause the class to be deleted). |
| 111 talk_base::CritScope scope(&cs_); |
| 112 |
| 113 // Check to see if the ip list was already filled (or errored out). |
| 114 if (!ip_list_.empty() || error_ != 0) { |
| 115 return; |
| 116 } |
| 117 |
| 118 // Worker thread is taking too long so timeout. |
| 119 error_ = WSAHOST_NOT_FOUND; |
| 120 |
| 121 // Rely on the caller to do the Release/Destroy. |
| 122 // |
| 123 // Doing this signal while holding cs_ won't cause a deadlock because |
| 124 // the AsyncDNSLookup::DoWork thread doesn't have any locks at this point, |
| 125 // and it is the only thread being held up by this. |
| 126 SignalWorkDone(this); |
| 127 |
| 128 // Ensure that no more "WorkDone" signaling is done. |
| 129 // Don't call Release or Destroy since that was already done |
| 130 // by the callback. |
| 131 SignalWorkDone.disconnect_all(); |
| 132 } |
| 133 } // namespace notifier |
OLD | NEW |