OLD | NEW |
| (Empty) |
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/proxydetect.h" | |
12 | |
13 #if defined(WEBRTC_WIN) | |
14 #include "webrtc/base/win32.h" | |
15 #include <shlobj.h> | |
16 #endif // WEBRTC_WIN | |
17 | |
18 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | |
19 #include <SystemConfiguration/SystemConfiguration.h> | |
20 #include <CoreFoundation/CoreFoundation.h> | |
21 #include <CoreServices/CoreServices.h> | |
22 #include <Security/Security.h> | |
23 #include "macconversion.h" | |
24 #include "webrtc/base/unixfilesystem.h" | |
25 #endif | |
26 | |
27 #ifdef WEBRTC_IOS | |
28 #include <CFNetwork/CFNetwork.h> | |
29 #include "macconversion.h" | |
30 #endif | |
31 | |
32 #include <map> | |
33 #include <memory> | |
34 | |
35 #include "webrtc/base/arraysize.h" | |
36 #include "webrtc/base/checks.h" | |
37 #include "webrtc/base/common.h" | |
38 #include "webrtc/base/fileutils.h" | |
39 #include "webrtc/base/httpcommon.h" | |
40 #include "webrtc/base/httpcommon-inl.h" | |
41 #include "webrtc/base/pathutils.h" | |
42 #include "webrtc/base/stringutils.h" | |
43 | |
44 #define _TRY_JSPROXY 0 | |
45 #define _TRY_WM_FINDPROXY 0 | |
46 | |
47 #if defined(WEBRTC_WIN) | |
48 #define _TRY_WINHTTP 1 | |
49 #define _TRY_IE_LAN_SETTINGS 1 | |
50 #else | |
51 #define _TRY_WINHTTP 0 | |
52 #define _TRY_IE_LAN_SETTINGS 0 | |
53 #endif // WEBRTC_WIN | |
54 | |
55 // For all platforms try Firefox. | |
56 #define _TRY_FIREFOX 1 | |
57 | |
58 // Use profiles.ini to find the correct profile for this user. | |
59 // If not set, we'll just look for the default one. | |
60 #define USE_FIREFOX_PROFILES_INI 1 | |
61 | |
62 static const size_t kMaxLineLength = 1024; | |
63 static const char kFirefoxPattern[] = "Firefox"; | |
64 static const char kInternetExplorerPattern[] = "MSIE"; | |
65 | |
66 struct StringMap { | |
67 public: | |
68 void Add(const char * name, const char * value) { map_[name] = value; } | |
69 const std::string& Get(const char * name, const char * def = "") const { | |
70 std::map<std::string, std::string>::const_iterator it = | |
71 map_.find(name); | |
72 if (it != map_.end()) | |
73 return it->second; | |
74 def_ = def; | |
75 return def_; | |
76 } | |
77 bool IsSet(const char * name) const { | |
78 return (map_.find(name) != map_.end()); | |
79 } | |
80 private: | |
81 std::map<std::string, std::string> map_; | |
82 mutable std::string def_; | |
83 }; | |
84 | |
85 enum UserAgent { | |
86 UA_FIREFOX, | |
87 UA_INTERNETEXPLORER, | |
88 UA_OTHER, | |
89 UA_UNKNOWN | |
90 }; | |
91 | |
92 #if _TRY_WINHTTP | |
93 //#include <winhttp.h> | |
94 // Note: From winhttp.h | |
95 | |
96 const char WINHTTP[] = "winhttp"; | |
97 | |
98 typedef LPVOID HINTERNET; | |
99 | |
100 typedef struct { | |
101 DWORD dwAccessType; // see WINHTTP_ACCESS_* types below | |
102 LPWSTR lpszProxy; // proxy server list | |
103 LPWSTR lpszProxyBypass; // proxy bypass list | |
104 } WINHTTP_PROXY_INFO, * LPWINHTTP_PROXY_INFO; | |
105 | |
106 typedef struct { | |
107 DWORD dwFlags; | |
108 DWORD dwAutoDetectFlags; | |
109 LPCWSTR lpszAutoConfigUrl; | |
110 LPVOID lpvReserved; | |
111 DWORD dwReserved; | |
112 BOOL fAutoLogonIfChallenged; | |
113 } WINHTTP_AUTOPROXY_OPTIONS; | |
114 | |
115 typedef struct { | |
116 BOOL fAutoDetect; | |
117 LPWSTR lpszAutoConfigUrl; | |
118 LPWSTR lpszProxy; | |
119 LPWSTR lpszProxyBypass; | |
120 } WINHTTP_CURRENT_USER_IE_PROXY_CONFIG; | |
121 | |
122 extern "C" { | |
123 typedef HINTERNET (WINAPI * pfnWinHttpOpen) | |
124 ( | |
125 IN LPCWSTR pwszUserAgent, | |
126 IN DWORD dwAccessType, | |
127 IN LPCWSTR pwszProxyName OPTIONAL, | |
128 IN LPCWSTR pwszProxyBypass OPTIONAL, | |
129 IN DWORD dwFlags | |
130 ); | |
131 typedef BOOL (STDAPICALLTYPE * pfnWinHttpCloseHandle) | |
132 ( | |
133 IN HINTERNET hInternet | |
134 ); | |
135 typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetProxyForUrl) | |
136 ( | |
137 IN HINTERNET hSession, | |
138 IN LPCWSTR lpcwszUrl, | |
139 IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, | |
140 OUT WINHTTP_PROXY_INFO * pProxyInfo | |
141 ); | |
142 typedef BOOL (STDAPICALLTYPE * pfnWinHttpGetIEProxyConfig) | |
143 ( | |
144 IN OUT WINHTTP_CURRENT_USER_IE_PROXY_CONFIG * pProxyConfig | |
145 ); | |
146 | |
147 } // extern "C" | |
148 | |
149 #define WINHTTP_AUTOPROXY_AUTO_DETECT 0x00000001 | |
150 #define WINHTTP_AUTOPROXY_CONFIG_URL 0x00000002 | |
151 #define WINHTTP_AUTOPROXY_RUN_INPROCESS 0x00010000 | |
152 #define WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY 0x00020000 | |
153 #define WINHTTP_AUTO_DETECT_TYPE_DHCP 0x00000001 | |
154 #define WINHTTP_AUTO_DETECT_TYPE_DNS_A 0x00000002 | |
155 #define WINHTTP_ACCESS_TYPE_DEFAULT_PROXY 0 | |
156 #define WINHTTP_ACCESS_TYPE_NO_PROXY 1 | |
157 #define WINHTTP_ACCESS_TYPE_NAMED_PROXY 3 | |
158 #define WINHTTP_NO_PROXY_NAME NULL | |
159 #define WINHTTP_NO_PROXY_BYPASS NULL | |
160 | |
161 #endif // _TRY_WINHTTP | |
162 | |
163 #if _TRY_JSPROXY | |
164 extern "C" { | |
165 typedef BOOL (STDAPICALLTYPE * pfnInternetGetProxyInfo) | |
166 ( | |
167 LPCSTR lpszUrl, | |
168 DWORD dwUrlLength, | |
169 LPSTR lpszUrlHostName, | |
170 DWORD dwUrlHostNameLength, | |
171 LPSTR * lplpszProxyHostName, | |
172 LPDWORD lpdwProxyHostNameLength | |
173 ); | |
174 } // extern "C" | |
175 #endif // _TRY_JSPROXY | |
176 | |
177 #if _TRY_WM_FINDPROXY | |
178 #include <comutil.h> | |
179 #include <wmnetsourcecreator.h> | |
180 #include <wmsinternaladminnetsource.h> | |
181 #endif // _TRY_WM_FINDPROXY | |
182 | |
183 #if _TRY_IE_LAN_SETTINGS | |
184 #include <wininet.h> | |
185 #include <string> | |
186 #endif // _TRY_IE_LAN_SETTINGS | |
187 | |
188 namespace rtc { | |
189 | |
190 ////////////////////////////////////////////////////////////////////// | |
191 // Utility Functions | |
192 ////////////////////////////////////////////////////////////////////// | |
193 | |
194 #if defined(WEBRTC_WIN) | |
195 #ifdef _UNICODE | |
196 | |
197 typedef std::wstring tstring; | |
198 std::string Utf8String(const tstring& str) { return ToUtf8(str); } | |
199 | |
200 #else // !_UNICODE | |
201 | |
202 typedef std::string tstring; | |
203 std::string Utf8String(const tstring& str) { return str; } | |
204 | |
205 #endif // !_UNICODE | |
206 #endif // WEBRTC_WIN | |
207 | |
208 bool ProxyItemMatch(const Url<char>& url, char * item, size_t len) { | |
209 // hostname:443 | |
210 if (char * port = ::strchr(item, ':')) { | |
211 *port++ = '\0'; | |
212 if (url.port() != atol(port)) { | |
213 return false; | |
214 } | |
215 } | |
216 | |
217 // A.B.C.D or A.B.C.D/24 | |
218 int a, b, c, d, m; | |
219 int match = sscanf(item, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m); | |
220 if (match >= 4) { | |
221 uint32_t ip = ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | | |
222 (d & 0xFF); | |
223 if ((match < 5) || (m > 32)) | |
224 m = 32; | |
225 else if (m < 0) | |
226 m = 0; | |
227 uint32_t mask = (m == 0) ? 0 : (~0UL) << (32 - m); | |
228 SocketAddress addr(url.host(), 0); | |
229 // TODO: Support IPv6 proxyitems. This code block is IPv4 only anyway. | |
230 return !addr.IsUnresolvedIP() && | |
231 ((addr.ipaddr().v4AddressAsHostOrderInteger() & mask) == (ip & mask)); | |
232 } | |
233 | |
234 // .foo.com | |
235 if (*item == '.') { | |
236 size_t hostlen = url.host().length(); | |
237 return (hostlen > len) | |
238 && (stricmp(url.host().c_str() + (hostlen - len), item) == 0); | |
239 } | |
240 | |
241 // localhost or www.*.com | |
242 if (!string_match(url.host().c_str(), item)) | |
243 return false; | |
244 | |
245 return true; | |
246 } | |
247 | |
248 bool ProxyListMatch(const Url<char>& url, const std::string& proxy_list, | |
249 char sep) { | |
250 const size_t BUFSIZE = 256; | |
251 char buffer[BUFSIZE]; | |
252 const char* list = proxy_list.c_str(); | |
253 while (*list) { | |
254 // Remove leading space | |
255 if (isspace(*list)) { | |
256 ++list; | |
257 continue; | |
258 } | |
259 // Break on separator | |
260 size_t len; | |
261 const char * start = list; | |
262 if (const char * end = ::strchr(list, sep)) { | |
263 len = (end - list); | |
264 list += len + 1; | |
265 } else { | |
266 len = strlen(list); | |
267 list += len; | |
268 } | |
269 // Remove trailing space | |
270 while ((len > 0) && isspace(start[len-1])) | |
271 --len; | |
272 // Check for oversized entry | |
273 if (len >= BUFSIZE) | |
274 continue; | |
275 memcpy(buffer, start, len); | |
276 buffer[len] = 0; | |
277 if (!ProxyItemMatch(url, buffer, len)) | |
278 continue; | |
279 return true; | |
280 } | |
281 return false; | |
282 } | |
283 | |
284 bool Better(ProxyType lhs, const ProxyType rhs) { | |
285 // PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN | |
286 const int PROXY_VALUE[5] = { 0, 2, 3, 1 }; | |
287 return (PROXY_VALUE[lhs] > PROXY_VALUE[rhs]); | |
288 } | |
289 | |
290 bool ParseProxy(const std::string& saddress, ProxyInfo* proxy) { | |
291 const size_t kMaxAddressLength = 1024; | |
292 // Allow semicolon, space, or tab as an address separator | |
293 const char* const kAddressSeparator = " ;\t"; | |
294 | |
295 ProxyType ptype; | |
296 std::string host; | |
297 uint16_t port; | |
298 | |
299 const char* address = saddress.c_str(); | |
300 while (*address) { | |
301 size_t len; | |
302 const char * start = address; | |
303 if (const char * sep = strchr(address, kAddressSeparator)) { | |
304 len = (sep - address); | |
305 address += len + 1; | |
306 while (*address != '\0' && ::strchr(kAddressSeparator, *address)) { | |
307 address += 1; | |
308 } | |
309 } else { | |
310 len = strlen(address); | |
311 address += len; | |
312 } | |
313 | |
314 if (len > kMaxAddressLength - 1) { | |
315 LOG(LS_WARNING) << "Proxy address too long [" << start << "]"; | |
316 continue; | |
317 } | |
318 | |
319 char buffer[kMaxAddressLength]; | |
320 memcpy(buffer, start, len); | |
321 buffer[len] = 0; | |
322 | |
323 char * colon = ::strchr(buffer, ':'); | |
324 if (!colon) { | |
325 LOG(LS_WARNING) << "Proxy address without port [" << buffer << "]"; | |
326 continue; | |
327 } | |
328 | |
329 *colon = 0; | |
330 char * endptr; | |
331 port = static_cast<uint16_t>(strtol(colon + 1, &endptr, 0)); | |
332 if (*endptr != 0) { | |
333 LOG(LS_WARNING) << "Proxy address with invalid port [" << buffer << "]"; | |
334 continue; | |
335 } | |
336 | |
337 if (char * equals = ::strchr(buffer, '=')) { | |
338 *equals = 0; | |
339 host = equals + 1; | |
340 if (_stricmp(buffer, "socks") == 0) { | |
341 ptype = PROXY_SOCKS5; | |
342 } else if (_stricmp(buffer, "https") == 0) { | |
343 ptype = PROXY_HTTPS; | |
344 } else { | |
345 LOG(LS_WARNING) << "Proxy address with unknown protocol [" | |
346 << buffer << "]"; | |
347 ptype = PROXY_UNKNOWN; | |
348 } | |
349 } else { | |
350 host = buffer; | |
351 ptype = PROXY_UNKNOWN; | |
352 } | |
353 | |
354 if (Better(ptype, proxy->type)) { | |
355 proxy->type = ptype; | |
356 proxy->address.SetIP(host); | |
357 proxy->address.SetPort(port); | |
358 } | |
359 } | |
360 | |
361 return proxy->type != PROXY_NONE; | |
362 } | |
363 | |
364 UserAgent GetAgent(const char* agent) { | |
365 if (agent) { | |
366 std::string agent_str(agent); | |
367 if (agent_str.find(kFirefoxPattern) != std::string::npos) { | |
368 return UA_FIREFOX; | |
369 } else if (agent_str.find(kInternetExplorerPattern) != std::string::npos) { | |
370 return UA_INTERNETEXPLORER; | |
371 } else if (agent_str.empty()) { | |
372 return UA_UNKNOWN; | |
373 } | |
374 } | |
375 return UA_OTHER; | |
376 } | |
377 | |
378 bool EndsWith(const std::string& a, const std::string& b) { | |
379 if (b.size() > a.size()) { | |
380 return false; | |
381 } | |
382 int result = a.compare(a.size() - b.size(), b.size(), b); | |
383 return result == 0; | |
384 } | |
385 | |
386 bool GetFirefoxProfilePath(Pathname* path) { | |
387 #if defined(WEBRTC_WIN) | |
388 wchar_t w_path[MAX_PATH]; | |
389 if (SHGetFolderPath(0, CSIDL_APPDATA, 0, SHGFP_TYPE_CURRENT, w_path) != | |
390 S_OK) { | |
391 LOG(LS_ERROR) << "SHGetFolderPath failed"; | |
392 return false; | |
393 } | |
394 path->SetFolder(ToUtf8(w_path, wcslen(w_path))); | |
395 path->AppendFolder("Mozilla"); | |
396 path->AppendFolder("Firefox"); | |
397 #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | |
398 rtc::UnixFilesystem filesystem; | |
399 filesystem.SetApplicationName("Firefox"); | |
400 bool result = filesystem.GetAppDataFolder(path, true); | |
401 return result; | |
402 #else | |
403 char* user_home = getenv("HOME"); | |
404 if (user_home == NULL) { | |
405 return false; | |
406 } | |
407 path->SetFolder(std::string(user_home)); | |
408 path->AppendFolder(".mozilla"); | |
409 path->AppendFolder("firefox"); | |
410 #endif // WEBRTC_WIN | |
411 return true; | |
412 } | |
413 | |
414 bool GetDefaultFirefoxProfile(Pathname* profile_path) { | |
415 RTC_DCHECK(NULL != profile_path); | |
416 Pathname path; | |
417 if (!GetFirefoxProfilePath(&path)) { | |
418 return false; | |
419 } | |
420 | |
421 #if USE_FIREFOX_PROFILES_INI | |
422 // [Profile0] | |
423 // Name=default | |
424 // IsRelative=1 | |
425 // Path=Profiles/2de53ejb.default | |
426 // Default=1 | |
427 | |
428 // Note: we are looking for the first entry with "Default=1", or the last | |
429 // entry in the file | |
430 path.SetFilename("profiles.ini"); | |
431 std::unique_ptr<FileStream> fs(Filesystem::OpenFile(path, "r")); | |
432 if (!fs) { | |
433 return false; | |
434 } | |
435 Pathname candidate; | |
436 bool relative = true; | |
437 std::string line; | |
438 while (fs->ReadLine(&line) == SR_SUCCESS) { | |
439 if (line.length() == 0) { | |
440 continue; | |
441 } | |
442 if (line.at(0) == '[') { | |
443 relative = true; | |
444 candidate.clear(); | |
445 } else if (line.find("IsRelative=") == 0 && | |
446 line.length() >= 12) { | |
447 // TODO: The initial Linux public launch revealed a fairly | |
448 // high number of machines where IsRelative= did not have anything after | |
449 // it. Perhaps that is legal profiles.ini syntax? | |
450 relative = (line.at(11) != '0'); | |
451 } else if (line.find("Path=") == 0 && | |
452 line.length() >= 6) { | |
453 if (relative) { | |
454 candidate = path; | |
455 } else { | |
456 candidate.clear(); | |
457 } | |
458 candidate.AppendFolder(line.substr(5)); | |
459 } else if (line.find("Default=") == 0 && | |
460 line.length() >= 9) { | |
461 if ((line.at(8) != '0') && !candidate.empty()) { | |
462 break; | |
463 } | |
464 } | |
465 } | |
466 fs->Close(); | |
467 if (candidate.empty()) { | |
468 return false; | |
469 } | |
470 profile_path->SetPathname(candidate.pathname()); | |
471 | |
472 #else // !USE_FIREFOX_PROFILES_INI | |
473 path.AppendFolder("Profiles"); | |
474 DirectoryIterator* it = Filesystem::IterateDirectory(); | |
475 it->Iterate(path); | |
476 std::string extension(".default"); | |
477 while (!EndsWith(it->Name(), extension)) { | |
478 if (!it->Next()) { | |
479 return false; | |
480 } | |
481 } | |
482 | |
483 profile_path->SetPathname(path); | |
484 profile->AppendFolder("Profiles"); | |
485 profile->AppendFolder(it->Name()); | |
486 delete it; | |
487 | |
488 #endif // !USE_FIREFOX_PROFILES_INI | |
489 | |
490 return true; | |
491 } | |
492 | |
493 bool ReadFirefoxPrefs(const Pathname& filename, | |
494 const char * prefix, | |
495 StringMap* settings) { | |
496 std::unique_ptr<FileStream> fs(Filesystem::OpenFile(filename, "r")); | |
497 if (!fs) { | |
498 LOG(LS_ERROR) << "Failed to open file: " << filename.pathname(); | |
499 return false; | |
500 } | |
501 | |
502 std::string line; | |
503 while (fs->ReadLine(&line) == SR_SUCCESS) { | |
504 size_t prefix_len = strlen(prefix); | |
505 | |
506 // Skip blank lines and too long lines. | |
507 if ((line.length() == 0) || (line.length() > kMaxLineLength) | |
508 || (line.at(0) == '#') || line.compare(0, 2, "/*") == 0 | |
509 || line.compare(0, 2, " *") == 0) { | |
510 continue; | |
511 } | |
512 | |
513 char buffer[kMaxLineLength]; | |
514 strcpyn(buffer, sizeof(buffer), line.c_str()); | |
515 int nstart = 0, nend = 0, vstart = 0, vend = 0; | |
516 sscanf(buffer, "user_pref(\"%n%*[^\"]%n\", %n%*[^)]%n);", | |
517 &nstart, &nend, &vstart, &vend); | |
518 if (vend > 0) { | |
519 char* name = buffer + nstart; | |
520 name[nend - nstart] = 0; | |
521 if ((vend - vstart >= 2) && (buffer[vstart] == '"')) { | |
522 vstart += 1; | |
523 vend -= 1; | |
524 } | |
525 char* value = buffer + vstart; | |
526 value[vend - vstart] = 0; | |
527 if ((strncmp(name, prefix, prefix_len) == 0) && *value) { | |
528 settings->Add(name + prefix_len, value); | |
529 } | |
530 } else { | |
531 LOG_F(LS_WARNING) << "Unparsed pref [" << buffer << "]"; | |
532 } | |
533 } | |
534 fs->Close(); | |
535 return true; | |
536 } | |
537 | |
538 bool GetFirefoxProxySettings(const char* url, ProxyInfo* proxy) { | |
539 Url<char> purl(url); | |
540 Pathname path; | |
541 bool success = false; | |
542 if (GetDefaultFirefoxProfile(&path)) { | |
543 StringMap settings; | |
544 path.SetFilename("prefs.js"); | |
545 if (ReadFirefoxPrefs(path, "network.proxy.", &settings)) { | |
546 success = true; | |
547 proxy->bypass_list = | |
548 settings.Get("no_proxies_on", "localhost, 127.0.0.1"); | |
549 if (settings.Get("type") == "1") { | |
550 // User has manually specified a proxy, try to figure out what | |
551 // type it is. | |
552 if (ProxyListMatch(purl, proxy->bypass_list.c_str(), ',')) { | |
553 // Our url is in the list of url's to bypass proxy. | |
554 } else if (settings.Get("share_proxy_settings") == "true") { | |
555 proxy->type = PROXY_UNKNOWN; | |
556 proxy->address.SetIP(settings.Get("http")); | |
557 proxy->address.SetPort(atoi(settings.Get("http_port").c_str())); | |
558 } else if (settings.IsSet("socks")) { | |
559 proxy->type = PROXY_SOCKS5; | |
560 proxy->address.SetIP(settings.Get("socks")); | |
561 proxy->address.SetPort(atoi(settings.Get("socks_port").c_str())); | |
562 } else if (settings.IsSet("ssl")) { | |
563 proxy->type = PROXY_HTTPS; | |
564 proxy->address.SetIP(settings.Get("ssl")); | |
565 proxy->address.SetPort(atoi(settings.Get("ssl_port").c_str())); | |
566 } else if (settings.IsSet("http")) { | |
567 proxy->type = PROXY_HTTPS; | |
568 proxy->address.SetIP(settings.Get("http")); | |
569 proxy->address.SetPort(atoi(settings.Get("http_port").c_str())); | |
570 } | |
571 } else if (settings.Get("type") == "2") { | |
572 // Browser is configured to get proxy settings from a given url. | |
573 proxy->autoconfig_url = settings.Get("autoconfig_url").c_str(); | |
574 } else if (settings.Get("type") == "4") { | |
575 // Browser is configured to auto detect proxy config. | |
576 proxy->autodetect = true; | |
577 } else { | |
578 // No proxy set. | |
579 } | |
580 } | |
581 } | |
582 return success; | |
583 } | |
584 | |
585 #if defined(WEBRTC_WIN) // Windows specific implementation for reading Internet | |
586 // Explorer proxy settings. | |
587 | |
588 void LogGetProxyFault() { | |
589 LOG_GLEM(LERROR, WINHTTP) << "WinHttpGetProxyForUrl faulted!!"; | |
590 } | |
591 | |
592 BOOL MyWinHttpGetProxyForUrl(pfnWinHttpGetProxyForUrl pWHGPFU, | |
593 HINTERNET hWinHttp, LPCWSTR url, | |
594 WINHTTP_AUTOPROXY_OPTIONS *options, | |
595 WINHTTP_PROXY_INFO *info) { | |
596 // WinHttpGetProxyForUrl() can call plugins which can crash. | |
597 // In the case of McAfee scriptproxy.dll, it does crash in | |
598 // older versions. Try to catch crashes here and treat as an | |
599 // error. | |
600 BOOL success = FALSE; | |
601 | |
602 #if (_HAS_EXCEPTIONS == 0) | |
603 __try { | |
604 success = pWHGPFU(hWinHttp, url, options, info); | |
605 } __except(EXCEPTION_EXECUTE_HANDLER) { | |
606 // This is a separate function to avoid | |
607 // Visual C++ error 2712 when compiling with C++ EH | |
608 LogGetProxyFault(); | |
609 } | |
610 #else | |
611 success = pWHGPFU(hWinHttp, url, options, info); | |
612 #endif // (_HAS_EXCEPTIONS == 0) | |
613 | |
614 return success; | |
615 } | |
616 | |
617 bool IsDefaultBrowserFirefox() { | |
618 HKEY key; | |
619 LONG result = RegOpenKeyEx(HKEY_CLASSES_ROOT, L"http\\shell\\open\\command", | |
620 0, KEY_READ, &key); | |
621 if (ERROR_SUCCESS != result) | |
622 return false; | |
623 | |
624 DWORD size, type; | |
625 bool success = false; | |
626 result = RegQueryValueEx(key, L"", 0, &type, NULL, &size); | |
627 if (result == ERROR_SUCCESS && type == REG_SZ) { | |
628 wchar_t* value = new wchar_t[size+1]; | |
629 BYTE* buffer = reinterpret_cast<BYTE*>(value); | |
630 result = RegQueryValueEx(key, L"", 0, &type, buffer, &size); | |
631 if (result == ERROR_SUCCESS) { | |
632 // Size returned by RegQueryValueEx is in bytes, convert to number of | |
633 // wchar_t's. | |
634 size /= sizeof(value[0]); | |
635 value[size] = L'\0'; | |
636 for (size_t i = 0; i < size; ++i) { | |
637 value[i] = tolowercase(value[i]); | |
638 } | |
639 success = (NULL != strstr(value, L"firefox.exe")); | |
640 } | |
641 delete[] value; | |
642 } | |
643 | |
644 RegCloseKey(key); | |
645 return success; | |
646 } | |
647 | |
648 bool GetWinHttpProxySettings(const char* url, ProxyInfo* proxy) { | |
649 HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll"); | |
650 if (winhttp_handle == NULL) { | |
651 LOG(LS_ERROR) << "Failed to load winhttp.dll."; | |
652 return false; | |
653 } | |
654 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG iecfg; | |
655 memset(&iecfg, 0, sizeof(iecfg)); | |
656 Url<char> purl(url); | |
657 pfnWinHttpGetIEProxyConfig pWHGIEPC = | |
658 reinterpret_cast<pfnWinHttpGetIEProxyConfig>( | |
659 GetProcAddress(winhttp_handle, | |
660 "WinHttpGetIEProxyConfigForCurrentUser")); | |
661 bool success = false; | |
662 if (pWHGIEPC && pWHGIEPC(&iecfg)) { | |
663 // We were read proxy config successfully. | |
664 success = true; | |
665 if (iecfg.fAutoDetect) { | |
666 proxy->autodetect = true; | |
667 } | |
668 if (iecfg.lpszAutoConfigUrl) { | |
669 proxy->autoconfig_url = ToUtf8(iecfg.lpszAutoConfigUrl); | |
670 GlobalFree(iecfg.lpszAutoConfigUrl); | |
671 } | |
672 if (iecfg.lpszProxyBypass) { | |
673 proxy->bypass_list = ToUtf8(iecfg.lpszProxyBypass); | |
674 GlobalFree(iecfg.lpszProxyBypass); | |
675 } | |
676 if (iecfg.lpszProxy) { | |
677 if (!ProxyListMatch(purl, proxy->bypass_list, ';')) { | |
678 ParseProxy(ToUtf8(iecfg.lpszProxy), proxy); | |
679 } | |
680 GlobalFree(iecfg.lpszProxy); | |
681 } | |
682 } | |
683 FreeLibrary(winhttp_handle); | |
684 return success; | |
685 } | |
686 | |
687 // Uses the WinHTTP API to auto detect proxy for the given url. Firefox and IE | |
688 // have slightly different option dialogs for proxy settings. In Firefox, | |
689 // either a location of a proxy configuration file can be specified or auto | |
690 // detection can be selected. In IE theese two options can be independently | |
691 // selected. For the case where both options are selected (only IE) we try to | |
692 // fetch the config file first, and if that fails we'll perform an auto | |
693 // detection. | |
694 // | |
695 // Returns true if we successfully performed an auto detection not depending on | |
696 // whether we found a proxy or not. Returns false on error. | |
697 bool WinHttpAutoDetectProxyForUrl(const char* agent, const char* url, | |
698 ProxyInfo* proxy) { | |
699 Url<char> purl(url); | |
700 bool success = true; | |
701 HMODULE winhttp_handle = LoadLibrary(L"winhttp.dll"); | |
702 if (winhttp_handle == NULL) { | |
703 LOG(LS_ERROR) << "Failed to load winhttp.dll."; | |
704 return false; | |
705 } | |
706 pfnWinHttpOpen pWHO = | |
707 reinterpret_cast<pfnWinHttpOpen>(GetProcAddress(winhttp_handle, | |
708 "WinHttpOpen")); | |
709 pfnWinHttpCloseHandle pWHCH = | |
710 reinterpret_cast<pfnWinHttpCloseHandle>( | |
711 GetProcAddress(winhttp_handle, "WinHttpCloseHandle")); | |
712 pfnWinHttpGetProxyForUrl pWHGPFU = | |
713 reinterpret_cast<pfnWinHttpGetProxyForUrl>( | |
714 GetProcAddress(winhttp_handle, "WinHttpGetProxyForUrl")); | |
715 if (pWHO && pWHCH && pWHGPFU) { | |
716 if (HINTERNET hWinHttp = pWHO(ToUtf16(agent).c_str(), | |
717 WINHTTP_ACCESS_TYPE_NO_PROXY, | |
718 WINHTTP_NO_PROXY_NAME, | |
719 WINHTTP_NO_PROXY_BYPASS, | |
720 0)) { | |
721 BOOL result = FALSE; | |
722 WINHTTP_PROXY_INFO info; | |
723 memset(&info, 0, sizeof(info)); | |
724 if (proxy->autodetect) { | |
725 // Use DHCP and DNS to try to find any proxy to use. | |
726 WINHTTP_AUTOPROXY_OPTIONS options; | |
727 memset(&options, 0, sizeof(options)); | |
728 options.fAutoLogonIfChallenged = TRUE; | |
729 | |
730 options.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT; | |
731 options.dwAutoDetectFlags |= WINHTTP_AUTO_DETECT_TYPE_DHCP | |
732 | WINHTTP_AUTO_DETECT_TYPE_DNS_A; | |
733 result = MyWinHttpGetProxyForUrl( | |
734 pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info); | |
735 } | |
736 if (!result && !proxy->autoconfig_url.empty()) { | |
737 // We have the location of a proxy config file. Download it and | |
738 // execute it to find proxy settings for our url. | |
739 WINHTTP_AUTOPROXY_OPTIONS options; | |
740 memset(&options, 0, sizeof(options)); | |
741 memset(&info, 0, sizeof(info)); | |
742 options.fAutoLogonIfChallenged = TRUE; | |
743 | |
744 std::wstring autoconfig_url16((ToUtf16)(proxy->autoconfig_url)); | |
745 options.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; | |
746 options.lpszAutoConfigUrl = autoconfig_url16.c_str(); | |
747 | |
748 result = MyWinHttpGetProxyForUrl( | |
749 pWHGPFU, hWinHttp, ToUtf16(url).c_str(), &options, &info); | |
750 } | |
751 if (result) { | |
752 // Either the given auto config url was valid or auto | |
753 // detection found a proxy on this network. | |
754 if (info.lpszProxy) { | |
755 // TODO: Does this bypass list differ from the list | |
756 // retreived from GetWinHttpProxySettings earlier? | |
757 if (info.lpszProxyBypass) { | |
758 proxy->bypass_list = ToUtf8(info.lpszProxyBypass); | |
759 GlobalFree(info.lpszProxyBypass); | |
760 } else { | |
761 proxy->bypass_list.clear(); | |
762 } | |
763 if (!ProxyListMatch(purl, proxy->bypass_list, ';')) { | |
764 // Found proxy for this URL. If parsing the address turns | |
765 // out ok then we are successful. | |
766 success = ParseProxy(ToUtf8(info.lpszProxy), proxy); | |
767 } | |
768 GlobalFree(info.lpszProxy); | |
769 } | |
770 } else { | |
771 // We could not find any proxy for this url. | |
772 LOG(LS_INFO) << "No proxy detected for " << url; | |
773 } | |
774 pWHCH(hWinHttp); | |
775 } | |
776 } else { | |
777 LOG(LS_ERROR) << "Failed loading WinHTTP functions."; | |
778 success = false; | |
779 } | |
780 FreeLibrary(winhttp_handle); | |
781 return success; | |
782 } | |
783 | |
784 #if 0 // Below functions currently not used. | |
785 | |
786 bool GetJsProxySettings(const char* url, ProxyInfo* proxy) { | |
787 Url<char> purl(url); | |
788 bool success = false; | |
789 | |
790 if (HMODULE hModJS = LoadLibrary(_T("jsproxy.dll"))) { | |
791 pfnInternetGetProxyInfo pIGPI = | |
792 reinterpret_cast<pfnInternetGetProxyInfo>( | |
793 GetProcAddress(hModJS, "InternetGetProxyInfo")); | |
794 if (pIGPI) { | |
795 char proxy[256], host[256]; | |
796 memset(proxy, 0, sizeof(proxy)); | |
797 char * ptr = proxy; | |
798 DWORD proxylen = sizeof(proxy); | |
799 std::string surl = Utf8String(url); | |
800 DWORD hostlen = _snprintf(host, sizeof(host), "http%s://%S", | |
801 purl.secure() ? "s" : "", purl.server()); | |
802 if (pIGPI(surl.data(), surl.size(), host, hostlen, &ptr, &proxylen)) { | |
803 LOG(INFO) << "Proxy: " << proxy; | |
804 } else { | |
805 LOG_GLE(INFO) << "InternetGetProxyInfo"; | |
806 } | |
807 } | |
808 FreeLibrary(hModJS); | |
809 } | |
810 return success; | |
811 } | |
812 | |
813 bool GetWmProxySettings(const char* url, ProxyInfo* proxy) { | |
814 Url<char> purl(url); | |
815 bool success = false; | |
816 | |
817 INSNetSourceCreator * nsc = 0; | |
818 HRESULT hr = CoCreateInstance(CLSID_ClientNetManager, 0, CLSCTX_ALL, | |
819 IID_INSNetSourceCreator, (LPVOID *) &nsc); | |
820 if (SUCCEEDED(hr)) { | |
821 if (SUCCEEDED(hr = nsc->Initialize())) { | |
822 VARIANT dispatch; | |
823 VariantInit(&dispatch); | |
824 if (SUCCEEDED(hr = nsc->GetNetSourceAdminInterface(L"http", &dispatch))) { | |
825 IWMSInternalAdminNetSource * ians = 0; | |
826 if (SUCCEEDED(hr = dispatch.pdispVal->QueryInterface( | |
827 IID_IWMSInternalAdminNetSource, (LPVOID *) &ians))) { | |
828 _bstr_t host(purl.server()); | |
829 BSTR proxy = 0; | |
830 BOOL bProxyEnabled = FALSE; | |
831 DWORD port, context = 0; | |
832 if (SUCCEEDED(hr = ians->FindProxyForURL( | |
833 L"http", host, &bProxyEnabled, &proxy, &port, &context))) { | |
834 success = true; | |
835 if (bProxyEnabled) { | |
836 _bstr_t sproxy = proxy; | |
837 proxy->ptype = PT_HTTPS; | |
838 proxy->host = sproxy; | |
839 proxy->port = port; | |
840 } | |
841 } | |
842 SysFreeString(proxy); | |
843 if (FAILED(hr = ians->ShutdownProxyContext(context))) { | |
844 LOG(LS_INFO) << "IWMSInternalAdminNetSource::ShutdownProxyContext" | |
845 << "failed: " << hr; | |
846 } | |
847 ians->Release(); | |
848 } | |
849 } | |
850 VariantClear(&dispatch); | |
851 if (FAILED(hr = nsc->Shutdown())) { | |
852 LOG(LS_INFO) << "INSNetSourceCreator::Shutdown failed: " << hr; | |
853 } | |
854 } | |
855 nsc->Release(); | |
856 } | |
857 return success; | |
858 } | |
859 | |
860 bool GetIePerConnectionProxySettings(const char* url, ProxyInfo* proxy) { | |
861 Url<char> purl(url); | |
862 bool success = false; | |
863 | |
864 INTERNET_PER_CONN_OPTION_LIST list; | |
865 INTERNET_PER_CONN_OPTION options[3]; | |
866 memset(&list, 0, sizeof(list)); | |
867 memset(&options, 0, sizeof(options)); | |
868 | |
869 list.dwSize = sizeof(list); | |
870 list.dwOptionCount = 3; | |
871 list.pOptions = options; | |
872 options[0].dwOption = INTERNET_PER_CONN_FLAGS; | |
873 options[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER; | |
874 options[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS; | |
875 DWORD dwSize = sizeof(list); | |
876 | |
877 if (!InternetQueryOption(0, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, | |
878 &dwSize)) { | |
879 LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); | |
880 } else if ((options[0].Value.dwValue & PROXY_TYPE_PROXY) != 0) { | |
881 success = true; | |
882 if (!ProxyListMatch(purl, nonnull(options[2].Value.pszValue), _T(';'))) { | |
883 ParseProxy(nonnull(options[1].Value.pszValue), proxy); | |
884 } | |
885 } else if ((options[0].Value.dwValue & PROXY_TYPE_DIRECT) != 0) { | |
886 success = true; | |
887 } else { | |
888 LOG(LS_INFO) << "unknown internet access type: " | |
889 << options[0].Value.dwValue; | |
890 } | |
891 if (options[1].Value.pszValue) { | |
892 GlobalFree(options[1].Value.pszValue); | |
893 } | |
894 if (options[2].Value.pszValue) { | |
895 GlobalFree(options[2].Value.pszValue); | |
896 } | |
897 return success; | |
898 } | |
899 | |
900 #endif // 0 | |
901 | |
902 // Uses the InternetQueryOption function to retrieve proxy settings | |
903 // from the registry. This will only give us the 'static' settings, | |
904 // ie, not any information about auto config etc. | |
905 bool GetIeLanProxySettings(const char* url, ProxyInfo* proxy) { | |
906 Url<char> purl(url); | |
907 bool success = false; | |
908 | |
909 wchar_t buffer[1024]; | |
910 memset(buffer, 0, sizeof(buffer)); | |
911 INTERNET_PROXY_INFO * info = reinterpret_cast<INTERNET_PROXY_INFO *>(buffer); | |
912 DWORD dwSize = sizeof(buffer); | |
913 | |
914 if (!InternetQueryOption(0, INTERNET_OPTION_PROXY, info, &dwSize)) { | |
915 LOG(LS_INFO) << "InternetQueryOption failed: " << GetLastError(); | |
916 } else if (info->dwAccessType == INTERNET_OPEN_TYPE_DIRECT) { | |
917 success = true; | |
918 } else if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY) { | |
919 success = true; | |
920 if (!ProxyListMatch(purl, nonnull(reinterpret_cast<const char*>( | |
921 info->lpszProxyBypass)), ' ')) { | |
922 ParseProxy(nonnull(reinterpret_cast<const char*>(info->lpszProxy)), | |
923 proxy); | |
924 } | |
925 } else { | |
926 LOG(LS_INFO) << "unknown internet access type: " << info->dwAccessType; | |
927 } | |
928 return success; | |
929 } | |
930 | |
931 bool GetIeProxySettings(const char* agent, const char* url, ProxyInfo* proxy) { | |
932 bool success = GetWinHttpProxySettings(url, proxy); | |
933 if (!success) { | |
934 // TODO: Should always call this if no proxy were detected by | |
935 // GetWinHttpProxySettings? | |
936 // WinHttp failed. Try using the InternetOptionQuery method instead. | |
937 return GetIeLanProxySettings(url, proxy); | |
938 } | |
939 return true; | |
940 } | |
941 | |
942 #endif // WEBRTC_WIN | |
943 | |
944 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) // WEBRTC_MAC && !defined(WEBRT
C_IOS) specific implementation for reading system wide | |
945 // proxy settings. | |
946 | |
947 bool p_getProxyInfoForTypeFromDictWithKeys(ProxyInfo* proxy, | |
948 ProxyType type, | |
949 const CFDictionaryRef proxyDict, | |
950 const CFStringRef enabledKey, | |
951 const CFStringRef hostKey, | |
952 const CFStringRef portKey) { | |
953 // whether or not we set up the proxy info. | |
954 bool result = false; | |
955 | |
956 // we use this as a scratch variable for determining if operations | |
957 // succeeded. | |
958 bool converted = false; | |
959 | |
960 // the data we need to construct the SocketAddress for the proxy. | |
961 std::string hostname; | |
962 int port; | |
963 | |
964 if ((proxyDict != NULL) && | |
965 (CFGetTypeID(proxyDict) == CFDictionaryGetTypeID())) { | |
966 // CoreFoundation stuff that we'll have to get from | |
967 // the dictionaries and interpret or convert into more usable formats. | |
968 CFNumberRef enabledCFNum; | |
969 CFNumberRef portCFNum; | |
970 CFStringRef hostCFStr; | |
971 | |
972 enabledCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, enabledKey); | |
973 | |
974 if (p_isCFNumberTrue(enabledCFNum)) { | |
975 // let's see if we can get the address and port. | |
976 hostCFStr = (CFStringRef)CFDictionaryGetValue(proxyDict, hostKey); | |
977 converted = p_convertHostCFStringRefToCPPString(hostCFStr, hostname); | |
978 if (converted) { | |
979 portCFNum = (CFNumberRef)CFDictionaryGetValue(proxyDict, portKey); | |
980 converted = p_convertCFNumberToInt(portCFNum, &port); | |
981 if (converted) { | |
982 // we have something enabled, with a hostname and a port. | |
983 // That's sufficient to set up the proxy info. | |
984 proxy->type = type; | |
985 proxy->address.SetIP(hostname); | |
986 proxy->address.SetPort(port); | |
987 result = true; | |
988 } | |
989 } | |
990 } | |
991 } | |
992 | |
993 return result; | |
994 } | |
995 | |
996 // Looks for proxy information in the given dictionary, | |
997 // return true if it found sufficient information to define one, | |
998 // false otherwise. This is guaranteed to not change the values in proxy | |
999 // unless a full-fledged proxy description was discovered in the dictionary. | |
1000 // However, at the present time this does not support username or password. | |
1001 // Checks first for a SOCKS proxy, then for HTTPS, then HTTP. | |
1002 bool GetMacProxySettingsFromDictionary(ProxyInfo* proxy, | |
1003 const CFDictionaryRef proxyDict) { | |
1004 // the function result. | |
1005 bool gotProxy = false; | |
1006 | |
1007 | |
1008 // first we see if there's a SOCKS proxy in place. | |
1009 gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy, | |
1010 PROXY_SOCKS5, | |
1011 proxyDict, | |
1012 kSCPropNetProxiesSOCKSEnable, | |
1013 kSCPropNetProxiesSOCKSProxy, | |
1014 kSCPropNetProxiesSOCKSPort); | |
1015 | |
1016 if (!gotProxy) { | |
1017 // okay, no SOCKS proxy, let's look for https. | |
1018 gotProxy = p_getProxyInfoForTypeFromDictWithKeys(proxy, | |
1019 PROXY_HTTPS, | |
1020 proxyDict, | |
1021 kSCPropNetProxiesHTTPSEnable, | |
1022 kSCPropNetProxiesHTTPSProxy, | |
1023 kSCPropNetProxiesHTTPSPort); | |
1024 if (!gotProxy) { | |
1025 // Finally, try HTTP proxy. Note that flute doesn't | |
1026 // differentiate between HTTPS and HTTP, hence we are using the | |
1027 // same flute type here, ie. PROXY_HTTPS. | |
1028 gotProxy = p_getProxyInfoForTypeFromDictWithKeys( | |
1029 proxy, PROXY_HTTPS, proxyDict, kSCPropNetProxiesHTTPEnable, | |
1030 kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort); | |
1031 } | |
1032 } | |
1033 return gotProxy; | |
1034 } | |
1035 | |
1036 // TODO(hughv) Update keychain functions. They work on 10.8, but are depricated. | |
1037 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |
1038 bool p_putPasswordInProxyInfo(ProxyInfo* proxy) { | |
1039 bool result = true; // by default we assume we're good. | |
1040 // for all we know there isn't any password. We'll set to false | |
1041 // if we find a problem. | |
1042 | |
1043 // Ask the keychain for an internet password search for the given protocol. | |
1044 OSStatus oss = 0; | |
1045 SecKeychainAttributeList attrList; | |
1046 attrList.count = 3; | |
1047 SecKeychainAttribute attributes[3]; | |
1048 attrList.attr = attributes; | |
1049 | |
1050 attributes[0].tag = kSecProtocolItemAttr; | |
1051 attributes[0].length = sizeof(SecProtocolType); | |
1052 SecProtocolType protocol; | |
1053 switch (proxy->type) { | |
1054 case PROXY_HTTPS : | |
1055 protocol = kSecProtocolTypeHTTPS; | |
1056 break; | |
1057 case PROXY_SOCKS5 : | |
1058 protocol = kSecProtocolTypeSOCKS; | |
1059 break; | |
1060 default : | |
1061 LOG(LS_ERROR) << "asked for proxy password for unknown proxy type."; | |
1062 result = false; | |
1063 break; | |
1064 } | |
1065 attributes[0].data = &protocol; | |
1066 | |
1067 UInt32 port = proxy->address.port(); | |
1068 attributes[1].tag = kSecPortItemAttr; | |
1069 attributes[1].length = sizeof(UInt32); | |
1070 attributes[1].data = &port; | |
1071 | |
1072 std::string ip = proxy->address.ipaddr().ToString(); | |
1073 attributes[2].tag = kSecServerItemAttr; | |
1074 attributes[2].length = ip.length(); | |
1075 attributes[2].data = const_cast<char*>(ip.c_str()); | |
1076 | |
1077 if (result) { | |
1078 LOG(LS_INFO) << "trying to get proxy username/password"; | |
1079 SecKeychainSearchRef sref; | |
1080 oss = SecKeychainSearchCreateFromAttributes(NULL, | |
1081 kSecInternetPasswordItemClass, | |
1082 &attrList, &sref); | |
1083 if (0 == oss) { | |
1084 LOG(LS_INFO) << "SecKeychainSearchCreateFromAttributes was good"; | |
1085 // Get the first item, if there is one. | |
1086 SecKeychainItemRef iref; | |
1087 oss = SecKeychainSearchCopyNext(sref, &iref); | |
1088 if (0 == oss) { | |
1089 LOG(LS_INFO) << "...looks like we have the username/password data"; | |
1090 // If there is, get the username and the password. | |
1091 | |
1092 SecKeychainAttributeInfo attribsToGet; | |
1093 attribsToGet.count = 1; | |
1094 UInt32 tag = kSecAccountItemAttr; | |
1095 UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING; | |
1096 void *data; | |
1097 UInt32 length; | |
1098 SecKeychainAttributeList *localList; | |
1099 | |
1100 attribsToGet.tag = &tag; | |
1101 attribsToGet.format = &format; | |
1102 OSStatus copyres = SecKeychainItemCopyAttributesAndData(iref, | |
1103 &attribsToGet, | |
1104 NULL, | |
1105 &localList, | |
1106 &length, | |
1107 &data); | |
1108 if (0 == copyres) { | |
1109 LOG(LS_INFO) << "...and we can pull it out."; | |
1110 // now, we know from experimentation (sadly not from docs) | |
1111 // that the username is in the local attribute list, | |
1112 // and the password in the data, | |
1113 // both without null termination but with info on their length. | |
1114 // grab the password from the data. | |
1115 std::string password; | |
1116 password.append(static_cast<const char*>(data), length); | |
1117 | |
1118 // make the password into a CryptString | |
1119 // huh, at the time of writing, you can't. | |
1120 // so we'll skip that for now and come back to it later. | |
1121 | |
1122 // now put the username in the proxy. | |
1123 if (1 <= localList->attr->length) { | |
1124 proxy->username.append( | |
1125 static_cast<const char*>(localList->attr->data), | |
1126 localList->attr->length); | |
1127 LOG(LS_INFO) << "username is " << proxy->username; | |
1128 } else { | |
1129 LOG(LS_ERROR) << "got keychain entry with no username"; | |
1130 result = false; | |
1131 } | |
1132 } else { | |
1133 LOG(LS_ERROR) << "couldn't copy info from keychain."; | |
1134 result = false; | |
1135 } | |
1136 SecKeychainItemFreeAttributesAndData(localList, data); | |
1137 } else if (errSecItemNotFound == oss) { | |
1138 LOG(LS_INFO) << "...username/password info not found"; | |
1139 } else { | |
1140 // oooh, neither 0 nor itemNotFound. | |
1141 LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss; | |
1142 result = false; | |
1143 } | |
1144 } else if (errSecItemNotFound == oss) { // noop | |
1145 } else { | |
1146 // oooh, neither 0 nor itemNotFound. | |
1147 LOG(LS_ERROR) << "Couldn't get keychain information, error code" << oss; | |
1148 result = false; | |
1149 } | |
1150 } | |
1151 | |
1152 return result; | |
1153 } | |
1154 | |
1155 bool GetMacProxySettings(ProxyInfo* proxy) { | |
1156 // based on the Apple Technical Q&A QA1234 | |
1157 // http://developer.apple.com/qa/qa2001/qa1234.html | |
1158 CFDictionaryRef proxyDict = SCDynamicStoreCopyProxies(NULL); | |
1159 bool result = false; | |
1160 | |
1161 if (proxyDict != NULL) { | |
1162 // sending it off to another function makes it easier to unit test | |
1163 // since we can make our own dictionary to hand to that function. | |
1164 result = GetMacProxySettingsFromDictionary(proxy, proxyDict); | |
1165 | |
1166 if (result) { | |
1167 result = p_putPasswordInProxyInfo(proxy); | |
1168 } | |
1169 | |
1170 CFRelease(proxyDict); | |
1171 } else { | |
1172 LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed"; | |
1173 } | |
1174 | |
1175 return result; | |
1176 } | |
1177 #endif // WEBRTC_MAC && !defined(WEBRTC_IOS) | |
1178 | |
1179 #ifdef WEBRTC_IOS | |
1180 // iOS has only http proxy | |
1181 bool GetiOSProxySettings(ProxyInfo* proxy) { | |
1182 | |
1183 bool result = false; | |
1184 | |
1185 CFDictionaryRef proxy_dict = CFNetworkCopySystemProxySettings(); | |
1186 if (!proxy_dict) { | |
1187 LOG(LS_ERROR) << "CFNetworkCopySystemProxySettings failed"; | |
1188 return false; | |
1189 } | |
1190 | |
1191 CFNumberRef proxiesHTTPEnable = (CFNumberRef)CFDictionaryGetValue( | |
1192 proxy_dict, kCFNetworkProxiesHTTPEnable); | |
1193 if (!p_isCFNumberTrue(proxiesHTTPEnable)) { | |
1194 CFRelease(proxy_dict); | |
1195 return false; | |
1196 } | |
1197 | |
1198 CFStringRef proxy_address = (CFStringRef)CFDictionaryGetValue( | |
1199 proxy_dict, kCFNetworkProxiesHTTPProxy); | |
1200 CFNumberRef proxy_port = (CFNumberRef)CFDictionaryGetValue( | |
1201 proxy_dict, kCFNetworkProxiesHTTPPort); | |
1202 | |
1203 // the data we need to construct the SocketAddress for the proxy. | |
1204 std::string hostname; | |
1205 int port; | |
1206 if (p_convertHostCFStringRefToCPPString(proxy_address, hostname) && | |
1207 p_convertCFNumberToInt(proxy_port, &port)) { | |
1208 // We have something enabled, with a hostname and a port. | |
1209 // That's sufficient to set up the proxy info. | |
1210 // Finally, try HTTP proxy. Note that flute doesn't | |
1211 // differentiate between HTTPS and HTTP, hence we are using the | |
1212 // same flute type here, ie. PROXY_HTTPS. | |
1213 proxy->type = PROXY_HTTPS; | |
1214 | |
1215 proxy->address.SetIP(hostname); | |
1216 proxy->address.SetPort(port); | |
1217 result = true; | |
1218 } | |
1219 | |
1220 CFRelease(proxy_dict); | |
1221 | |
1222 return result; | |
1223 } | |
1224 #endif // WEBRTC_IOS | |
1225 | |
1226 bool AutoDetectProxySettings(const char* agent, const char* url, | |
1227 ProxyInfo* proxy) { | |
1228 #if defined(WEBRTC_WIN) | |
1229 return WinHttpAutoDetectProxyForUrl(agent, url, proxy); | |
1230 #else | |
1231 LOG(LS_WARNING) << "Proxy auto-detection not implemented for this platform"; | |
1232 return false; | |
1233 #endif | |
1234 } | |
1235 | |
1236 bool GetSystemDefaultProxySettings(const char* agent, const char* url, | |
1237 ProxyInfo* proxy) { | |
1238 #if defined(WEBRTC_WIN) | |
1239 return GetIeProxySettings(agent, url, proxy); | |
1240 #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | |
1241 return GetMacProxySettings(proxy); | |
1242 #elif defined(WEBRTC_IOS) | |
1243 return GetiOSProxySettings(proxy); | |
1244 #else | |
1245 // TODO: Get System settings if browser is not firefox. | |
1246 return GetFirefoxProxySettings(url, proxy); | |
1247 #endif | |
1248 } | |
1249 | |
1250 bool GetProxySettingsForUrl(const char* agent, const char* url, | |
1251 ProxyInfo* proxy, bool long_operation) { | |
1252 UserAgent a = GetAgent(agent); | |
1253 bool result; | |
1254 switch (a) { | |
1255 case UA_FIREFOX: { | |
1256 result = GetFirefoxProxySettings(url, proxy); | |
1257 break; | |
1258 } | |
1259 #if defined(WEBRTC_WIN) | |
1260 case UA_INTERNETEXPLORER: | |
1261 result = GetIeProxySettings(agent, url, proxy); | |
1262 break; | |
1263 case UA_UNKNOWN: | |
1264 // Agent not defined, check default browser. | |
1265 if (IsDefaultBrowserFirefox()) { | |
1266 result = GetFirefoxProxySettings(url, proxy); | |
1267 } else { | |
1268 result = GetIeProxySettings(agent, url, proxy); | |
1269 } | |
1270 break; | |
1271 #endif // WEBRTC_WIN | |
1272 default: | |
1273 result = GetSystemDefaultProxySettings(agent, url, proxy); | |
1274 break; | |
1275 } | |
1276 | |
1277 // TODO: Consider using the 'long_operation' parameter to | |
1278 // decide whether to do the auto detection. | |
1279 if (result && (proxy->autodetect || | |
1280 !proxy->autoconfig_url.empty())) { | |
1281 // Use WinHTTP to auto detect proxy for us. | |
1282 result = AutoDetectProxySettings(agent, url, proxy); | |
1283 if (!result) { | |
1284 // Either auto detection is not supported or we simply didn't | |
1285 // find any proxy, reset type. | |
1286 proxy->type = rtc::PROXY_NONE; | |
1287 } | |
1288 } | |
1289 return result; | |
1290 } | |
1291 | |
1292 } // namespace rtc | |
OLD | NEW |