1 /* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/base/autodetectproxy.h" 29 #include "talk/base/httpcommon.h" 30 #include "talk/base/httpcommon-inl.h" 31 #include "talk/base/proxydetect.h" 32 33 namespace talk_base { 34 35 enum { MSG_TIMEOUT = SignalThread::ST_MSG_FIRST_AVAILABLE }; 36 37 static const ProxyType TEST_ORDER[] = { 38 PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN 39 }; 40 41 AutoDetectProxy::AutoDetectProxy(const std::string& user_agent) 42 : agent_(user_agent), socket_(NULL), next_(0) { 43 } 44 45 AutoDetectProxy::~AutoDetectProxy() { 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 GetProxySettingsForUrl(agent_.c_str(), server_url_.c_str(), proxy_, true); 53 LOG(LS_INFO) << "GetProxySettingsForUrl - stop"; 54 } 55 Url<char> url(proxy_.address.IPAsString()); 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(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_TIMEOUT == msg->message_id) { 76 OnCloseEvent(socket_, ETIMEDOUT); 77 } else { 78 SignalThread::OnMessage(msg); 79 } 80 } 81 82 void AutoDetectProxy::Next() { 83 if (TEST_ORDER[next_] >= PROXY_UNKNOWN) { 84 Complete(PROXY_UNKNOWN); 85 return; 86 } 87 88 LOG(LS_VERBOSE) << "AutoDetectProxy connecting to " 89 << proxy_.address.ToString(); 90 91 if (socket_) { 92 Thread::Current()->Clear(this, MSG_TIMEOUT); 93 socket_->Close(); 94 Thread::Current()->Dispose(socket_); 95 socket_ = NULL; 96 } 97 98 socket_ = Thread::Current()->socketserver()->CreateAsyncSocket(SOCK_STREAM); 99 socket_->SignalConnectEvent.connect(this, &AutoDetectProxy::OnConnectEvent); 100 socket_->SignalReadEvent.connect(this, &AutoDetectProxy::OnReadEvent); 101 socket_->SignalCloseEvent.connect(this, &AutoDetectProxy::OnCloseEvent); 102 socket_->Connect(proxy_.address); 103 104 // Timeout after 2 seconds 105 Thread::Current()->PostDelayed(2000, this, MSG_TIMEOUT); 106 } 107 108 void AutoDetectProxy::Complete(ProxyType type) { 109 Thread::Current()->Clear(this, MSG_TIMEOUT); 110 socket_->Close(); 111 112 proxy_.type = type; 113 LoggingSeverity sev = (proxy_.type == PROXY_UNKNOWN) ? LS_ERROR : LS_INFO; 114 LOG_V(sev) << "AutoDetectProxy detected " << proxy_.address.ToString() 115 << " as type " << proxy_.type; 116 117 Thread::Current()->Quit(); 118 } 119 120 void AutoDetectProxy::OnConnectEvent(AsyncSocket * socket) { 121 std::string probe; 122 123 switch (TEST_ORDER[next_]) { 124 case PROXY_HTTPS: 125 probe.assign("CONNECT www.google.com:443 HTTP/1.0\r\n" 126 "User-Agent: "); 127 probe.append(agent_); 128 probe.append("\r\n" 129 "Host: www.google.com\r\n" 130 "Content-Length: 0\r\n" 131 "Proxy-Connection: Keep-Alive\r\n" 132 "\r\n"); 133 break; 134 case PROXY_SOCKS5: 135 probe.assign("\005\001\000", 3); 136 break; 137 default: 138 ASSERT(false); 139 return; 140 } 141 142 LOG(LS_VERBOSE) << "AutoDetectProxy probing type " << TEST_ORDER[next_] 143 << " sending " << probe.size() << " bytes"; 144 socket_->Send(probe.data(), probe.size()); 145 } 146 147 void AutoDetectProxy::OnReadEvent(AsyncSocket * socket) { 148 char data[257]; 149 int len = socket_->Recv(data, 256); 150 if (len > 0) { 151 data[len] = 0; 152 LOG(LS_VERBOSE) << "AutoDetectProxy read " << len << " bytes"; 153 } 154 155 switch (TEST_ORDER[next_]) { 156 case PROXY_HTTPS: 157 if ((len >= 2) && (data[0] == '\x05')) { 158 Complete(PROXY_SOCKS5); 159 return; 160 } 161 if ((len >= 5) && (strncmp(data, "HTTP/", 5) == 0)) { 162 Complete(PROXY_HTTPS); 163 return; 164 } 165 break; 166 case PROXY_SOCKS5: 167 if ((len >= 2) && (data[0] == '\x05')) { 168 Complete(PROXY_SOCKS5); 169 return; 170 } 171 break; 172 default: 173 ASSERT(false); 174 return; 175 } 176 177 ++next_; 178 Next(); 179 } 180 181 void AutoDetectProxy::OnCloseEvent(AsyncSocket * socket, int error) { 182 LOG(LS_VERBOSE) << "AutoDetectProxy closed with error: " << error; 183 ++next_; 184 Next(); 185 } 186 187 } // namespace talk_base 188