• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SaveStringToStack(char * dst,const std::string & src,size_t dst_size)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 
AutoDetectProxy(const std::string & user_agent)31 AutoDetectProxy::AutoDetectProxy(const std::string& user_agent)
32     : agent_(user_agent), resolver_(NULL), socket_(NULL), next_(0) {
33 }
34 
GetProxyForUrl(const char * agent,const char * url,rtc::ProxyInfo * proxy)35 bool AutoDetectProxy::GetProxyForUrl(const char* agent,
36                                      const char* url,
37                                      rtc::ProxyInfo* proxy) {
38   return GetProxySettingsForUrl(agent, url, proxy, true);
39 }
40 
~AutoDetectProxy()41 AutoDetectProxy::~AutoDetectProxy() {
42   if (resolver_) {
43     resolver_->Destroy(false);
44   }
45 }
46 
DoWork()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 
OnMessage(Message * msg)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 
OnResolveResult(AsyncResolverInterface * resolver)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 
Next()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 
DoConnect()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 
Complete(ProxyType type)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 
OnConnectEvent(AsyncSocket * socket)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 
OnReadEvent(AsyncSocket * socket)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 
OnCloseEvent(AsyncSocket * socket,int error)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