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/. */
6 
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Attributes.h"
9 
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 "nsIScriptSecurityManager.h"
22 #include "nsIPrefService.h"
23 #include "nsIPrefBranch.h"
24 #include "nsThreadUtils.h"
25 #include "nsSOCKSIOLayer.h"
26 #include "nsString.h"
27 #include "nsNetUtil.h"
28 #include "nsNetCID.h"
29 #include "plstr.h"
30 #include "prnetdb.h"
31 #include "nsPACMan.h"
32 #include "nsProxyRelease.h"
33 #include "mozilla/Mutex.h"
34 #include "mozilla/CondVar.h"
35 #include "nsISystemProxySettings.h"
36 #include "nsINetworkLinkService.h"
37 #include "nsIHttpChannelInternal.h"
38 #include "mozilla/Logging.h"
39 #include "mozilla/Tokenizer.h"
40 
41 //----------------------------------------------------------------------------
42 
43 namespace mozilla {
44 namespace net {
45 
46   extern const char kProxyType_HTTP[];
47   extern const char kProxyType_HTTPS[];
48   extern const char kProxyType_SOCKS[];
49   extern const char kProxyType_SOCKS4[];
50   extern const char kProxyType_SOCKS5[];
51   extern const char kProxyType_DIRECT[];
52 
53 #undef LOG
54 #define LOG(args) MOZ_LOG(gProxyLog, LogLevel::Debug, args)
55 
56 //----------------------------------------------------------------------------
57 
58 #define PROXY_PREF_BRANCH  "network.proxy"
59 #define PROXY_PREF(x)      PROXY_PREF_BRANCH "." x
60 
61 #define WPAD_URL "http://wpad/wpad.dat"
62 
63 //----------------------------------------------------------------------------
64 
65 // This structure is intended to be allocated on the stack
66 struct nsProtocolInfo {
67     nsAutoCString scheme;
68     uint32_t flags;
69     int32_t defaultPort;
70 };
71 
72 //----------------------------------------------------------------------------
73 
74 // Return the channel's proxy URI, or if it doesn't exist, the
75 // channel's main URI.
76 static nsresult
GetProxyURI(nsIChannel * channel,nsIURI ** aOut)77 GetProxyURI(nsIChannel *channel, nsIURI **aOut)
78 {
79   nsresult rv = NS_OK;
80   nsCOMPtr<nsIURI> proxyURI;
81   nsCOMPtr<nsIHttpChannelInternal> httpChannel(do_QueryInterface(channel));
82   if (httpChannel) {
83     rv = httpChannel->GetProxyURI(getter_AddRefs(proxyURI));
84   }
85   if (!proxyURI) {
86     rv = channel->GetURI(getter_AddRefs(proxyURI));
87   }
88   if (NS_FAILED(rv)) {
89     return rv;
90   }
91   proxyURI.forget(aOut);
92   return NS_OK;
93 }
94 
95 //-----------------------------------------------------------------------------
96 
97 // The nsPACManCallback portion of this implementation should be run
98 // on the main thread - so call nsPACMan::AsyncGetProxyForURI() with
99 // a true mainThreadResponse parameter.
100 class nsAsyncResolveRequest final : public nsIRunnable
101                                   , public nsPACManCallback
102                                   , public nsICancelable
103 {
104 public:
105     NS_DECL_THREADSAFE_ISUPPORTS
106 
nsAsyncResolveRequest(nsProtocolProxyService * pps,nsIChannel * channel,uint32_t aResolveFlags,nsIProtocolProxyCallback * callback)107     nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIChannel *channel,
108                           uint32_t aResolveFlags,
109                           nsIProtocolProxyCallback *callback)
110         : mStatus(NS_OK)
111         , mDispatched(false)
112         , mResolveFlags(aResolveFlags)
113         , mPPS(pps)
114         , mXPComPPS(pps)
115         , mChannel(channel)
116         , mCallback(callback)
117     {
118         NS_ASSERTION(mCallback, "null callback");
119     }
120 
121 private:
~nsAsyncResolveRequest()122     ~nsAsyncResolveRequest()
123     {
124         if (!NS_IsMainThread()) {
125             // these xpcom pointers might need to be proxied back to the
126             // main thread to delete safely, but if this request had its
127             // callbacks called normally they will all be null and this is a nop
128 
129             if (mChannel) {
130                 NS_ReleaseOnMainThread(mChannel.forget());
131             }
132 
133             if (mCallback) {
134                 NS_ReleaseOnMainThread(mCallback.forget());
135             }
136 
137             if (mProxyInfo) {
138                 NS_ReleaseOnMainThread(mProxyInfo.forget());
139             }
140 
141             if (mXPComPPS) {
142                 NS_ReleaseOnMainThread(mXPComPPS.forget());
143             }
144         }
145     }
146 
147 public:
SetResult(nsresult status,nsIProxyInfo * pi)148     void SetResult(nsresult status, nsIProxyInfo *pi)
149     {
150         mStatus = status;
151         mProxyInfo = pi;
152     }
153 
Run()154     NS_IMETHOD Run() override
155     {
156         if (mCallback)
157             DoCallback();
158         return NS_OK;
159     }
160 
Cancel(nsresult reason)161     NS_IMETHOD Cancel(nsresult reason) override
162     {
163         NS_ENSURE_ARG(NS_FAILED(reason));
164 
165         // If we've already called DoCallback then, nothing more to do.
166         if (!mCallback)
167             return NS_OK;
168 
169         SetResult(reason, nullptr);
170         return DispatchCallback();
171     }
172 
DispatchCallback()173     nsresult DispatchCallback()
174     {
175         if (mDispatched)  // Only need to dispatch once
176             return NS_OK;
177 
178         nsresult rv = NS_DispatchToCurrentThread(this);
179         if (NS_FAILED(rv))
180             NS_WARNING("unable to dispatch callback event");
181         else {
182             mDispatched = true;
183             return NS_OK;
184         }
185 
186         mCallback = nullptr;  // break possible reference cycle
187         return rv;
188     }
189 
190 private:
191 
192     // Called asynchronously, so we do not need to post another PLEvent
193     // before calling DoCallback.
OnQueryComplete(nsresult status,const nsCString & pacString,const nsCString & newPACURL)194     void OnQueryComplete(nsresult status,
195                          const nsCString &pacString,
196                          const nsCString &newPACURL) override
197     {
198         // If we've already called DoCallback then, nothing more to do.
199         if (!mCallback)
200             return;
201 
202         // Provided we haven't been canceled...
203         if (mStatus == NS_OK) {
204             mStatus = status;
205             mPACString = pacString;
206             mPACURL = newPACURL;
207         }
208 
209         // In the cancelation case, we may still have another PLEvent in
210         // the queue that wants to call DoCallback.  No need to wait for
211         // it, just run the callback now.
212         DoCallback();
213     }
214 
DoCallback()215     void DoCallback()
216     {
217         bool pacAvailable = true;
218         if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) {
219             // If the PAC service is not avail (e.g. failed pac load
220             // or shutdown) then we will be going direct. Make that
221             // mapping now so that any filters are still applied.
222             mPACString = NS_LITERAL_CSTRING("DIRECT;");
223             mStatus = NS_OK;
224 
225             LOG(("pac not available, use DIRECT\n"));
226             pacAvailable = false;
227         }
228 
229         // Generate proxy info from the PAC string if appropriate
230         if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty()) {
231             mPPS->ProcessPACString(mPACString, mResolveFlags,
232                                    getter_AddRefs(mProxyInfo));
233             nsCOMPtr<nsIURI> proxyURI;
234             GetProxyURI(mChannel, getter_AddRefs(proxyURI));
235 
236             // Now apply proxy filters
237             nsProtocolInfo info;
238             mStatus = mPPS->GetProtocolInfo(proxyURI, &info);
239             if (NS_SUCCEEDED(mStatus))
240                 mPPS->ApplyFilters(mChannel, info, mProxyInfo);
241             else
242                 mProxyInfo = nullptr;
243 
244             if(pacAvailable) {
245                 // if !pacAvailable, it was already logged above
246                 LOG(("pac thread callback %s\n", mPACString.get()));
247             }
248             if (NS_SUCCEEDED(mStatus))
249                 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
250             mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
251         }
252         else if (NS_SUCCEEDED(mStatus) && !mPACURL.IsEmpty()) {
253             LOG(("pac thread callback indicates new pac file load\n"));
254 
255             nsCOMPtr<nsIURI> proxyURI;
256             GetProxyURI(mChannel, getter_AddRefs(proxyURI));
257 
258             // trigger load of new pac url
259             nsresult rv = mPPS->ConfigureFromPAC(mPACURL, false);
260             if (NS_SUCCEEDED(rv)) {
261                 // now that the load is triggered, we can resubmit the query
262                 RefPtr<nsAsyncResolveRequest> newRequest =
263                     new nsAsyncResolveRequest(mPPS, mChannel, mResolveFlags,
264                                               mCallback);
265                 rv = mPPS->mPACMan->AsyncGetProxyForURI(proxyURI,
266                                                         newRequest,
267                                                         true);
268             }
269 
270             if (NS_FAILED(rv))
271                 mCallback->OnProxyAvailable(this, mChannel, nullptr, rv);
272 
273             // do not call onproxyavailable() in SUCCESS case - the newRequest will
274             // take care of that
275         }
276         else {
277             LOG(("pac thread callback did not provide information %X\n", mStatus));
278             if (NS_SUCCEEDED(mStatus))
279                 mPPS->MaybeDisableDNSPrefetch(mProxyInfo);
280             mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus);
281         }
282 
283         // We are on the main thread now and don't need these any more so
284         // release them to avoid having to proxy them back to the main thread
285         // in the dtor
286         mCallback = nullptr;  // in case the callback holds an owning ref to us
287         mPPS = nullptr;
288         mXPComPPS = nullptr;
289         mChannel = nullptr;
290         mProxyInfo = nullptr;
291     }
292 
293 private:
294 
295     nsresult  mStatus;
296     nsCString mPACString;
297     nsCString mPACURL;
298     bool      mDispatched;
299     uint32_t  mResolveFlags;
300 
301     nsProtocolProxyService            *mPPS;
302     nsCOMPtr<nsIProtocolProxyService>  mXPComPPS;
303     nsCOMPtr<nsIChannel>               mChannel;
304     nsCOMPtr<nsIProtocolProxyCallback> mCallback;
305     nsCOMPtr<nsIProxyInfo>             mProxyInfo;
306 };
307 
NS_IMPL_ISUPPORTS(nsAsyncResolveRequest,nsICancelable,nsIRunnable)308 NS_IMPL_ISUPPORTS(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
309 
310 //----------------------------------------------------------------------------
311 
312 #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
313 
314 //
315 // apply mask to address (zeros out excluded bits).
316 //
317 // NOTE: we do the byte swapping here to minimize overall swapping.
318 //
319 static void
320 proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len)
321 {
322     if (mask_len == 128)
323         return;
324 
325     if (mask_len > 96) {
326         addr.pr_s6_addr32[3] = PR_htonl(
327                 PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
328     }
329     else if (mask_len > 64) {
330         addr.pr_s6_addr32[3] = 0;
331         addr.pr_s6_addr32[2] = PR_htonl(
332                 PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
333     }
334     else if (mask_len > 32) {
335         addr.pr_s6_addr32[3] = 0;
336         addr.pr_s6_addr32[2] = 0;
337         addr.pr_s6_addr32[1] = PR_htonl(
338                 PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
339     }
340     else {
341         addr.pr_s6_addr32[3] = 0;
342         addr.pr_s6_addr32[2] = 0;
343         addr.pr_s6_addr32[1] = 0;
344         addr.pr_s6_addr32[0] = PR_htonl(
345                 PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
346     }
347 }
348 
349 static void
proxy_GetStringPref(nsIPrefBranch * aPrefBranch,const char * aPref,nsCString & aResult)350 proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
351                     const char    *aPref,
352                     nsCString     &aResult)
353 {
354     nsXPIDLCString temp;
355     nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
356     if (NS_FAILED(rv))
357         aResult.Truncate();
358     else {
359         aResult.Assign(temp);
360         // all of our string prefs are hostnames, so we should remove any
361         // whitespace characters that the user might have unknowingly entered.
362         aResult.StripWhitespace();
363     }
364 }
365 
366 static void
proxy_GetIntPref(nsIPrefBranch * aPrefBranch,const char * aPref,int32_t & aResult)367 proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
368                  const char    *aPref,
369                  int32_t       &aResult)
370 {
371     int32_t temp;
372     nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
373     if (NS_FAILED(rv))
374         aResult = -1;
375     else
376         aResult = temp;
377 }
378 
379 static void
proxy_GetBoolPref(nsIPrefBranch * aPrefBranch,const char * aPref,bool & aResult)380 proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
381                  const char    *aPref,
382                  bool          &aResult)
383 {
384     bool temp;
385     nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
386     if (NS_FAILED(rv))
387         aResult = false;
388     else
389         aResult = temp;
390 }
391 
392 //----------------------------------------------------------------------------
393 
394 static const int32_t PROXYCONFIG_DIRECT4X = 3;
395 static const int32_t PROXYCONFIG_COUNT = 6;
396 
397 NS_IMPL_ADDREF(nsProtocolProxyService)
398 NS_IMPL_RELEASE(nsProtocolProxyService)
399 NS_IMPL_CLASSINFO(nsProtocolProxyService, nullptr, nsIClassInfo::SINGLETON,
400                   NS_PROTOCOLPROXYSERVICE_CID)
401 
402 // NS_IMPL_QUERY_INTERFACE_CI with the nsProtocolProxyService QI change
403 NS_INTERFACE_MAP_BEGIN(nsProtocolProxyService)
404 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService)
405 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyService2)
406 NS_INTERFACE_MAP_ENTRY(nsIObserver)
407 if ( aIID.Equals(NS_GET_IID(nsProtocolProxyService)) )  foundInterface = static_cast<nsIProtocolProxyService2*>(this); else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIProtocolProxyService)408 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolProxyService)
409 NS_IMPL_QUERY_CLASSINFO(nsProtocolProxyService)
410 NS_INTERFACE_MAP_END
411 
412 NS_IMPL_CI_INTERFACE_GETTER(nsProtocolProxyService,
413                             nsIProtocolProxyService,
414                             nsIProtocolProxyService2)
415 
416 nsProtocolProxyService::nsProtocolProxyService()
417     : mFilterLocalHosts(false)
418     , mFilters(nullptr)
419     , mProxyConfig(PROXYCONFIG_DIRECT)
420     , mHTTPProxyPort(-1)
421     , mFTPProxyPort(-1)
422     , mHTTPSProxyPort(-1)
423     , mSOCKSProxyPort(-1)
424     , mSOCKSProxyVersion(4)
425     , mSOCKSProxyRemoteDNS(false)
426     , mProxyOverTLS(true)
427     , mPACMan(nullptr)
428     , mSessionStart(PR_Now())
429     , mFailedProxyTimeout(30 * 60) // 30 minute default
430 {
431 }
432 
~nsProtocolProxyService()433 nsProtocolProxyService::~nsProtocolProxyService()
434 {
435     // These should have been cleaned up in our Observe method.
436     NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nullptr &&
437                  mPACMan == nullptr, "what happened to xpcom-shutdown?");
438 }
439 
440 // nsProtocolProxyService methods
441 nsresult
Init()442 nsProtocolProxyService::Init()
443 {
444     // failure to access prefs is non-fatal
445     nsCOMPtr<nsIPrefBranch> prefBranch =
446             do_GetService(NS_PREFSERVICE_CONTRACTID);
447     if (prefBranch) {
448         // monitor proxy prefs
449         prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
450 
451         // read all prefs
452         PrefsChanged(prefBranch, nullptr);
453     }
454 
455     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
456     if (obs) {
457         // register for shutdown notification so we can clean ourselves up
458         // properly.
459         obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
460         obs->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
461     }
462 
463     return NS_OK;
464 }
465 
466 // ReloadNetworkPAC() checks if there's a non-networked PAC in use then avoids
467 // to call ReloadPAC()
468 nsresult
ReloadNetworkPAC()469 nsProtocolProxyService::ReloadNetworkPAC()
470 {
471     nsCOMPtr<nsIPrefBranch> prefs =
472         do_GetService(NS_PREFSERVICE_CONTRACTID);
473     if (!prefs) {
474         return NS_OK;
475     }
476 
477     int32_t type;
478     nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
479     if (NS_FAILED(rv)) {
480         return NS_OK;
481     }
482 
483     if (type == PROXYCONFIG_PAC) {
484         nsXPIDLCString pacSpec;
485         prefs->GetCharPref(PROXY_PREF("autoconfig_url"),
486                            getter_Copies(pacSpec));
487         if (!pacSpec.IsEmpty()) {
488             nsCOMPtr<nsIURI> pacURI;
489             rv = NS_NewURI(getter_AddRefs(pacURI), pacSpec);
490             if(!NS_SUCCEEDED(rv)) {
491                 return rv;
492             }
493 
494             nsProtocolInfo pac;
495             rv = GetProtocolInfo(pacURI, &pac);
496             if(!NS_SUCCEEDED(rv)) {
497                 return rv;
498             }
499 
500             if (!pac.scheme.EqualsLiteral("file") &&
501                 !pac.scheme.EqualsLiteral("data")) {
502                 LOG((": received network changed event, reload PAC"));
503                 ReloadPAC();
504             }
505         }
506     } else if ((type == PROXYCONFIG_WPAD) || (type == PROXYCONFIG_SYSTEM)) {
507         ReloadPAC();
508     }
509 
510     return NS_OK;
511 }
512 
513 
514 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)515 nsProtocolProxyService::Observe(nsISupports     *aSubject,
516                                 const char      *aTopic,
517                                 const char16_t *aData)
518 {
519     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
520         // cleanup
521         if (mHostFiltersArray.Length() > 0) {
522             mHostFiltersArray.Clear();
523         }
524         if (mFilters) {
525             delete mFilters;
526             mFilters = nullptr;
527         }
528         if (mPACMan) {
529             mPACMan->Shutdown();
530             mPACMan = nullptr;
531         }
532 
533         nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
534         if (obs) {
535             obs->RemoveObserver(this, NS_NETWORK_LINK_TOPIC);
536             obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
537         }
538 
539     } else if (strcmp(aTopic, NS_NETWORK_LINK_TOPIC) == 0) {
540         nsCString converted = NS_ConvertUTF16toUTF8(aData);
541         const char *state = converted.get();
542         if (!strcmp(state, NS_NETWORK_LINK_DATA_CHANGED)) {
543             ReloadNetworkPAC();
544         }
545     }
546     else {
547         NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
548                      "what is this random observer event?");
549         nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
550         if (prefs)
551             PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
552     }
553     return NS_OK;
554 }
555 
556 void
PrefsChanged(nsIPrefBranch * prefBranch,const char * pref)557 nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
558                                      const char    *pref)
559 {
560     nsresult rv = NS_OK;
561     bool reloadPAC = false;
562     nsXPIDLCString tempString;
563 
564     if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
565         int32_t type = -1;
566         rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
567         if (NS_SUCCEEDED(rv)) {
568             // bug 115720 - for ns4.x backwards compatibility
569             if (type == PROXYCONFIG_DIRECT4X) {
570                 type = PROXYCONFIG_DIRECT;
571                 // Reset the type so that the dialog looks correct, and we
572                 // don't have to handle this case everywhere else
573                 // I'm paranoid about a loop of some sort - only do this
574                 // if we're enumerating all prefs, and ignore any error
575                 if (!pref)
576                     prefBranch->SetIntPref(PROXY_PREF("type"), type);
577             } else if (type >= PROXYCONFIG_COUNT) {
578                 LOG(("unknown proxy type: %lu; assuming direct\n", type));
579                 type = PROXYCONFIG_DIRECT;
580             }
581             mProxyConfig = type;
582             reloadPAC = true;
583         }
584 
585         if (mProxyConfig == PROXYCONFIG_SYSTEM) {
586             mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
587             if (!mSystemProxySettings)
588                 mProxyConfig = PROXYCONFIG_DIRECT;
589             ResetPACThread();
590         } else {
591             if (mSystemProxySettings) {
592                 mSystemProxySettings = nullptr;
593                 ResetPACThread();
594             }
595         }
596     }
597 
598     if (!pref || !strcmp(pref, PROXY_PREF("http")))
599         proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
600 
601     if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
602         proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
603 
604     if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
605         proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
606 
607     if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
608         proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
609 
610     if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
611         proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
612 
613     if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
614         proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
615 
616     if (!pref || !strcmp(pref, PROXY_PREF("socks")))
617         proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyTarget);
618 
619     if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
620         proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
621 
622     if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
623         int32_t version;
624         proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
625         // make sure this preference value remains sane
626         if (version == 5)
627             mSOCKSProxyVersion = 5;
628         else
629             mSOCKSProxyVersion = 4;
630     }
631 
632     if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
633         proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
634                           mSOCKSProxyRemoteDNS);
635 
636     if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
637         proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"),
638                           mProxyOverTLS);
639     }
640 
641     if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
642         proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
643                          mFailedProxyTimeout);
644 
645     if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
646         rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
647                                      getter_Copies(tempString));
648         if (NS_SUCCEEDED(rv))
649             LoadHostFilters(tempString);
650     }
651 
652     // We're done if not using something that could give us a PAC URL
653     // (PAC, WPAD or System)
654     if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
655         mProxyConfig != PROXYCONFIG_SYSTEM)
656         return;
657 
658     // OK, we need to reload the PAC file if:
659     //  1) network.proxy.type changed, or
660     //  2) network.proxy.autoconfig_url changed and PAC is configured
661 
662     if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
663         reloadPAC = true;
664 
665     if (reloadPAC) {
666         tempString.Truncate();
667         if (mProxyConfig == PROXYCONFIG_PAC) {
668             prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
669                                     getter_Copies(tempString));
670             if (mPACMan && !mPACMan->IsPACURI(tempString)) {
671                 LOG(("PAC Thread URI Changed - Reset Pac Thread"));
672                 ResetPACThread();
673             }
674         } else if (mProxyConfig == PROXYCONFIG_WPAD) {
675             // We diverge from the WPAD spec here in that we don't walk the
676             // hosts's FQDN, stripping components until we hit a TLD.  Doing so
677             // is dangerous in the face of an incomplete list of TLDs, and TLDs
678             // get added over time.  We could consider doing only a single
679             // substitution of the first component, if that proves to help
680             // compatibility.
681             tempString.AssignLiteral(WPAD_URL);
682         } else if (mSystemProxySettings) {
683             // Get System Proxy settings if available
684             mSystemProxySettings->GetPACURI(tempString);
685         }
686         if (!tempString.IsEmpty())
687             ConfigureFromPAC(tempString, false);
688     }
689 }
690 
691 bool
CanUseProxy(nsIURI * aURI,int32_t defaultPort)692 nsProtocolProxyService::CanUseProxy(nsIURI *aURI, int32_t defaultPort)
693 {
694     if (mHostFiltersArray.Length() == 0)
695         return true;
696 
697     int32_t port;
698     nsAutoCString host;
699 
700     nsresult rv = aURI->GetAsciiHost(host);
701     if (NS_FAILED(rv) || host.IsEmpty())
702         return false;
703 
704     rv = aURI->GetPort(&port);
705     if (NS_FAILED(rv))
706         return false;
707     if (port == -1)
708         port = defaultPort;
709 
710     PRNetAddr addr;
711     bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
712 
713     PRIPv6Addr ipv6;
714     if (is_ipaddr) {
715         // convert parsed address to IPv6
716         if (addr.raw.family == PR_AF_INET) {
717             // convert to IPv4-mapped address
718             PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
719         }
720         else if (addr.raw.family == PR_AF_INET6) {
721             // copy the address
722             memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
723         }
724         else {
725             NS_WARNING("unknown address family");
726             return true; // allow proxying
727         }
728     }
729 
730     // Don't use proxy for local hosts (plain hostname, no dots)
731     if ((!is_ipaddr && mFilterLocalHosts && !host.Contains('.')) ||
732         host.EqualsLiteral("127.0.0.1") ||
733         host.EqualsLiteral("::1")) {
734         LOG(("Not using proxy for this local host [%s]!\n", host.get()));
735         return false; // don't allow proxying
736     }
737 
738     int32_t index = -1;
739     while (++index < int32_t(mHostFiltersArray.Length())) {
740         HostInfo *hinfo = mHostFiltersArray[index];
741 
742         if (is_ipaddr != hinfo->is_ipaddr)
743             continue;
744         if (hinfo->port && hinfo->port != port)
745             continue;
746 
747         if (is_ipaddr) {
748             // generate masked version of target IPv6 address
749             PRIPv6Addr masked;
750             memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
751             proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
752 
753             // check for a match
754             if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
755                 return false; // proxy disallowed
756         }
757         else {
758             uint32_t host_len = host.Length();
759             uint32_t filter_host_len = hinfo->name.host_len;
760 
761             if (host_len >= filter_host_len) {
762                 //
763                 // compare last |filter_host_len| bytes of target hostname.
764                 //
765                 const char *host_tail = host.get() + host_len - filter_host_len;
766                 if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len)) {
767                     // If the tail of the host string matches the filter
768 
769                     if (filter_host_len > 0 && hinfo->name.host[0] == '.') {
770                         // If the filter was of the form .foo.bar.tld, all such
771                         // matches are correct
772                         return false; // proxy disallowed
773                     }
774 
775                     // abc-def.example.org should not match def.example.org
776                     // however, *.def.example.org should match .def.example.org
777                     // We check that the filter doesn't start with a `.`. If it does,
778                     // then the strncasecmp above should suffice. If it doesn't,
779                     // then we should only consider it a match if the strncasecmp happened
780                     // at a subdomain boundary
781                     if (host_len > filter_host_len && *(host_tail - 1) == '.') {
782                             // If the host was something.foo.bar.tld and the filter
783                             // was foo.bar.tld, it's still a match.
784                             // the character right before the tail must be a
785                             // `.` for this to work
786                             return false; // proxy disallowed
787                     }
788 
789                     if (host_len == filter_host_len) {
790                         // If the host and filter are of the same length,
791                         // they should match
792                         return false; // proxy disallowed
793                     }
794                 }
795 
796             }
797         }
798     }
799     return true;
800 }
801 
802 // kProxyType\* may be referred to externally in
803 // nsProxyInfo in order to compare by string pointer
804 const char kProxyType_HTTP[]    = "http";
805 const char kProxyType_HTTPS[]   = "https";
806 const char kProxyType_PROXY[]   = "proxy";
807 const char kProxyType_SOCKS[]   = "socks";
808 const char kProxyType_SOCKS4[]  = "socks4";
809 const char kProxyType_SOCKS5[]  = "socks5";
810 const char kProxyType_DIRECT[]  = "direct";
811 
812 const char *
ExtractProxyInfo(const char * start,uint32_t aResolveFlags,nsProxyInfo ** result)813 nsProtocolProxyService::ExtractProxyInfo(const char *start,
814                                          uint32_t aResolveFlags,
815                                          nsProxyInfo **result)
816 {
817     *result = nullptr;
818     uint32_t flags = 0;
819 
820     // see BNF in ProxyAutoConfig.h and notes in nsISystemProxySettings.idl
821 
822     // find end of proxy info delimiter
823     const char *end = start;
824     while (*end && *end != ';') ++end;
825 
826     // find end of proxy type delimiter
827     const char *sp = start;
828     while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
829 
830     uint32_t len = sp - start;
831     const char *type = nullptr;
832     switch (len) {
833     case 4:
834         if (PL_strncasecmp(start, kProxyType_HTTP, 5) == 0) {
835             type = kProxyType_HTTP;
836         }
837         break;
838     case 5:
839         if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0) {
840             type = kProxyType_HTTP;
841         } else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0) {
842             type = kProxyType_SOCKS4; // assume v4 for 4x compat
843         } else if (PL_strncasecmp(start, kProxyType_HTTPS, 5) == 0) {
844             type = kProxyType_HTTPS;
845         }
846         break;
847     case 6:
848         if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
849             type = kProxyType_DIRECT;
850         else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
851             type = kProxyType_SOCKS4;
852         else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
853             // map "SOCKS5" to "socks" to match contract-id of registered
854             // SOCKS-v5 socket provider.
855             type = kProxyType_SOCKS;
856         break;
857     }
858     if (type) {
859         const char *host = nullptr, *hostEnd = nullptr;
860         int32_t port = -1;
861 
862         // If it's a SOCKS5 proxy, do name resolution on the server side.
863         // We could use this with SOCKS4a servers too, but they might not
864         // support it.
865         if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
866             flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
867 
868         // extract host:port
869         start = sp;
870         while ((*start == ' ' || *start == '\t') && start < end)
871             start++;
872 
873         // port defaults
874         if (type == kProxyType_HTTP) {
875             port = 80;
876         } else if (type == kProxyType_HTTPS) {
877             port = 443;
878         } else {
879             port = 1080;
880         }
881 
882         nsProxyInfo *pi = new nsProxyInfo();
883         pi->mType = type;
884         pi->mFlags = flags;
885         pi->mResolveFlags = aResolveFlags;
886         pi->mTimeout = mFailedProxyTimeout;
887 
888         // www.foo.com:8080 and http://www.foo.com:8080
889         nsDependentCSubstring maybeURL(start, end - start);
890         nsCOMPtr<nsIURI> pacURI;
891 
892         nsAutoCString urlHost;
893         if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pacURI), maybeURL)) &&
894             NS_SUCCEEDED(pacURI->GetAsciiHost(urlHost)) &&
895             !urlHost.IsEmpty()) {
896             // http://www.example.com:8080
897 
898             pi->mHost = urlHost;
899 
900             int32_t tPort;
901             if (NS_SUCCEEDED(pacURI->GetPort(&tPort)) && tPort != -1) {
902                 port = tPort;
903             }
904             pi->mPort = port;
905         }
906         else {
907             // www.example.com:8080
908             if (start < end) {
909                 host = start;
910                 hostEnd = strchr(host, ':');
911                 if (!hostEnd || hostEnd > end) {
912                     hostEnd = end;
913                     // no port, so assume default
914                 }
915                 else {
916                     port = atoi(hostEnd + 1);
917                 }
918             }
919             // YES, it is ok to specify a null proxy host.
920             if (host) {
921                 pi->mHost.Assign(host, hostEnd - host);
922                 pi->mPort = port;
923             }
924         }
925         NS_ADDREF(*result = pi);
926     }
927 
928     while (*end == ';' || *end == ' ' || *end == '\t')
929         ++end;
930     return end;
931 }
932 
933 void
GetProxyKey(nsProxyInfo * pi,nsCString & key)934 nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
935 {
936     key.AssignASCII(pi->mType);
937     if (!pi->mHost.IsEmpty()) {
938         key.Append(' ');
939         key.Append(pi->mHost);
940         key.Append(':');
941         key.AppendInt(pi->mPort);
942     }
943 }
944 
945 uint32_t
SecondsSinceSessionStart()946 nsProtocolProxyService::SecondsSinceSessionStart()
947 {
948     PRTime now = PR_Now();
949 
950     // get time elapsed since session start
951     int64_t diff = now - mSessionStart;
952 
953     // convert microseconds to seconds
954     diff /= PR_USEC_PER_SEC;
955 
956     // return converted 32 bit value
957     return uint32_t(diff);
958 }
959 
960 void
EnableProxy(nsProxyInfo * pi)961 nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
962 {
963     nsAutoCString key;
964     GetProxyKey(pi, key);
965     mFailedProxies.Remove(key);
966 }
967 
968 void
DisableProxy(nsProxyInfo * pi)969 nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
970 {
971     nsAutoCString key;
972     GetProxyKey(pi, key);
973 
974     uint32_t dsec = SecondsSinceSessionStart();
975 
976     // Add timeout to interval (this is the time when the proxy can
977     // be tried again).
978     dsec += pi->mTimeout;
979 
980     // NOTE: The classic codebase would increase the timeout value
981     //       incrementally each time a subsequent failure occurred.
982     //       We could do the same, but it would require that we not
983     //       remove proxy entries in IsProxyDisabled or otherwise
984     //       change the way we are recording disabled proxies.
985     //       Simpler is probably better for now, and at least the
986     //       user can tune the timeout setting via preferences.
987 
988     LOG(("DisableProxy %s %d\n", key.get(), dsec));
989 
990     // If this fails, oh well... means we don't have enough memory
991     // to remember the failed proxy.
992     mFailedProxies.Put(key, dsec);
993 }
994 
995 bool
IsProxyDisabled(nsProxyInfo * pi)996 nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
997 {
998     nsAutoCString key;
999     GetProxyKey(pi, key);
1000 
1001     uint32_t val;
1002     if (!mFailedProxies.Get(key, &val))
1003         return false;
1004 
1005     uint32_t dsec = SecondsSinceSessionStart();
1006 
1007     // if time passed has exceeded interval, then try proxy again.
1008     if (dsec > val) {
1009         mFailedProxies.Remove(key);
1010         return false;
1011     }
1012 
1013     return true;
1014 }
1015 
1016 nsresult
SetupPACThread()1017 nsProtocolProxyService::SetupPACThread()
1018 {
1019     if (mPACMan)
1020         return NS_OK;
1021 
1022     mPACMan = new nsPACMan();
1023 
1024     bool mainThreadOnly;
1025     nsresult rv;
1026     if (mSystemProxySettings &&
1027         NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1028         !mainThreadOnly) {
1029         rv = mPACMan->Init(mSystemProxySettings);
1030     }
1031     else {
1032         rv = mPACMan->Init(nullptr);
1033     }
1034 
1035     if (NS_FAILED(rv))
1036         mPACMan = nullptr;
1037     return rv;
1038 }
1039 
1040 nsresult
ResetPACThread()1041 nsProtocolProxyService::ResetPACThread()
1042 {
1043     if (!mPACMan)
1044         return NS_OK;
1045 
1046     mPACMan->Shutdown();
1047     mPACMan = nullptr;
1048     return SetupPACThread();
1049 }
1050 
1051 nsresult
ConfigureFromPAC(const nsCString & spec,bool forceReload)1052 nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
1053                                          bool forceReload)
1054 {
1055     SetupPACThread();
1056 
1057     if (mPACMan->IsPACURI(spec) && !forceReload)
1058         return NS_OK;
1059 
1060     mFailedProxies.Clear();
1061 
1062     return mPACMan->LoadPACFromURI(spec);
1063 }
1064 
1065 void
ProcessPACString(const nsCString & pacString,uint32_t aResolveFlags,nsIProxyInfo ** result)1066 nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
1067                                          uint32_t aResolveFlags,
1068                                          nsIProxyInfo **result)
1069 {
1070     if (pacString.IsEmpty()) {
1071         *result = nullptr;
1072         return;
1073     }
1074 
1075     const char *proxies = pacString.get();
1076 
1077     nsProxyInfo *pi = nullptr, *first = nullptr, *last = nullptr;
1078     while (*proxies) {
1079         proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
1080         if (pi && (pi->mType == kProxyType_HTTPS) && !mProxyOverTLS) {
1081             delete pi;
1082             pi = nullptr;
1083         }
1084 
1085         if (pi) {
1086             if (last) {
1087                 NS_ASSERTION(last->mNext == nullptr, "leaking nsProxyInfo");
1088                 last->mNext = pi;
1089             }
1090             else
1091                 first = pi;
1092             last = pi;
1093         }
1094     }
1095     *result = first;
1096 }
1097 
1098 // nsIProtocolProxyService2
1099 NS_IMETHODIMP
ReloadPAC()1100 nsProtocolProxyService::ReloadPAC()
1101 {
1102     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
1103     if (!prefs)
1104         return NS_OK;
1105 
1106     int32_t type;
1107     nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
1108     if (NS_FAILED(rv))
1109         return NS_OK;
1110 
1111     nsXPIDLCString pacSpec;
1112     if (type == PROXYCONFIG_PAC)
1113         prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
1114     else if (type == PROXYCONFIG_WPAD)
1115         pacSpec.AssignLiteral(WPAD_URL);
1116 
1117     if (!pacSpec.IsEmpty())
1118         ConfigureFromPAC(pacSpec, true);
1119     return NS_OK;
1120 }
1121 
1122 // When sync interface is removed this can go away too
1123 // The nsPACManCallback portion of this implementation should be run
1124 // off the main thread, because it uses a condvar for signaling and
1125 // the main thread is blocking on that condvar -
1126 //  so call nsPACMan::AsyncGetProxyForURI() with
1127 // a false mainThreadResponse parameter.
1128 class nsAsyncBridgeRequest final  : public nsPACManCallback
1129 {
1130     NS_DECL_THREADSAFE_ISUPPORTS
1131 
nsAsyncBridgeRequest()1132      nsAsyncBridgeRequest()
1133         : mMutex("nsDeprecatedCallback")
1134         , mCondVar(mMutex, "nsDeprecatedCallback")
1135         , mStatus(NS_OK)
1136         , mCompleted(false)
1137     {
1138     }
1139 
OnQueryComplete(nsresult status,const nsCString & pacString,const nsCString & newPACURL)1140     void OnQueryComplete(nsresult status,
1141                          const nsCString &pacString,
1142                          const nsCString &newPACURL) override
1143     {
1144         MutexAutoLock lock(mMutex);
1145         mCompleted = true;
1146         mStatus = status;
1147         mPACString = pacString;
1148         mPACURL = newPACURL;
1149         mCondVar.Notify();
1150     }
1151 
Lock()1152     void Lock()   { mMutex.Lock(); }
Unlock()1153     void Unlock() { mMutex.Unlock(); }
Wait()1154     void Wait()   { mCondVar.Wait(PR_SecondsToInterval(3)); }
1155 
1156 private:
~nsAsyncBridgeRequest()1157     ~nsAsyncBridgeRequest()
1158     {
1159     }
1160 
1161     friend class nsProtocolProxyService;
1162 
1163     Mutex    mMutex;
1164     CondVar  mCondVar;
1165 
1166     nsresult  mStatus;
1167     nsCString mPACString;
1168     nsCString mPACURL;
1169     bool      mCompleted;
1170 };
NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)1171 NS_IMPL_ISUPPORTS0(nsAsyncBridgeRequest)
1172 
1173 // nsProtocolProxyService
1174 nsresult
1175 nsProtocolProxyService::DeprecatedBlockingResolve(nsIChannel *aChannel,
1176                                                   uint32_t aFlags,
1177                                                   nsIProxyInfo **retval)
1178 {
1179     NS_ENSURE_ARG_POINTER(aChannel);
1180 
1181     nsCOMPtr<nsIURI> uri;
1182     nsresult rv = GetProxyURI(aChannel, getter_AddRefs(uri));
1183     if (NS_FAILED(rv)) return rv;
1184 
1185     nsProtocolInfo info;
1186     rv = GetProtocolInfo(uri, &info);
1187     if (NS_FAILED(rv))
1188         return rv;
1189 
1190     nsCOMPtr<nsIProxyInfo> pi;
1191     bool usePACThread;
1192 
1193     // SystemProxySettings and PAC files can block the main thread
1194     // but if neither of them are in use, we can just do the work
1195     // right here and directly invoke the callback
1196 
1197     rv = Resolve_Internal(aChannel, info, aFlags,
1198                           &usePACThread, getter_AddRefs(pi));
1199     if (NS_FAILED(rv))
1200         return rv;
1201 
1202     if (!usePACThread || !mPACMan) {
1203         ApplyFilters(aChannel, info, pi);
1204         pi.forget(retval);
1205         return NS_OK;
1206     }
1207 
1208     // Use the PAC thread to do the work, so we don't have to reimplement that
1209     // code, but block this thread on that completion.
1210     RefPtr<nsAsyncBridgeRequest> ctx = new nsAsyncBridgeRequest();
1211     ctx->Lock();
1212     if (NS_SUCCEEDED(mPACMan->AsyncGetProxyForURI(uri, ctx, false))) {
1213         // this can really block the main thread, so cap it at 3 seconds
1214        ctx->Wait();
1215     }
1216     ctx->Unlock();
1217     if (!ctx->mCompleted)
1218         return NS_ERROR_FAILURE;
1219     if (NS_FAILED(ctx->mStatus))
1220         return ctx->mStatus;
1221 
1222     // pretty much duplicate real DoCallback logic
1223 
1224     // Generate proxy info from the PAC string if appropriate
1225     if (!ctx->mPACString.IsEmpty()) {
1226         LOG(("sync pac thread callback %s\n", ctx->mPACString.get()));
1227         ProcessPACString(ctx->mPACString, 0, getter_AddRefs(pi));
1228         ApplyFilters(aChannel, info, pi);
1229         pi.forget(retval);
1230         return NS_OK;
1231     }
1232 
1233     if (!ctx->mPACURL.IsEmpty()) {
1234         NS_WARNING("sync pac thread callback indicates new pac file load\n");
1235         // This is a problem and is one of the reasons this blocking interface
1236         // is deprecated. The main loop needs to spin to make this reload happen. So
1237         // we are going to kick off the reload and return an error - it will work
1238         // next time. Because this sync interface is only used in the java plugin it
1239         // is extremely likely that the pac file has already been loaded anyhow.
1240 
1241         rv = ConfigureFromPAC(ctx->mPACURL, false);
1242         if (NS_FAILED(rv))
1243             return rv;
1244         return NS_ERROR_NOT_AVAILABLE;
1245     }
1246 
1247     *retval = nullptr;
1248     return NS_OK;
1249 }
1250 
1251 nsresult
AsyncResolveInternal(nsIChannel * channel,uint32_t flags,nsIProtocolProxyCallback * callback,nsICancelable ** result,bool isSyncOK)1252 nsProtocolProxyService::AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
1253                                              nsIProtocolProxyCallback *callback,
1254                                              nsICancelable **result,
1255                                              bool isSyncOK)
1256 {
1257     NS_ENSURE_ARG_POINTER(channel);
1258     NS_ENSURE_ARG_POINTER(callback);
1259 
1260     nsCOMPtr<nsIURI> uri;
1261     nsresult rv = GetProxyURI(channel, getter_AddRefs(uri));
1262     if (NS_FAILED(rv)) return rv;
1263 
1264     *result = nullptr;
1265     RefPtr<nsAsyncResolveRequest> ctx =
1266         new nsAsyncResolveRequest(this, channel, flags, callback);
1267 
1268     nsProtocolInfo info;
1269     rv = GetProtocolInfo(uri, &info);
1270     if (NS_FAILED(rv))
1271         return rv;
1272 
1273     nsCOMPtr<nsIProxyInfo> pi;
1274     bool usePACThread;
1275 
1276     // SystemProxySettings and PAC files can block the main thread
1277     // but if neither of them are in use, we can just do the work
1278     // right here and directly invoke the callback
1279 
1280     rv = Resolve_Internal(channel, info, flags,
1281                           &usePACThread, getter_AddRefs(pi));
1282     if (NS_FAILED(rv))
1283         return rv;
1284 
1285     if (!usePACThread || !mPACMan) {
1286         // we can do it locally
1287         ApplyFilters(channel, info, pi);
1288         ctx->SetResult(NS_OK, pi);
1289         if (isSyncOK) {
1290             ctx->Run();
1291             return NS_OK;
1292         }
1293 
1294         rv = ctx->DispatchCallback();
1295         if (NS_SUCCEEDED(rv))
1296             ctx.forget(result);
1297         return rv;
1298     }
1299 
1300     // else kick off a PAC thread query
1301 
1302     rv = mPACMan->AsyncGetProxyForURI(uri, ctx, true);
1303     if (NS_SUCCEEDED(rv))
1304         ctx.forget(result);
1305     return rv;
1306 }
1307 
1308 // nsIProtocolProxyService
1309 NS_IMETHODIMP
AsyncResolve2(nsIChannel * channel,uint32_t flags,nsIProtocolProxyCallback * callback,nsICancelable ** result)1310 nsProtocolProxyService::AsyncResolve2(nsIChannel *channel, uint32_t flags,
1311                                       nsIProtocolProxyCallback *callback,
1312                                       nsICancelable **result)
1313 {
1314     return AsyncResolveInternal(channel, flags, callback, result, true);
1315 }
1316 
1317 NS_IMETHODIMP
AsyncResolve(nsISupports * channelOrURI,uint32_t flags,nsIProtocolProxyCallback * callback,nsICancelable ** result)1318 nsProtocolProxyService::AsyncResolve(nsISupports *channelOrURI, uint32_t flags,
1319                                      nsIProtocolProxyCallback *callback,
1320                                      nsICancelable **result)
1321 {
1322 
1323     nsresult rv;
1324     // Check if we got a channel:
1325     nsCOMPtr<nsIChannel> channel = do_QueryInterface(channelOrURI);
1326     if (!channel) {
1327         nsCOMPtr<nsIURI> uri = do_QueryInterface(channelOrURI);
1328         if (!uri) {
1329             return NS_ERROR_NO_INTERFACE;
1330         }
1331 
1332         nsCOMPtr<nsIScriptSecurityManager> secMan(
1333             do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
1334         NS_ENSURE_SUCCESS(rv, rv);
1335         nsCOMPtr<nsIPrincipal> systemPrincipal;
1336         rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
1337         NS_ENSURE_SUCCESS(rv, rv);
1338 
1339         // creating a temporary channel from the URI which is not
1340         // used to perform any network loads, hence its safe to
1341         // use systemPrincipal as the loadingPrincipal.
1342         rv = NS_NewChannel(getter_AddRefs(channel),
1343                            uri,
1344                            systemPrincipal,
1345                            nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1346                            nsIContentPolicy::TYPE_OTHER);
1347         NS_ENSURE_SUCCESS(rv, rv);
1348     }
1349 
1350     return AsyncResolveInternal(channel, flags, callback, result, false);
1351 }
1352 
1353 NS_IMETHODIMP
NewProxyInfo(const nsACString & aType,const nsACString & aHost,int32_t aPort,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,nsIProxyInfo ** aResult)1354 nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
1355                                      const nsACString &aHost,
1356                                      int32_t aPort,
1357                                      uint32_t aFlags,
1358                                      uint32_t aFailoverTimeout,
1359                                      nsIProxyInfo *aFailoverProxy,
1360                                      nsIProxyInfo **aResult)
1361 {
1362     return NewProxyInfoWithAuth(aType, aHost, aPort,
1363                                 EmptyCString(), EmptyCString(),
1364                                 aFlags, aFailoverTimeout,
1365                                 aFailoverProxy, aResult);
1366 }
1367 
1368 NS_IMETHODIMP
NewProxyInfoWithAuth(const nsACString & aType,const nsACString & aHost,int32_t aPort,const nsACString & aUsername,const nsACString & aPassword,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,nsIProxyInfo ** aResult)1369 nsProtocolProxyService::NewProxyInfoWithAuth(const nsACString &aType,
1370                                              const nsACString &aHost,
1371                                              int32_t aPort,
1372                                              const nsACString &aUsername,
1373                                              const nsACString &aPassword,
1374                                              uint32_t aFlags,
1375                                              uint32_t aFailoverTimeout,
1376                                              nsIProxyInfo *aFailoverProxy,
1377                                              nsIProxyInfo **aResult)
1378 {
1379     static const char *types[] = {
1380         kProxyType_HTTP,
1381         kProxyType_HTTPS,
1382         kProxyType_SOCKS,
1383         kProxyType_SOCKS4,
1384         kProxyType_DIRECT
1385     };
1386 
1387     // resolve type; this allows us to avoid copying the type string into each
1388     // proxy info instance.  we just reference the string literals directly :)
1389     const char *type = nullptr;
1390     for (uint32_t i = 0; i < ArrayLength(types); ++i) {
1391         if (aType.LowerCaseEqualsASCII(types[i])) {
1392             type = types[i];
1393             break;
1394         }
1395     }
1396     NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
1397 
1398     // We have only implemented username/password for SOCKS proxies.
1399     if ((!aUsername.IsEmpty() || !aPassword.IsEmpty()) &&
1400         !aType.LowerCaseEqualsASCII(kProxyType_SOCKS) &&
1401         !aType.LowerCaseEqualsASCII(kProxyType_SOCKS4)) {
1402         return NS_ERROR_NOT_IMPLEMENTED;
1403     }
1404 
1405     return NewProxyInfo_Internal(type, aHost, aPort,
1406                                  aUsername, aPassword,
1407                                  aFlags, aFailoverTimeout,
1408                                  aFailoverProxy, 0, aResult);
1409 }
1410 
1411 NS_IMETHODIMP
GetFailoverForProxy(nsIProxyInfo * aProxy,nsIURI * aURI,nsresult aStatus,nsIProxyInfo ** aResult)1412 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
1413                                             nsIURI        *aURI,
1414                                             nsresult       aStatus,
1415                                             nsIProxyInfo **aResult)
1416 {
1417     // We only support failover when a PAC file is configured, either
1418     // directly or via system settings
1419     if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
1420         mProxyConfig != PROXYCONFIG_SYSTEM)
1421         return NS_ERROR_NOT_AVAILABLE;
1422 
1423     // Verify that |aProxy| is one of our nsProxyInfo objects.
1424     nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1425     NS_ENSURE_ARG(pi);
1426     // OK, the QI checked out.  We can proceed.
1427 
1428     // Remember that this proxy is down.
1429     DisableProxy(pi);
1430 
1431     // NOTE: At this point, we might want to prompt the user if we have
1432     //       not already tried going DIRECT.  This is something that the
1433     //       classic codebase supported; however, IE6 does not prompt.
1434 
1435     if (!pi->mNext)
1436         return NS_ERROR_NOT_AVAILABLE;
1437 
1438     LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
1439         pi->mType, pi->mHost.get(), pi->mPort,
1440         pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
1441 
1442     NS_ADDREF(*aResult = pi->mNext);
1443     return NS_OK;
1444 }
1445 
1446 nsresult
InsertFilterLink(FilterLink * link,uint32_t position)1447 nsProtocolProxyService::InsertFilterLink(FilterLink *link, uint32_t position)
1448 {
1449     if (!mFilters) {
1450         mFilters = link;
1451         return NS_OK;
1452     }
1453 
1454     // insert into mFilters in sorted order
1455     FilterLink *last = nullptr;
1456     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1457         if (position < iter->position) {
1458             if (last) {
1459                 link->next = last->next;
1460                 last->next = link;
1461             }
1462             else {
1463                 link->next = mFilters;
1464                 mFilters = link;
1465             }
1466             return NS_OK;
1467         }
1468         last = iter;
1469     }
1470     // our position is equal to or greater than the last link in the list
1471     last->next = link;
1472     return NS_OK;
1473 }
1474 
1475 NS_IMETHODIMP
RegisterFilter(nsIProtocolProxyFilter * filter,uint32_t position)1476 nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
1477                                        uint32_t position)
1478 {
1479     UnregisterFilter(filter); // remove this filter if we already have it
1480 
1481     FilterLink *link = new FilterLink(position, filter);
1482     if (!link) {
1483         return NS_ERROR_OUT_OF_MEMORY;
1484     }
1485     return InsertFilterLink(link, position);
1486 }
1487 
1488 NS_IMETHODIMP
RegisterChannelFilter(nsIProtocolProxyChannelFilter * channelFilter,uint32_t position)1489 nsProtocolProxyService::RegisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter,
1490                                               uint32_t position)
1491 {
1492     UnregisterChannelFilter(channelFilter);  // remove this filter if we already have it
1493 
1494     FilterLink *link = new FilterLink(position, channelFilter);
1495     if (!link) {
1496         return NS_ERROR_OUT_OF_MEMORY;
1497     }
1498     return InsertFilterLink(link, position);
1499 }
1500 
1501 nsresult
RemoveFilterLink(nsISupports * givenObject)1502 nsProtocolProxyService::RemoveFilterLink(nsISupports* givenObject)
1503 {
1504     FilterLink *last = nullptr;
1505     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1506         nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
1507         nsCOMPtr<nsISupports> object2 = do_QueryInterface(iter->channelFilter);
1508         if (object == givenObject || object2 == givenObject) {
1509             if (last)
1510                 last->next = iter->next;
1511             else
1512                 mFilters = iter->next;
1513             iter->next = nullptr;
1514             delete iter;
1515             return NS_OK;
1516         }
1517         last = iter;
1518     }
1519 
1520     // No need to throw an exception in this case.
1521     return NS_OK;
1522 }
1523 
1524 NS_IMETHODIMP
UnregisterFilter(nsIProtocolProxyFilter * filter)1525 nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter) {
1526     // QI to nsISupports so we can safely test object identity.
1527     nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1528     return RemoveFilterLink(givenObject);
1529 }
1530 
1531 NS_IMETHODIMP
UnregisterChannelFilter(nsIProtocolProxyChannelFilter * channelFilter)1532 nsProtocolProxyService::UnregisterChannelFilter(nsIProtocolProxyChannelFilter *channelFilter) {
1533     // QI to nsISupports so we can safely test object identity.
1534     nsCOMPtr<nsISupports> givenObject = do_QueryInterface(channelFilter);
1535     return RemoveFilterLink(givenObject);
1536 }
1537 
1538 NS_IMETHODIMP
GetProxyConfigType(uint32_t * aProxyConfigType)1539 nsProtocolProxyService::GetProxyConfigType(uint32_t* aProxyConfigType)
1540 {
1541   *aProxyConfigType = mProxyConfig;
1542   return NS_OK;
1543 }
1544 
1545 void
LoadHostFilters(const nsACString & aFilters)1546 nsProtocolProxyService::LoadHostFilters(const nsACString& aFilters)
1547 {
1548     // check to see the owners flag? /!?/ TODO
1549     if (mHostFiltersArray.Length() > 0) {
1550         mHostFiltersArray.Clear();
1551     }
1552 
1553     if (aFilters.IsEmpty()) {
1554         return;
1555     }
1556 
1557     //
1558     // filter  = ( host | domain | ipaddr ["/" mask] ) [":" port]
1559     // filters = filter *( "," LWS filter)
1560     //
1561     // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
1562     mFilterLocalHosts = false;
1563 
1564     mozilla::Tokenizer t(aFilters);
1565     mozilla::Tokenizer::Token token;
1566     bool eof = false;
1567     // while (*filters) {
1568     while (!eof) {
1569         // skip over spaces and ,
1570         t.SkipWhites();
1571         while (t.CheckChar(',')) {
1572             t.SkipWhites();
1573         }
1574 
1575         nsAutoCString portStr;
1576         nsAutoCString hostStr;
1577         nsAutoCString maskStr;
1578         t.Record();
1579 
1580         bool parsingIPv6 = false;
1581         bool parsingPort = false;
1582         bool parsingMask = false;
1583         while (t.Next(token)) {
1584             if (token.Equals(mozilla::Tokenizer::Token::EndOfFile()))  {
1585                 eof = true;
1586                 break;
1587             }
1588             if (token.Equals(mozilla::Tokenizer::Token::Char(',')) ||
1589                 token.Type() == mozilla::Tokenizer::TOKEN_WS) {
1590                 break;
1591             }
1592 
1593             if (token.Equals(mozilla::Tokenizer::Token::Char('['))) {
1594                 parsingIPv6 = true;
1595                 continue;
1596             }
1597 
1598             if (!parsingIPv6 && token.Equals(mozilla::Tokenizer::Token::Char(':'))) {
1599                 // Port is starting. Claim the previous as host.
1600                 if (parsingMask) {
1601                     t.Claim(maskStr);
1602                 } else {
1603                     t.Claim(hostStr);
1604                 }
1605                 t.Record();
1606                 parsingPort = true;
1607                 continue;
1608             } else if (token.Equals(mozilla::Tokenizer::Token::Char('/'))) {
1609                 t.Claim(hostStr);
1610                 t.Record();
1611                 parsingMask = true;
1612                 continue;
1613             } else if (token.Equals(mozilla::Tokenizer::Token::Char(']'))) {
1614                 parsingIPv6 = false;
1615                 continue;
1616             }
1617         }
1618         if (!parsingPort && !parsingMask) {
1619             t.Claim(hostStr);
1620         } else if (parsingPort) {
1621             t.Claim(portStr);
1622         } else if (parsingMask) {
1623             t.Claim(maskStr);
1624         } else {
1625             NS_WARNING("Could not parse this rule");
1626             continue;
1627         }
1628 
1629         if (hostStr.IsEmpty()) {
1630             continue;
1631         }
1632 
1633         // If the current host filter is "<local>", then all local (i.e.
1634         // no dots in the hostname) hosts should bypass the proxy
1635         if (hostStr.EqualsIgnoreCase("<local>")) {
1636             mFilterLocalHosts = true;
1637             LOG(("loaded filter for local hosts "
1638                  "(plain host names, no dots)\n"));
1639             // Continue to next host filter;
1640             continue;
1641         }
1642 
1643         // For all other host filters, create HostInfo object and add to list
1644         HostInfo *hinfo = new HostInfo();
1645         nsresult rv = NS_OK;
1646 
1647         int32_t port = portStr.ToInteger(&rv);
1648         if (NS_FAILED(rv)) {
1649             port = 0;
1650         }
1651         hinfo->port = port;
1652 
1653         int32_t maskLen = maskStr.ToInteger(&rv);
1654         if (NS_FAILED(rv)) {
1655             maskLen = 128;
1656         }
1657 
1658         // PR_StringToNetAddr can't parse brackets enclosed IPv6
1659         nsAutoCString addrString = hostStr;
1660         if (hostStr.First() == '[' && hostStr.Last() == ']') {
1661             addrString = Substring(hostStr, 1, hostStr.Length() - 2);
1662         }
1663 
1664         PRNetAddr addr;
1665         if (PR_StringToNetAddr(addrString.get(), &addr) == PR_SUCCESS) {
1666             hinfo->is_ipaddr   = true;
1667             hinfo->ip.family   = PR_AF_INET6; // we always store address as IPv6
1668             hinfo->ip.mask_len = maskLen;
1669 
1670             if (hinfo->ip.mask_len == 0) {
1671                 NS_WARNING("invalid mask");
1672                 goto loser;
1673             }
1674 
1675             if (addr.raw.family == PR_AF_INET) {
1676                 // convert to IPv4-mapped address
1677                 PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1678                 // adjust mask_len accordingly
1679                 if (hinfo->ip.mask_len <= 32)
1680                     hinfo->ip.mask_len += 96;
1681             }
1682             else if (addr.raw.family == PR_AF_INET6) {
1683                 // copy the address
1684                 memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1685             }
1686             else {
1687                 NS_WARNING("unknown address family");
1688                 goto loser;
1689             }
1690 
1691             // apply mask to IPv6 address
1692             proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1693         }
1694         else {
1695             nsAutoCString host;
1696             if (hostStr.First() == '*') {
1697                 host = Substring(hostStr, 1);
1698             } else {
1699                 host = hostStr;
1700             }
1701 
1702             if (host.IsEmpty()) {
1703                 hinfo->name.host = nullptr;
1704                 goto loser;
1705             }
1706 
1707             hinfo->name.host_len = host.Length();
1708 
1709             hinfo->is_ipaddr = false;
1710             hinfo->name.host = ToNewCString(host);
1711 
1712             if (!hinfo->name.host)
1713                 goto loser;
1714         }
1715 
1716 //#define DEBUG_DUMP_FILTERS
1717 #ifdef DEBUG_DUMP_FILTERS
1718         printf("loaded filter[%zu]:\n", mHostFiltersArray.Length());
1719         printf("  is_ipaddr = %u\n", hinfo->is_ipaddr);
1720         printf("  port = %u\n", hinfo->port);
1721         printf("  host = %s\n", hostStr.get());
1722         if (hinfo->is_ipaddr) {
1723             printf("  ip.family = %x\n", hinfo->ip.family);
1724             printf("  ip.mask_len = %u\n", hinfo->ip.mask_len);
1725 
1726             PRNetAddr netAddr;
1727             PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1728             memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1729 
1730             char buf[256];
1731             PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1732 
1733             printf("  ip.addr = %s\n", buf);
1734         }
1735         else {
1736             printf("  name.host = %s\n", hinfo->name.host);
1737         }
1738 #endif
1739 
1740         mHostFiltersArray.AppendElement(hinfo);
1741         hinfo = nullptr;
1742 loser:
1743         delete hinfo;
1744     }
1745 }
1746 
1747 nsresult
GetProtocolInfo(nsIURI * uri,nsProtocolInfo * info)1748 nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
1749 {
1750     NS_PRECONDITION(uri, "URI is null");
1751     NS_PRECONDITION(info, "info is null");
1752 
1753     nsresult rv;
1754 
1755     rv = uri->GetScheme(info->scheme);
1756     if (NS_FAILED(rv))
1757         return rv;
1758 
1759     nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
1760     if (NS_FAILED(rv))
1761         return rv;
1762 
1763     nsCOMPtr<nsIProtocolHandler> handler;
1764     rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
1765     if (NS_FAILED(rv))
1766         return rv;
1767 
1768     rv = handler->DoGetProtocolFlags(uri, &info->flags);
1769     if (NS_FAILED(rv))
1770         return rv;
1771 
1772     rv = handler->GetDefaultPort(&info->defaultPort);
1773     return rv;
1774 }
1775 
1776 nsresult
NewProxyInfo_Internal(const char * aType,const nsACString & aHost,int32_t aPort,const nsACString & aUsername,const nsACString & aPassword,uint32_t aFlags,uint32_t aFailoverTimeout,nsIProxyInfo * aFailoverProxy,uint32_t aResolveFlags,nsIProxyInfo ** aResult)1777 nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
1778                                               const nsACString &aHost,
1779                                               int32_t aPort,
1780                                               const nsACString &aUsername,
1781                                               const nsACString &aPassword,
1782                                               uint32_t aFlags,
1783                                               uint32_t aFailoverTimeout,
1784                                               nsIProxyInfo *aFailoverProxy,
1785                                               uint32_t aResolveFlags,
1786                                               nsIProxyInfo **aResult)
1787 {
1788     if (aPort <= 0)
1789         aPort = -1;
1790 
1791     nsCOMPtr<nsProxyInfo> failover;
1792     if (aFailoverProxy) {
1793         failover = do_QueryInterface(aFailoverProxy);
1794         NS_ENSURE_ARG(failover);
1795     }
1796 
1797     nsProxyInfo *proxyInfo = new nsProxyInfo();
1798     if (!proxyInfo)
1799         return NS_ERROR_OUT_OF_MEMORY;
1800 
1801     proxyInfo->mType = aType;
1802     proxyInfo->mHost = aHost;
1803     proxyInfo->mPort = aPort;
1804     proxyInfo->mUsername = aUsername;
1805     proxyInfo->mPassword = aPassword;
1806     proxyInfo->mFlags = aFlags;
1807     proxyInfo->mResolveFlags = aResolveFlags;
1808     proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
1809         ? mFailedProxyTimeout : aFailoverTimeout;
1810     failover.swap(proxyInfo->mNext);
1811 
1812     NS_ADDREF(*aResult = proxyInfo);
1813     return NS_OK;
1814 }
1815 
1816 nsresult
Resolve_Internal(nsIChannel * channel,const nsProtocolInfo & info,uint32_t flags,bool * usePACThread,nsIProxyInfo ** result)1817 nsProtocolProxyService::Resolve_Internal(nsIChannel *channel,
1818                                          const nsProtocolInfo &info,
1819                                          uint32_t flags,
1820                                          bool *usePACThread,
1821                                          nsIProxyInfo **result)
1822 {
1823     NS_ENSURE_ARG_POINTER(channel);
1824     nsresult rv = SetupPACThread();
1825     if (NS_FAILED(rv))
1826         return rv;
1827 
1828     *usePACThread = false;
1829     *result = nullptr;
1830 
1831     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1832         return NS_OK;  // Can't proxy this (filters may not override)
1833 
1834     nsCOMPtr<nsIURI> uri;
1835     rv = GetProxyURI(channel, getter_AddRefs(uri));
1836     if (NS_FAILED(rv)) return rv;
1837 
1838     // See bug #586908.
1839     // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
1840     // here means that we will not use a proxy for this connection.
1841     if (mPACMan && mPACMan->IsPACURI(uri))
1842         return NS_OK;
1843 
1844     bool mainThreadOnly;
1845     if (mSystemProxySettings &&
1846         mProxyConfig == PROXYCONFIG_SYSTEM &&
1847         NS_SUCCEEDED(mSystemProxySettings->GetMainThreadOnly(&mainThreadOnly)) &&
1848         !mainThreadOnly) {
1849         *usePACThread = true;
1850         return NS_OK;
1851     }
1852 
1853     if (mSystemProxySettings && mProxyConfig == PROXYCONFIG_SYSTEM) {
1854         // If the system proxy setting implementation is not threadsafe (e.g
1855         // linux gconf), we'll do it inline here. Such implementations promise
1856         // not to block
1857 
1858         nsAutoCString PACURI;
1859         nsAutoCString pacString;
1860 
1861         if (NS_SUCCEEDED(mSystemProxySettings->GetPACURI(PACURI)) &&
1862             !PACURI.IsEmpty()) {
1863             // There is a PAC URI configured. If it is unchanged, then
1864             // just execute the PAC thread. If it is changed then load
1865             // the new value
1866 
1867             if (mPACMan && mPACMan->IsPACURI(PACURI)) {
1868                 // unchanged
1869                 *usePACThread = true;
1870                 return NS_OK;
1871             }
1872 
1873             ConfigureFromPAC(PACURI, false);
1874             return NS_OK;
1875         }
1876 
1877         nsAutoCString spec;
1878         nsAutoCString host;
1879         nsAutoCString scheme;
1880         int32_t port = -1;
1881 
1882         uri->GetAsciiSpec(spec);
1883         uri->GetAsciiHost(host);
1884         uri->GetScheme(scheme);
1885         uri->GetPort(&port);
1886 
1887         // now try the system proxy settings for this particular url
1888         if (NS_SUCCEEDED(mSystemProxySettings->
1889                          GetProxyForURI(spec, scheme, host, port,
1890                                         pacString))) {
1891             ProcessPACString(pacString, 0, result);
1892             return NS_OK;
1893         }
1894     }
1895 
1896     // if proxies are enabled and this host:port combo is supposed to use a
1897     // proxy, check for a proxy.
1898     if (mProxyConfig == PROXYCONFIG_DIRECT ||
1899         (mProxyConfig == PROXYCONFIG_MANUAL &&
1900          !CanUseProxy(uri, info.defaultPort)))
1901         return NS_OK;
1902 
1903     // Proxy auto config magic...
1904     if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD) {
1905         // Do not query PAC now.
1906         *usePACThread = true;
1907         return NS_OK;
1908     }
1909 
1910     // If we aren't in manual proxy configuration mode then we don't
1911     // want to honor any manual specific prefs that might be still set
1912     if (mProxyConfig != PROXYCONFIG_MANUAL)
1913         return NS_OK;
1914 
1915     // proxy info values for manual configuration mode
1916     const char *type = nullptr;
1917     const nsACString *host = nullptr;
1918     int32_t port = -1;
1919 
1920     uint32_t proxyFlags = 0;
1921 
1922     if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
1923         !mSOCKSProxyTarget.IsEmpty() &&
1924         (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
1925       host = &mSOCKSProxyTarget;
1926       if (mSOCKSProxyVersion == 4)
1927           type = kProxyType_SOCKS4;
1928       else
1929           type = kProxyType_SOCKS;
1930       port = mSOCKSProxyPort;
1931       if (mSOCKSProxyRemoteDNS)
1932           proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1933     }
1934     else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
1935              !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
1936         host = &mHTTPSProxyHost;
1937         type = kProxyType_HTTP;
1938         port = mHTTPSProxyPort;
1939     }
1940     else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
1941              ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
1942               info.scheme.EqualsLiteral("http"))) {
1943         host = &mHTTPProxyHost;
1944         type = kProxyType_HTTP;
1945         port = mHTTPProxyPort;
1946     }
1947     else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
1948              !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1949              info.scheme.EqualsLiteral("https")) {
1950         host = &mHTTPSProxyHost;
1951         type = kProxyType_HTTP;
1952         port = mHTTPSProxyPort;
1953     }
1954     else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
1955              !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1956              info.scheme.EqualsLiteral("ftp")) {
1957         host = &mFTPProxyHost;
1958         type = kProxyType_HTTP;
1959         port = mFTPProxyPort;
1960     }
1961     else if (!mSOCKSProxyTarget.IsEmpty() &&
1962         (IsHostLocalTarget(mSOCKSProxyTarget) || mSOCKSProxyPort > 0)) {
1963         host = &mSOCKSProxyTarget;
1964         if (mSOCKSProxyVersion == 4)
1965             type = kProxyType_SOCKS4;
1966         else
1967             type = kProxyType_SOCKS;
1968         port = mSOCKSProxyPort;
1969         if (mSOCKSProxyRemoteDNS)
1970             proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1971     }
1972 
1973     if (type) {
1974         rv = NewProxyInfo_Internal(type, *host, port,
1975                                    EmptyCString(), EmptyCString(),
1976                                    proxyFlags, UINT32_MAX, nullptr, flags,
1977                                    result);
1978         if (NS_FAILED(rv))
1979             return rv;
1980     }
1981 
1982     return NS_OK;
1983 }
1984 
1985 void
MaybeDisableDNSPrefetch(nsIProxyInfo * aProxy)1986 nsProtocolProxyService::MaybeDisableDNSPrefetch(nsIProxyInfo *aProxy)
1987 {
1988     // Disable Prefetch in the DNS service if a proxy is in use.
1989     if (!aProxy)
1990         return;
1991 
1992     nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
1993     if (!pi ||
1994         !pi->mType ||
1995         pi->mType == kProxyType_DIRECT)
1996         return;
1997 
1998     nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
1999     if (!dns)
2000         return;
2001     nsCOMPtr<nsPIDNSService> pdns = do_QueryInterface(dns);
2002     if (!pdns)
2003         return;
2004 
2005     // We lose the prefetch optimization for the life of the dns service.
2006     pdns->SetPrefetchEnabled(false);
2007 }
2008 
2009 void
ApplyFilters(nsIChannel * channel,const nsProtocolInfo & info,nsIProxyInfo ** list)2010 nsProtocolProxyService::ApplyFilters(nsIChannel *channel,
2011                                      const nsProtocolInfo &info,
2012                                      nsIProxyInfo **list)
2013 {
2014     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
2015         return;
2016 
2017     // We prune the proxy list prior to invoking each filter.  This may be
2018     // somewhat inefficient, but it seems like a good idea since we want each
2019     // filter to "see" a valid proxy list.
2020 
2021     nsCOMPtr<nsIProxyInfo> result;
2022 
2023     for (FilterLink *iter = mFilters; iter; iter = iter->next) {
2024         PruneProxyInfo(info, list);
2025         nsresult rv = NS_OK;
2026         if (iter->filter) {
2027           nsCOMPtr<nsIURI> uri;
2028           rv = GetProxyURI(channel, getter_AddRefs(uri));
2029           if (uri) {
2030             rv = iter->filter->ApplyFilter(this, uri, *list,
2031                                            getter_AddRefs(result));
2032           }
2033         } else if (iter->channelFilter) {
2034           rv = iter->channelFilter->ApplyFilter(this, channel, *list,
2035                                                 getter_AddRefs(result));
2036         }
2037         if (NS_FAILED(rv))
2038             continue;
2039         result.swap(*list);
2040     }
2041 
2042     PruneProxyInfo(info, list);
2043 }
2044 
2045 void
PruneProxyInfo(const nsProtocolInfo & info,nsIProxyInfo ** list)2046 nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
2047                                        nsIProxyInfo **list)
2048 {
2049     if (!*list)
2050         return;
2051     nsProxyInfo *head = nullptr;
2052     CallQueryInterface(*list, &head);
2053     if (!head) {
2054         NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
2055         return;
2056     }
2057     NS_RELEASE(*list);
2058 
2059     // Pruning of disabled proxies works like this:
2060     //   - If all proxies are disabled, return the full list
2061     //   - Otherwise, remove the disabled proxies.
2062     //
2063     // Pruning of disallowed proxies works like this:
2064     //   - If the protocol handler disallows the proxy, then we disallow it.
2065 
2066     // Start by removing all disallowed proxies if required:
2067     if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
2068         nsProxyInfo *last = nullptr, *iter = head;
2069         while (iter) {
2070             if ((iter->Type() == kProxyType_HTTP) ||
2071                 (iter->Type() == kProxyType_HTTPS)) {
2072                 // reject!
2073                 if (last)
2074                     last->mNext = iter->mNext;
2075                 else
2076                     head = iter->mNext;
2077                 nsProxyInfo *next = iter->mNext;
2078                 iter->mNext = nullptr;
2079                 iter->Release();
2080                 iter = next;
2081             } else {
2082                 last = iter;
2083                 iter = iter->mNext;
2084             }
2085         }
2086         if (!head)
2087             return;
2088     }
2089 
2090     // Now, scan to see if all remaining proxies are disabled.  If so, then
2091     // we'll just bail and return them all.  Otherwise, we'll go and prune the
2092     // disabled ones.
2093 
2094     bool allDisabled = true;
2095 
2096     nsProxyInfo *iter;
2097     for (iter = head; iter; iter = iter->mNext) {
2098         if (!IsProxyDisabled(iter)) {
2099             allDisabled = false;
2100             break;
2101         }
2102     }
2103 
2104     if (allDisabled)
2105         LOG(("All proxies are disabled, so trying all again"));
2106     else {
2107         // remove any disabled proxies.
2108         nsProxyInfo *last = nullptr;
2109         for (iter = head; iter; ) {
2110             if (IsProxyDisabled(iter)) {
2111                 // reject!
2112                 nsProxyInfo *reject = iter;
2113 
2114                 iter = iter->mNext;
2115                 if (last)
2116                     last->mNext = iter;
2117                 else
2118                     head = iter;
2119 
2120                 reject->mNext = nullptr;
2121                 NS_RELEASE(reject);
2122                 continue;
2123             }
2124 
2125             // since we are about to use this proxy, make sure it is not on
2126             // the disabled proxy list.  we'll add it back to that list if
2127             // we have to (in GetFailoverForProxy).
2128             //
2129             // XXX(darin): It might be better to do this as a final pass.
2130             //
2131             EnableProxy(iter);
2132 
2133             last = iter;
2134             iter = iter->mNext;
2135         }
2136     }
2137 
2138     // if only DIRECT was specified then return no proxy info, and we're done.
2139     if (head && !head->mNext && head->mType == kProxyType_DIRECT)
2140         NS_RELEASE(head);
2141 
2142     *list = head;  // Transfer ownership
2143 }
2144 
2145 } // namespace net
2146 } // namespace mozilla
2147