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