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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Malcolm Smith <malsmith@cs.rmit.edu.au>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsProtocolProxyService.h"
41 #include "nsProxyInfo.h"
42 #include "nsIClassInfoImpl.h"
43 #include "nsIServiceManager.h"
44 #include "nsXPIDLString.h"
45 #include "nsIProxyAutoConfig.h"
46 #include "nsAutoLock.h"
47 #include "nsIIOService.h"
48 #include "nsIObserverService.h"
49 #include "nsIProtocolHandler.h"
50 #include "nsIProtocolProxyCallback.h"
51 #include "nsICancelable.h"
52 #include "nsIDNSService.h"
53 #include "nsIPrefService.h"
54 #include "nsIPrefBranch2.h"
55 #include "nsReadableUtils.h"
56 #include "nsThreadUtils.h"
58 #include "nsNetUtil.h"
63 //----------------------------------------------------------------------------
66 #if defined(PR_LOGGING)
67 static PRLogModuleInfo
*sLog
= PR_NewLogModule("proxy");
69 #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
71 //----------------------------------------------------------------------------
73 #define PROXY_PREF_BRANCH "network.proxy"
74 #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
76 #define WPAD_URL "http://wpad/wpad.dat"
78 //----------------------------------------------------------------------------
80 // This structure is intended to be allocated on the stack
81 struct nsProtocolInfo
{
87 //----------------------------------------------------------------------------
89 class nsAsyncResolveRequest
: public nsIRunnable
90 , public nsPACManCallback
91 , public nsICancelable
96 nsAsyncResolveRequest(nsProtocolProxyService
*pps
, nsIURI
*uri
,
97 nsIProtocolProxyCallback
*callback
)
99 , mDispatched(PR_FALSE
)
102 , mCallback(callback
)
104 NS_ASSERTION(mCallback
, "null callback");
107 void SetResult(nsresult status
, nsIProxyInfo
*pi
)
120 NS_IMETHOD
Cancel(nsresult reason
)
122 NS_ENSURE_ARG(NS_FAILED(reason
));
124 // If we've already called DoCallback then, nothing more to do.
128 SetResult(reason
, nsnull
);
129 return DispatchCallback();
132 nsresult
DispatchCallback()
134 if (mDispatched
) // Only need to dispatch once
137 nsresult rv
= NS_DispatchToCurrentThread(this);
139 NS_WARNING("unable to dispatch callback event");
141 mDispatched
= PR_TRUE
;
145 mCallback
= nsnull
; // break possible reference cycle
151 // Called asynchronously, so we do not need to post another PLEvent
152 // before calling DoCallback.
153 void OnQueryComplete(nsresult status
, const nsCString
&pacString
)
155 // If we've already called DoCallback then, nothing more to do.
159 // Provided we haven't been canceled...
160 if (mStatus
== NS_OK
) {
162 mPACString
= pacString
;
165 // In the cancelation case, we may still have another PLEvent in
166 // the queue that wants to call DoCallback. No need to wait for
167 // it, just run the callback now.
173 // Generate proxy info from the PAC string if appropriate
174 if (NS_SUCCEEDED(mStatus
) && !mProxyInfo
&& !mPACString
.IsEmpty())
175 mPPS
->ProcessPACString(mPACString
, getter_AddRefs(mProxyInfo
));
177 // Now apply proxy filters
178 if (NS_SUCCEEDED(mStatus
)) {
180 mStatus
= mPPS
->GetProtocolInfo(mURI
, &info
);
181 if (NS_SUCCEEDED(mStatus
))
182 mPPS
->ApplyFilters(mURI
, info
, mProxyInfo
);
187 mCallback
->OnProxyAvailable(this, mURI
, mProxyInfo
, mStatus
);
188 mCallback
= nsnull
; // in case the callback holds an owning ref to us
194 nsCString mPACString
;
197 nsRefPtr
<nsProtocolProxyService
> mPPS
;
198 nsCOMPtr
<nsIURI
> mURI
;
199 nsCOMPtr
<nsIProtocolProxyCallback
> mCallback
;
200 nsCOMPtr
<nsIProxyInfo
> mProxyInfo
;
203 NS_IMPL_ISUPPORTS2(nsAsyncResolveRequest
, nsICancelable
, nsIRunnable
)
205 //----------------------------------------------------------------------------
207 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
210 // apply mask to address (zeros out excluded bits).
212 // NOTE: we do the byte swapping here to minimize overall swapping.
215 proxy_MaskIPv6Addr(PRIPv6Addr
&addr
, PRUint16 mask_len
)
221 addr
.pr_s6_addr32
[3] = PR_htonl(
222 PR_ntohl(addr
.pr_s6_addr32
[3]) & (~0L << (128 - mask_len
)));
224 else if (mask_len
> 64) {
225 addr
.pr_s6_addr32
[3] = 0;
226 addr
.pr_s6_addr32
[2] = PR_htonl(
227 PR_ntohl(addr
.pr_s6_addr32
[2]) & (~0L << (96 - mask_len
)));
229 else if (mask_len
> 32) {
230 addr
.pr_s6_addr32
[3] = 0;
231 addr
.pr_s6_addr32
[2] = 0;
232 addr
.pr_s6_addr32
[1] = PR_htonl(
233 PR_ntohl(addr
.pr_s6_addr32
[1]) & (~0L << (64 - mask_len
)));
236 addr
.pr_s6_addr32
[3] = 0;
237 addr
.pr_s6_addr32
[2] = 0;
238 addr
.pr_s6_addr32
[1] = 0;
239 addr
.pr_s6_addr32
[0] = PR_htonl(
240 PR_ntohl(addr
.pr_s6_addr32
[0]) & (~0L << (32 - mask_len
)));
245 proxy_GetStringPref(nsIPrefBranch
*aPrefBranch
,
250 nsresult rv
= aPrefBranch
->GetCharPref(aPref
, getter_Copies(temp
));
254 aResult
.Assign(temp
);
255 // all of our string prefs are hostnames, so we should remove any
256 // whitespace characters that the user might have unknowingly entered.
257 aResult
.StripWhitespace();
262 proxy_GetIntPref(nsIPrefBranch
*aPrefBranch
,
267 nsresult rv
= aPrefBranch
->GetIntPref(aPref
, &temp
);
275 proxy_GetBoolPref(nsIPrefBranch
*aPrefBranch
,
280 nsresult rv
= aPrefBranch
->GetBoolPref(aPref
, &temp
);
287 //----------------------------------------------------------------------------
289 NS_IMPL_ADDREF(nsProtocolProxyService
)
290 NS_IMPL_RELEASE(nsProtocolProxyService
)
291 NS_IMPL_QUERY_INTERFACE3_CI(nsProtocolProxyService
,
292 nsIProtocolProxyService
,
293 nsIProtocolProxyService2
,
295 NS_IMPL_CI_INTERFACE_GETTER2(nsProtocolProxyService
,
296 nsIProtocolProxyService
,
297 nsIProtocolProxyService2
)
299 nsProtocolProxyService::nsProtocolProxyService()
301 , mProxyConfig(eProxyConfig_Direct
)
304 , mGopherProxyPort(-1)
305 , mHTTPSProxyPort(-1)
306 , mSOCKSProxyPort(-1)
307 , mSOCKSProxyVersion(4)
308 , mSOCKSProxyRemoteDNS(PR_FALSE
)
310 , mSessionStart(PR_Now())
311 , mFailedProxyTimeout(30 * 60) // 30 minute default
315 nsProtocolProxyService::~nsProtocolProxyService()
317 // These should have been cleaned up in our Observe method.
318 NS_ASSERTION(mHostFiltersArray
.Count() == 0 && mFilters
== nsnull
&&
319 mPACMan
== nsnull
, "what happened to xpcom-shutdown?");
322 // nsProtocolProxyService methods
324 nsProtocolProxyService::Init()
326 if (!mFailedProxies
.Init())
327 return NS_ERROR_OUT_OF_MEMORY
;
329 // failure to access prefs is non-fatal
330 nsCOMPtr
<nsIPrefBranch2
> prefBranch
=
331 do_GetService(NS_PREFSERVICE_CONTRACTID
);
333 // monitor proxy prefs
334 prefBranch
->AddObserver(PROXY_PREF_BRANCH
, this, PR_FALSE
);
337 PrefsChanged(prefBranch
, nsnull
);
340 // register for shutdown notification so we can clean ourselves up properly.
341 nsCOMPtr
<nsIObserverService
> obs
=
342 do_GetService("@mozilla.org/observer-service;1");
344 obs
->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
, PR_FALSE
);
350 nsProtocolProxyService::Observe(nsISupports
*aSubject
,
352 const PRUnichar
*aData
)
354 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
356 if (mHostFiltersArray
.Count() > 0) {
357 mHostFiltersArray
.EnumerateForwards(CleanupFilterArray
, nsnull
);
358 mHostFiltersArray
.Clear();
370 NS_ASSERTION(strcmp(aTopic
, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
) == 0,
371 "what is this random observer event?");
372 nsCOMPtr
<nsIPrefBranch
> prefs
= do_QueryInterface(aSubject
);
374 PrefsChanged(prefs
, NS_LossyConvertUTF16toASCII(aData
).get());
380 nsProtocolProxyService::PrefsChanged(nsIPrefBranch
*prefBranch
,
384 PRBool reloadPAC
= PR_FALSE
;
385 nsXPIDLCString tempString
;
387 if (!pref
|| !strcmp(pref
, PROXY_PREF("type"))) {
389 rv
= prefBranch
->GetIntPref(PROXY_PREF("type"), &type
);
390 if (NS_SUCCEEDED(rv
)) {
391 // bug 115720 - for ns4.x backwards compatability
392 if (type
== eProxyConfig_Direct4x
) {
393 type
= eProxyConfig_Direct
;
394 // Reset the type so that the dialog looks correct, and we
395 // don't have to handle this case everywhere else
396 // I'm paranoid about a loop of some sort - only do this
397 // if we're enumerating all prefs, and ignore any error
399 prefBranch
->SetIntPref(PROXY_PREF("type"), type
);
400 } else if (type
>= eProxyConfig_Last
) {
401 LOG(("unknown proxy type: %lu; assuming direct\n", type
));
402 type
= eProxyConfig_Direct
;
404 mProxyConfig
= static_cast<ProxyConfig
>(type
);
408 if (mProxyConfig
== eProxyConfig_System
) {
409 mSystemProxySettings
= do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID
);
411 mSystemProxySettings
= nsnull
;
415 if (!pref
|| !strcmp(pref
, PROXY_PREF("http")))
416 proxy_GetStringPref(prefBranch
, PROXY_PREF("http"), mHTTPProxyHost
);
418 if (!pref
|| !strcmp(pref
, PROXY_PREF("http_port")))
419 proxy_GetIntPref(prefBranch
, PROXY_PREF("http_port"), mHTTPProxyPort
);
421 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl")))
422 proxy_GetStringPref(prefBranch
, PROXY_PREF("ssl"), mHTTPSProxyHost
);
424 if (!pref
|| !strcmp(pref
, PROXY_PREF("ssl_port")))
425 proxy_GetIntPref(prefBranch
, PROXY_PREF("ssl_port"), mHTTPSProxyPort
);
427 if (!pref
|| !strcmp(pref
, PROXY_PREF("ftp")))
428 proxy_GetStringPref(prefBranch
, PROXY_PREF("ftp"), mFTPProxyHost
);
430 if (!pref
|| !strcmp(pref
, PROXY_PREF("ftp_port")))
431 proxy_GetIntPref(prefBranch
, PROXY_PREF("ftp_port"), mFTPProxyPort
);
433 if (!pref
|| !strcmp(pref
, PROXY_PREF("gopher")))
434 proxy_GetStringPref(prefBranch
, PROXY_PREF("gopher"), mGopherProxyHost
);
436 if (!pref
|| !strcmp(pref
, PROXY_PREF("gopher_port")))
437 proxy_GetIntPref(prefBranch
, PROXY_PREF("gopher_port"), mGopherProxyPort
);
439 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks")))
440 proxy_GetStringPref(prefBranch
, PROXY_PREF("socks"), mSOCKSProxyHost
);
442 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_port")))
443 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_port"), mSOCKSProxyPort
);
445 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_version"))) {
447 proxy_GetIntPref(prefBranch
, PROXY_PREF("socks_version"), version
);
448 // make sure this preference value remains sane
450 mSOCKSProxyVersion
= 5;
452 mSOCKSProxyVersion
= 4;
455 if (!pref
|| !strcmp(pref
, PROXY_PREF("socks_remote_dns")))
456 proxy_GetBoolPref(prefBranch
, PROXY_PREF("socks_remote_dns"),
457 mSOCKSProxyRemoteDNS
);
459 if (!pref
|| !strcmp(pref
, PROXY_PREF("failover_timeout")))
460 proxy_GetIntPref(prefBranch
, PROXY_PREF("failover_timeout"),
461 mFailedProxyTimeout
);
463 if (!pref
|| !strcmp(pref
, PROXY_PREF("no_proxies_on"))) {
464 rv
= prefBranch
->GetCharPref(PROXY_PREF("no_proxies_on"),
465 getter_Copies(tempString
));
466 if (NS_SUCCEEDED(rv
))
467 LoadHostFilters(tempString
.get());
470 // We're done if not using something that could give us a PAC URL
471 // (PAC, WPAD or System)
472 if (mProxyConfig
!= eProxyConfig_PAC
&& mProxyConfig
!= eProxyConfig_WPAD
&&
473 mProxyConfig
!= eProxyConfig_System
)
476 // OK, we need to reload the PAC file if:
477 // 1) network.proxy.type changed, or
478 // 2) network.proxy.autoconfig_url changed and PAC is configured
480 if (!pref
|| !strcmp(pref
, PROXY_PREF("autoconfig_url")))
484 tempString
.Truncate();
485 if (mProxyConfig
== eProxyConfig_PAC
) {
486 prefBranch
->GetCharPref(PROXY_PREF("autoconfig_url"),
487 getter_Copies(tempString
));
488 } else if (mProxyConfig
== eProxyConfig_WPAD
) {
489 // We diverge from the WPAD spec here in that we don't walk the
490 // hosts's FQDN, stripping components until we hit a TLD. Doing so
491 // is dangerous in the face of an incomplete list of TLDs, and TLDs
492 // get added over time. We could consider doing only a single
493 // substitution of the first component, if that proves to help
495 tempString
.AssignLiteral(WPAD_URL
);
496 } else if (mSystemProxySettings
) {
497 // Get System Proxy settings if available
498 mSystemProxySettings
->GetPACURI(tempString
);
500 if (!tempString
.IsEmpty())
501 ConfigureFromPAC(tempString
, PR_FALSE
);
506 nsProtocolProxyService::CanUseProxy(nsIURI
*aURI
, PRInt32 defaultPort
)
508 if (mHostFiltersArray
.Count() == 0)
514 nsresult rv
= aURI
->GetAsciiHost(host
);
515 if (NS_FAILED(rv
) || host
.IsEmpty())
518 rv
= aURI
->GetPort(&port
);
525 PRBool is_ipaddr
= (PR_StringToNetAddr(host
.get(), &addr
) == PR_SUCCESS
);
529 // convert parsed address to IPv6
530 if (addr
.raw
.family
== PR_AF_INET
) {
531 // convert to IPv4-mapped address
532 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &ipv6
);
534 else if (addr
.raw
.family
== PR_AF_INET6
) {
536 memcpy(&ipv6
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
539 NS_WARNING("unknown address family");
540 return PR_TRUE
; // allow proxying
545 while (++index
< mHostFiltersArray
.Count()) {
546 HostInfo
*hinfo
= (HostInfo
*) mHostFiltersArray
[index
];
548 if (is_ipaddr
!= hinfo
->is_ipaddr
)
550 if (hinfo
->port
&& hinfo
->port
!= port
)
554 // generate masked version of target IPv6 address
556 memcpy(&masked
, &ipv6
, sizeof(PRIPv6Addr
));
557 proxy_MaskIPv6Addr(masked
, hinfo
->ip
.mask_len
);
560 if (memcmp(&masked
, &hinfo
->ip
.addr
, sizeof(PRIPv6Addr
)) == 0)
561 return PR_FALSE
; // proxy disallowed
564 PRUint32 host_len
= host
.Length();
565 PRUint32 filter_host_len
= hinfo
->name
.host_len
;
567 if (host_len
>= filter_host_len
) {
569 // compare last |filter_host_len| bytes of target hostname.
571 const char *host_tail
= host
.get() + host_len
- filter_host_len
;
572 if (!PL_strncasecmp(host_tail
, hinfo
->name
.host
, filter_host_len
))
573 return PR_FALSE
; // proxy disallowed
580 static const char kProxyType_HTTP
[] = "http";
581 static const char kProxyType_PROXY
[] = "proxy";
582 static const char kProxyType_SOCKS
[] = "socks";
583 static const char kProxyType_SOCKS4
[] = "socks4";
584 static const char kProxyType_SOCKS5
[] = "socks5";
585 static const char kProxyType_DIRECT
[] = "direct";
586 static const char kProxyType_UNKNOWN
[] = "unknown";
589 nsProtocolProxyService::ExtractProxyInfo(const char *start
, nsProxyInfo
**result
)
594 // see BNF in nsIProxyAutoConfig.idl
596 // find end of proxy info delimiter
597 const char *end
= start
;
598 while (*end
&& *end
!= ';') ++end
;
600 // find end of proxy type delimiter
601 const char *sp
= start
;
602 while (sp
< end
&& *sp
!= ' ' && *sp
!= '\t') ++sp
;
604 PRUint32 len
= sp
- start
;
605 const char *type
= nsnull
;
608 if (PL_strncasecmp(start
, kProxyType_PROXY
, 5) == 0)
609 type
= kProxyType_HTTP
;
610 else if (PL_strncasecmp(start
, kProxyType_SOCKS
, 5) == 0)
611 type
= kProxyType_SOCKS4
; // assume v4 for 4x compat
614 if (PL_strncasecmp(start
, kProxyType_DIRECT
, 6) == 0)
615 type
= kProxyType_DIRECT
;
616 else if (PL_strncasecmp(start
, kProxyType_SOCKS4
, 6) == 0)
617 type
= kProxyType_SOCKS4
;
618 else if (PL_strncasecmp(start
, kProxyType_SOCKS5
, 6) == 0)
619 // map "SOCKS5" to "socks" to match contract-id of registered
620 // SOCKS-v5 socket provider.
621 type
= kProxyType_SOCKS
;
625 const char *host
= nsnull
, *hostEnd
= nsnull
;
628 // If it's a SOCKS5 proxy, do name resolution on the server side.
629 // We could use this with SOCKS4a servers too, but they might not
631 if (type
== kProxyType_SOCKS
)
632 flags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
636 while ((*start
== ' ' || *start
== '\t') && start
< end
)
640 hostEnd
= strchr(host
, ':');
641 if (!hostEnd
|| hostEnd
> end
) {
643 // no port, so assume default
644 if (type
== kProxyType_HTTP
)
650 port
= atoi(hostEnd
+ 1);
652 nsProxyInfo
*pi
= new nsProxyInfo
;
656 pi
->mTimeout
= mFailedProxyTimeout
;
657 // YES, it is ok to specify a null proxy host.
659 pi
->mHost
.Assign(host
, hostEnd
- host
);
662 NS_ADDREF(*result
= pi
);
666 while (*end
== ';' || *end
== ' ' || *end
== '\t')
672 nsProtocolProxyService::GetProxyKey(nsProxyInfo
*pi
, nsCString
&key
)
674 key
.AssignASCII(pi
->mType
);
675 if (!pi
->mHost
.IsEmpty()) {
677 key
.Append(pi
->mHost
);
679 key
.AppendInt(pi
->mPort
);
684 nsProtocolProxyService::SecondsSinceSessionStart()
686 PRTime now
= PR_Now();
688 // get time elapsed since session start
690 LL_SUB(diff
, now
, mSessionStart
);
692 // convert microseconds to seconds
694 LL_I2L(ups
, PR_USEC_PER_SEC
);
695 LL_DIV(diff
, diff
, ups
);
697 // convert to 32 bit value
705 nsProtocolProxyService::EnableProxy(nsProxyInfo
*pi
)
708 GetProxyKey(pi
, key
);
709 mFailedProxies
.Remove(key
);
713 nsProtocolProxyService::DisableProxy(nsProxyInfo
*pi
)
716 GetProxyKey(pi
, key
);
718 PRUint32 dsec
= SecondsSinceSessionStart();
720 // Add timeout to interval (this is the time when the proxy can
722 dsec
+= pi
->mTimeout
;
724 // NOTE: The classic codebase would increase the timeout value
725 // incrementally each time a subsequent failure occured.
726 // We could do the same, but it would require that we not
727 // remove proxy entries in IsProxyDisabled or otherwise
728 // change the way we are recording disabled proxies.
729 // Simpler is probably better for now, and at least the
730 // user can tune the timeout setting via preferences.
732 LOG(("DisableProxy %s %d\n", key
.get(), dsec
));
734 // If this fails, oh well... means we don't have enough memory
735 // to remember the failed proxy.
736 mFailedProxies
.Put(key
, dsec
);
740 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo
*pi
)
743 GetProxyKey(pi
, key
);
746 if (!mFailedProxies
.Get(key
, &val
))
749 PRUint32 dsec
= SecondsSinceSessionStart();
751 // if time passed has exceeded interval, then try proxy again.
753 mFailedProxies
.Remove(key
);
761 nsProtocolProxyService::ConfigureFromPAC(const nsCString
&spec
,
765 mPACMan
= new nsPACMan();
767 return NS_ERROR_OUT_OF_MEMORY
;
770 nsCOMPtr
<nsIURI
> pacURI
;
771 nsresult rv
= NS_NewURI(getter_AddRefs(pacURI
), spec
);
775 if (mPACMan
->IsPACURI(pacURI
) && !forceReload
)
778 mFailedProxies
.Clear();
780 return mPACMan
->LoadPACFromURI(pacURI
);
784 nsProtocolProxyService::ProcessPACString(const nsCString
&pacString
,
785 nsIProxyInfo
**result
)
787 if (pacString
.IsEmpty()) {
792 const char *proxies
= pacString
.get();
794 nsProxyInfo
*pi
= nsnull
, *first
= nsnull
, *last
= nsnull
;
796 proxies
= ExtractProxyInfo(proxies
, &pi
);
799 NS_ASSERTION(last
->mNext
== nsnull
, "leaking nsProxyInfo");
810 // nsIProtocolProxyService2
812 nsProtocolProxyService::ReloadPAC()
814 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
819 nsresult rv
= prefs
->GetIntPref(PROXY_PREF("type"), &type
);
823 nsXPIDLCString pacSpec
;
824 if (type
== eProxyConfig_PAC
)
825 prefs
->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec
));
826 else if (type
== eProxyConfig_WPAD
)
827 pacSpec
.AssignLiteral(WPAD_URL
);
829 if (!pacSpec
.IsEmpty())
830 ConfigureFromPAC(pacSpec
, PR_TRUE
);
834 // nsIProtocolProxyService
836 nsProtocolProxyService::Resolve(nsIURI
*uri
, PRUint32 flags
,
837 nsIProxyInfo
**result
)
840 nsresult rv
= GetProtocolInfo(uri
, &info
);
845 rv
= Resolve_Internal(uri
, info
, &usePAC
, result
);
849 if (usePAC
&& mPACMan
) {
850 NS_ASSERTION(*result
== nsnull
, "we should not have a result yet");
852 // If the caller didn't want us to invoke PAC, then error out.
853 if (flags
& RESOLVE_NON_BLOCKING
)
854 return NS_BASE_STREAM_WOULD_BLOCK
;
856 // Query the PAC file synchronously.
858 rv
= mPACMan
->GetProxyForURI(uri
, pacString
);
859 if (NS_SUCCEEDED(rv
))
860 ProcessPACString(pacString
, result
);
861 else if (rv
== NS_ERROR_IN_PROGRESS
) {
862 // Construct a special UNKNOWN proxy entry that informs the caller
863 // that the proxy info is yet to be determined.
864 rv
= NewProxyInfo_Internal(kProxyType_UNKNOWN
, EmptyCString(), -1,
865 0, 0, nsnull
, result
);
870 NS_WARNING("failed querying PAC file; trying DIRECT");
873 ApplyFilters(uri
, info
, result
);
878 nsProtocolProxyService::AsyncResolve(nsIURI
*uri
, PRUint32 flags
,
879 nsIProtocolProxyCallback
*callback
,
880 nsICancelable
**result
)
882 nsRefPtr
<nsAsyncResolveRequest
> ctx
=
883 new nsAsyncResolveRequest(this, uri
, callback
);
885 return NS_ERROR_OUT_OF_MEMORY
;
888 nsresult rv
= GetProtocolInfo(uri
, &info
);
893 nsCOMPtr
<nsIProxyInfo
> pi
;
894 rv
= Resolve_Internal(uri
, info
, &usePAC
, getter_AddRefs(pi
));
898 if (!usePAC
|| !mPACMan
) {
899 ApplyFilters(uri
, info
, pi
);
901 ctx
->SetResult(NS_OK
, pi
);
902 return ctx
->DispatchCallback();
905 // else kick off a PAC query
906 rv
= mPACMan
->AsyncGetProxyForURI(uri
, ctx
);
907 if (NS_SUCCEEDED(rv
)) {
915 nsProtocolProxyService::NewProxyInfo(const nsACString
&aType
,
916 const nsACString
&aHost
,
919 PRUint32 aFailoverTimeout
,
920 nsIProxyInfo
*aFailoverProxy
,
921 nsIProxyInfo
**aResult
)
923 static const char *types
[] = {
930 // resolve type; this allows us to avoid copying the type string into each
931 // proxy info instance. we just reference the string literals directly :)
932 const char *type
= nsnull
;
933 for (PRUint32 i
=0; i
<NS_ARRAY_LENGTH(types
); ++i
) {
934 if (aType
.LowerCaseEqualsASCII(types
[i
])) {
939 NS_ENSURE_TRUE(type
, NS_ERROR_INVALID_ARG
);
944 return NewProxyInfo_Internal(type
, aHost
, aPort
, aFlags
, aFailoverTimeout
,
945 aFailoverProxy
, aResult
);
949 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo
*aProxy
,
952 nsIProxyInfo
**aResult
)
954 // We only support failover when a PAC file is configured, either
955 // directly or via system settings
956 if (mProxyConfig
!= eProxyConfig_PAC
&& mProxyConfig
!= eProxyConfig_WPAD
&&
957 mProxyConfig
!= eProxyConfig_System
)
958 return NS_ERROR_NOT_AVAILABLE
;
960 // Verify that |aProxy| is one of our nsProxyInfo objects.
961 nsCOMPtr
<nsProxyInfo
> pi
= do_QueryInterface(aProxy
);
963 // OK, the QI checked out. We can proceed.
965 // Remember that this proxy is down.
968 // NOTE: At this point, we might want to prompt the user if we have
969 // not already tried going DIRECT. This is something that the
970 // classic codebase supported; however, IE6 does not prompt.
973 return NS_ERROR_NOT_AVAILABLE
;
975 LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
976 pi
->mType
, pi
->mHost
.get(), pi
->mPort
,
977 pi
->mNext
->mType
, pi
->mNext
->mHost
.get(), pi
->mNext
->mPort
));
979 NS_ADDREF(*aResult
= pi
->mNext
);
984 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter
*filter
,
987 UnregisterFilter(filter
); // remove this filter if we already have it
989 FilterLink
*link
= new FilterLink(position
, filter
);
991 return NS_ERROR_OUT_OF_MEMORY
;
998 // insert into mFilters in sorted order
999 FilterLink
*last
= nsnull
;
1000 for (FilterLink
*iter
= mFilters
; iter
; iter
= iter
->next
) {
1001 if (position
< iter
->position
) {
1003 link
->next
= last
->next
;
1007 link
->next
= mFilters
;
1014 // our position is equal to or greater than the last link in the list
1020 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter
*filter
)
1022 // QI to nsISupports so we can safely test object identity.
1023 nsCOMPtr
<nsISupports
> givenObject
= do_QueryInterface(filter
);
1025 FilterLink
*last
= nsnull
;
1026 for (FilterLink
*iter
= mFilters
; iter
; iter
= iter
->next
) {
1027 nsCOMPtr
<nsISupports
> object
= do_QueryInterface(iter
->filter
);
1028 if (object
== givenObject
) {
1030 last
->next
= iter
->next
;
1032 mFilters
= iter
->next
;
1033 iter
->next
= nsnull
;
1040 // No need to throw an exception in this case.
1045 nsProtocolProxyService::CleanupFilterArray(void *aElement
, void *aData
)
1048 delete (HostInfo
*) aElement
;
1054 nsProtocolProxyService::LoadHostFilters(const char *filters
)
1056 // check to see the owners flag? /!?/ TODO
1057 if (mHostFiltersArray
.Count() > 0) {
1058 mHostFiltersArray
.EnumerateForwards(CleanupFilterArray
, nsnull
);
1059 mHostFiltersArray
.Clear();
1063 return; // fail silently...
1066 // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1067 // filters = filter *( "," LWS filter)
1070 // skip over spaces and ,
1071 while (*filters
&& (*filters
== ',' || IS_ASCII_SPACE(*filters
)))
1074 const char *starthost
= filters
;
1075 const char *endhost
= filters
+ 1; // at least that...
1076 const char *portLocation
= 0;
1077 const char *maskLocation
= 0;
1080 // XXX this needs to be fixed to support IPv6 address literals,
1081 // which in this context will need to be []-escaped.
1083 while (*endhost
&& (*endhost
!= ',' && !IS_ASCII_SPACE(*endhost
))) {
1084 if (*endhost
== ':')
1085 portLocation
= endhost
;
1086 else if (*endhost
== '/')
1087 maskLocation
= endhost
;
1091 filters
= endhost
; // advance iterator up front
1093 HostInfo
*hinfo
= new HostInfo();
1095 return; // fail silently
1096 hinfo
->port
= portLocation
? atoi(portLocation
+ 1) : 0;
1098 // locate end of host
1099 const char *end
= maskLocation
? maskLocation
:
1100 portLocation
? portLocation
:
1103 nsCAutoString
str(starthost
, end
- starthost
);
1106 if (PR_StringToNetAddr(str
.get(), &addr
) == PR_SUCCESS
) {
1107 hinfo
->is_ipaddr
= PR_TRUE
;
1108 hinfo
->ip
.family
= PR_AF_INET6
; // we always store address as IPv6
1109 hinfo
->ip
.mask_len
= maskLocation
? atoi(maskLocation
+ 1) : 128;
1111 if (hinfo
->ip
.mask_len
== 0) {
1112 NS_WARNING("invalid mask");
1116 if (addr
.raw
.family
== PR_AF_INET
) {
1117 // convert to IPv4-mapped address
1118 PR_ConvertIPv4AddrToIPv6(addr
.inet
.ip
, &hinfo
->ip
.addr
);
1119 // adjust mask_len accordingly
1120 if (hinfo
->ip
.mask_len
<= 32)
1121 hinfo
->ip
.mask_len
+= 96;
1123 else if (addr
.raw
.family
== PR_AF_INET6
) {
1125 memcpy(&hinfo
->ip
.addr
, &addr
.ipv6
.ip
, sizeof(PRIPv6Addr
));
1128 NS_WARNING("unknown address family");
1132 // apply mask to IPv6 address
1133 proxy_MaskIPv6Addr(hinfo
->ip
.addr
, hinfo
->ip
.mask_len
);
1136 PRUint32 startIndex
, endIndex
;
1137 if (str
.First() == '*')
1138 startIndex
= 1; // *.domain -> .domain
1141 endIndex
= (portLocation
? portLocation
: endhost
) - starthost
;
1143 hinfo
->is_ipaddr
= PR_FALSE
;
1144 hinfo
->name
.host
= ToNewCString(Substring(str
, startIndex
, endIndex
));
1146 if (!hinfo
->name
.host
)
1149 hinfo
->name
.host_len
= endIndex
- startIndex
;
1152 //#define DEBUG_DUMP_FILTERS
1153 #ifdef DEBUG_DUMP_FILTERS
1154 printf("loaded filter[%u]:\n", mHostFiltersArray
.Count());
1155 printf(" is_ipaddr = %u\n", hinfo
->is_ipaddr
);
1156 printf(" port = %u\n", hinfo
->port
);
1157 if (hinfo
->is_ipaddr
) {
1158 printf(" ip.family = %x\n", hinfo
->ip
.family
);
1159 printf(" ip.mask_len = %u\n", hinfo
->ip
.mask_len
);
1162 PR_SetNetAddr(PR_IpAddrNull
, PR_AF_INET6
, 0, &netAddr
);
1163 memcpy(&netAddr
.ipv6
.ip
, &hinfo
->ip
.addr
, sizeof(hinfo
->ip
.addr
));
1166 PR_NetAddrToString(&netAddr
, buf
, sizeof(buf
));
1168 printf(" ip.addr = %s\n", buf
);
1171 printf(" name.host = %s\n", hinfo
->name
.host
);
1175 mHostFiltersArray
.AppendElement(hinfo
);
1184 nsProtocolProxyService::GetProtocolInfo(nsIURI
*uri
, nsProtocolInfo
*info
)
1188 rv
= uri
->GetScheme(info
->scheme
);
1192 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
1196 nsCOMPtr
<nsIProtocolHandler
> handler
;
1197 rv
= ios
->GetProtocolHandler(info
->scheme
.get(), getter_AddRefs(handler
));
1201 rv
= handler
->GetProtocolFlags(&info
->flags
);
1205 rv
= handler
->GetDefaultPort(&info
->defaultPort
);
1210 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType
,
1211 const nsACString
&aHost
,
1214 PRUint32 aFailoverTimeout
,
1215 nsIProxyInfo
*aFailoverProxy
,
1216 nsIProxyInfo
**aResult
)
1218 nsCOMPtr
<nsProxyInfo
> failover
;
1219 if (aFailoverProxy
) {
1220 failover
= do_QueryInterface(aFailoverProxy
);
1221 NS_ENSURE_ARG(failover
);
1224 nsProxyInfo
*proxyInfo
= new nsProxyInfo();
1226 return NS_ERROR_OUT_OF_MEMORY
;
1228 proxyInfo
->mType
= aType
;
1229 proxyInfo
->mHost
= aHost
;
1230 proxyInfo
->mPort
= aPort
;
1231 proxyInfo
->mFlags
= aFlags
;
1232 proxyInfo
->mTimeout
= aFailoverTimeout
== PR_UINT32_MAX
1233 ? mFailedProxyTimeout
: aFailoverTimeout
;
1234 failover
.swap(proxyInfo
->mNext
);
1236 NS_ADDREF(*aResult
= proxyInfo
);
1241 nsProtocolProxyService::Resolve_Internal(nsIURI
*uri
,
1242 const nsProtocolInfo
&info
,
1244 nsIProxyInfo
**result
)
1246 NS_ENSURE_ARG_POINTER(uri
);
1251 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY
))
1252 return NS_OK
; // Can't proxy this (filters may not override)
1254 if (mSystemProxySettings
) {
1255 nsCAutoString PACURI
;
1256 if (NS_SUCCEEDED(mSystemProxySettings
->GetPACURI(PACURI
)) &&
1257 !PACURI
.IsEmpty()) {
1258 // Switch to new PAC file if that setting has changed. If the setting
1259 // hasn't changed, ConfigureFromPAC will exit early.
1260 nsresult rv
= ConfigureFromPAC(PACURI
, PR_FALSE
);
1264 nsCAutoString proxy
;
1265 nsresult rv
= mSystemProxySettings
->GetProxyForURI(uri
, proxy
);
1266 if (NS_SUCCEEDED(rv
)) {
1267 ProcessPACString(proxy
, result
);
1270 // no proxy, stop search
1275 // if proxies are enabled and this host:port combo is supposed to use a
1276 // proxy, check for a proxy.
1277 if (mProxyConfig
== eProxyConfig_Direct
||
1278 (mProxyConfig
== eProxyConfig_Manual
&&
1279 !CanUseProxy(uri
, info
.defaultPort
)))
1282 // Proxy auto config magic...
1283 if (mProxyConfig
== eProxyConfig_PAC
|| mProxyConfig
== eProxyConfig_WPAD
||
1284 mProxyConfig
== eProxyConfig_System
) {
1285 // Do not query PAC now.
1290 // proxy info values
1291 const char *type
= nsnull
;
1292 const nsACString
*host
= nsnull
;
1295 PRUint32 proxyFlags
= 0;
1297 if (!mHTTPProxyHost
.IsEmpty() && mHTTPProxyPort
> 0 &&
1298 info
.scheme
.EqualsLiteral("http")) {
1299 host
= &mHTTPProxyHost
;
1300 type
= kProxyType_HTTP
;
1301 port
= mHTTPProxyPort
;
1303 else if (!mHTTPSProxyHost
.IsEmpty() && mHTTPSProxyPort
> 0 &&
1304 info
.scheme
.EqualsLiteral("https")) {
1305 host
= &mHTTPSProxyHost
;
1306 type
= kProxyType_HTTP
;
1307 port
= mHTTPSProxyPort
;
1309 else if (!mFTPProxyHost
.IsEmpty() && mFTPProxyPort
> 0 &&
1310 info
.scheme
.EqualsLiteral("ftp")) {
1311 host
= &mFTPProxyHost
;
1312 type
= kProxyType_HTTP
;
1313 port
= mFTPProxyPort
;
1315 else if (!mGopherProxyHost
.IsEmpty() && mGopherProxyPort
> 0 &&
1316 info
.scheme
.EqualsLiteral("gopher")) {
1317 host
= &mGopherProxyHost
;
1318 type
= kProxyType_HTTP
;
1319 port
= mGopherProxyPort
;
1321 else if (!mSOCKSProxyHost
.IsEmpty() && mSOCKSProxyPort
> 0) {
1322 host
= &mSOCKSProxyHost
;
1323 if (mSOCKSProxyVersion
== 4)
1324 type
= kProxyType_SOCKS4
;
1326 type
= kProxyType_SOCKS
;
1327 port
= mSOCKSProxyPort
;
1328 if (mSOCKSProxyRemoteDNS
)
1329 proxyFlags
|= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST
;
1333 nsresult rv
= NewProxyInfo_Internal(type
, *host
, port
, proxyFlags
,
1334 PR_UINT32_MAX
, nsnull
, result
);
1343 nsProtocolProxyService::ApplyFilters(nsIURI
*uri
, const nsProtocolInfo
&info
,
1344 nsIProxyInfo
**list
)
1346 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY
))
1349 // We prune the proxy list prior to invoking each filter. This may be
1350 // somewhat inefficient, but it seems like a good idea since we want each
1351 // filter to "see" a valid proxy list.
1354 nsCOMPtr
<nsIProxyInfo
> result
;
1356 for (FilterLink
*iter
= mFilters
; iter
; iter
= iter
->next
) {
1357 PruneProxyInfo(info
, list
);
1359 rv
= iter
->filter
->ApplyFilter(this, uri
, *list
,
1360 getter_AddRefs(result
));
1366 PruneProxyInfo(info
, list
);
1370 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo
&info
,
1371 nsIProxyInfo
**list
)
1375 nsProxyInfo
*head
= nsnull
;
1376 CallQueryInterface(*list
, &head
);
1378 NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
1383 // Pruning of disabled proxies works like this:
1384 // - If all proxies are disabled, return the full list
1385 // - Otherwise, remove the disabled proxies.
1387 // Pruning of disallowed proxies works like this:
1388 // - If the protocol handler disallows the proxy, then we disallow it.
1390 // Start by removing all disallowed proxies if required:
1391 if (!(info
.flags
& nsIProtocolHandler::ALLOWS_PROXY_HTTP
)) {
1392 nsProxyInfo
*last
= nsnull
, *iter
= head
;
1394 if (iter
->Type() == kProxyType_HTTP
) {
1397 last
->mNext
= iter
->mNext
;
1400 nsProxyInfo
*next
= iter
->mNext
;
1401 iter
->mNext
= nsnull
;
1413 // Now, scan to see if all remaining proxies are disabled. If so, then
1414 // we'll just bail and return them all. Otherwise, we'll go and prune the
1417 PRBool allDisabled
= PR_TRUE
;
1420 for (iter
= head
; iter
; iter
= iter
->mNext
) {
1421 if (!IsProxyDisabled(iter
)) {
1422 allDisabled
= PR_FALSE
;
1428 LOG(("All proxies are disabled, so trying all again"));
1430 // remove any disabled proxies.
1431 nsProxyInfo
*last
= nsnull
;
1432 for (iter
= head
; iter
; ) {
1433 if (IsProxyDisabled(iter
)) {
1435 nsProxyInfo
*reject
= iter
;
1443 reject
->mNext
= nsnull
;
1448 // since we are about to use this proxy, make sure it is not on
1449 // the disabled proxy list. we'll add it back to that list if
1450 // we have to (in GetFailoverForProxy).
1452 // XXX(darin): It might be better to do this as a final pass.
1461 // if only DIRECT was specified then return no proxy info, and we're done.
1462 if (head
&& !head
->mNext
&& head
->mType
== kProxyType_DIRECT
)
1465 *list
= head
; // Transfer ownership