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