1 //
2 // Copyright (C) 2011 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "update_engine/chrome_browser_proxy_resolver.h"
18 
19 #include <deque>
20 #include <map>
21 #include <string>
22 #include <utility>
23 
24 #include <base/bind.h>
25 #include <base/strings/string_tokenizer.h>
26 #include <base/strings/string_util.h>
27 
28 #include "update_engine/common/utils.h"
29 
30 namespace chromeos_update_engine {
31 
32 using base::StringTokenizer;
33 using base::TimeDelta;
34 using brillo::MessageLoop;
35 using std::deque;
36 using std::make_pair;
37 using std::pair;
38 using std::string;
39 
40 const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
41 const char kLibCrosProxyResolveName[] = "ProxyResolved";
42 const char kLibCrosProxyResolveSignalInterface[] =
43     "org.chromium.UpdateEngineLibcrosProxyResolvedInterface";
44 
45 namespace {
46 
47 const int kTimeout = 5;  // seconds
48 
49 }  // namespace
50 
ChromeBrowserProxyResolver(LibCrosProxy * libcros_proxy)51 ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
52     LibCrosProxy* libcros_proxy)
53     : libcros_proxy_(libcros_proxy), timeout_(kTimeout) {}
54 
Init()55 bool ChromeBrowserProxyResolver::Init() {
56   libcros_proxy_->ue_proxy_resolved_interface()
57       ->RegisterProxyResolvedSignalHandler(
58           base::Bind(&ChromeBrowserProxyResolver::OnProxyResolvedSignal,
59                      base::Unretained(this)),
60           base::Bind(&ChromeBrowserProxyResolver::OnSignalConnected,
61                      base::Unretained(this)));
62   return true;
63 }
64 
~ChromeBrowserProxyResolver()65 ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
66   // Kill outstanding timers.
67   for (auto& timer : timers_) {
68     MessageLoop::current()->CancelTask(timer.second);
69     timer.second = MessageLoop::kTaskIdNull;
70   }
71 }
72 
GetProxiesForUrl(const string & url,ProxiesResolvedFn callback,void * data)73 bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
74                                                   ProxiesResolvedFn callback,
75                                                   void* data) {
76   int timeout = timeout_;
77   brillo::ErrorPtr error;
78   if (!libcros_proxy_->service_interface_proxy()->ResolveNetworkProxy(
79           url.c_str(),
80           kLibCrosProxyResolveSignalInterface,
81           kLibCrosProxyResolveName,
82           &error)) {
83     LOG(WARNING) << "Can't resolve the proxy. Continuing with no proxy.";
84     timeout = 0;
85   }
86 
87   callbacks_.insert(make_pair(url, make_pair(callback, data)));
88   MessageLoop::TaskId timer = MessageLoop::current()->PostDelayedTask(
89       FROM_HERE,
90       base::Bind(&ChromeBrowserProxyResolver::HandleTimeout,
91                  base::Unretained(this),
92                  url),
93       TimeDelta::FromSeconds(timeout));
94   timers_.insert(make_pair(url, timer));
95   return true;
96 }
97 
DeleteUrlState(const string & source_url,bool delete_timer,pair<ProxiesResolvedFn,void * > * callback)98 bool ChromeBrowserProxyResolver::DeleteUrlState(
99     const string& source_url,
100     bool delete_timer,
101     pair<ProxiesResolvedFn, void*>* callback) {
102   {
103     CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
104     TEST_AND_RETURN_FALSE(it != callbacks_.end());
105     TEST_AND_RETURN_FALSE(it->first == source_url);
106     if (callback)
107       *callback = it->second;
108     callbacks_.erase(it);
109   }
110   {
111     TimeoutsMap::iterator it = timers_.lower_bound(source_url);
112     TEST_AND_RETURN_FALSE(it != timers_.end());
113     TEST_AND_RETURN_FALSE(it->first == source_url);
114     if (delete_timer)
115       MessageLoop::current()->CancelTask(it->second);
116     timers_.erase(it);
117   }
118   return true;
119 }
120 
OnSignalConnected(const string & interface_name,const string & signal_name,bool successful)121 void ChromeBrowserProxyResolver::OnSignalConnected(const string& interface_name,
122                                                    const string& signal_name,
123                                                    bool successful) {
124   if (!successful) {
125     LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
126                << signal_name;
127   }
128 }
129 
OnProxyResolvedSignal(const string & source_url,const string & proxy_info,const string & error_message)130 void ChromeBrowserProxyResolver::OnProxyResolvedSignal(
131     const string& source_url,
132     const string& proxy_info,
133     const string& error_message) {
134   pair<ProxiesResolvedFn, void*> callback;
135   TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
136   if (!error_message.empty()) {
137     LOG(WARNING) << "ProxyResolved error: " << error_message;
138   }
139   (*callback.first)(ParseProxyString(proxy_info), callback.second);
140 }
141 
HandleTimeout(string source_url)142 void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
143   LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
144   pair<ProxiesResolvedFn, void*> callback;
145   TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
146   deque<string> proxies;
147   proxies.push_back(kNoProxy);
148   (*callback.first)(proxies, callback.second);
149 }
150 
ParseProxyString(const string & input)151 deque<string> ChromeBrowserProxyResolver::ParseProxyString(
152     const string& input) {
153   deque<string> ret;
154   // Some of this code taken from
155   // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
156   // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
157   StringTokenizer entry_tok(input, ";");
158   while (entry_tok.GetNext()) {
159     string token = entry_tok.token();
160     base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
161 
162     // Start by finding the first space (if any).
163     string::iterator space;
164     for (space = token.begin(); space != token.end(); ++space) {
165       if (base::IsAsciiWhitespace(*space)) {
166         break;
167       }
168     }
169 
170     string scheme = base::ToLowerASCII(string(token.begin(), space));
171     // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
172     if (scheme == "socks")
173       scheme += "4";
174     else if (scheme == "proxy")
175       scheme = "http";
176     else if (scheme != "https" &&
177              scheme != "socks4" &&
178              scheme != "socks5" &&
179              scheme != "direct")
180       continue;  // Invalid proxy scheme
181 
182     string host_and_port = string(space, token.end());
183     base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
184     if (scheme != "direct" && host_and_port.empty())
185       continue;  // Must supply host/port when non-direct proxy used.
186     ret.push_back(scheme + "://" + host_and_port);
187   }
188   if (ret.empty() || *ret.rbegin() != kNoProxy)
189     ret.push_back(kNoProxy);
190   return ret;
191 }
192 
193 }  // namespace chromeos_update_engine
194