Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
10 #include "nsProtocolProxyService.h"
11 #include "nsProxyInfo.h"
12 #include "nsIClassInfoImpl.h"
13 #include "nsIIOService.h"
14 #include "nsIObserverService.h"
15 #include "nsIProtocolHandler.h"
16 #include "nsIProtocolProxyCallback.h"
17 #include "nsIChannel.h"
18 #include "nsICancelable.h"
19 #include "nsIDNSService.h"
20 #include "nsPIDNSService.h"
21 #include "nsIPrefService.h"
22 #include "nsIPrefBranch.h"
23 #include "nsThreadUtils.h"
24 #include "nsString.h"
25 #include "nsNetUtil.h"
26 #include "nsNetCID.h"
27 #include "prnetdb.h"
28 #include "nsPACMan.h"
29 #include "nsProxyRelease.h"
30 #include "mozilla/Mutex.h"
31 #include "mozilla/CondVar.h"
32 #include "nsISystemProxySettings.h"
34 //----------------------------------------------------------------------------
36 namespace mozilla {
37 extern const char kProxyType_HTTP[];
38 extern const char kProxyType_SOCKS[];
39 extern const char kProxyType_SOCKS4[];
40 extern const char kProxyType_SOCKS5[];
41 extern const char kProxyType_DIRECT[];
42 }
44 using namespace mozilla;
46 #include "prlog.h"
47 #if defined(PR_LOGGING)
48 #endif
49 #undef LOG
50 #define LOG(args) PR_LOG(net::GetProxyLog(), PR_LOG_DEBUG, args)
52 //----------------------------------------------------------------------------
54 #define PROXY_PREF_BRANCH "network.proxy"
55 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
57 #define WPAD_URL "http://wpad/wpad.dat"
59 //----------------------------------------------------------------------------
61 // This structure is intended to be allocated on the stack
62 struct nsProtocolInfo {
63 nsAutoCString scheme;
64 uint32_t flags;
65 int32_t defaultPort;
66 };
68 //----------------------------------------------------------------------------
70 // The nsPACManCallback portion of this implementation should be run
71 // on the main thread - so call nsPACMan::AsyncGetProxyForChannel() with
72 // a true mainThreadResponse parameter.
73 class nsAsyncResolveRequest MOZ_FINAL : public nsIRunnable
74 , public nsPACManCallback
75 , public nsICancelable
76 {
77 public:
78 NS_DECL_THREADSAFE_ISUPPORTS
80 nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
81 uint32_t aResolveFlags,
82 nsIProtocolProxyCallback *callback)
83 : mStatus(NS_OK)
84 , mDispatched(false)
85 , mResolveFlags(aResolveFlags)
86 , mPPS(pps)
87 , mXPComPPS(pps)
88 , mChannel(channel)
89 , mCallback(callback)
90 {
91 NS_ASSERTION(mCallback, "null callback");
92 }
94 ~nsAsyncResolveRequest()
95 {
96 if (!NS_IsMainThread()) {
97 // these xpcom pointers might need to be proxied back to the
98 // main thread to delete safely, but if this request had its
99 // callbacks called normally they will all be null and this is a nop
101 nsCOMPtr<nsIThread> mainThread;
102 NS_GetMainThread(getter_AddRefs(mainThread));
104 if (mChannel) {
105 nsIChannel *forgettable;
106 mChannel.forget(&forgettable);
107 NS_ProxyRelease(mainThread, forgettable, false);
108 }
110 if (mCallback) {
111 nsIProtocolProxyCallback *forgettable;
112 mCallback.forget(&forgettable);
113 NS_ProxyRelease(mainThread, forgettable, false);
114 }
116 if (mProxyInfo) {
117 nsIProxyInfo *forgettable;
118 mProxyInfo.forget(&forgettable);
119 NS_ProxyRelease(mainThread, forgettable, false);
120 }
122 if (mXPComPPS) {
123 nsIProtocolProxyService *forgettable;
124 mXPComPPS.forget(&forgettable);
125 NS_ProxyRelease(mainThread, forgettable, false);
126 }
127 }
128 }
130 void SetResult(nsresult status, nsIProxyInfo *pi)
131 {
132 mStatus = status;
133 mProxyInfo = pi;
134 }
136 NS_IMETHOD Run()
137 {
138 if (mCallback)
139 DoCallback();
140 return NS_OK;
141 }
143 NS_IMETHOD Cancel(nsresult reason)
144 {
145 NS_ENSURE_ARG(NS_FAILED(reason));
147 // If we've already called DoCallback then, nothing more to do.
148 if (!mCallback)
149 return NS_OK;
151 SetResult(reason, nullptr);
152 return DispatchCallback();
153 }
155 nsresult DispatchCallback()
156 {
157 if (mDispatched) // Only need to dispatch once
158 return NS_OK;
160 nsresult rv = NS_DispatchToCurrentThread(this);
161 if (NS_FAILED(rv))
162 NS_WARNING("unable to dispatch callback event");
163 else {
164 mDispatched = true;
165 return NS_OK;
166 }
168 mCallback = nullptr; // break possible reference cycle
169 return rv;
170 }
172 private:
174 // Called asynchronously, so we do not need to post another PLEvent
175 // before calling DoCallback.
176 void OnQueryComplete(nsresult status,
177 const nsCString &pacString,
178 const nsCString &newPACURL)
179 {
180 // If we've already called DoCallback then, nothing more to do.
181 if (!mCallback)
182 return;
184 // Provided we haven't been canceled...
185 if (mStatus == NS_OK) {
186 mStatus = status;
187 mPACString = pacString;
188 mPACURL = newPACURL;
189 }
191 // In the cancelation case, we may still have another PLEvent in
192 // the queue that wants to call DoCallback. No need to wait for
193 // it, just run the callback now.
194 DoCallback();
195 }
197 void DoCallback()
198 {
199 if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
200 // If the PAC service is not avail (e.g. failed pac load
201 // or shutdown) then we will be going direct. Make that
202 // mapping now so that any filters are still applied.
203 mPACString = NS_LITERAL_CSTRING("DIRECT;");
204 mStatus = NS_OK;
205 }
207 // Generate proxy info from the PAC string if appropriate
208 if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
209 mPPS->ProcessPACString(mPACString, mResolveFlags,
210 getter_AddRefs(mProxyInfo));
211 nsCOMPtr<nsIURI> uri;
212 mChannel->GetURI(getter_AddRefs(uri));
214 // Now apply proxy filters
215 nsProtocolInfo info;
216 mStatus = mPPS->GetProtocolInfo(uri, &info);
217 if (NS_SUCCEEDED(mStatus))
218 mPPS->ApplyFilters(mChannel, info, mProxyInfo);
219 else
220 mProxyInfo = nullptr;
222 LOG(("pac thread callback %s\n", mPACString.get()));
223 if (NS_SUCCEEDED(mStatus))
224 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
225 mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
226 }
227 else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
228 LOG(("pac thread callback indicates new pac file load\n"));
230 // trigger load of new pac url
231 nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
232 if (NS_SUCCEEDED(rv)) {
233 // now that the load is triggered, we can resubmit the query
234 nsRefPtr<nsAsyncResolveRequest> newRequest =
235 new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags, mCallback);
236 rv = mPPS->mPACMan->AsyncGetProxyForChannel(mChannel, newRequest, true);
237 }
239 if (NS_FAILED(rv))
240 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
242 // do not call onproxyavailable() in SUCCESS case - the newRequest will
243 // take care of that
244 }
245 else {
246 LOG(("pac thread callback did not provide information %X\n", mStatus));
247 if (NS_SUCCEEDED(mStatus))
248 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
249 mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
250 }
252 // We are on the main thread now and don't need these any more so
253 // release them to avoid having to proxy them back to the main thread
254 // in the dtor
255 mCallback = nullptr; // in case the callback holds an owning ref to us
256 mPPS = nullptr;
257 mXPComPPS = nullptr;
258 mChannel = nullptr;
259 mProxyInfo = nullptr;
260 }
262 private:
264 nsresult mStatus;
265 nsCString mPACString;
266 nsCString mPACURL;
267 bool mDispatched;
268 uint32_t mResolveFlags;
270 nsProtocolProxyService *mPPS;
271 nsCOMPtr<nsIProtocolProxyService> mXPComPPS;
272 nsCOMPtr<nsIChannel> mChannel;
273 nsCOMPtr<nsIProtocolProxyCallback> mCallback;
274 nsCOMPtr<nsIProxyInfo> mProxyInfo;
275 };
277 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
279 //----------------------------------------------------------------------------
281 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
283 //
284 // apply mask to address (zeros out excluded bits).
285 //
286 // NOTE: we do the byte swapping here to minimize overall swapping.
287 //
288 static void
289 proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len)
290 {
291 if (mask_len == 128)
292 return;
294 if (mask_len > 96) {
295 addr.pr_s6_addr32[3] = PR_htonl(
296 PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
297 }
298 else if (mask_len > 64) {
299 addr.pr_s6_addr32[3] = 0;
300 addr.pr_s6_addr32[2] = PR_htonl(
301 PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
302 }
303 else if (mask_len > 32) {
304 addr.pr_s6_addr32[3] = 0;
305 addr.pr_s6_addr32[2] = 0;
306 addr.pr_s6_addr32[1] = PR_htonl(
307 PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
308 }
309 else {
310 addr.pr_s6_addr32[3] = 0;
311 addr.pr_s6_addr32[2] = 0;
312 addr.pr_s6_addr32[1] = 0;
313 addr.pr_s6_addr32[0] = PR_htonl(
314 PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
315 }
316 }
318 static void
319 proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
320 const char *aPref,
321 nsCString &aResult)
322 {
323 nsXPIDLCString temp;
324 nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
325 if (NS_FAILED(rv))
326 aResult.Truncate();
327 else {
328 aResult.Assign(temp);
329 // all of our string prefs are hostnames, so we should remove any
330 // whitespace characters that the user might have unknowingly entered.
331 aResult.StripWhitespace();
332 }
333 }
335 static void
336 proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
337 const char *aPref,
338 int32_t &aResult)
339 {
340 int32_t temp;
341 nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
342 if (NS_FAILED(rv))
343 aResult = -1;
344 else
345 aResult = temp;
346 }
348 static void
349 proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
350 const char *aPref,
351 bool &aResult)
352 {
353 bool temp;
354 nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
355 if (NS_FAILED(rv))
356 aResult = false;
357 else
358 aResult = temp;
359 }
361 //----------------------------------------------------------------------------
363 static const int32_t PROXYCONFIG_DIRECT4X = 3;
364 static const int32_t PROXYCONFIG_COUNT = 6;
366 NS_IMPL_ADDREF(nsProtocolProxyService)
367 NS_IMPL_RELEASE(nsProtocolProxyService)
368 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
369 NS_PROTOCOLPROXYSERVICE_CID)
370 NS_IMPL_QUERY_INTERFACE_CI(nsProtocolProxyService,
371 nsIProtocolProxyService,
372 nsIProtocolProxyService2,
373 nsIObserver)
374 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService,
375 nsIProtocolProxyService,
376 nsIProtocolProxyService2)
378 nsProtocolProxyService::nsProtocolProxyService()
379 : mFilterLocalHosts(false)
380 , mFilters(nullptr)
381 , mProxyConfig(PROXYCONFIG_DIRECT)
382 , mHTTPProxyPort(-1)
383 , mFTPProxyPort(-1)
384 , mHTTPSProxyPort(-1)
385 , mSOCKSProxyPort(-1)
386 , mSOCKSProxyVersion(4)
387 , mSOCKSProxyRemoteDNS(false)
388 , mPACMan(nullptr)
389 , mSessionStart(PR_Now())
390 , mFailedProxyTimeout(30 * 60) // 30 minute default
391 {
392 }
394 nsProtocolProxyService::~nsProtocolProxyService()
395 {
396 // These should have been cleaned up in our Observe method.
397 NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr &&
398 mPACMan == nullptr, "what happened to xpcom-shutdown?");
399 }
401 // nsProtocolProxyService methods
402 nsresult
403 nsProtocolProxyService::Init()
404 {
405 // failure to access prefs is non-fatal
406 nsCOMPtr<nsIPrefBranch> prefBranch =
407 do_GetService(NS_PREFSERVICE_CONTRACTID);
408 if (prefBranch) {
409 // monitor proxy prefs
410 prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
412 // read all prefs
413 PrefsChanged(prefBranch, nullptr);
414 }
416 // register for shutdown notification so we can clean ourselves up properly.
417 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
418 if (obs)
419 obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
421 return NS_OK;
422 }
424 NS_IMETHODIMP
425 nsProtocolProxyService::Observe(nsISupports *aSubject,
426 const char *aTopic,
427 const char16_t *aData)
428 {
429 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
430 // cleanup
431 if (mHostFiltersArray.Length() > 0) {
432 mHostFiltersArray.Clear();
433 }
434 if (mFilters) {
435 delete mFilters;
436 mFilters = nullptr;
437 }
438 if (mPACMan) {
439 mPACMan->Shutdown();
440 mPACMan = nullptr;
441 }
442 }
443 else {
444 NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
445 "what is this random observer event?");
446 nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
447 if (prefs)
448 PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
449 }
450 return NS_OK;
451 }
453 void
454 nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
455 const char *pref)
456 {
457 nsresult rv = NS_OK;
458 bool reloadPAC = false;
459 nsXPIDLCString tempString;
461 if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
462 int32_t type = -1;
463 rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
464 if (NS_SUCCEEDED(rv)) {
465 // bug 115720 - for ns4.x backwards compatibility
466 if (type == PROXYCONFIG_DIRECT4X) {
467 type = PROXYCONFIG_DIRECT;
468 // Reset the type so that the dialog looks correct, and we
469 // don't have to handle this case everywhere else
470 // I'm paranoid about a loop of some sort - only do this
471 // if we're enumerating all prefs, and ignore any error
472 if (!pref)
473 prefBranch->SetIntPref(PROXY_PREF("type"), type);
474 } else if (type >= PROXYCONFIG_COUNT) {
475 LOG(("unknown proxy type: %lu; assuming direct\n", type));
476 type = PROXYCONFIG_DIRECT;
477 }
478 mProxyConfig = type;
479 reloadPAC = true;
480 }
482 if (mProxyConfig == PROXYCONFIG_SYSTEM) {
483 mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
484 if (!mSystemProxySettings)
485 mProxyConfig = PROXYCONFIG_DIRECT;
486 ResetPACThread();
487 } else {
488 if (mSystemProxySettings) {
489 mSystemProxySettings = nullptr;
490 ResetPACThread();
491 }
492 }
493 }
495 if (!pref || !strcmp(pref, PROXY_PREF("http")))
496 proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
498 if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
499 proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
501 if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
502 proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
504 if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
505 proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
507 if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
508 proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
510 if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
511 proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
513 if (!pref || !strcmp(pref, PROXY_PREF("socks")))
514 proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
516 if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
517 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
519 if (!pref || !strcmp(pref, PROXY_PREF("socks_username")))
520 proxy_GetStringPref(prefBranch, PROXY_PREF("socks_username"), mSOCKSProxyUsername);
522 if (!pref || !strcmp(pref, PROXY_PREF("socks_password")))
523 proxy_GetStringPref(prefBranch, PROXY_PREF("socks_password"), mSOCKSProxyPassword);
525 if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
526 int32_t version;
527 proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
528 // make sure this preference value remains sane
529 if (version == 5)
530 mSOCKSProxyVersion = 5;
531 else
532 mSOCKSProxyVersion = 4;
533 }
535 if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
536 proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
537 mSOCKSProxyRemoteDNS);
539 if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
540 proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
541 mFailedProxyTimeout);
543 if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
544 rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
545 getter_Copies(tempString));
546 if (NS_SUCCEEDED(rv))
547 LoadHostFilters(tempString.get());
548 }
550 // We're done if not using something that could give us a PAC URL
551 // (PAC, WPAD or System)
552 if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
553 mProxyConfig != PROXYCONFIG_SYSTEM)
554 return;
556 // OK, we need to reload the PAC file if:
557 // 1) network.proxy.type changed, or
558 // 2) network.proxy.autoconfig_url changed and PAC is configured
560 if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
561 reloadPAC = true;
563 if (reloadPAC) {
564 tempString.Truncate();
565 if (mProxyConfig == PROXYCONFIG_PAC) {
566 prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
567 getter_Copies(tempString));
568 } else if (mProxyConfig == PROXYCONFIG_WPAD) {
569 // We diverge from the WPAD spec here in that we don't walk the
570 // hosts's FQDN, stripping components until we hit a TLD. Doing so
571 // is dangerous in the face of an incomplete list of TLDs, and TLDs
572 // get added over time. We could consider doing only a single
573 // substitution of the first component, if that proves to help
574 // compatibility.
575 tempString.AssignLiteral(WPAD_URL);
576 } else if (mSystemProxySettings) {
577 // Get System Proxy settings if available
578 mSystemProxySettings->GetPACURI(tempString);
579 }
580 if (!tempString.IsEmpty())
581 ConfigureFromPAC(tempString, false);
582 }
583 }
585 bool
586 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort)
587 {
588 if (mHostFiltersArray.Length() == 0)
589 return true;
591 int32_t port;
592 nsAutoCString host;
594 nsresult rv = aURI->GetAsciiHost(host);
595 if (NS_FAILED(rv) || host.IsEmpty())
596 return false;
598 rv = aURI->GetPort(&port);
599 if (NS_FAILED(rv))
600 return false;
601 if (port == -1)
602 port = defaultPort;
604 PRNetAddr addr;
605 bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
607 PRIPv6Addr ipv6;
608 if (is_ipaddr) {
609 // convert parsed address to IPv6
610 if (addr.raw.family == PR_AF_INET) {
611 // convert to IPv4-mapped address
612 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
613 }
614 else if (addr.raw.family == PR_AF_INET6) {
615 // copy the address
616 memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
617 }
618 else {
619 NS_WARNING("unknown address family");
620 return true; // allow proxying
621 }
622 }
624 // Don't use proxy for local hosts (plain hostname, no dots)
625 if (!is_ipaddr && mFilterLocalHosts && (kNotFound == host.FindChar('.'))) {
626 LOG(("Not using proxy for this local host [%s]!\n", host.get()));
627 return false; // don't allow proxying
628 }
630 int32_t index = -1;
631 while (++index < int32_t(mHostFiltersArray.Length())) {
632 HostInfo *hinfo = mHostFiltersArray[index];
634 if (is_ipaddr != hinfo->is_ipaddr)
635 continue;
636 if (hinfo->port && hinfo->port != port)
637 continue;
639 if (is_ipaddr) {
640 // generate masked version of target IPv6 address
641 PRIPv6Addr masked;
642 memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
643 proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
645 // check for a match
646 if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
647 return false; // proxy disallowed
648 }
649 else {
650 uint32_t host_len = host.Length();
651 uint32_t filter_host_len = hinfo->name.host_len;
653 if (host_len >= filter_host_len) {
654 //
655 // compare last |filter_host_len| bytes of target hostname.
656 //
657 const char *host_tail = host.get() + host_len - filter_host_len;
658 if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len))
659 return false; // proxy disallowed
660 }
661 }
662 }
663 return true;
664 }
666 // kProxyType\* may be referred to externally in
667 // nsProxyInfo in order to compare by string pointer
668 namespace mozilla {
669 const char kProxyType_HTTP[] = "http";
670 const char kProxyType_PROXY[] = "proxy";
671 const char kProxyType_SOCKS[] = "socks";
672 const char kProxyType_SOCKS4[] = "socks4";
673 const char kProxyType_SOCKS5[] = "socks5";
674 const char kProxyType_DIRECT[] = "direct";
675 const char kProxyType_UNKNOWN[] = "unknown";
676 }
678 const char *
679 nsProtocolProxyService::ExtractProxyInfo(const char *start,
680 uint32_t aResolveFlags,
681 nsProxyInfo **result)
682 {
683 *result = nullptr;
684 uint32_t flags = 0;
686 // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
688 // find end of proxy info delimiter
689 const char *end = start;
690 while (*end && *end != ';') ++end;
692 // find end of proxy type delimiter
693 const char *sp = start;
694 while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
696 uint32_t len = sp - start;
697 const char *type = nullptr;
698 switch (len) {
699 case 5:
700 if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0)
701 type = kProxyType_HTTP;
702 else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0)
703 type = kProxyType_SOCKS4; // assume v4 for 4x compat
704 break;
705 case 6:
706 if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
707 type = kProxyType_DIRECT;
708 else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
709 type = kProxyType_SOCKS4;
710 else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
711 // map "SOCKS5" to "socks" to match contract-id of registered
712 // SOCKS-v5 socket provider.
713 type = kProxyType_SOCKS;
714 break;
715 }
716 if (type) {
717 const char *host = nullptr, *hostEnd = nullptr;
718 int32_t port = -1;
720 // If it's a SOCKS5 proxy, do name resolution on the server side.
721 // We could use this with SOCKS4a servers too, but they might not
722 // support it.
723 if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
724 flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
726 // extract host:port
727 start = sp;
728 while ((*start == ' ' || *start == '\t') && start < end)
729 start++;
731 // port defaults
732 if (type == kProxyType_HTTP)
733 port = 80;
734 else
735 port = 1080;
737 nsProxyInfo *pi = new nsProxyInfo();
738 pi->mType = type;
739 pi->mFlags = flags;
740 pi->mResolveFlags = aResolveFlags;
741 pi->mTimeout = mFailedProxyTimeout;
743 // www.foo.com:8080 and http://www.foo.com:8080
744 nsDependentCSubstring maybeURL(start, end - start);
745 nsCOMPtr<nsIURI> pacURI;
747 nsAutoCString urlHost;
748 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) &&
749 NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) &&
750 !urlHost.IsEmpty()) {
751 // http://www.example.com:8080
753 pi->mHost = urlHost;
755 int32_t tPort;
756 if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
757 port = tPort;
758 }
759 pi->mPort = port;
760 }
761 else {
762 // www.example.com:8080
763 if (start < end) {
764 host = start;
765 hostEnd = strchr(host, ':');
766 if (!hostEnd || hostEnd > end) {
767 hostEnd = end;
768 // no port, so assume default
769 }
770 else {
771 port = atoi(hostEnd + 1);
772 }
773 }
774 // YES, it is ok to specify a null proxy host.
775 if (host) {
776 pi->mHost.Assign(host, hostEnd - host);
777 pi->mPort = port;
778 }
779 }
780 NS_ADDREF(*result = pi);
781 }
783 while (*end == ';' || *end == ' ' || *end == '\t')
784 ++end;
785 return end;
786 }
788 void
789 nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
790 {
791 key.AssignASCII(pi->mType);
792 if (!pi->mHost.IsEmpty()) {
793 key.Append(' ');
794 key.Append(pi->mHost);
795 key.Append(':');
796 key.AppendInt(pi->mPort);
797 }
798 }
800 uint32_t
801 nsProtocolProxyService::SecondsSinceSessionStart()
802 {
803 PRTime now = PR_Now();
805 // get time elapsed since session start
806 int64_t diff = now - mSessionStart;
808 // convert microseconds to seconds
809 diff /= PR_USEC_PER_SEC;
811 // return converted 32 bit value
812 return uint32_t(diff);
813 }
815 void
816 nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
817 {
818 nsAutoCString key;
819 GetProxyKey(pi, key);
820 mFailedProxies.Remove(key);
821 }
823 void
824 nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
825 {
826 nsAutoCString key;
827 GetProxyKey(pi, key);
829 uint32_t dsec = SecondsSinceSessionStart();
831 // Add timeout to interval (this is the time when the proxy can
832 // be tried again).
833 dsec += pi->mTimeout;
835 // NOTE: The classic codebase would increase the timeout value
836 // incrementally each time a subsequent failure occurred.
837 // We could do the same, but it would require that we not
838 // remove proxy entries in IsProxyDisabled or otherwise
839 // change the way we are recording disabled proxies.
840 // Simpler is probably better for now, and at least the
841 // user can tune the timeout setting via preferences.
843 LOG(("DisableProxy %s %d\n", key.get(), dsec));
845 // If this fails, oh well... means we don't have enough memory
846 // to remember the failed proxy.
847 mFailedProxies.Put(key, dsec);
848 }
850 bool
851 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
852 {
853 nsAutoCString key;
854 GetProxyKey(pi, key);
856 uint32_t val;
857 if (!mFailedProxies.Get(key, &val))
858 return false;
860 uint32_t dsec = SecondsSinceSessionStart();
862 // if time passed has exceeded interval, then try proxy again.
863 if (dsec > val) {
864 mFailedProxies.Remove(key);
865 return false;
866 }
868 return true;
869 }
871 nsresult
872 nsProtocolProxyService::SetupPACThread()
873 {
874 if (mPACMan)
875 return NS_OK;
877 mPACMan = new nsPACMan();
879 bool mainThreadOnly;
880 nsresult rv;
881 if (mSystemProxySettings &&
882 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
883 !mainThreadOnly) {
884 rv = mPACMan->Init(mSystemProxySettings);
885 }
886 else {
887 rv = mPACMan->Init(nullptr);
888 }
890 if (NS_FAILED(rv))
891 mPACMan = nullptr;
892 return rv;
893 }
895 nsresult
896 nsProtocolProxyService::ResetPACThread()
897 {
898 if (!mPACMan)
899 return NS_OK;
901 mPACMan->Shutdown();
902 mPACMan = nullptr;
903 return SetupPACThread();
904 }
906 nsresult
907 nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
908 bool forceReload)
909 {
910 SetupPACThread();
912 if (mPACMan->IsPACURI(spec) && !forceReload)
913 return NS_OK;
915 mFailedProxies.Clear();
917 return mPACMan->LoadPACFromURI(spec);
918 }
920 void
921 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
922 uint32_t aResolveFlags,
923 nsIProxyInfo **result)
924 {
925 if (pacString.IsEmpty()) {
926 *result = nullptr;
927 return;
928 }
930 const char *proxies = pacString.get();
932 nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
933 while (*proxies) {
934 proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
935 if (pi) {
936 if (last) {
937 NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
938 last->mNext = pi;
939 }
940 else
941 first = pi;
942 last = pi;
943 }
944 }
945 *result = first;
946 }
948 // nsIProtocolProxyService2
949 NS_IMETHODIMP
950 nsProtocolProxyService::ReloadPAC()
951 {
952 nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
953 if (!prefs)
954 return NS_OK;
956 int32_t type;
957 nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
958 if (NS_FAILED(rv))
959 return NS_OK;
961 nsXPIDLCString pacSpec;
962 if (type == PROXYCONFIG_PAC)
963 prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
964 else if (type == PROXYCONFIG_WPAD)
965 pacSpec.AssignLiteral(WPAD_URL);
967 if (!pacSpec.IsEmpty())
968 ConfigureFromPAC(pacSpec, true);
969 return NS_OK;
970 }
972 // When sync interface is removed this can go away too
973 // The nsPACManCallback portion of this implementation should be run
974 // off the main thread, because it uses a condvar for signaling and
975 // the main thread is blocking on that condvar -
976 // so call nsPACMan::AsyncGetProxyForChannel() with
977 // a false mainThreadResponse parameter.
978 class nsAsyncBridgeRequest MOZ_FINAL : public nsPACManCallback
979 {
980 NS_DECL_THREADSAFE_ISUPPORTS
982 nsAsyncBridgeRequest()
983 : mMutex("nsDeprecatedCallback")
984 , mCondVar(mMutex, "nsDeprecatedCallback")
985 , mCompleted(false)
986 {
987 }
989 void OnQueryComplete(nsresult status,
990 const nsCString &pacString,
991 const nsCString &newPACURL)
992 {
993 MutexAutoLock lock(mMutex);
994 mCompleted = true;
995 mStatus = status;
996 mPACString = pacString;
997 mPACURL = newPACURL;
998 mCondVar.Notify();
999 }
1001 void Lock() { mMutex.Lock(); }
1002 void Unlock() { mMutex.Unlock(); }
1003 void Wait() { mCondVar.Wait(PR_SecondsToInterval(3)); }
1005 private:
1006 ~nsAsyncBridgeRequest()
1007 {
1008 }
1010 friend class nsProtocolProxyService;
1012 Mutex mMutex;
1013 CondVar mCondVar;
1015 nsresult mStatus;
1016 nsCString mPACString;
1017 nsCString mPACURL;
1018 bool mCompleted;
1019 };
1020 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
1022 // nsProtocolProxyService
1023 nsresult
1024 nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel,
1025 uint32_t aFlags,
1026 nsIProxyInfo **retval)
1027 {
1028 NS_ENSURE_ARG_POINTER(aChannel);
1030 nsCOMPtr<nsIURI> uri;
1031 aChannel->GetURI(getter_AddRefs(uri));
1033 nsProtocolInfo info;
1034 nsresult rv = GetProtocolInfo(uri, &info);
1035 if (NS_FAILED(rv))
1036 return rv;
1038 nsCOMPtr<nsIProxyInfo> pi;
1039 bool usePACThread;
1041 // SystemProxySettings and PAC files can block the main thread
1042 // but if neither of them are in use, we can just do the work
1043 // right here and directly invoke the callback
1045 rv = Resolve_Internal(aChannel, info, aFlags, &usePACThread, getter_AddRefs(pi));
1046 if (NS_FAILED(rv))
1047 return rv;
1049 if (!usePACThread || !mPACMan) {
1050 ApplyFilters(aChannel, info, pi);
1051 pi.forget(retval);
1052 return NS_OK;
1053 }
1055 // Use the PAC thread to do the work, so we don't have to reimplement that
1056 // code, but block this thread on that completion.
1057 nsRefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
1058 ctx->Lock();
1059 if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForChannel(aChannel, ctx, false))) {
1060 // this can really block the main thread, so cap it at 3 seconds
1061 ctx->Wait();
1062 }
1063 ctx->Unlock();
1064 if (!ctx->mCompleted)
1065 return NS_ERROR_FAILURE;
1066 if (NS_FAILED(ctx->mStatus))
1067 return ctx->mStatus;
1069 // pretty much duplicate real DoCallback logic
1071 // Generate proxy info from the PAC string if appropriate
1072 if (!ctx->mPACString.IsEmpty()) {
1073 LOG(("sync pac thread callback %s\n", ctx->mPACString.get()));
1074 ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi));
1075 ApplyFilters(aChannel, info, pi);
1076 pi.forget(retval);
1077 return NS_OK;
1078 }
1080 if (!ctx->mPACURL.IsEmpty()) {
1081 NS_WARNING("sync pac thread callback indicates new pac file load\n");
1082 // This is a problem and is one of the reasons this blocking interface
1083 // is deprecated. The main loop needs to spin to make this reload happen. So
1084 // we are going to kick off the reload and return an error - it will work
1085 // next time. Because this sync interface is only used in the java plugin it
1086 // is extremely likely that the pac file has already been loaded anyhow.
1088 rv = ConfigureFromPAC(ctx->mPACURL, false);
1089 if (NS_FAILED(rv))
1090 return rv;
1091 return NS_ERROR_NOT_AVAILABLE;
1092 }
1094 *retval = nullptr;
1095 return NS_OK;
1096 }
1098 nsresult
1099 nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
1100 nsIProtocolProxyCallback *callback,
1101 nsICancelable **result,
1102 bool isSyncOK)
1103 {
1104 NS_ENSURE_ARG_POINTER(channel);
1105 NS_ENSURE_ARG_POINTER(callback);
1107 nsCOMPtr<nsIURI> uri;
1108 channel->GetURI(getter_AddRefs(uri));
1110 *result = nullptr;
1111 nsRefPtr<nsAsyncResolveRequest> ctx =
1112 new nsAsyncResolveRequest(this, channel, flags, callback);
1114 nsProtocolInfo info;
1115 nsresult rv = GetProtocolInfo(uri, &info);
1116 if (NS_FAILED(rv))
1117 return rv;
1119 nsCOMPtr<nsIProxyInfo> pi;
1120 bool usePACThread;
1122 // SystemProxySettings and PAC files can block the main thread
1123 // but if neither of them are in use, we can just do the work
1124 // right here and directly invoke the callback
1126 rv = Resolve_Internal(channel, info, flags, &usePACThread, getter_AddRefs(pi));
1127 if (NS_FAILED(rv))
1128 return rv;
1130 if (!usePACThread || !mPACMan) {
1131 // we can do it locally
1132 ApplyFilters(channel, info, pi);
1133 ctx->SetResult(NS_OK, pi);
1134 if (isSyncOK) {
1135 ctx->Run();
1136 return NS_OK;
1137 }
1139 rv = ctx->DispatchCallback();
1140 if (NS_SUCCEEDED(rv))
1141 ctx.forget(result);
1142 return rv;
1143 }
1145 // else kick off a PAC thread query
1147 rv = mPACMan->AsyncGetProxyForChannel(channel, ctx, true);
1148 if (NS_SUCCEEDED(rv))
1149 ctx.forget(result);
1150 return rv;
1151 }
1153 // nsIProtocolProxyService
1154 NS_IMETHODIMP
1155 nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
1156 nsIProtocolProxyCallback *callback,
1157 nsICancelable **result)
1158 {
1159 return AsyncResolveInternal(channel, flags, callback, result, true);
1160 }
1162 NS_IMETHODIMP
1163 nsProtocolProxyService::AsyncResolve(nsIChannel *channel, uint32_t flags,
1164 nsIProtocolProxyCallback *callback,
1165 nsICancelable **result)
1166 {
1167 return AsyncResolveInternal(channel, flags, callback, result, false);
1168 }
1170 NS_IMETHODIMP
1171 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
1172 const nsACString &aHost,
1173 int32_t aPort,
1174 uint32_t aFlags,
1175 uint32_t aFailoverTimeout,
1176 nsIProxyInfo *aFailoverProxy,
1177 nsIProxyInfo **aResult)
1178 {
1179 static const char *types[] = {
1180 kProxyType_HTTP,
1181 kProxyType_SOCKS,
1182 kProxyType_SOCKS4,
1183 kProxyType_DIRECT
1184 };
1186 // resolve type; this allows us to avoid copying the type string into each
1187 // proxy info instance. we just reference the string literals directly :)
1188 const char *type = nullptr;
1189 for (uint32_t i=0; i<ArrayLength(types); ++i) {
1190 if (aType.LowerCaseEqualsASCII(types[i])) {
1191 type = types[i];
1192 break;
1193 }
1194 }
1195 NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
1197 return NewProxyInfo_Internal(type, aHost, aPort,
1198 mSOCKSProxyUsername, mSOCKSProxyPassword,
1199 aFlags, aFailoverTimeout,
1200 aFailoverProxy, 0, aResult);
1201 }
1203 NS_IMETHODIMP
1204 nsProtocolProxyService::NewSOCKSProxyInfo(const nsACString &aHost,
1205 int32_t aPort,
1206 const nsACString &aUsername,
1207 const nsACString &aPassword,
1208 uint32_t aFlags,
1209 uint32_t aFailoverTimeout,
1210 nsIProxyInfo *aFailoverProxy,
1211 nsIProxyInfo **aResult)
1212 {
1213 return NewProxyInfo_Internal(kProxyType_SOCKS, aHost, aPort,
1214 aUsername, aPassword,
1215 aFlags, aFailoverTimeout,
1216 aFailoverProxy, 0, aResult);
1217 }
1219 NS_IMETHODIMP
1220 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy,
1221 nsIURI *aURI,
1222 nsresult aStatus,
1223 nsIProxyInfo **aResult)
1224 {
1225 // We only support failover when a PAC file is configured, either
1226 // directly or via system settings
1227 if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1228 mProxyConfig != PROXYCONFIG_SYSTEM)
1229 return NS_ERROR_NOT_AVAILABLE;
1231 // Verify that |aProxy| is one of our nsProxyInfo objects.
1232 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1233 NS_ENSURE_ARG(pi);
1234 // OK, the QI checked out. We can proceed.
1236 // Remember that this proxy is down.
1237 DisableProxy(pi);
1239 // NOTE: At this point, we might want to prompt the user if we have
1240 // not already tried going DIRECT. This is something that the
1241 // classic codebase supported; however, IE6 does not prompt.
1243 if (!pi->mNext)
1244 return NS_ERROR_NOT_AVAILABLE;
1246 LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
1247 pi->mType, pi->mHost.get(), pi->mPort,
1248 pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
1250 NS_ADDREF(*aResult = pi->mNext);
1251 return NS_OK;
1252 }
1254 nsresult
1255 nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
1256 {
1257 if (!mFilters) {
1258 mFilters = link;
1259 return NS_OK;
1260 }
1262 // insert into mFilters in sorted order
1263 FilterLink *last = nullptr;
1264 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1265 if (position < iter->position) {
1266 if (last) {
1267 link->next = last->next;
1268 last->next = link;
1269 }
1270 else {
1271 link->next = mFilters;
1272 mFilters = link;
1273 }
1274 return NS_OK;
1275 }
1276 last = iter;
1277 }
1278 // our position is equal to or greater than the last link in the list
1279 last->next = link;
1280 return NS_OK;
1281 }
1283 NS_IMETHODIMP
1284 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
1285 uint32_t position)
1286 {
1287 UnregisterFilter(filter); // remove this filter if we already have it
1289 FilterLink *link = new FilterLink(position, filter);
1290 if (!link)
1291 return NS_ERROR_OUT_OF_MEMORY;
1292 return InsertFilterLink(link, position);
1293 }
1295 NS_IMETHODIMP
1296 nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter,
1297 uint32_t position)
1298 {
1299 UnregisterChannelFilter(channelFilter); // remove this filter if we already have it
1301 FilterLink *link = new FilterLink(position, channelFilter);
1302 if (!link)
1303 return NS_ERROR_OUT_OF_MEMORY;
1304 return InsertFilterLink(link, position);
1305 }
1307 nsresult
1308 nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
1309 {
1310 FilterLink *last = nullptr;
1311 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1312 nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
1313 if (object == givenObject) {
1314 if (last)
1315 last->next = iter->next;
1316 else
1317 mFilters = iter->next;
1318 iter->next = nullptr;
1319 delete iter;
1320 return NS_OK;
1321 }
1322 last = iter;
1323 }
1325 // No need to throw an exception in this case.
1326 return NS_OK;
1327 }
1329 NS_IMETHODIMP
1330 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
1331 // QI to nsISupports so we can safely test object identity.
1332 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1333 return RemoveFilterLink(givenObject);
1334 }
1336 NS_IMETHODIMP
1337 nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
1338 // QI to nsISupports so we can safely test object identity.
1339 nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
1340 return RemoveFilterLink(givenObject);
1341 }
1343 NS_IMETHODIMP
1344 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType)
1345 {
1346 *aProxyConfigType = mProxyConfig;
1347 return NS_OK;
1348 }
1350 void
1351 nsProtocolProxyService::LoadHostFilters(const char *filters)
1352 {
1353 // check to see the owners flag? /!?/ TODO
1354 if (mHostFiltersArray.Length() > 0) {
1355 mHostFiltersArray.Clear();
1356 }
1358 if (!filters)
1359 return; // fail silently...
1361 //
1362 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1363 // filters = filter *( "," LWS filter)
1364 //
1365 // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
1366 mFilterLocalHosts = false;
1367 while (*filters) {
1368 // skip over spaces and ,
1369 while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
1370 filters++;
1372 const char *starthost = filters;
1373 const char *endhost = filters + 1; // at least that...
1374 const char *portLocation = 0;
1375 const char *maskLocation = 0;
1377 while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) {
1378 if (*endhost == ':')
1379 portLocation = endhost;
1380 else if (*endhost == '/')
1381 maskLocation = endhost;
1382 else if (*endhost == ']') // IPv6 address literals
1383 portLocation = 0;
1384 endhost++;
1385 }
1387 filters = endhost; // advance iterator up front
1389 // locate end of host
1390 const char *end = maskLocation ? maskLocation :
1391 portLocation ? portLocation :
1392 endhost;
1394 nsAutoCString str(starthost, end - starthost);
1396 // If the current host filter is "<local>", then all local (i.e.
1397 // no dots in the hostname) hosts should bypass the proxy
1398 if (str.EqualsIgnoreCase("<local>")) {
1399 mFilterLocalHosts = true;
1400 LOG(("loaded filter for local hosts "
1401 "(plain host names, no dots)\n"));
1402 // Continue to next host filter;
1403 continue;
1404 }
1406 // For all other host filters, create HostInfo object and add to list
1407 HostInfo *hinfo = new HostInfo();
1408 hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
1410 PRNetAddr addr;
1411 if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
1412 hinfo->is_ipaddr = true;
1413 hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6
1414 hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
1416 if (hinfo->ip.mask_len == 0) {
1417 NS_WARNING("invalid mask");
1418 goto loser;
1419 }
1421 if (addr.raw.family == PR_AF_INET) {
1422 // convert to IPv4-mapped address
1423 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1424 // adjust mask_len accordingly
1425 if (hinfo->ip.mask_len <= 32)
1426 hinfo->ip.mask_len += 96;
1427 }
1428 else if (addr.raw.family == PR_AF_INET6) {
1429 // copy the address
1430 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1431 }
1432 else {
1433 NS_WARNING("unknown address family");
1434 goto loser;
1435 }
1437 // apply mask to IPv6 address
1438 proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1439 }
1440 else {
1441 uint32_t startIndex, endIndex;
1442 if (str.First() == '*')
1443 startIndex = 1; // *.domain -> .domain
1444 else
1445 startIndex = 0;
1446 endIndex = (portLocation ? portLocation : endhost) - starthost;
1448 hinfo->is_ipaddr = false;
1449 hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex));
1451 if (!hinfo->name.host)
1452 goto loser;
1454 hinfo->name.host_len = endIndex - startIndex;
1455 }
1457 //#define DEBUG_DUMP_FILTERS
1458 #ifdef DEBUG_DUMP_FILTERS
1459 printf("loaded filter[%u]:\n", mHostFiltersArray.Length());
1460 printf(" is_ipaddr = %u\n", hinfo->is_ipaddr);
1461 printf(" port = %u\n", hinfo->port);
1462 if (hinfo->is_ipaddr) {
1463 printf(" ip.family = %x\n", hinfo->ip.family);
1464 printf(" ip.mask_len = %u\n", hinfo->ip.mask_len);
1466 PRNetAddr netAddr;
1467 PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1468 memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1470 char buf[256];
1471 PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1473 printf(" ip.addr = %s\n", buf);
1474 }
1475 else {
1476 printf(" name.host = %s\n", hinfo->name.host);
1477 }
1478 #endif
1480 mHostFiltersArray.AppendElement(hinfo);
1481 hinfo = nullptr;
1482 loser:
1483 delete hinfo;
1484 }
1485 }
1487 nsresult
1488 nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
1489 {
1490 NS_PRECONDITION(uri, "URI is null");
1491 NS_PRECONDITION(info, "info is null");
1493 nsresult rv;
1495 rv = uri->GetScheme(info->scheme);
1496 if (NS_FAILED(rv))
1497 return rv;
1499 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
1500 if (NS_FAILED(rv))
1501 return rv;
1503 nsCOMPtr<nsIProtocolHandler> handler;
1504 rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
1505 if (NS_FAILED(rv))
1506 return rv;
1508 rv = handler->GetProtocolFlags(&info->flags);
1509 if (NS_FAILED(rv))
1510 return rv;
1512 rv = handler->GetDefaultPort(&info->defaultPort);
1513 return rv;
1514 }
1516 nsresult
1517 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
1518 const nsACString &aHost,
1519 int32_t aPort,
1520 const nsACString &aUsername,
1521 const nsACString &aPassword,
1522 uint32_t aFlags,
1523 uint32_t aFailoverTimeout,
1524 nsIProxyInfo *aFailoverProxy,
1525 uint32_t aResolveFlags,
1526 nsIProxyInfo **aResult)
1527 {
1528 if (aPort <= 0)
1529 aPort = -1;
1531 nsCOMPtr<nsProxyInfo> failover;
1532 if (aFailoverProxy) {
1533 failover = do_QueryInterface(aFailoverProxy);
1534 NS_ENSURE_ARG(failover);
1535 }
1537 nsProxyInfo *proxyInfo = new nsProxyInfo();
1538 if (!proxyInfo)
1539 return NS_ERROR_OUT_OF_MEMORY;
1541 proxyInfo->mType = aType;
1542 proxyInfo->mHost = aHost;
1543 proxyInfo->mPort = aPort;
1544 proxyInfo->mUsername = aUsername;
1545 proxyInfo->mPassword = aPassword;
1546 proxyInfo->mFlags = aFlags;
1547 proxyInfo->mResolveFlags = aResolveFlags;
1548 proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
1549 ? mFailedProxyTimeout : aFailoverTimeout;
1550 failover.swap(proxyInfo->mNext);
1552 NS_ADDREF(*aResult = proxyInfo);
1553 return NS_OK;
1554 }
1556 nsresult
1557 nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
1558 const nsProtocolInfo &info,
1559 uint32_t flags,
1560 bool *usePACThread,
1561 nsIProxyInfo **result)
1562 {
1563 NS_ENSURE_ARG_POINTER(channel);
1564 nsresult rv = SetupPACThread();
1565 if (NS_FAILED(rv))
1566 return rv;
1568 *usePACThread = false;
1569 *result = nullptr;
1571 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1572 return NS_OK; // Can't proxy this (filters may not override)
1574 nsCOMPtr<nsIURI> uri;
1575 channel->GetURI(getter_AddRefs(uri));
1577 // See bug #586908.
1578 // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
1579 // here means that we will not use a proxy for this connection.
1580 if (mPACMan && mPACMan->IsPACURI(uri))
1581 return NS_OK;
1583 bool mainThreadOnly;
1584 if (mSystemProxySettings &&
1585 mProxyConfig == PROXYCONFIG_SYSTEM &&
1586 NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1587 !mainThreadOnly) {
1588 *usePACThread = true;
1589 return NS_OK;
1590 }
1592 if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
1593 // If the system proxy setting implementation is not threadsafe (e.g
1594 // linux gconf), we'll do it inline here. Such implementations promise
1595 // not to block
1597 nsAutoCString PACURI;
1598 nsAutoCString pacString;
1600 if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
1601 !PACURI.IsEmpty()) {
1602 // There is a PAC URI configured. If it is unchanged, then
1603 // just execute the PAC thread. If it is changed then load
1604 // the new value
1606 if (mPACMan && mPACMan->IsPACURI(PACURI)) {
1607 // unchanged
1608 *usePACThread = true;
1609 return NS_OK;
1610 }
1612 ConfigureFromPAC(PACURI, false);
1613 return NS_OK;
1614 }
1616 nsAutoCString spec;
1617 nsAutoCString host;
1618 nsAutoCString scheme;
1619 int32_t port = -1;
1621 uri->GetAsciiSpec(spec);
1622 uri->GetAsciiHost(host);
1623 uri->GetScheme(scheme);
1624 uri->GetPort(&port);
1626 // now try the system proxy settings for this particular url
1627 if (NS_SUCCEEDED(mSystemProxySettings->
1628 GetProxyForURI(spec, scheme, host, port,
1629 pacString))) {
1630 ProcessPACString(pacString, 0, result);
1631 return NS_OK;
1632 }
1633 }
1635 // if proxies are enabled and this host:port combo is supposed to use a
1636 // proxy, check for a proxy.
1637 if (mProxyConfig == PROXYCONFIG_DIRECT ||
1638 (mProxyConfig == PROXYCONFIG_MANUAL &&
1639 !CanUseProxy(uri, info.defaultPort)))
1640 return NS_OK;
1642 // Proxy auto config magic...
1643 if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
1644 // Do not query PAC now.
1645 *usePACThread = true;
1646 return NS_OK;
1647 }
1649 // If we aren't in manual proxy configuration mode then we don't
1650 // want to honor any manual specific prefs that might be still set
1651 if (mProxyConfig != PROXYCONFIG_MANUAL)
1652 return NS_OK;
1654 // proxy info values for manual configuration mode
1655 const char *type = nullptr;
1656 const nsACString *host = nullptr;
1657 int32_t port = -1;
1659 uint32_t proxyFlags = 0;
1661 if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
1662 !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
1663 host = &mSOCKSProxyHost;
1664 if (mSOCKSProxyVersion == 4)
1665 type = kProxyType_SOCKS4;
1666 else
1667 type = kProxyType_SOCKS;
1668 port = mSOCKSProxyPort;
1669 if (mSOCKSProxyRemoteDNS)
1670 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1671 }
1672 else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
1673 !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
1674 host = &mHTTPSProxyHost;
1675 type = kProxyType_HTTP;
1676 port = mHTTPSProxyPort;
1677 }
1678 else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
1679 ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
1680 info.scheme.EqualsLiteral("http"))) {
1681 host = &mHTTPProxyHost;
1682 type = kProxyType_HTTP;
1683 port = mHTTPProxyPort;
1684 }
1685 else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
1686 !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1687 info.scheme.EqualsLiteral("https")) {
1688 host = &mHTTPSProxyHost;
1689 type = kProxyType_HTTP;
1690 port = mHTTPSProxyPort;
1691 }
1692 else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
1693 !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1694 info.scheme.EqualsLiteral("ftp")) {
1695 host = &mFTPProxyHost;
1696 type = kProxyType_HTTP;
1697 port = mFTPProxyPort;
1698 }
1699 else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
1700 host = &mSOCKSProxyHost;
1701 if (mSOCKSProxyVersion == 4)
1702 type = kProxyType_SOCKS4;
1703 else
1704 type = kProxyType_SOCKS;
1705 port = mSOCKSProxyPort;
1706 if (mSOCKSProxyRemoteDNS)
1707 proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1708 }
1710 if (type) {
1711 rv = NewProxyInfo_Internal(type, *host, port,
1712 mSOCKSProxyUsername, mSOCKSProxyPassword,
1713 proxyFlags, UINT32_MAX, nullptr, flags,
1714 result);
1715 if (NS_FAILED(rv))
1716 return rv;
1717 }
1719 return NS_OK;
1720 }
1722 void
1723 nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy)
1724 {
1725 // Disable Prefetch in the DNS service if a proxy is in use.
1726 if (!aProxy)
1727 return;
1729 nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1730 if (!pi ||
1731 !pi->mType ||
1732 pi->mType == kProxyType_DIRECT)
1733 return;
1735 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
1736 if (!dns)
1737 return;
1738 nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
1739 if (!pdns)
1740 return;
1742 // We lose the prefetch optimization for the life of the dns service.
1743 pdns->SetPrefetchEnabled(false);
1744 }
1746 void
1747 nsProtocolProxyService::ApplyFilters(nsIChannel *channel, const nsProtocolInfo &info,
1748 nsIProxyInfo **list)
1749 {
1750 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1751 return;
1753 // We prune the proxy list prior to invoking each filter. This may be
1754 // somewhat inefficient, but it seems like a good idea since we want each
1755 // filter to "see" a valid proxy list.
1757 nsresult rv;
1758 nsCOMPtr<nsIProxyInfo> result;
1760 for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1761 PruneProxyInfo(info, list);
1762 if (!!iter->filter) {
1763 nsCOMPtr<nsIURI> uri;
1764 channel->GetURI(getter_AddRefs(uri));
1765 if (!!uri) {
1766 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
1767 nsCOMPtr<nsIURI> proxyURI = nullptr;
1768 if (!!httpChannel) {
1769 httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
1770 }
1771 rv = iter->filter->ApplyFilter(this, proxyURI ? proxyURI : uri, *list,
1772 getter_AddRefs(result));
1773 }
1774 } else if (!!iter->channelFilter) {
1775 rv = iter->channelFilter->ApplyFilter(this, channel, *list,
1776 getter_AddRefs(result));
1777 }
1778 if (NS_FAILED(rv))
1779 continue;
1780 result.swap(*list);
1781 }
1783 PruneProxyInfo(info, list);
1784 }
1786 void
1787 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
1788 nsIProxyInfo **list)
1789 {
1790 if (!*list)
1791 return;
1792 nsProxyInfo *head = nullptr;
1793 CallQueryInterface(*list, &head);
1794 if (!head) {
1795 NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
1796 return;
1797 }
1798 NS_RELEASE(*list);
1800 // Pruning of disabled proxies works like this:
1801 // - If all proxies are disabled, return the full list
1802 // - Otherwise, remove the disabled proxies.
1803 //
1804 // Pruning of disallowed proxies works like this:
1805 // - If the protocol handler disallows the proxy, then we disallow it.
1807 // Start by removing all disallowed proxies if required:
1808 if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
1809 nsProxyInfo *last = nullptr, *iter = head;
1810 while (iter) {
1811 if (iter->Type() == kProxyType_HTTP) {
1812 // reject!
1813 if (last)
1814 last->mNext = iter->mNext;
1815 else
1816 head = iter->mNext;
1817 nsProxyInfo *next = iter->mNext;
1818 iter->mNext = nullptr;
1819 iter->Release();
1820 iter = next;
1821 } else {
1822 last = iter;
1823 iter = iter->mNext;
1824 }
1825 }
1826 if (!head)
1827 return;
1828 }
1830 // Now, scan to see if all remaining proxies are disabled. If so, then
1831 // we'll just bail and return them all. Otherwise, we'll go and prune the
1832 // disabled ones.
1834 bool allDisabled = true;
1836 nsProxyInfo *iter;
1837 for (iter = head; iter; iter = iter->mNext) {
1838 if (!IsProxyDisabled(iter)) {
1839 allDisabled = false;
1840 break;
1841 }
1842 }
1844 if (allDisabled)
1845 LOG(("All proxies are disabled, so trying all again"));
1846 else {
1847 // remove any disabled proxies.
1848 nsProxyInfo *last = nullptr;
1849 for (iter = head; iter; ) {
1850 if (IsProxyDisabled(iter)) {
1851 // reject!
1852 nsProxyInfo *reject = iter;
1854 iter = iter->mNext;
1855 if (last)
1856 last->mNext = iter;
1857 else
1858 head = iter;
1860 reject->mNext = nullptr;
1861 NS_RELEASE(reject);
1862 continue;
1863 }
1865 // since we are about to use this proxy, make sure it is not on
1866 // the disabled proxy list. we'll add it back to that list if
1867 // we have to (in GetFailoverForProxy).
1868 //
1869 // XXX(darin): It might be better to do this as a final pass.
1870 //
1871 EnableProxy(iter);
1873 last = iter;
1874 iter = iter->mNext;
1875 }
1876 }
1878 // if only DIRECT was specified then return no proxy info, and we're done.
1879 if (head && !head->mNext && head->mType == kProxyType_DIRECT)
1880 NS_RELEASE(head);
1882 *list = head; // Transfer ownership
1883 }