Home | History | Annotate | Download | only in base
      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 AutoDetectProxy::~AutoDetectProxy() {
     36   if (resolver_) {
     37     resolver_->Destroy(false);
     38   }
     39 }
     40 
     41 void 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 
     67 void 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 
    125 void 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 
    146 void 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 
    180 bool 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 
    199 void 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 
    215 void 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 
    242 void 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 
    276 void 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