1/* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include "webrtc/base/autodetectproxy.h" 12#include "webrtc/base/httpcommon.h" 13#include "webrtc/base/httpcommon-inl.h" 14#include "webrtc/base/nethelpers.h" 15 16namespace rtc { 17 18static const ProxyType TEST_ORDER[] = { 19 PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN 20}; 21 22static const int kSavedStringLimit = 128; 23 24static void SaveStringToStack(char *dst, 25 const std::string &src, 26 size_t dst_size) { 27 strncpy(dst, src.c_str(), dst_size - 1); 28 dst[dst_size - 1] = '\0'; 29} 30 31AutoDetectProxy::AutoDetectProxy(const std::string& user_agent) 32 : agent_(user_agent), resolver_(NULL), socket_(NULL), next_(0) { 33} 34 35AutoDetectProxy::~AutoDetectProxy() { 36 if (resolver_) { 37 resolver_->Destroy(false); 38 } 39} 40 41void AutoDetectProxy::DoWork() { 42 // TODO: Try connecting to server_url without proxy first here? 43 if (!server_url_.empty()) { 44 LOG(LS_INFO) << "GetProxySettingsForUrl(" << server_url_ << ") - start"; 45 GetProxyForUrl(agent_.c_str(), server_url_.c_str(), &proxy_); 46 LOG(LS_INFO) << "GetProxySettingsForUrl - stop"; 47 } 48 Url<char> url(proxy_.address.HostAsURIString()); 49 if (url.valid()) { 50 LOG(LS_WARNING) << "AutoDetectProxy removing http prefix on proxy host"; 51 proxy_.address.SetIP(url.host()); 52 } 53 LOG(LS_INFO) << "AutoDetectProxy found proxy at " << proxy_.address; 54 if (proxy_.type == PROXY_UNKNOWN) { 55 LOG(LS_INFO) << "AutoDetectProxy initiating proxy classification"; 56 Next(); 57 // Process I/O until Stop() 58 Thread::Current()->ProcessMessages(kForever); 59 // Clean up the autodetect socket, from the thread that created it 60 delete socket_; 61 } 62 // TODO: If we found a proxy, try to use it to verify that it 63 // works by sending a request to server_url. This could either be 64 // done here or by the HttpPortAllocator. 65} 66 67void AutoDetectProxy::OnMessage(Message *msg) { 68 if (MSG_UNRESOLVABLE == msg->message_id) { 69 // If we can't resolve the proxy, skip straight to failure. 70 Complete(PROXY_UNKNOWN); 71 } else if (MSG_TIMEOUT == msg->message_id) { 72 OnCloseEvent(socket_, ETIMEDOUT); 73 } else { 74 // This must be the ST_MSG_WORKER_DONE message that deletes the 75 // AutoDetectProxy object. We have observed crashes within this stack that 76 // seem to be highly reproducible for a small subset of users and thus are 77 // probably correlated with a specific proxy setting, so copy potentially 78 // relevant information onto the stack to make it available in Windows 79 // minidumps. 80 81 // Save the user agent and the number of auto-detection passes that we 82 // needed. 83 char agent[kSavedStringLimit]; 84 SaveStringToStack(agent, agent_, sizeof agent); 85 86 int next = next_; 87 88 // Now the detected proxy config (minus the password field, which could be 89 // sensitive). 90 ProxyType type = proxy().type; 91 92 char address_hostname[kSavedStringLimit]; 93 SaveStringToStack(address_hostname, 94 proxy().address.hostname(), 95 sizeof address_hostname); 96 97 IPAddress address_ip = proxy().address.ipaddr(); 98 99 uint16 address_port = proxy().address.port(); 100 101 char autoconfig_url[kSavedStringLimit]; 102 SaveStringToStack(autoconfig_url, 103 proxy().autoconfig_url, 104 sizeof autoconfig_url); 105 106 bool autodetect = proxy().autodetect; 107 108 char bypass_list[kSavedStringLimit]; 109 SaveStringToStack(bypass_list, proxy().bypass_list, sizeof bypass_list); 110 111 char username[kSavedStringLimit]; 112 SaveStringToStack(username, proxy().username, sizeof username); 113 114 SignalThread::OnMessage(msg); 115 116 // Log the gathered data at a log level that will never actually be enabled 117 // so that the compiler is forced to retain the data on the stack. 118 LOG(LS_SENSITIVE) << agent << " " << next << " " << type << " " 119 << address_hostname << " " << address_ip << " " 120 << address_port << " " << autoconfig_url << " " 121 << autodetect << " " << bypass_list << " " << username; 122 } 123} 124 125void AutoDetectProxy::OnResolveResult(AsyncResolverInterface* resolver) { 126 if (resolver != resolver_) { 127 return; 128 } 129 int error = resolver_->GetError(); 130 if (error == 0) { 131 LOG(LS_VERBOSE) << "Resolved " << proxy_.address << " to " 132 << resolver_->address(); 133 proxy_.address = resolver_->address(); 134 if (!DoConnect()) { 135 Thread::Current()->Post(this, MSG_TIMEOUT); 136 } 137 } else { 138 LOG(LS_INFO) << "Failed to resolve " << resolver_->address(); 139 resolver_->Destroy(false); 140 resolver_ = NULL; 141 proxy_.address = SocketAddress(); 142 Thread::Current()->Post(this, MSG_UNRESOLVABLE); 143 } 144} 145 146void AutoDetectProxy::Next() { 147 if (TEST_ORDER[next_] >= PROXY_UNKNOWN) { 148 Complete(PROXY_UNKNOWN); 149 return; 150 } 151 152 LOG(LS_VERBOSE) << "AutoDetectProxy connecting to " 153 << proxy_.address.ToSensitiveString(); 154 155 if (socket_) { 156 Thread::Current()->Clear(this, MSG_TIMEOUT); 157 Thread::Current()->Clear(this, MSG_UNRESOLVABLE); 158 socket_->Close(); 159 Thread::Current()->Dispose(socket_); 160 socket_ = NULL; 161 } 162 int timeout = 2000; 163 if (proxy_.address.IsUnresolvedIP()) { 164 // Launch an asyncresolver. This thread will spin waiting for it. 165 timeout += 2000; 166 if (!resolver_) { 167 resolver_ = new AsyncResolver(); 168 } 169 resolver_->SignalDone.connect(this, &AutoDetectProxy::OnResolveResult); 170 resolver_->Start(proxy_.address); 171 } else { 172 if (!DoConnect()) { 173 Thread::Current()->Post(this, MSG_TIMEOUT); 174 return; 175 } 176 } 177 Thread::Current()->PostDelayed(timeout, this, MSG_TIMEOUT); 178} 179 180bool AutoDetectProxy::DoConnect() { 181 if (resolver_) { 182 resolver_->Destroy(false); 183 resolver_ = NULL; 184 } 185 socket_ = 186 Thread::Current()->socketserver()->CreateAsyncSocket( 187 proxy_.address.family(), SOCK_STREAM); 188 if (!socket_) { 189 LOG(LS_VERBOSE) << "Unable to create socket for " << proxy_.address; 190 return false; 191 } 192 socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent); 193 socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent); 194 socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent); 195 socket_->Connect(proxy_.address); 196 return true; 197} 198 199void AutoDetectProxy::Complete(ProxyType type) { 200 Thread::Current()->Clear(this, MSG_TIMEOUT); 201 Thread::Current()->Clear(this, MSG_UNRESOLVABLE); 202 if (socket_) { 203 socket_->Close(); 204 } 205 206 proxy_.type = type; 207 LoggingSeverity sev = (proxy_.type == PROXY_UNKNOWN) ? LS_ERROR : LS_INFO; 208 LOG_V(sev) << "AutoDetectProxy detected " 209 << proxy_.address.ToSensitiveString() 210 << " as type " << proxy_.type; 211 212 Thread::Current()->Quit(); 213} 214 215void AutoDetectProxy::OnConnectEvent(AsyncSocket * socket) { 216 std::string probe; 217 218 switch (TEST_ORDER[next_]) { 219 case PROXY_HTTPS: 220 probe.assign("CONNECT www.google.com:443 HTTP/1.0\r\n" 221 "User-Agent: "); 222 probe.append(agent_); 223 probe.append("\r\n" 224 "Host: www.google.com\r\n" 225 "Content-Length: 0\r\n" 226 "Proxy-Connection: Keep-Alive\r\n" 227 "\r\n"); 228 break; 229 case PROXY_SOCKS5: 230 probe.assign("\005\001\000", 3); 231 break; 232 default: 233 ASSERT(false); 234 return; 235 } 236 237 LOG(LS_VERBOSE) << "AutoDetectProxy probing type " << TEST_ORDER[next_] 238 << " sending " << probe.size() << " bytes"; 239 socket_->Send(probe.data(), probe.size()); 240} 241 242void AutoDetectProxy::OnReadEvent(AsyncSocket * socket) { 243 char data[257]; 244 int len = socket_->Recv(data, 256); 245 if (len > 0) { 246 data[len] = 0; 247 LOG(LS_VERBOSE) << "AutoDetectProxy read " << len << " bytes"; 248 } 249 250 switch (TEST_ORDER[next_]) { 251 case PROXY_HTTPS: 252 if ((len >= 2) && (data[0] == '\x05')) { 253 Complete(PROXY_SOCKS5); 254 return; 255 } 256 if ((len >= 5) && (strncmp(data, "HTTP/", 5) == 0)) { 257 Complete(PROXY_HTTPS); 258 return; 259 } 260 break; 261 case PROXY_SOCKS5: 262 if ((len >= 2) && (data[0] == '\x05')) { 263 Complete(PROXY_SOCKS5); 264 return; 265 } 266 break; 267 default: 268 ASSERT(false); 269 return; 270 } 271 272 ++next_; 273 Next(); 274} 275 276void AutoDetectProxy::OnCloseEvent(AsyncSocket * socket, int error) { 277 LOG(LS_VERBOSE) << "AutoDetectProxy closed with error: " << error; 278 ++next_; 279 Next(); 280} 281 282} // namespace rtc 283