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