netwerk/base/src/nsProtocolProxyService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial