| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 // TODO(omaha): move EnclosePath and UnenclosePath functions from path.h to | |
| 17 // string.h | |
| 18 // TODO(omaha): Firefox detector does not handle proxy bypass. | |
| 19 | |
| 20 #include "omaha/net/detector.h" | |
| 21 | |
| 22 #include "base/scoped_ptr.h" | |
| 23 #include "omaha/base/atl_regexp.h" | |
| 24 #include "omaha/base/browser_utils.h" | |
| 25 #include "omaha/base/constants.h" | |
| 26 #include "omaha/base/debug.h" | |
| 27 #include "omaha/base/file_reader.h" | |
| 28 #include "omaha/base/path.h" | |
| 29 #include "omaha/base/reg_key.h" | |
| 30 #include "omaha/base/safe_format.h" | |
| 31 #include "omaha/base/string.h" | |
| 32 #include "omaha/base/user_info.h" | |
| 33 #include "omaha/base/utils.h" | |
| 34 #include "omaha/base/time.h" | |
| 35 #include "omaha/net/http_client.h" | |
| 36 #include "omaha/net/network_config.h" | |
| 37 | |
| 38 namespace omaha { | |
| 39 | |
| 40 HRESULT RegistryOverrideProxyDetector::Detect(ProxyConfig* config) { | |
| 41 ASSERT1(config); | |
| 42 RegKey reg_key; | |
| 43 HRESULT hr = reg_key.Open(reg_path_, KEY_READ); | |
| 44 if (FAILED(hr)) { | |
| 45 return hr; | |
| 46 } | |
| 47 CString proxy_host; | |
| 48 hr = reg_key.GetValue(kRegValueProxyHost, &proxy_host); | |
| 49 if (FAILED(hr)) { | |
| 50 return hr; | |
| 51 } | |
| 52 DWORD proxy_port(0); | |
| 53 hr = reg_key.GetValue(kRegValueProxyPort, &proxy_port); | |
| 54 if (FAILED(hr)) { | |
| 55 return hr; | |
| 56 } | |
| 57 *config = ProxyConfig(); | |
| 58 SafeCStringFormat(&config->proxy, _T("%s:%d"), proxy_host, proxy_port); | |
| 59 config->source = source(); | |
| 60 config->priority = ProxyConfig::PROXY_PRIORITY_OVERRIDE; | |
| 61 return S_OK; | |
| 62 } | |
| 63 | |
| 64 UpdateDevProxyDetector::UpdateDevProxyDetector() | |
| 65 : registry_detector_(MACHINE_REG_UPDATE_DEV) { | |
| 66 } | |
| 67 | |
| 68 FirefoxProxyDetector::FirefoxProxyDetector() | |
| 69 : cached_prefs_last_modified_(0), | |
| 70 cached_config_(new ProxyConfig) { | |
| 71 } | |
| 72 | |
| 73 HRESULT FirefoxProxyDetector::Detect(ProxyConfig* config) { | |
| 74 ASSERT1(config); | |
| 75 | |
| 76 // The Firefox profile is not available when running as a local system. | |
| 77 if (user_info::IsRunningAsSystem()) { | |
| 78 return E_FAIL; | |
| 79 } | |
| 80 | |
| 81 const TCHAR* const kFirefoxPrefsJsFile = _T("\\prefs.js"); | |
| 82 | |
| 83 CString name, path; | |
| 84 HRESULT hr = GetFirefoxDefaultProfile(&name, &path); | |
| 85 if (FAILED(hr)) { | |
| 86 return hr; | |
| 87 } | |
| 88 path.Append(kFirefoxPrefsJsFile); | |
| 89 | |
| 90 // Has the current profile been modified? Check the name, path, and | |
| 91 // last modified time of the profile are the same as their cached values. | |
| 92 FILETIME filetime_last_modified = {0}; | |
| 93 int64 last_modified = 0; | |
| 94 if (SUCCEEDED(File::GetFileTime(path, | |
| 95 NULL, | |
| 96 NULL, | |
| 97 &filetime_last_modified))) { | |
| 98 last_modified = FileTimeToInt64(filetime_last_modified); | |
| 99 } | |
| 100 if (name.CompareNoCase(cached_prefs_name_) == 0 && | |
| 101 path.CompareNoCase(cached_prefs_file_path_) == 0 && | |
| 102 last_modified == cached_prefs_last_modified_ && | |
| 103 last_modified) { | |
| 104 NET_LOG(L4, (_T("[using FF cached profile][%s]"), path)); | |
| 105 *config = *cached_config_; | |
| 106 return S_OK; | |
| 107 } | |
| 108 | |
| 109 hr = ParsePrefsFile(name, path, config); | |
| 110 if (SUCCEEDED(hr) && last_modified) { | |
| 111 // If FireFox is the default brower, promotes its proxy priority. | |
| 112 BrowserType browser_type(BROWSER_UNKNOWN); | |
| 113 if (SUCCEEDED(GetDefaultBrowserType(&browser_type)) && | |
| 114 browser_type == BROWSER_FIREFOX) { | |
| 115 config->priority = ProxyConfig::PROXY_PRIORITY_DEFAULT_BROWSER; | |
| 116 } | |
| 117 NET_LOG(L4, (_T("[cache FF profile][%s]"), path)); | |
| 118 cached_prefs_name_ = name; | |
| 119 cached_prefs_file_path_ = path; | |
| 120 cached_prefs_last_modified_ = last_modified; | |
| 121 *cached_config_ = *config; | |
| 122 } | |
| 123 return hr; | |
| 124 } | |
| 125 | |
| 126 // This is what the proxy configuration in Firefox looks like: | |
| 127 // user_pref("network.proxy.autoconfig_url", "http://wpad/wpad.dat"); | |
| 128 // user_pref("network.proxy.ftp", "127.0.0.1"); | |
| 129 // user_pref("network.proxy.ftp_port", 8888); | |
| 130 // user_pref("network.proxy.gopher", "127.0.0.1"); | |
| 131 // user_pref("network.proxy.gopher_port", 8888); | |
| 132 // user_pref("network.proxy.http", "127.0.0.1"); | |
| 133 // user_pref("network.proxy.http_port", 8888); | |
| 134 // user_pref("network.proxy.share_proxy_settings", true); | |
| 135 // user_pref("network.proxy.socks", "127.0.0.1"); | |
| 136 // user_pref("network.proxy.socks_port", 8888); | |
| 137 // user_pref("network.proxy.ssl", "127.0.0.1"); | |
| 138 // user_pref("network.proxy.ssl_port", 8888); | |
| 139 // user_pref("network.proxy.type", 4); | |
| 140 HRESULT FirefoxProxyDetector::ParsePrefsFile(const TCHAR* name, | |
| 141 const TCHAR* file_path, | |
| 142 ProxyConfig* config) { | |
| 143 ASSERT1(name); | |
| 144 ASSERT1(file_path); | |
| 145 ASSERT1(config); | |
| 146 | |
| 147 *config = ProxyConfig(); | |
| 148 config->source = source(); | |
| 149 | |
| 150 // TODO(omaha): implement optimization not to parse the file again if it | |
| 151 // did not change. | |
| 152 UNREFERENCED_PARAMETER(name); | |
| 153 | |
| 154 // There were issues in production where the code fails to allocate | |
| 155 // the 1MB memory buffer as it had been initially requested by a previous | |
| 156 // version of the code. | |
| 157 // | |
| 158 // The assert below is somehow flaky but useful to detect the unlikely cases | |
| 159 // when the prefs file can't be opened. | |
| 160 FileReader prefs_file; | |
| 161 const size_t kBufferSize = 0x10000; // 64KB buffer. | |
| 162 HRESULT hr = prefs_file.Init(file_path, kBufferSize); | |
| 163 ASSERT1(SUCCEEDED(hr)); | |
| 164 if (FAILED(hr)) { | |
| 165 return hr; | |
| 166 } | |
| 167 | |
| 168 CString proxy_type; | |
| 169 CString proxy_config_url; | |
| 170 CString proxy_http_host; | |
| 171 CString proxy_http_port; | |
| 172 CString proxy_ssl_host; | |
| 173 CString proxy_ssl_port; | |
| 174 | |
| 175 // For each line in the prefs.js, try to parse the proxy information out. | |
| 176 char line[1024] = {0}; | |
| 177 while (SUCCEEDED(prefs_file.ReadLineAnsi(arraysize(line), line))) { | |
| 178 ParsePrefsLine(line, | |
| 179 &proxy_type, | |
| 180 &proxy_config_url, | |
| 181 &proxy_http_host, | |
| 182 &proxy_http_port, | |
| 183 &proxy_ssl_host, | |
| 184 &proxy_ssl_port); | |
| 185 } | |
| 186 | |
| 187 // The default in FireFox is direct connection so it may be that the | |
| 188 // network.proxy.type is missing. | |
| 189 int type = PROXY_TYPE_NO_PROXY; | |
| 190 if (!proxy_type.IsEmpty() && | |
| 191 !String_StringToDecimalIntChecked(proxy_type, &type)) { | |
| 192 return E_UNEXPECTED; | |
| 193 } | |
| 194 | |
| 195 // Direct connection. | |
| 196 if (type == PROXY_TYPE_NO_PROXY) { | |
| 197 return S_OK; | |
| 198 } | |
| 199 | |
| 200 // We look for both proxy auto-detect and proxy config url, to emulate | |
| 201 // the IE behavior, where when the auto-detect fails it defaults to the | |
| 202 // auto config url. Firefox remembers the auto config url even if not used, | |
| 203 // so it might not hurt to try it out. | |
| 204 if (type & PROXY_TYPE_AUTO_DETECT) { | |
| 205 config->auto_detect = true; | |
| 206 } | |
| 207 if ((type & PROXY_TYPE_AUTO_CONFIG_URL) && !proxy_config_url.IsEmpty()) { | |
| 208 UnenclosePath(&proxy_config_url); | |
| 209 config->auto_config_url = proxy_config_url; | |
| 210 } | |
| 211 | |
| 212 // Named proxy. | |
| 213 if (!(type & PROXY_TYPE_NAMED_PROXY)) { | |
| 214 return S_OK; | |
| 215 } | |
| 216 | |
| 217 CString proxy; | |
| 218 hr = BuildProxyString(proxy_http_host, | |
| 219 proxy_http_port, | |
| 220 proxy_ssl_host, | |
| 221 proxy_ssl_port, | |
| 222 &proxy); | |
| 223 if (FAILED(hr)) { | |
| 224 return hr; | |
| 225 } | |
| 226 | |
| 227 config->proxy = proxy; | |
| 228 return S_OK; | |
| 229 } | |
| 230 | |
| 231 HRESULT FirefoxProxyDetector::BuildProxyString(const CString& proxy_http_host, | |
| 232 const CString& http_port, | |
| 233 const CString& proxy_ssl_host, | |
| 234 const CString& ssl_port, | |
| 235 CString* proxy) { | |
| 236 ASSERT1(proxy); | |
| 237 | |
| 238 CString http_host = proxy_http_host; | |
| 239 CString ssl_host = proxy_ssl_host; | |
| 240 | |
| 241 // The host names in the prefs file are strings literals. | |
| 242 UnenclosePath(&http_host); | |
| 243 UnenclosePath(&ssl_host); | |
| 244 | |
| 245 // Validate the port values. | |
| 246 if (!http_port.IsEmpty()) { | |
| 247 int http_port_num = 0; | |
| 248 if (!String_StringToDecimalIntChecked(http_port, &http_port_num) || | |
| 249 http_port_num <= 0 && | |
| 250 http_port_num > INTERNET_MAX_PORT_NUMBER_VALUE) { | |
| 251 return E_INVALIDARG; | |
| 252 } | |
| 253 } | |
| 254 if (!ssl_port.IsEmpty()) { | |
| 255 int ssl_port_num = 0; | |
| 256 if (!String_StringToDecimalIntChecked(ssl_port, &ssl_port_num) || | |
| 257 ssl_port_num <= 0 || | |
| 258 ssl_port_num > INTERNET_MAX_PORT_NUMBER_VALUE) { | |
| 259 return E_INVALIDARG; | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 // Format the proxy string. | |
| 264 CString str; | |
| 265 if (!http_host.IsEmpty()) { | |
| 266 SafeCStringAppendFormat(&str, _T("http=%s"), http_host); | |
| 267 if (!http_port.IsEmpty()) { | |
| 268 SafeCStringAppendFormat(&str, _T(":%s"), http_port); | |
| 269 } | |
| 270 } | |
| 271 if (!ssl_host.IsEmpty()) { | |
| 272 // Append a separator if needed. | |
| 273 if (!str.IsEmpty()) { | |
| 274 str += _T(';'); | |
| 275 } | |
| 276 SafeCStringAppendFormat(&str, _T("https=%s"), ssl_host); | |
| 277 if (!ssl_port.IsEmpty()) { | |
| 278 SafeCStringAppendFormat(&str, _T(":%s"), ssl_port); | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 *proxy = str; | |
| 283 return S_OK; | |
| 284 } | |
| 285 | |
| 286 // Parses a line from the prefs.js. An example of line to parse is: | |
| 287 // user_pref("network.proxy.http", "foo"); | |
| 288 void FirefoxProxyDetector::ParsePrefsLine(const char* ansi_line, | |
| 289 CString* proxy_type, | |
| 290 CString* proxy_config_url, | |
| 291 CString* proxy_http_host, | |
| 292 CString* proxy_http_port, | |
| 293 CString* proxy_ssl_host, | |
| 294 CString* proxy_ssl_port) { | |
| 295 // Skip the lines that do not contain "network.proxy" to speed up the | |
| 296 // parsing. This is important for large prefs files. | |
| 297 if (strstr(ansi_line, "network.proxy.") == NULL) { | |
| 298 return; | |
| 299 } | |
| 300 | |
| 301 AtlRE proxy_type_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.type
\\\"\\b*,\\b*{\\d+}\\)"), false); // NOLINT | |
| 302 AtlRE proxy_config_url_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\
\.autoconfig_url\\\"\\b*,\\b*{\\q}\\)"), false); // NOLINT | |
| 303 AtlRE proxy_http_host_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\
.http\\\"\\b*,\\b*{\\q}\\)"), false); // NOLINT | |
| 304 AtlRE proxy_http_port_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\
.http_port\\\"\\b*,\\b*{\\d+}\\)"), false); // NOLINT | |
| 305 AtlRE proxy_ssl_host_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.
ssl\\\"\\b*,\\b*{\\q}\\)"), false); // NOLINT | |
| 306 AtlRE proxy_ssl_port_regex(_T("^\\b*user_pref\\b*\\(\\b*\\\"network\\.proxy\\.
ssl_port\\\"\\b*,\\b*{\\d+}\\)"), false); // NOLINT | |
| 307 | |
| 308 CString line(ansi_line); | |
| 309 if (AtlRE::PartialMatch(line, proxy_type_regex, proxy_type)) { | |
| 310 return; | |
| 311 } | |
| 312 if (AtlRE::PartialMatch(line, proxy_config_url_regex, proxy_config_url)) { | |
| 313 return; | |
| 314 } | |
| 315 if (AtlRE::PartialMatch(line, proxy_http_host_regex, proxy_http_host)) { | |
| 316 return; | |
| 317 } | |
| 318 if (AtlRE::PartialMatch(line, proxy_http_port_regex, proxy_http_port)) { | |
| 319 return; | |
| 320 } | |
| 321 if (AtlRE::PartialMatch(line, proxy_ssl_host_regex, proxy_ssl_host)) { | |
| 322 return; | |
| 323 } | |
| 324 if (AtlRE::PartialMatch(line, proxy_ssl_port_regex, proxy_ssl_port)) { | |
| 325 return; | |
| 326 } | |
| 327 } | |
| 328 | |
| 329 HRESULT DefaultProxyDetector::Detect(ProxyConfig* config) { | |
| 330 ASSERT1(config); | |
| 331 | |
| 332 scoped_ptr<HttpClient> http_client(CreateHttpClient()); | |
| 333 | |
| 334 // We expect to be able to instantiate either of the http clients. | |
| 335 ASSERT1(http_client.get()); | |
| 336 if (!http_client.get()) { | |
| 337 return E_UNEXPECTED; | |
| 338 } | |
| 339 HRESULT hr = http_client->Initialize(); | |
| 340 if (FAILED(hr)) { | |
| 341 return hr; | |
| 342 } | |
| 343 HttpClient::ProxyInfo proxy_info = {0}; | |
| 344 hr = http_client->GetDefaultProxyConfiguration(&proxy_info); | |
| 345 if (FAILED(hr)) { | |
| 346 return hr; | |
| 347 } | |
| 348 if (proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) { | |
| 349 ProxyConfig proxy_config; | |
| 350 proxy_config.source = source(); | |
| 351 proxy_config.proxy = proxy_info.proxy; | |
| 352 proxy_config.proxy_bypass = proxy_info.proxy_bypass; | |
| 353 *config = proxy_config; | |
| 354 return S_OK; | |
| 355 } else { | |
| 356 return E_FAIL; | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 HRESULT IEProxyDetector::Detect(ProxyConfig* config) { | |
| 361 ASSERT1(config); | |
| 362 | |
| 363 // Internet Explorer proxy configuration is not available when running as | |
| 364 // local system. | |
| 365 if (user_info::IsRunningAsSystem()) { | |
| 366 return E_FAIL; | |
| 367 } | |
| 368 | |
| 369 scoped_ptr<HttpClient> http_client(CreateHttpClient()); | |
| 370 | |
| 371 // We expect to be able to instantiate either of the http clients. | |
| 372 ASSERT1(http_client.get()); | |
| 373 if (!http_client.get()) { | |
| 374 return E_UNEXPECTED; | |
| 375 } | |
| 376 HRESULT hr = http_client->Initialize(); | |
| 377 if (FAILED(hr)) { | |
| 378 return hr; | |
| 379 } | |
| 380 HttpClient::CurrentUserIEProxyConfig ie_proxy_config = {0}; | |
| 381 hr = http_client->GetIEProxyConfiguration(&ie_proxy_config); | |
| 382 if (FAILED(hr)) { | |
| 383 return hr; | |
| 384 } | |
| 385 config->source = source(); | |
| 386 config->auto_detect = ie_proxy_config.auto_detect; | |
| 387 config->auto_config_url = ie_proxy_config.auto_config_url; | |
| 388 config->proxy = ie_proxy_config.proxy; | |
| 389 config->proxy_bypass = ie_proxy_config.proxy_bypass; | |
| 390 | |
| 391 // If IE is the default brower, promotes its proxy priority. | |
| 392 BrowserType browser_type(BROWSER_UNKNOWN); | |
| 393 if (SUCCEEDED(GetDefaultBrowserType(&browser_type)) && | |
| 394 browser_type == BROWSER_IE) { | |
| 395 config->priority = ProxyConfig::PROXY_PRIORITY_DEFAULT_BROWSER; | |
| 396 } | |
| 397 | |
| 398 ::GlobalFree(const_cast<TCHAR*>(ie_proxy_config.auto_config_url)); | |
| 399 ::GlobalFree(const_cast<TCHAR*>(ie_proxy_config.proxy)); | |
| 400 ::GlobalFree(const_cast<TCHAR*>(ie_proxy_config.proxy_bypass)); | |
| 401 | |
| 402 return S_OK; | |
| 403 }; | |
| 404 | |
| 405 } // namespace omaha | |
| 406 | |
| OLD | NEW |