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
19 namespace net {
20
21 namespace {
22
23 // Event parameters for a PAC error message (line number + message).
24 class ErrorNetlogParams : public NetLog::EventParameters {
25 public:
ErrorNetlogParams(int line_number,const string16 & message)26 ErrorNetlogParams(int line_number,
27 const string16& message)
28 : line_number_(line_number),
29 message_(message) {
30 }
31
ToValue() const32 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().
47 class AlertNetlogParams : public NetLog::EventParameters {
48 public:
AlertNetlogParams(const string16 & message)49 explicit AlertNetlogParams(const string16& message) : message_(message) {
50 }
51
ToValue() const52 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.
65 class DefaultJSBindings : public ProxyResolverJSBindings {
66 public:
DefaultJSBindings(HostResolver * host_resolver,NetLog * net_log)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)".
Alert(const string16 & 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().
MyIpAddress(std::string * first_ip_address)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()".
MyIpAddressEx(std::string * ip_address_list)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)".
DnsResolve(const std::string & host,std::string * first_ip_address)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)".
DnsResolveEx(const std::string & host,std::string * ip_address_list)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.
OnError(int line_number,const string16 & message)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
Shutdown()155 virtual void Shutdown() {
156 host_resolver_->Shutdown();
157 }
158
159 private:
MyIpAddressImpl(std::string * first_ip_address)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
MyIpAddressExImpl(std::string * ip_address_list)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
DnsResolveImpl(const std::string & host,std::string * first_ip_address)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
DnsResolveExImpl(const std::string & host,std::string * ip_address_list)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.
DnsResolveHelper(const HostResolver::RequestInfo & info,AddressList * address_list)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
LogEventToCurrentRequest(NetLog::EventPhase phase,NetLog::EventType type,scoped_refptr<NetLog::EventParameters> params)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
LogEventToCurrentRequestAndGlobally(NetLog::EventType type,scoped_refptr<NetLog::EventParameters> params)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
CreateDefault(HostResolver * host_resolver,NetLog * net_log)295 ProxyResolverJSBindings* ProxyResolverJSBindings::CreateDefault(
296 HostResolver* host_resolver, NetLog* net_log) {
297 return new DefaultJSBindings(host_resolver, net_log);
298 }
299
300 } // namespace net
301