1// Copyright (c) 2010 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 "net/proxy/proxy_resolver_js_bindings.h"
6
7#include "base/logging.h"
8#include "base/string_util.h"
9#include "base/values.h"
10#include "net/base/address_list.h"
11#include "net/base/host_cache.h"
12#include "net/base/host_resolver.h"
13#include "net/base/net_errors.h"
14#include "net/base/net_log.h"
15#include "net/base/net_util.h"
16#include "net/base/sys_addrinfo.h"
17#include "net/proxy/proxy_resolver_request_context.h"
18
19namespace net {
20
21namespace {
22
23// Event parameters for a PAC error message (line number + message).
24class ErrorNetlogParams : public NetLog::EventParameters {
25 public:
26  ErrorNetlogParams(int line_number,
27                    const string16& message)
28      : line_number_(line_number),
29        message_(message) {
30  }
31
32  virtual Value* ToValue() const {
33    DictionaryValue* dict = new DictionaryValue();
34    dict->SetInteger("line_number", line_number_);
35    dict->SetString("message", message_);
36    return dict;
37  }
38
39 private:
40  const int line_number_;
41  const string16 message_;
42
43  DISALLOW_COPY_AND_ASSIGN(ErrorNetlogParams);
44};
45
46// Event parameters for a PAC alert().
47class AlertNetlogParams : public NetLog::EventParameters {
48 public:
49  explicit AlertNetlogParams(const string16& message) : message_(message) {
50  }
51
52  virtual Value* ToValue() const {
53    DictionaryValue* dict = new DictionaryValue();
54    dict->SetString("message", message_);
55    return dict;
56  }
57
58 private:
59  const string16 message_;
60
61  DISALLOW_COPY_AND_ASSIGN(AlertNetlogParams);
62};
63
64// ProxyResolverJSBindings implementation.
65class DefaultJSBindings : public ProxyResolverJSBindings {
66 public:
67  DefaultJSBindings(HostResolver* host_resolver, NetLog* net_log)
68      : host_resolver_(host_resolver),
69        net_log_(net_log) {
70  }
71
72  // Handler for "alert(message)".
73  virtual void Alert(const string16& message) {
74    VLOG(1) << "PAC-alert: " << message;
75
76    // Send to the NetLog.
77    LogEventToCurrentRequestAndGlobally(NetLog::TYPE_PAC_JAVASCRIPT_ALERT,
78                                        new AlertNetlogParams(message));
79  }
80
81  // Handler for "myIpAddress()".
82  // TODO(eroman): Perhaps enumerate the interfaces directly, using
83  // getifaddrs().
84  virtual bool MyIpAddress(std::string* first_ip_address) {
85    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
86                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS,
87                             NULL);
88
89    bool ok = MyIpAddressImpl(first_ip_address);
90
91    LogEventToCurrentRequest(NetLog::PHASE_END,
92                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS,
93                             NULL);
94    return ok;
95  }
96
97  // Handler for "myIpAddressEx()".
98  virtual bool MyIpAddressEx(std::string* ip_address_list) {
99    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
100                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX,
101                             NULL);
102
103    bool ok = MyIpAddressExImpl(ip_address_list);
104
105    LogEventToCurrentRequest(NetLog::PHASE_END,
106                             NetLog::TYPE_PAC_JAVASCRIPT_MY_IP_ADDRESS_EX,
107                             NULL);
108    return ok;
109  }
110
111  // Handler for "dnsResolve(host)".
112  virtual bool DnsResolve(const std::string& host,
113                          std::string* first_ip_address) {
114    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
115                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE,
116                             NULL);
117
118    bool ok = DnsResolveImpl(host, first_ip_address);
119
120    LogEventToCurrentRequest(NetLog::PHASE_END,
121                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE,
122                             NULL);
123    return ok;
124  }
125
126  // Handler for "dnsResolveEx(host)".
127  virtual bool DnsResolveEx(const std::string& host,
128                            std::string* ip_address_list) {
129    LogEventToCurrentRequest(NetLog::PHASE_BEGIN,
130                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX,
131                             NULL);
132
133    bool ok = DnsResolveExImpl(host, ip_address_list);
134
135    LogEventToCurrentRequest(NetLog::PHASE_END,
136                             NetLog::TYPE_PAC_JAVASCRIPT_DNS_RESOLVE_EX,
137                             NULL);
138    return ok;
139  }
140
141  // Handler for when an error is encountered. |line_number| may be -1.
142  virtual void OnError(int line_number, const string16& message) {
143    // Send to the chrome log.
144    if (line_number == -1)
145      VLOG(1) << "PAC-error: " << message;
146    else
147      VLOG(1) << "PAC-error: " << "line: " << line_number << ": " << message;
148
149    // Send the error to the NetLog.
150    LogEventToCurrentRequestAndGlobally(
151        NetLog::TYPE_PAC_JAVASCRIPT_ERROR,
152        new ErrorNetlogParams(line_number, message));
153  }
154
155  virtual void Shutdown() {
156    host_resolver_->Shutdown();
157  }
158
159 private:
160  bool MyIpAddressImpl(std::string* first_ip_address) {
161    std::string my_hostname = GetHostName();
162    if (my_hostname.empty())
163      return false;
164    return DnsResolveImpl(my_hostname, first_ip_address);
165  }
166
167  bool MyIpAddressExImpl(std::string* ip_address_list) {
168    std::string my_hostname = GetHostName();
169    if (my_hostname.empty())
170      return false;
171    return DnsResolveExImpl(my_hostname, ip_address_list);
172  }
173
174  bool DnsResolveImpl(const std::string& host,
175                      std::string* first_ip_address) {
176    // Do a sync resolve of the hostname (port doesn't matter).
177    // Disable IPv6 results. We do this because the PAC specification isn't
178    // really IPv6 friendly, and Internet Explorer also restricts to IPv4.
179    // Consequently a lot of existing PAC scripts assume they will only get
180    // IPv4 results, and will misbehave if they get an IPv6 result.
181    // See http://crbug.com/24641 for more details.
182    HostResolver::RequestInfo info(HostPortPair(host, 80));
183    info.set_address_family(ADDRESS_FAMILY_IPV4);
184    AddressList address_list;
185
186    int result = DnsResolveHelper(info, &address_list);
187    if (result != OK)
188      return false;
189
190    // There may be multiple results; we will just use the first one.
191    // This returns empty string on failure.
192    *first_ip_address = net::NetAddressToString(address_list.head());
193    if (first_ip_address->empty())
194      return false;
195
196    return true;
197  }
198
199  bool DnsResolveExImpl(const std::string& host,
200                        std::string* ip_address_list) {
201    // Do a sync resolve of the hostname (port doesn't matter).
202    HostResolver::RequestInfo info(HostPortPair(host, 80));
203    AddressList address_list;
204    int result = DnsResolveHelper(info, &address_list);
205
206    if (result != OK)
207      return false;
208
209    // Stringify all of the addresses in the address list, separated
210    // by semicolons.
211    std::string address_list_str;
212    const struct addrinfo* current_address = address_list.head();
213    while (current_address) {
214      if (!address_list_str.empty())
215        address_list_str += ";";
216      const std::string address_string = NetAddressToString(current_address);
217      if (address_string.empty())
218        return false;
219      address_list_str += address_string;
220      current_address = current_address->ai_next;
221    }
222
223    *ip_address_list = address_list_str;
224    return true;
225  }
226
227  // Helper to execute a synchronous DNS resolve, using the per-request
228  // DNS cache if there is one.
229  int DnsResolveHelper(const HostResolver::RequestInfo& info,
230                       AddressList* address_list) {
231    HostCache::Key cache_key(info.hostname(),
232                             info.address_family(),
233                             info.host_resolver_flags());
234
235    HostCache* host_cache = current_request_context() ?
236        current_request_context()->host_cache : NULL;
237
238    // First try to service this request from the per-request DNS cache.
239    // (we cache DNS failures much more aggressively within the context
240    // of a FindProxyForURL() request).
241    if (host_cache) {
242      const HostCache::Entry* entry =
243          host_cache->Lookup(cache_key, base::TimeTicks::Now());
244      if (entry) {
245        if (entry->error == OK)
246          *address_list = entry->addrlist;
247        return entry->error;
248      }
249    }
250
251    // Otherwise ask the resolver.
252    int result = host_resolver_->Resolve(info, address_list, NULL, NULL,
253                                         BoundNetLog());
254
255    // Save the result back to the per-request DNS cache.
256    if (host_cache) {
257      host_cache->Set(cache_key, result, *address_list,
258                      base::TimeTicks::Now());
259    }
260
261    return result;
262  }
263
264  void LogEventToCurrentRequest(
265      NetLog::EventPhase phase,
266      NetLog::EventType type,
267      scoped_refptr<NetLog::EventParameters> params) {
268    if (current_request_context() && current_request_context()->net_log)
269      current_request_context()->net_log->AddEntry(type, phase, params);
270  }
271
272  void LogEventToCurrentRequestAndGlobally(
273      NetLog::EventType type,
274      scoped_refptr<NetLog::EventParameters> params) {
275    LogEventToCurrentRequest(NetLog::PHASE_NONE, type, params);
276
277    // Emit to the global NetLog event stream.
278    if (net_log_) {
279      net_log_->AddEntry(
280          type,
281          base::TimeTicks::Now(),
282          NetLog::Source(),
283          NetLog::PHASE_NONE,
284          params);
285    }
286  }
287
288  HostResolver* const host_resolver_;
289  NetLog* net_log_;
290};
291
292}  // namespace
293
294// static
295ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault(
296    HostResolver* host_resolver, NetLog* net_log) {
297  return new DefaultJSBindings(host_resolver, net_log);
298}
299
300}  // namespace net
301