summaryrefslogtreecommitdiffstats
path: root/chrome/common/net/notifier/base/async_dns_lookup.cc
blob: 4cc254d208caee920f566a09b66de3885f786e08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/net/notifier/base/async_dns_lookup.h"

#include "build/build_config.h"

#if defined(OS_POSIX)
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/types.h>
#endif  // defined(OS_POSIX)

// Apparently, inet_aton() is available for Windows, but just not
// declared anywhere.  We'd use inet_pton(), but it's Vista-only.
#if defined(OS_WIN)
int inet_aton(const char* cp, struct in_addr* inp);
#endif  // defined(OS_WIN)

#include <vector>

#include "base/basictypes.h"
#include "base/logging.h"
#include "chrome/common/net/notifier/base/nethelpers.h"
#include "talk/base/byteorder.h"
#include "talk/base/common.h"
#include "talk/base/socketaddress.h"
#include "talk/base/thread.h"

enum { MSG_TIMEOUT = talk_base::SignalThread::ST_MSG_FIRST_AVAILABLE };

#ifndef WIN32
const int WSAHOST_NOT_FOUND = 11001;  // Follows the format in winsock2.h.
#endif  // WIN32

namespace notifier {

AsyncDNSLookup::AsyncDNSLookup(const talk_base::SocketAddress& server)
  : server_(new talk_base::SocketAddress(server)),
    error_(0) {
  // Timeout after 5 seconds.
  talk_base::Thread::Current()->PostDelayed(5000, this, MSG_TIMEOUT);
}

AsyncDNSLookup::~AsyncDNSLookup() {
}

void AsyncDNSLookup::DoWork() {
  std::string hostname(server_->IPAsString());

  in_addr addr;
  if (inet_aton(hostname.c_str(), &addr)) {
    talk_base::CritScope scope(&cs_);
    ip_list_.push_back(talk_base::NetworkToHost32(addr.s_addr));
  } else {
    LOG(INFO) << "(" << hostname << ")";
    hostent ent;
    char buffer[8192];
    int errcode = 0;
    hostent* host = SafeGetHostByName(hostname.c_str(), &ent,
                                      buffer, sizeof(buffer),
                                      &errcode);
    talk_base::Thread::Current()->Clear(this, MSG_TIMEOUT);
    if (host) {
      talk_base::CritScope scope(&cs_);

      // Check to see if this already timed out.
      if (error_ == 0) {
        for (int index = 0; true; ++index) {
          uint32* addr = reinterpret_cast<uint32*>(host->h_addr_list[index]);
          if (addr == 0) {  // 0 = end of list.
            break;
          }
          uint32 ip = talk_base::NetworkToHost32(*addr);
          LOG(INFO) << "(" << hostname << ") resolved to: "
                    << talk_base::SocketAddress::IPToString(ip);
          ip_list_.push_back(ip);
        }
        // Maintain the invariant that either the list is not empty or the
        // error is non zero when we are done with processing the dnslookup.
        if (ip_list_.empty() && error_ == 0) {
          error_ = WSAHOST_NOT_FOUND;
        }
      }
      FreeHostEnt(host);
    } else {
      {  // Scoping for the critical section.
        talk_base::CritScope scope(&cs_);

        // Check to see if this already timed out.
        if (error_ == 0) {
          error_ = errcode;
        }
      }
      LOG(ERROR) << "(" << hostname << ") error: " << error_;
    }
  }
}

void AsyncDNSLookup::OnMessage(talk_base::Message* message) {
  DCHECK(message);
  if (message->message_id == MSG_TIMEOUT) {
    OnTimeout();
  } else {
    talk_base::SignalThread::OnMessage(message);
  }
}

void AsyncDNSLookup::OnTimeout() {
  // Allow the scope for the critical section to be the whole method, just to
  // be sure that the worker thread can't exit while we are doing
  // SignalWorkDone (because that could possibly cause the class to be
  // deleted).
  talk_base::CritScope scope(&cs_);

  // Check to see if the ip list was already filled (or errored out).
  if (!ip_list_.empty() || error_ != 0) {
    return;
  }

  // Worker thread is taking too long so timeout.
  error_ = WSAHOST_NOT_FOUND;

  // Rely on the caller to do the Release/Destroy.
  //
  // Doing this signal while holding cs_ won't cause a deadlock because the
  // AsyncDNSLookup::DoWork thread doesn't have any locks at this point, and it
  // is the only thread being held up by this.
  SignalWorkDone(this);

  // Ensure that no more "WorkDone" signaling is done.
  // Don't call Release or Destroy since that was already done by the callback.
  SignalWorkDone.disconnect_all();
}

}  // namespace notifier