Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 | } |