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
5 : *
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/
10 : *
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
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
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.
22 : *
23 : * Contributor(s):
24 : * Malcolm Smith <malsmith@cs.rmit.edu.au>
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "nsProtocolProxyService.h"
43 : #include "nsProxyInfo.h"
44 : #include "nsIClassInfoImpl.h"
45 : #include "nsIServiceManager.h"
46 : #include "nsXPIDLString.h"
47 : #include "nsIProxyAutoConfig.h"
48 : #include "nsIIOService.h"
49 : #include "nsIObserverService.h"
50 : #include "nsIProtocolHandler.h"
51 : #include "nsIProtocolProxyCallback.h"
52 : #include "nsICancelable.h"
53 : #include "nsIDNSService.h"
54 : #include "nsIPrefService.h"
55 : #include "nsIPrefBranch.h"
56 : #include "nsReadableUtils.h"
57 : #include "nsThreadUtils.h"
58 : #include "nsString.h"
59 : #include "nsNetUtil.h"
60 : #include "nsNetCID.h"
61 : #include "nsCRT.h"
62 : #include "prnetdb.h"
63 : #include "nsPACMan.h"
64 :
65 : //----------------------------------------------------------------------------
66 :
67 : using namespace mozilla;
68 :
69 : #include "prlog.h"
70 : #if defined(PR_LOGGING)
71 1464 : static PRLogModuleInfo *sLog = PR_NewLogModule("proxy");
72 : #endif
73 : #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
74 :
75 : //----------------------------------------------------------------------------
76 :
77 : #define PROXY_PREF_BRANCH "network.proxy"
78 : #define PROXY_PREF(x) PROXY_PREF_BRANCH "." x
79 :
80 : #define WPAD_URL "http://wpad/wpad.dat"
81 :
82 : //----------------------------------------------------------------------------
83 :
84 : // This structure is intended to be allocated on the stack
85 7152 : struct nsProtocolInfo {
86 : nsCAutoString scheme;
87 : PRUint32 flags;
88 : PRInt32 defaultPort;
89 : };
90 :
91 : //----------------------------------------------------------------------------
92 :
93 : class nsAsyncResolveRequest : public nsIRunnable
94 : , public nsPACManCallback
95 : , public nsICancelable
96 12 : {
97 : public:
98 : NS_DECL_ISUPPORTS
99 :
100 12 : nsAsyncResolveRequest(nsProtocolProxyService *pps, nsIURI *uri,
101 : PRUint32 aResolveFlags,
102 : nsIProtocolProxyCallback *callback)
103 : : mStatus(NS_OK)
104 : , mDispatched(false)
105 : , mResolveFlags(0)
106 : , mPPS(pps)
107 : , mURI(uri)
108 12 : , mCallback(callback)
109 : {
110 12 : NS_ASSERTION(mCallback, "null callback");
111 12 : }
112 :
113 2 : void SetResult(nsresult status, nsIProxyInfo *pi)
114 : {
115 2 : mStatus = status;
116 2 : mProxyInfo = pi;
117 2 : }
118 :
119 2 : NS_IMETHOD Run()
120 : {
121 2 : if (mCallback)
122 2 : DoCallback();
123 2 : return NS_OK;
124 : }
125 :
126 1 : NS_IMETHOD Cancel(nsresult reason)
127 : {
128 1 : NS_ENSURE_ARG(NS_FAILED(reason));
129 :
130 : // If we've already called DoCallback then, nothing more to do.
131 1 : if (!mCallback)
132 0 : return NS_OK;
133 :
134 1 : SetResult(reason, nsnull);
135 1 : return DispatchCallback();
136 : }
137 :
138 2 : nsresult DispatchCallback()
139 : {
140 2 : if (mDispatched) // Only need to dispatch once
141 0 : return NS_OK;
142 :
143 2 : nsresult rv = NS_DispatchToCurrentThread(this);
144 2 : if (NS_FAILED(rv))
145 0 : NS_WARNING("unable to dispatch callback event");
146 : else {
147 2 : mDispatched = true;
148 2 : return NS_OK;
149 : }
150 :
151 0 : mCallback = nsnull; // break possible reference cycle
152 0 : return rv;
153 : }
154 :
155 : private:
156 :
157 : // Called asynchronously, so we do not need to post another PLEvent
158 : // before calling DoCallback.
159 11 : void OnQueryComplete(nsresult status, const nsCString &pacString)
160 : {
161 : // If we've already called DoCallback then, nothing more to do.
162 11 : if (!mCallback)
163 1 : return;
164 :
165 : // Provided we haven't been canceled...
166 10 : if (mStatus == NS_OK) {
167 10 : mStatus = status;
168 10 : mPACString = pacString;
169 : }
170 :
171 : // In the cancelation case, we may still have another PLEvent in
172 : // the queue that wants to call DoCallback. No need to wait for
173 : // it, just run the callback now.
174 10 : DoCallback();
175 : }
176 :
177 12 : void DoCallback()
178 : {
179 : // Generate proxy info from the PAC string if appropriate
180 12 : if (NS_SUCCEEDED(mStatus) && !mProxyInfo && !mPACString.IsEmpty())
181 : mPPS->ProcessPACString(mPACString, mResolveFlags,
182 10 : getter_AddRefs(mProxyInfo));
183 :
184 : // Now apply proxy filters
185 12 : if (NS_SUCCEEDED(mStatus)) {
186 22 : nsProtocolInfo info;
187 11 : mStatus = mPPS->GetProtocolInfo(mURI, &info);
188 11 : if (NS_SUCCEEDED(mStatus))
189 11 : mPPS->ApplyFilters(mURI, info, mProxyInfo);
190 : else
191 0 : mProxyInfo = nsnull;
192 : }
193 :
194 12 : mCallback->OnProxyAvailable(this, mURI, mProxyInfo, mStatus);
195 12 : mCallback = nsnull; // in case the callback holds an owning ref to us
196 12 : }
197 :
198 : private:
199 :
200 : nsresult mStatus;
201 : nsCString mPACString;
202 : bool mDispatched;
203 : PRUint32 mResolveFlags;
204 :
205 : nsRefPtr<nsProtocolProxyService> mPPS;
206 : nsCOMPtr<nsIURI> mURI;
207 : nsCOMPtr<nsIProtocolProxyCallback> mCallback;
208 : nsCOMPtr<nsIProxyInfo> mProxyInfo;
209 : };
210 :
211 149 : NS_IMPL_ISUPPORTS2(nsAsyncResolveRequest, nsICancelable, nsIRunnable)
212 :
213 : //----------------------------------------------------------------------------
214 :
215 : #define IS_ASCII_SPACE(_c) ((_c) == ' ' || (_c) == '\t')
216 :
217 : //
218 : // apply mask to address (zeros out excluded bits).
219 : //
220 : // NOTE: we do the byte swapping here to minimize overall swapping.
221 : //
222 : static void
223 277 : proxy_MaskIPv6Addr(PRIPv6Addr &addr, PRUint16 mask_len)
224 : {
225 277 : if (mask_len == 128)
226 277 : return;
227 :
228 0 : if (mask_len > 96) {
229 : addr.pr_s6_addr32[3] = PR_htonl(
230 0 : PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len)));
231 : }
232 0 : else if (mask_len > 64) {
233 0 : addr.pr_s6_addr32[3] = 0;
234 : addr.pr_s6_addr32[2] = PR_htonl(
235 0 : PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len)));
236 : }
237 0 : else if (mask_len > 32) {
238 0 : addr.pr_s6_addr32[3] = 0;
239 0 : addr.pr_s6_addr32[2] = 0;
240 : addr.pr_s6_addr32[1] = PR_htonl(
241 0 : PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len)));
242 : }
243 : else {
244 0 : addr.pr_s6_addr32[3] = 0;
245 0 : addr.pr_s6_addr32[2] = 0;
246 0 : addr.pr_s6_addr32[1] = 0;
247 : addr.pr_s6_addr32[0] = PR_htonl(
248 0 : PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len)));
249 : }
250 : }
251 :
252 : static void
253 1116 : proxy_GetStringPref(nsIPrefBranch *aPrefBranch,
254 : const char *aPref,
255 : nsCString &aResult)
256 : {
257 2232 : nsXPIDLCString temp;
258 1116 : nsresult rv = aPrefBranch->GetCharPref(aPref, getter_Copies(temp));
259 1116 : if (NS_FAILED(rv))
260 0 : aResult.Truncate();
261 : else {
262 1116 : aResult.Assign(temp);
263 : // all of our string prefs are hostnames, so we should remove any
264 : // whitespace characters that the user might have unknowingly entered.
265 1116 : aResult.StripWhitespace();
266 : }
267 1116 : }
268 :
269 : static void
270 1672 : proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
271 : const char *aPref,
272 : PRInt32 &aResult)
273 : {
274 : PRInt32 temp;
275 1672 : nsresult rv = aPrefBranch->GetIntPref(aPref, &temp);
276 1672 : if (NS_FAILED(rv))
277 0 : aResult = -1;
278 : else
279 1672 : aResult = temp;
280 1672 : }
281 :
282 : static void
283 278 : proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
284 : const char *aPref,
285 : bool &aResult)
286 : {
287 : bool temp;
288 278 : nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
289 278 : if (NS_FAILED(rv))
290 0 : aResult = false;
291 : else
292 278 : aResult = temp;
293 278 : }
294 :
295 : //----------------------------------------------------------------------------
296 :
297 : static const PRInt32 PROXYCONFIG_DIRECT4X = 3;
298 : static const PRInt32 PROXYCONFIG_COUNT = 6;
299 :
300 3155 : NS_IMPL_ADDREF(nsProtocolProxyService)
301 3433 : NS_IMPL_RELEASE(nsProtocolProxyService)
302 : NS_IMPL_CLASSINFO(nsProtocolProxyService, NULL, nsIClassInfo::SINGLETON,
303 : NS_PROTOCOLPROXYSERVICE_CID)
304 895 : NS_IMPL_QUERY_INTERFACE3_CI(nsProtocolProxyService,
305 : nsIProtocolProxyService,
306 : nsIProtocolProxyService2,
307 1 : nsIObserver)
308 1 : NS_IMPL_CI_INTERFACE_GETTER2(nsProtocolProxyService,
309 : nsIProtocolProxyService,
310 1 : nsIProtocolProxyService2)
311 :
312 278 : nsProtocolProxyService::nsProtocolProxyService()
313 : : mFilterLocalHosts(false)
314 : , mFilters(nsnull)
315 : , mProxyConfig(PROXYCONFIG_DIRECT)
316 : , mHTTPProxyPort(-1)
317 : , mFTPProxyPort(-1)
318 : , mHTTPSProxyPort(-1)
319 : , mSOCKSProxyPort(-1)
320 : , mSOCKSProxyVersion(4)
321 : , mSOCKSProxyRemoteDNS(false)
322 : , mPACMan(nsnull)
323 278 : , mSessionStart(PR_Now())
324 556 : , mFailedProxyTimeout(30 * 60) // 30 minute default
325 : {
326 278 : }
327 :
328 556 : nsProtocolProxyService::~nsProtocolProxyService()
329 : {
330 : // These should have been cleaned up in our Observe method.
331 278 : NS_ASSERTION(mHostFiltersArray.Length() == 0 && mFilters == nsnull &&
332 : mPACMan == nsnull, "what happened to xpcom-shutdown?");
333 278 : }
334 :
335 : // nsProtocolProxyService methods
336 : nsresult
337 278 : nsProtocolProxyService::Init()
338 : {
339 278 : if (!mFailedProxies.Init())
340 0 : return NS_ERROR_OUT_OF_MEMORY;
341 :
342 : // failure to access prefs is non-fatal
343 : nsCOMPtr<nsIPrefBranch> prefBranch =
344 556 : do_GetService(NS_PREFSERVICE_CONTRACTID);
345 278 : if (prefBranch) {
346 : // monitor proxy prefs
347 278 : prefBranch->AddObserver(PROXY_PREF_BRANCH, this, false);
348 :
349 : // read all prefs
350 278 : PrefsChanged(prefBranch, nsnull);
351 : }
352 :
353 : // register for shutdown notification so we can clean ourselves up properly.
354 556 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
355 278 : if (obs)
356 278 : obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
357 :
358 278 : return NS_OK;
359 : }
360 :
361 : NS_IMETHODIMP
362 304 : nsProtocolProxyService::Observe(nsISupports *aSubject,
363 : const char *aTopic,
364 : const PRUnichar *aData)
365 : {
366 304 : if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
367 : // cleanup
368 278 : if (mHostFiltersArray.Length() > 0) {
369 276 : mHostFiltersArray.Clear();
370 : }
371 278 : if (mFilters) {
372 0 : delete mFilters;
373 0 : mFilters = nsnull;
374 : }
375 278 : if (mPACMan) {
376 10 : mPACMan->Shutdown();
377 10 : mPACMan = nsnull;
378 : }
379 : }
380 : else {
381 26 : NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
382 : "what is this random observer event?");
383 52 : nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
384 26 : if (prefs)
385 26 : PrefsChanged(prefs, NS_LossyConvertUTF16toASCII(aData).get());
386 : }
387 304 : return NS_OK;
388 : }
389 :
390 : void
391 304 : nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
392 : const char *pref)
393 : {
394 304 : nsresult rv = NS_OK;
395 304 : bool reloadPAC = false;
396 608 : nsXPIDLCString tempString;
397 :
398 304 : if (!pref || !strcmp(pref, PROXY_PREF("type"))) {
399 287 : PRInt32 type = -1;
400 287 : rv = prefBranch->GetIntPref(PROXY_PREF("type"), &type);
401 287 : if (NS_SUCCEEDED(rv)) {
402 : // bug 115720 - for ns4.x backwards compatibility
403 287 : if (type == PROXYCONFIG_DIRECT4X) {
404 0 : type = PROXYCONFIG_DIRECT;
405 : // Reset the type so that the dialog looks correct, and we
406 : // don't have to handle this case everywhere else
407 : // I'm paranoid about a loop of some sort - only do this
408 : // if we're enumerating all prefs, and ignore any error
409 0 : if (!pref)
410 0 : prefBranch->SetIntPref(PROXY_PREF("type"), type);
411 287 : } else if (type >= PROXYCONFIG_COUNT) {
412 0 : LOG(("unknown proxy type: %lu; assuming direct\n", type));
413 0 : type = PROXYCONFIG_DIRECT;
414 : }
415 287 : mProxyConfig = type;
416 287 : reloadPAC = true;
417 : }
418 :
419 287 : if (mProxyConfig == PROXYCONFIG_SYSTEM) {
420 273 : mSystemProxySettings = do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
421 273 : if (!mSystemProxySettings)
422 0 : mProxyConfig = PROXYCONFIG_DIRECT;
423 : } else {
424 14 : mSystemProxySettings = nsnull;
425 : }
426 : }
427 :
428 304 : if (!pref || !strcmp(pref, PROXY_PREF("http")))
429 281 : proxy_GetStringPref(prefBranch, PROXY_PREF("http"), mHTTPProxyHost);
430 :
431 304 : if (!pref || !strcmp(pref, PROXY_PREF("http_port")))
432 281 : proxy_GetIntPref(prefBranch, PROXY_PREF("http_port"), mHTTPProxyPort);
433 :
434 304 : if (!pref || !strcmp(pref, PROXY_PREF("ssl")))
435 278 : proxy_GetStringPref(prefBranch, PROXY_PREF("ssl"), mHTTPSProxyHost);
436 :
437 304 : if (!pref || !strcmp(pref, PROXY_PREF("ssl_port")))
438 278 : proxy_GetIntPref(prefBranch, PROXY_PREF("ssl_port"), mHTTPSProxyPort);
439 :
440 304 : if (!pref || !strcmp(pref, PROXY_PREF("ftp")))
441 278 : proxy_GetStringPref(prefBranch, PROXY_PREF("ftp"), mFTPProxyHost);
442 :
443 304 : if (!pref || !strcmp(pref, PROXY_PREF("ftp_port")))
444 278 : proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
445 :
446 304 : if (!pref || !strcmp(pref, PROXY_PREF("socks")))
447 279 : proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
448 :
449 304 : if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
450 279 : proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
451 :
452 304 : if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
453 : PRInt32 version;
454 278 : proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
455 : // make sure this preference value remains sane
456 278 : if (version == 5)
457 278 : mSOCKSProxyVersion = 5;
458 : else
459 0 : mSOCKSProxyVersion = 4;
460 : }
461 :
462 304 : if (!pref || !strcmp(pref, PROXY_PREF("socks_remote_dns")))
463 : proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
464 278 : mSOCKSProxyRemoteDNS);
465 :
466 304 : if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
467 : proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
468 278 : mFailedProxyTimeout);
469 :
470 304 : if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
471 : rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
472 281 : getter_Copies(tempString));
473 281 : if (NS_SUCCEEDED(rv))
474 281 : LoadHostFilters(tempString.get());
475 : }
476 :
477 : // We're done if not using something that could give us a PAC URL
478 : // (PAC, WPAD or System)
479 304 : if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
480 : mProxyConfig != PROXYCONFIG_SYSTEM)
481 : return;
482 :
483 : // OK, we need to reload the PAC file if:
484 : // 1) network.proxy.type changed, or
485 : // 2) network.proxy.autoconfig_url changed and PAC is configured
486 :
487 286 : if (!pref || !strcmp(pref, PROXY_PREF("autoconfig_url")))
488 283 : reloadPAC = true;
489 :
490 286 : if (reloadPAC) {
491 286 : tempString.Truncate();
492 286 : if (mProxyConfig == PROXYCONFIG_PAC) {
493 : prefBranch->GetCharPref(PROXY_PREF("autoconfig_url"),
494 13 : getter_Copies(tempString));
495 273 : } else if (mProxyConfig == PROXYCONFIG_WPAD) {
496 : // We diverge from the WPAD spec here in that we don't walk the
497 : // hosts's FQDN, stripping components until we hit a TLD. Doing so
498 : // is dangerous in the face of an incomplete list of TLDs, and TLDs
499 : // get added over time. We could consider doing only a single
500 : // substitution of the first component, if that proves to help
501 : // compatibility.
502 0 : tempString.AssignLiteral(WPAD_URL);
503 273 : } else if (mSystemProxySettings) {
504 : // Get System Proxy settings if available
505 273 : mSystemProxySettings->GetPACURI(tempString);
506 : }
507 286 : if (!tempString.IsEmpty())
508 11 : ConfigureFromPAC(tempString, false);
509 : }
510 : }
511 :
512 : bool
513 32 : nsProtocolProxyService::CanUseProxy(nsIURI *aURI, PRInt32 defaultPort)
514 : {
515 32 : if (mHostFiltersArray.Length() == 0)
516 7 : return true;
517 :
518 : PRInt32 port;
519 50 : nsCAutoString host;
520 :
521 25 : nsresult rv = aURI->GetAsciiHost(host);
522 25 : if (NS_FAILED(rv) || host.IsEmpty())
523 0 : return false;
524 :
525 25 : rv = aURI->GetPort(&port);
526 25 : if (NS_FAILED(rv))
527 0 : return false;
528 25 : if (port == -1)
529 25 : port = defaultPort;
530 :
531 : PRNetAddr addr;
532 25 : bool is_ipaddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS);
533 :
534 : PRIPv6Addr ipv6;
535 25 : if (is_ipaddr) {
536 : // convert parsed address to IPv6
537 0 : if (addr.raw.family == PR_AF_INET) {
538 : // convert to IPv4-mapped address
539 0 : PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &ipv6);
540 : }
541 0 : else if (addr.raw.family == PR_AF_INET6) {
542 : // copy the address
543 0 : memcpy(&ipv6, &addr.ipv6.ip, sizeof(PRIPv6Addr));
544 : }
545 : else {
546 0 : NS_WARNING("unknown address family");
547 0 : return true; // allow proxying
548 : }
549 : }
550 :
551 : // Don't use proxy for local hosts (plain hostname, no dots)
552 25 : if (!is_ipaddr && mFilterLocalHosts && (kNotFound == host.FindChar('.'))) {
553 0 : LOG(("Not using proxy for this local host [%s]!\n", host.get()));
554 0 : return false; // don't allow proxying
555 : }
556 :
557 25 : PRInt32 index = -1;
558 91 : while (++index < PRInt32(mHostFiltersArray.Length())) {
559 56 : HostInfo *hinfo = mHostFiltersArray[index];
560 :
561 56 : if (is_ipaddr != hinfo->is_ipaddr)
562 3 : continue;
563 53 : if (hinfo->port && hinfo->port != port)
564 0 : continue;
565 :
566 53 : if (is_ipaddr) {
567 : // generate masked version of target IPv6 address
568 : PRIPv6Addr masked;
569 0 : memcpy(&masked, &ipv6, sizeof(PRIPv6Addr));
570 0 : proxy_MaskIPv6Addr(masked, hinfo->ip.mask_len);
571 :
572 : // check for a match
573 0 : if (memcmp(&masked, &hinfo->ip.addr, sizeof(PRIPv6Addr)) == 0)
574 0 : return false; // proxy disallowed
575 : }
576 : else {
577 53 : PRUint32 host_len = host.Length();
578 53 : PRUint32 filter_host_len = hinfo->name.host_len;
579 :
580 53 : if (host_len >= filter_host_len) {
581 : //
582 : // compare last |filter_host_len| bytes of target hostname.
583 : //
584 53 : const char *host_tail = host.get() + host_len - filter_host_len;
585 53 : if (!PL_strncasecmp(host_tail, hinfo->name.host, filter_host_len))
586 15 : return false; // proxy disallowed
587 : }
588 : }
589 : }
590 10 : return true;
591 : }
592 :
593 : static const char kProxyType_HTTP[] = "http";
594 : static const char kProxyType_PROXY[] = "proxy";
595 : static const char kProxyType_SOCKS[] = "socks";
596 : static const char kProxyType_SOCKS4[] = "socks4";
597 : static const char kProxyType_SOCKS5[] = "socks5";
598 : static const char kProxyType_DIRECT[] = "direct";
599 : static const char kProxyType_UNKNOWN[] = "unknown";
600 :
601 : const char *
602 3516 : nsProtocolProxyService::ExtractProxyInfo(const char *start,
603 : PRUint32 aResolveFlags,
604 : nsProxyInfo **result)
605 : {
606 3516 : *result = nsnull;
607 3516 : PRUint32 flags = 0;
608 :
609 : // see BNF in nsIProxyAutoConfig.idl
610 :
611 : // find end of proxy info delimiter
612 3516 : const char *end = start;
613 3516 : while (*end && *end != ';') ++end;
614 :
615 : // find end of proxy type delimiter
616 3516 : const char *sp = start;
617 3516 : while (sp < end && *sp != ' ' && *sp != '\t') ++sp;
618 :
619 3516 : PRUint32 len = sp - start;
620 3516 : const char *type = nsnull;
621 3516 : switch (len) {
622 : case 5:
623 10 : if (PL_strncasecmp(start, kProxyType_PROXY, 5) == 0)
624 10 : type = kProxyType_HTTP;
625 0 : else if (PL_strncasecmp(start, kProxyType_SOCKS, 5) == 0)
626 0 : type = kProxyType_SOCKS4; // assume v4 for 4x compat
627 10 : break;
628 : case 6:
629 3506 : if (PL_strncasecmp(start, kProxyType_DIRECT, 6) == 0)
630 3506 : type = kProxyType_DIRECT;
631 0 : else if (PL_strncasecmp(start, kProxyType_SOCKS4, 6) == 0)
632 0 : type = kProxyType_SOCKS4;
633 0 : else if (PL_strncasecmp(start, kProxyType_SOCKS5, 6) == 0)
634 : // map "SOCKS5" to "socks" to match contract-id of registered
635 : // SOCKS-v5 socket provider.
636 0 : type = kProxyType_SOCKS;
637 3506 : break;
638 : }
639 3516 : if (type) {
640 3516 : const char *host = nsnull, *hostEnd = nsnull;
641 3516 : PRInt32 port = -1;
642 :
643 : // If it's a SOCKS5 proxy, do name resolution on the server side.
644 : // We could use this with SOCKS4a servers too, but they might not
645 : // support it.
646 3516 : if (type == kProxyType_SOCKS || mSOCKSProxyRemoteDNS)
647 0 : flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
648 :
649 : // extract host:port
650 3516 : start = sp;
651 7042 : while ((*start == ' ' || *start == '\t') && start < end)
652 10 : start++;
653 3516 : if (start < end) {
654 10 : host = start;
655 10 : hostEnd = strchr(host, ':');
656 10 : if (!hostEnd || hostEnd > end) {
657 0 : hostEnd = end;
658 : // no port, so assume default
659 0 : if (type == kProxyType_HTTP)
660 0 : port = 80;
661 : else
662 0 : port = 1080;
663 : }
664 : else
665 10 : port = atoi(hostEnd + 1);
666 : }
667 3516 : nsProxyInfo *pi = new nsProxyInfo;
668 3516 : if (pi) {
669 3516 : pi->mType = type;
670 3516 : pi->mFlags = flags;
671 3516 : pi->mResolveFlags = aResolveFlags;
672 3516 : pi->mTimeout = mFailedProxyTimeout;
673 : // YES, it is ok to specify a null proxy host.
674 3516 : if (host) {
675 10 : pi->mHost.Assign(host, hostEnd - host);
676 10 : pi->mPort = port;
677 : }
678 3516 : NS_ADDREF(*result = pi);
679 : }
680 : }
681 :
682 7042 : while (*end == ';' || *end == ' ' || *end == '\t')
683 10 : ++end;
684 3516 : return end;
685 : }
686 :
687 : void
688 10650 : nsProtocolProxyService::GetProxyKey(nsProxyInfo *pi, nsCString &key)
689 : {
690 10650 : key.AssignASCII(pi->mType);
691 10650 : if (!pi->mHost.IsEmpty()) {
692 95 : key.Append(' ');
693 95 : key.Append(pi->mHost);
694 95 : key.Append(':');
695 95 : key.AppendInt(pi->mPort);
696 : }
697 10650 : }
698 :
699 : PRUint32
700 2 : nsProtocolProxyService::SecondsSinceSessionStart()
701 : {
702 2 : PRTime now = PR_Now();
703 :
704 : // get time elapsed since session start
705 : PRInt64 diff;
706 2 : LL_SUB(diff, now, mSessionStart);
707 :
708 : // convert microseconds to seconds
709 : PRTime ups;
710 2 : LL_I2L(ups, PR_USEC_PER_SEC);
711 2 : LL_DIV(diff, diff, ups);
712 :
713 : // convert to 32 bit value
714 : PRUint32 dsec;
715 2 : LL_L2UI(dsec, diff);
716 :
717 2 : return dsec;
718 : }
719 :
720 : void
721 3552 : nsProtocolProxyService::EnableProxy(nsProxyInfo *pi)
722 : {
723 7104 : nsCAutoString key;
724 3552 : GetProxyKey(pi, key);
725 3552 : mFailedProxies.Remove(key);
726 3552 : }
727 :
728 : void
729 2 : nsProtocolProxyService::DisableProxy(nsProxyInfo *pi)
730 : {
731 4 : nsCAutoString key;
732 2 : GetProxyKey(pi, key);
733 :
734 2 : PRUint32 dsec = SecondsSinceSessionStart();
735 :
736 : // Add timeout to interval (this is the time when the proxy can
737 : // be tried again).
738 2 : dsec += pi->mTimeout;
739 :
740 : // NOTE: The classic codebase would increase the timeout value
741 : // incrementally each time a subsequent failure occurred.
742 : // We could do the same, but it would require that we not
743 : // remove proxy entries in IsProxyDisabled or otherwise
744 : // change the way we are recording disabled proxies.
745 : // Simpler is probably better for now, and at least the
746 : // user can tune the timeout setting via preferences.
747 :
748 2 : LOG(("DisableProxy %s %d\n", key.get(), dsec));
749 :
750 : // If this fails, oh well... means we don't have enough memory
751 : // to remember the failed proxy.
752 2 : mFailedProxies.Put(key, dsec);
753 2 : }
754 :
755 : bool
756 7096 : nsProtocolProxyService::IsProxyDisabled(nsProxyInfo *pi)
757 : {
758 14192 : nsCAutoString key;
759 7096 : GetProxyKey(pi, key);
760 :
761 : PRUint32 val;
762 7096 : if (!mFailedProxies.Get(key, &val))
763 7096 : return false;
764 :
765 0 : PRUint32 dsec = SecondsSinceSessionStart();
766 :
767 : // if time passed has exceeded interval, then try proxy again.
768 0 : if (dsec > val) {
769 0 : mFailedProxies.Remove(key);
770 0 : return false;
771 : }
772 :
773 0 : return true;
774 : }
775 :
776 : nsresult
777 102 : nsProtocolProxyService::ConfigureFromPAC(const nsCString &spec,
778 : bool forceReload)
779 : {
780 102 : if (!mPACMan) {
781 10 : mPACMan = new nsPACMan();
782 10 : if (!mPACMan)
783 0 : return NS_ERROR_OUT_OF_MEMORY;
784 : }
785 :
786 204 : nsCOMPtr<nsIURI> pacURI;
787 102 : nsresult rv = NS_NewURI(getter_AddRefs(pacURI), spec);
788 102 : if (NS_FAILED(rv))
789 0 : return rv;
790 :
791 102 : if (mPACMan->IsPACURI(pacURI) && !forceReload)
792 92 : return NS_OK;
793 :
794 10 : mFailedProxies.Clear();
795 :
796 10 : return mPACMan->LoadPACFromURI(pacURI);
797 : }
798 :
799 : void
800 3511 : nsProtocolProxyService::ProcessPACString(const nsCString &pacString,
801 : PRUint32 aResolveFlags,
802 : nsIProxyInfo **result)
803 : {
804 3511 : if (pacString.IsEmpty()) {
805 0 : *result = nsnull;
806 0 : return;
807 : }
808 :
809 3511 : const char *proxies = pacString.get();
810 :
811 3511 : nsProxyInfo *pi = nsnull, *first = nsnull, *last = nsnull;
812 10538 : while (*proxies) {
813 3516 : proxies = ExtractProxyInfo(proxies, aResolveFlags, &pi);
814 3516 : if (pi) {
815 3516 : if (last) {
816 5 : NS_ASSERTION(last->mNext == nsnull, "leaking nsProxyInfo");
817 5 : last->mNext = pi;
818 : }
819 : else
820 3511 : first = pi;
821 3516 : last = pi;
822 : }
823 : }
824 3511 : *result = first;
825 : }
826 :
827 : // nsIProtocolProxyService2
828 : NS_IMETHODIMP
829 6 : nsProtocolProxyService::ReloadPAC()
830 : {
831 12 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
832 6 : if (!prefs)
833 0 : return NS_OK;
834 :
835 : PRInt32 type;
836 6 : nsresult rv = prefs->GetIntPref(PROXY_PREF("type"), &type);
837 6 : if (NS_FAILED(rv))
838 0 : return NS_OK;
839 :
840 12 : nsXPIDLCString pacSpec;
841 6 : if (type == PROXYCONFIG_PAC)
842 0 : prefs->GetCharPref(PROXY_PREF("autoconfig_url"), getter_Copies(pacSpec));
843 6 : else if (type == PROXYCONFIG_WPAD)
844 0 : pacSpec.AssignLiteral(WPAD_URL);
845 :
846 6 : if (!pacSpec.IsEmpty())
847 0 : ConfigureFromPAC(pacSpec, true);
848 6 : return NS_OK;
849 : }
850 :
851 : // nsIProtocolProxyService
852 : NS_IMETHODIMP
853 3553 : nsProtocolProxyService::Resolve(nsIURI *uri, PRUint32 flags,
854 : nsIProxyInfo **result)
855 : {
856 7106 : nsProtocolInfo info;
857 3553 : nsresult rv = GetProtocolInfo(uri, &info);
858 3553 : if (NS_FAILED(rv))
859 0 : return rv;
860 :
861 : bool usePAC;
862 3553 : rv = Resolve_Internal(uri, info, flags, &usePAC, result);
863 3553 : if (NS_FAILED(rv)) {
864 0 : LOG(("Resolve_Internal returned rv(0x%08x)\n", rv));
865 0 : return rv;
866 : }
867 :
868 3553 : if (usePAC && mPACMan) {
869 96 : NS_ASSERTION(*result == nsnull, "we should not have a result yet");
870 :
871 : // If the caller didn't want us to invoke PAC, then error out.
872 96 : if (flags & RESOLVE_NON_BLOCKING)
873 1 : return NS_BASE_STREAM_WOULD_BLOCK;
874 :
875 : // Query the PAC file synchronously.
876 190 : nsCString pacString;
877 95 : rv = mPACMan->GetProxyForURI(uri, pacString);
878 95 : if (NS_SUCCEEDED(rv))
879 84 : ProcessPACString(pacString, flags, result);
880 11 : else if (rv == NS_ERROR_IN_PROGRESS) {
881 : // Construct a special UNKNOWN proxy entry that informs the caller
882 : // that the proxy info is yet to be determined.
883 11 : rv = NewProxyInfo_Internal(kProxyType_UNKNOWN, EmptyCString(), -1,
884 11 : 0, 0, nsnull, flags, result);
885 11 : if (NS_FAILED(rv))
886 0 : return rv;
887 : }
888 : else
889 0 : NS_WARNING("failed querying PAC file; trying DIRECT");
890 : }
891 :
892 3552 : ApplyFilters(uri, info, result);
893 3552 : return NS_OK;
894 : }
895 :
896 : NS_IMETHODIMP
897 12 : nsProtocolProxyService::AsyncResolve(nsIURI *uri, PRUint32 flags,
898 : nsIProtocolProxyCallback *callback,
899 : nsICancelable **result)
900 : {
901 : nsRefPtr<nsAsyncResolveRequest> ctx =
902 24 : new nsAsyncResolveRequest(this, uri, flags, callback);
903 12 : if (!ctx)
904 0 : return NS_ERROR_OUT_OF_MEMORY;
905 :
906 24 : nsProtocolInfo info;
907 12 : nsresult rv = GetProtocolInfo(uri, &info);
908 12 : if (NS_FAILED(rv))
909 0 : return rv;
910 :
911 : bool usePAC;
912 24 : nsCOMPtr<nsIProxyInfo> pi;
913 12 : rv = Resolve_Internal(uri, info, flags, &usePAC, getter_AddRefs(pi));
914 12 : if (NS_FAILED(rv))
915 0 : return rv;
916 :
917 12 : if (!usePAC || !mPACMan) {
918 1 : ApplyFilters(uri, info, pi);
919 :
920 1 : ctx->SetResult(NS_OK, pi);
921 1 : return ctx->DispatchCallback();
922 : }
923 :
924 : // else kick off a PAC query
925 11 : rv = mPACMan->AsyncGetProxyForURI(uri, ctx);
926 11 : if (NS_SUCCEEDED(rv)) {
927 11 : *result = ctx;
928 11 : NS_ADDREF(*result);
929 : }
930 11 : return rv;
931 : }
932 :
933 : NS_IMETHODIMP
934 9 : nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
935 : const nsACString &aHost,
936 : PRInt32 aPort,
937 : PRUint32 aFlags,
938 : PRUint32 aFailoverTimeout,
939 : nsIProxyInfo *aFailoverProxy,
940 : nsIProxyInfo **aResult)
941 : {
942 : static const char *types[] = {
943 : kProxyType_HTTP,
944 : kProxyType_SOCKS,
945 : kProxyType_SOCKS4,
946 : kProxyType_DIRECT,
947 : kProxyType_UNKNOWN
948 : };
949 :
950 : // resolve type; this allows us to avoid copying the type string into each
951 : // proxy info instance. we just reference the string literals directly :)
952 9 : const char *type = nsnull;
953 18 : for (PRUint32 i=0; i<ArrayLength(types); ++i) {
954 18 : if (aType.LowerCaseEqualsASCII(types[i])) {
955 9 : type = types[i];
956 9 : break;
957 : }
958 : }
959 9 : NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
960 :
961 9 : if (aPort <= 0)
962 3 : aPort = -1;
963 :
964 : return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aFailoverTimeout,
965 9 : aFailoverProxy, 0, aResult);
966 : }
967 :
968 : NS_IMETHODIMP
969 2 : nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo *aProxy,
970 : nsIURI *aURI,
971 : nsresult aStatus,
972 : nsIProxyInfo **aResult)
973 : {
974 : // We only support failover when a PAC file is configured, either
975 : // directly or via system settings
976 2 : if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
977 : mProxyConfig != PROXYCONFIG_SYSTEM)
978 0 : return NS_ERROR_NOT_AVAILABLE;
979 :
980 : // Verify that |aProxy| is one of our nsProxyInfo objects.
981 4 : nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
982 2 : NS_ENSURE_ARG(pi);
983 : // OK, the QI checked out. We can proceed.
984 :
985 : // Remember that this proxy is down.
986 2 : DisableProxy(pi);
987 :
988 : // NOTE: At this point, we might want to prompt the user if we have
989 : // not already tried going DIRECT. This is something that the
990 : // classic codebase supported; however, IE6 does not prompt.
991 :
992 2 : if (!pi->mNext)
993 0 : return NS_ERROR_NOT_AVAILABLE;
994 :
995 2 : LOG(("PAC failover from %s %s:%d to %s %s:%d\n",
996 : pi->mType, pi->mHost.get(), pi->mPort,
997 : pi->mNext->mType, pi->mNext->mHost.get(), pi->mNext->mPort));
998 :
999 2 : NS_ADDREF(*aResult = pi->mNext);
1000 2 : return NS_OK;
1001 : }
1002 :
1003 : NS_IMETHODIMP
1004 4 : nsProtocolProxyService::RegisterFilter(nsIProtocolProxyFilter *filter,
1005 : PRUint32 position)
1006 : {
1007 4 : UnregisterFilter(filter); // remove this filter if we already have it
1008 :
1009 4 : FilterLink *link = new FilterLink(position, filter);
1010 4 : if (!link)
1011 0 : return NS_ERROR_OUT_OF_MEMORY;
1012 :
1013 4 : if (!mFilters) {
1014 2 : mFilters = link;
1015 2 : return NS_OK;
1016 : }
1017 :
1018 : // insert into mFilters in sorted order
1019 2 : FilterLink *last = nsnull;
1020 3 : for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1021 2 : if (position < iter->position) {
1022 1 : if (last) {
1023 0 : link->next = last->next;
1024 0 : last->next = link;
1025 : }
1026 : else {
1027 1 : link->next = mFilters;
1028 1 : mFilters = link;
1029 : }
1030 1 : return NS_OK;
1031 : }
1032 1 : last = iter;
1033 : }
1034 : // our position is equal to or greater than the last link in the list
1035 1 : last->next = link;
1036 1 : return NS_OK;
1037 : }
1038 :
1039 : NS_IMETHODIMP
1040 8 : nsProtocolProxyService::UnregisterFilter(nsIProtocolProxyFilter *filter)
1041 : {
1042 : // QI to nsISupports so we can safely test object identity.
1043 16 : nsCOMPtr<nsISupports> givenObject = do_QueryInterface(filter);
1044 :
1045 8 : FilterLink *last = nsnull;
1046 11 : for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1047 14 : nsCOMPtr<nsISupports> object = do_QueryInterface(iter->filter);
1048 7 : if (object == givenObject) {
1049 4 : if (last)
1050 1 : last->next = iter->next;
1051 : else
1052 3 : mFilters = iter->next;
1053 4 : iter->next = nsnull;
1054 4 : delete iter;
1055 4 : return NS_OK;
1056 : }
1057 10 : last = iter;
1058 : }
1059 :
1060 : // No need to throw an exception in this case.
1061 4 : return NS_OK;
1062 : }
1063 :
1064 : NS_IMETHODIMP
1065 0 : nsProtocolProxyService::GetProxyConfigType(PRUint32* aProxyConfigType)
1066 : {
1067 0 : *aProxyConfigType = mProxyConfig;
1068 0 : return NS_OK;
1069 : }
1070 :
1071 : void
1072 281 : nsProtocolProxyService::LoadHostFilters(const char *filters)
1073 : {
1074 : // check to see the owners flag? /!?/ TODO
1075 281 : if (mHostFiltersArray.Length() > 0) {
1076 3 : mHostFiltersArray.Clear();
1077 : }
1078 :
1079 281 : if (!filters)
1080 0 : return; // fail silently...
1081 :
1082 : //
1083 : // filter = ( host | domain | ipaddr ["/" mask] ) [":" port]
1084 : // filters = filter *( "," LWS filter)
1085 : //
1086 : // Reset mFilterLocalHosts - will be set to true if "<local>" is in pref string
1087 281 : mFilterLocalHosts = false;
1088 1127 : while (*filters) {
1089 : // skip over spaces and ,
1090 1702 : while (*filters && (*filters == ',' || IS_ASCII_SPACE(*filters)))
1091 572 : filters++;
1092 :
1093 565 : const char *starthost = filters;
1094 565 : const char *endhost = filters + 1; // at least that...
1095 565 : const char *portLocation = 0;
1096 565 : const char *maskLocation = 0;
1097 :
1098 5680 : while (*endhost && (*endhost != ',' && !IS_ASCII_SPACE(*endhost))) {
1099 4550 : if (*endhost == ':')
1100 0 : portLocation = endhost;
1101 4550 : else if (*endhost == '/')
1102 0 : maskLocation = endhost;
1103 4550 : else if (*endhost == ']') // IPv6 address literals
1104 0 : portLocation = 0;
1105 4550 : endhost++;
1106 : }
1107 :
1108 565 : filters = endhost; // advance iterator up front
1109 :
1110 : // locate end of host
1111 : const char *end = maskLocation ? maskLocation :
1112 : portLocation ? portLocation :
1113 565 : endhost;
1114 :
1115 1130 : nsCAutoString str(starthost, end - starthost);
1116 :
1117 : // If the current host filter is "<local>", then all local (i.e.
1118 : // no dots in the hostname) hosts should bypass the proxy
1119 565 : if (str.EqualsIgnoreCase("<local>")) {
1120 1 : mFilterLocalHosts = true;
1121 1 : LOG(("loaded filter for local hosts "
1122 : "(plain host names, no dots)\n"));
1123 : // Continue to next host filter;
1124 1 : continue;
1125 : }
1126 :
1127 : // For all other host filters, create HostInfo object and add to list
1128 564 : HostInfo *hinfo = new HostInfo();
1129 564 : hinfo->port = portLocation ? atoi(portLocation + 1) : 0;
1130 :
1131 : PRNetAddr addr;
1132 564 : if (PR_StringToNetAddr(str.get(), &addr) == PR_SUCCESS) {
1133 277 : hinfo->is_ipaddr = true;
1134 277 : hinfo->ip.family = PR_AF_INET6; // we always store address as IPv6
1135 277 : hinfo->ip.mask_len = maskLocation ? atoi(maskLocation + 1) : 128;
1136 :
1137 277 : if (hinfo->ip.mask_len == 0) {
1138 0 : NS_WARNING("invalid mask");
1139 0 : goto loser;
1140 : }
1141 :
1142 277 : if (addr.raw.family == PR_AF_INET) {
1143 : // convert to IPv4-mapped address
1144 277 : PR_ConvertIPv4AddrToIPv6(addr.inet.ip, &hinfo->ip.addr);
1145 : // adjust mask_len accordingly
1146 277 : if (hinfo->ip.mask_len <= 32)
1147 0 : hinfo->ip.mask_len += 96;
1148 : }
1149 0 : else if (addr.raw.family == PR_AF_INET6) {
1150 : // copy the address
1151 0 : memcpy(&hinfo->ip.addr, &addr.ipv6.ip, sizeof(PRIPv6Addr));
1152 : }
1153 : else {
1154 0 : NS_WARNING("unknown address family");
1155 0 : goto loser;
1156 : }
1157 :
1158 : // apply mask to IPv6 address
1159 277 : proxy_MaskIPv6Addr(hinfo->ip.addr, hinfo->ip.mask_len);
1160 : }
1161 : else {
1162 : PRUint32 startIndex, endIndex;
1163 287 : if (str.First() == '*')
1164 0 : startIndex = 1; // *.domain -> .domain
1165 : else
1166 287 : startIndex = 0;
1167 287 : endIndex = (portLocation ? portLocation : endhost) - starthost;
1168 :
1169 287 : hinfo->is_ipaddr = false;
1170 287 : hinfo->name.host = ToNewCString(Substring(str, startIndex, endIndex));
1171 :
1172 287 : if (!hinfo->name.host)
1173 0 : goto loser;
1174 :
1175 287 : hinfo->name.host_len = endIndex - startIndex;
1176 : }
1177 :
1178 : //#define DEBUG_DUMP_FILTERS
1179 : #ifdef DEBUG_DUMP_FILTERS
1180 : printf("loaded filter[%u]:\n", mHostFiltersArray.Length());
1181 : printf(" is_ipaddr = %u\n", hinfo->is_ipaddr);
1182 : printf(" port = %u\n", hinfo->port);
1183 : if (hinfo->is_ipaddr) {
1184 : printf(" ip.family = %x\n", hinfo->ip.family);
1185 : printf(" ip.mask_len = %u\n", hinfo->ip.mask_len);
1186 :
1187 : PRNetAddr netAddr;
1188 : PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, 0, &netAddr);
1189 : memcpy(&netAddr.ipv6.ip, &hinfo->ip.addr, sizeof(hinfo->ip.addr));
1190 :
1191 : char buf[256];
1192 : PR_NetAddrToString(&netAddr, buf, sizeof(buf));
1193 :
1194 : printf(" ip.addr = %s\n", buf);
1195 : }
1196 : else {
1197 : printf(" name.host = %s\n", hinfo->name.host);
1198 : }
1199 : #endif
1200 :
1201 564 : mHostFiltersArray.AppendElement(hinfo);
1202 564 : hinfo = nsnull;
1203 : loser:
1204 564 : delete hinfo;
1205 : }
1206 : }
1207 :
1208 : nsresult
1209 3576 : nsProtocolProxyService::GetProtocolInfo(nsIURI *uri, nsProtocolInfo *info)
1210 : {
1211 : nsresult rv;
1212 :
1213 3576 : rv = uri->GetScheme(info->scheme);
1214 3576 : if (NS_FAILED(rv))
1215 0 : return rv;
1216 :
1217 7152 : nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
1218 3576 : if (NS_FAILED(rv))
1219 0 : return rv;
1220 :
1221 7152 : nsCOMPtr<nsIProtocolHandler> handler;
1222 3576 : rv = ios->GetProtocolHandler(info->scheme.get(), getter_AddRefs(handler));
1223 3576 : if (NS_FAILED(rv))
1224 0 : return rv;
1225 :
1226 3576 : rv = handler->GetProtocolFlags(&info->flags);
1227 3576 : if (NS_FAILED(rv))
1228 0 : return rv;
1229 :
1230 3576 : rv = handler->GetDefaultPort(&info->defaultPort);
1231 3576 : return rv;
1232 : }
1233 :
1234 : nsresult
1235 36 : nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
1236 : const nsACString &aHost,
1237 : PRInt32 aPort,
1238 : PRUint32 aFlags,
1239 : PRUint32 aFailoverTimeout,
1240 : nsIProxyInfo *aFailoverProxy,
1241 : PRUint32 aResolveFlags,
1242 : nsIProxyInfo **aResult)
1243 : {
1244 72 : nsCOMPtr<nsProxyInfo> failover;
1245 36 : if (aFailoverProxy) {
1246 3 : failover = do_QueryInterface(aFailoverProxy);
1247 3 : NS_ENSURE_ARG(failover);
1248 : }
1249 :
1250 36 : nsProxyInfo *proxyInfo = new nsProxyInfo();
1251 36 : if (!proxyInfo)
1252 0 : return NS_ERROR_OUT_OF_MEMORY;
1253 :
1254 36 : proxyInfo->mType = aType;
1255 36 : proxyInfo->mHost = aHost;
1256 36 : proxyInfo->mPort = aPort;
1257 36 : proxyInfo->mFlags = aFlags;
1258 36 : proxyInfo->mResolveFlags = aResolveFlags;
1259 : proxyInfo->mTimeout = aFailoverTimeout == PR_UINT32_MAX
1260 36 : ? mFailedProxyTimeout : aFailoverTimeout;
1261 36 : failover.swap(proxyInfo->mNext);
1262 :
1263 36 : NS_ADDREF(*aResult = proxyInfo);
1264 36 : return NS_OK;
1265 : }
1266 :
1267 : nsresult
1268 3565 : nsProtocolProxyService::Resolve_Internal(nsIURI *uri,
1269 : const nsProtocolInfo &info,
1270 : PRUint32 flags,
1271 : bool *usePAC,
1272 : nsIProxyInfo **result)
1273 : {
1274 3565 : NS_ENSURE_ARG_POINTER(uri);
1275 :
1276 3565 : *usePAC = false;
1277 3565 : *result = nsnull;
1278 :
1279 3565 : if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1280 0 : return NS_OK; // Can't proxy this (filters may not override)
1281 :
1282 3565 : if (mSystemProxySettings) {
1283 7032 : nsCAutoString PACURI;
1284 7032 : if (NS_FAILED(mSystemProxySettings->GetPACURI(PACURI)) ||
1285 3516 : PACURI.IsEmpty()) {
1286 6840 : nsCAutoString proxy;
1287 3420 : nsresult rv = mSystemProxySettings->GetProxyForURI(uri, proxy);
1288 3420 : if (NS_SUCCEEDED(rv)) {
1289 3417 : ProcessPACString(proxy, flags, result);
1290 3417 : return NS_OK;
1291 : }
1292 : // no proxy, stop search
1293 3 : return NS_OK;
1294 : }
1295 :
1296 : // See bug #586908.
1297 : // Avoid endless loop if |uri| is the current PAC-URI. Returning OK
1298 : // here means that we will not use a proxy for this connection.
1299 96 : if (mPACMan && mPACMan->IsPACURI(uri))
1300 5 : return NS_OK;
1301 :
1302 : // Switch to new PAC file if that setting has changed. If the setting
1303 : // hasn't changed, ConfigureFromPAC will exit early.
1304 91 : nsresult rv = ConfigureFromPAC(PACURI, false);
1305 91 : if (NS_FAILED(rv))
1306 0 : return rv;
1307 : }
1308 :
1309 : // if proxies are enabled and this host:port combo is supposed to use a
1310 : // proxy, check for a proxy.
1311 172 : if (mProxyConfig == PROXYCONFIG_DIRECT ||
1312 : (mProxyConfig == PROXYCONFIG_MANUAL &&
1313 32 : !CanUseProxy(uri, info.defaultPort)))
1314 16 : return NS_OK;
1315 :
1316 : // Proxy auto config magic...
1317 124 : if (mProxyConfig == PROXYCONFIG_PAC || mProxyConfig == PROXYCONFIG_WPAD ||
1318 : mProxyConfig == PROXYCONFIG_SYSTEM) {
1319 : // Do not query PAC now.
1320 107 : *usePAC = true;
1321 107 : return NS_OK;
1322 : }
1323 :
1324 : // proxy info values
1325 17 : const char *type = nsnull;
1326 17 : const nsACString *host = nsnull;
1327 17 : PRInt32 port = -1;
1328 :
1329 17 : PRUint32 proxyFlags = 0;
1330 :
1331 17 : if ((flags & RESOLVE_PREFER_SOCKS_PROXY) &&
1332 0 : !mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
1333 0 : host = &mSOCKSProxyHost;
1334 0 : if (mSOCKSProxyVersion == 4)
1335 0 : type = kProxyType_SOCKS4;
1336 : else
1337 0 : type = kProxyType_SOCKS;
1338 0 : port = mSOCKSProxyPort;
1339 0 : if (mSOCKSProxyRemoteDNS)
1340 0 : proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1341 : }
1342 17 : else if ((flags & RESOLVE_PREFER_HTTPS_PROXY) &&
1343 0 : !mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0) {
1344 0 : host = &mHTTPSProxyHost;
1345 0 : type = kProxyType_HTTP;
1346 0 : port = mHTTPSProxyPort;
1347 : }
1348 32 : else if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
1349 : ((flags & RESOLVE_IGNORE_URI_SCHEME) ||
1350 15 : info.scheme.EqualsLiteral("http"))) {
1351 15 : host = &mHTTPProxyHost;
1352 15 : type = kProxyType_HTTP;
1353 15 : port = mHTTPProxyPort;
1354 : }
1355 2 : else if (!mHTTPSProxyHost.IsEmpty() && mHTTPSProxyPort > 0 &&
1356 0 : !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1357 0 : info.scheme.EqualsLiteral("https")) {
1358 0 : host = &mHTTPSProxyHost;
1359 0 : type = kProxyType_HTTP;
1360 0 : port = mHTTPSProxyPort;
1361 : }
1362 2 : else if (!mFTPProxyHost.IsEmpty() && mFTPProxyPort > 0 &&
1363 0 : !(flags & RESOLVE_IGNORE_URI_SCHEME) &&
1364 0 : info.scheme.EqualsLiteral("ftp")) {
1365 0 : host = &mFTPProxyHost;
1366 0 : type = kProxyType_HTTP;
1367 0 : port = mFTPProxyPort;
1368 : }
1369 2 : else if (!mSOCKSProxyHost.IsEmpty() && mSOCKSProxyPort > 0) {
1370 1 : host = &mSOCKSProxyHost;
1371 1 : if (mSOCKSProxyVersion == 4)
1372 0 : type = kProxyType_SOCKS4;
1373 : else
1374 1 : type = kProxyType_SOCKS;
1375 1 : port = mSOCKSProxyPort;
1376 1 : if (mSOCKSProxyRemoteDNS)
1377 0 : proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
1378 : }
1379 :
1380 17 : if (type) {
1381 : nsresult rv = NewProxyInfo_Internal(type, *host, port, proxyFlags,
1382 : PR_UINT32_MAX, nsnull, flags,
1383 16 : result);
1384 16 : if (NS_FAILED(rv))
1385 0 : return rv;
1386 : }
1387 :
1388 17 : return NS_OK;
1389 : }
1390 :
1391 : void
1392 3564 : nsProtocolProxyService::ApplyFilters(nsIURI *uri, const nsProtocolInfo &info,
1393 : nsIProxyInfo **list)
1394 : {
1395 3564 : if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY))
1396 0 : return;
1397 :
1398 : // We prune the proxy list prior to invoking each filter. This may be
1399 : // somewhat inefficient, but it seems like a good idea since we want each
1400 : // filter to "see" a valid proxy list.
1401 :
1402 : nsresult rv;
1403 7128 : nsCOMPtr<nsIProxyInfo> result;
1404 :
1405 3570 : for (FilterLink *iter = mFilters; iter; iter = iter->next) {
1406 6 : PruneProxyInfo(info, list);
1407 :
1408 6 : rv = iter->filter->ApplyFilter(this, uri, *list,
1409 6 : getter_AddRefs(result));
1410 6 : if (NS_FAILED(rv))
1411 0 : continue;
1412 6 : result.swap(*list);
1413 : }
1414 :
1415 3564 : PruneProxyInfo(info, list);
1416 : }
1417 :
1418 : void
1419 3570 : nsProtocolProxyService::PruneProxyInfo(const nsProtocolInfo &info,
1420 : nsIProxyInfo **list)
1421 : {
1422 3570 : if (!*list)
1423 26 : return;
1424 3544 : nsProxyInfo *head = nsnull;
1425 3544 : CallQueryInterface(*list, &head);
1426 3544 : if (!head) {
1427 0 : NS_NOTREACHED("nsIProxyInfo must QI to nsProxyInfo");
1428 0 : return;
1429 : }
1430 3544 : NS_RELEASE(*list);
1431 :
1432 : // Pruning of disabled proxies works like this:
1433 : // - If all proxies are disabled, return the full list
1434 : // - Otherwise, remove the disabled proxies.
1435 : //
1436 : // Pruning of disallowed proxies works like this:
1437 : // - If the protocol handler disallows the proxy, then we disallow it.
1438 :
1439 : // Start by removing all disallowed proxies if required:
1440 3544 : if (!(info.flags & nsIProtocolHandler::ALLOWS_PROXY_HTTP)) {
1441 1 : nsProxyInfo *last = nsnull, *iter = head;
1442 4 : while (iter) {
1443 2 : if (iter->Type() == kProxyType_HTTP) {
1444 : // reject!
1445 1 : if (last)
1446 0 : last->mNext = iter->mNext;
1447 : else
1448 1 : head = iter->mNext;
1449 1 : nsProxyInfo *next = iter->mNext;
1450 1 : iter->mNext = nsnull;
1451 1 : iter->Release();
1452 1 : iter = next;
1453 : } else {
1454 1 : last = iter;
1455 1 : iter = iter->mNext;
1456 : }
1457 : }
1458 1 : if (!head)
1459 0 : return;
1460 : }
1461 :
1462 : // Now, scan to see if all remaining proxies are disabled. If so, then
1463 : // we'll just bail and return them all. Otherwise, we'll go and prune the
1464 : // disabled ones.
1465 :
1466 3544 : bool allDisabled = true;
1467 :
1468 : nsProxyInfo *iter;
1469 3544 : for (iter = head; iter; iter = iter->mNext) {
1470 3544 : if (!IsProxyDisabled(iter)) {
1471 3544 : allDisabled = false;
1472 3544 : break;
1473 : }
1474 : }
1475 :
1476 3544 : if (allDisabled)
1477 0 : LOG(("All proxies are disabled, so trying all again"));
1478 : else {
1479 : // remove any disabled proxies.
1480 3544 : nsProxyInfo *last = nsnull;
1481 10640 : for (iter = head; iter; ) {
1482 3552 : if (IsProxyDisabled(iter)) {
1483 : // reject!
1484 0 : nsProxyInfo *reject = iter;
1485 :
1486 0 : iter = iter->mNext;
1487 0 : if (last)
1488 0 : last->mNext = iter;
1489 : else
1490 0 : head = iter;
1491 :
1492 0 : reject->mNext = nsnull;
1493 0 : NS_RELEASE(reject);
1494 0 : continue;
1495 : }
1496 :
1497 : // since we are about to use this proxy, make sure it is not on
1498 : // the disabled proxy list. we'll add it back to that list if
1499 : // we have to (in GetFailoverForProxy).
1500 : //
1501 : // XXX(darin): It might be better to do this as a final pass.
1502 : //
1503 3552 : EnableProxy(iter);
1504 :
1505 3552 : last = iter;
1506 3552 : iter = iter->mNext;
1507 : }
1508 : }
1509 :
1510 : // if only DIRECT was specified then return no proxy info, and we're done.
1511 3544 : if (head && !head->mNext && head->mType == kProxyType_DIRECT)
1512 3504 : NS_RELEASE(head);
1513 :
1514 3544 : *list = head; // Transfer ownership
1515 4392 : }
|