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: 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(). 47 class 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. 65 class 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 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