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