/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // HttpLog.h should generally be included first #include "HttpLog.h" // Log on level :5, instead of default :4. #undef LOG #define LOG(args) LOG5(args) #undef LOG_ENABLED #define LOG_ENABLED() LOG5_ENABLED() #include "nsHttpConnectionInfo.h" #include "mozilla/net/DNS.h" #include "nsComponentManagerUtils.h" #include "nsICryptoHash.h" #include "nsIProtocolProxyService.h" #include "nsNetCID.h" #include "prnetdb.h" static nsresult SHA256(const char* aPlainText, nsAutoCString& aResult) { nsresult rv; nsCOMPtr hasher = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); if (NS_FAILED(rv)) { LOG(("nsHttpDigestAuth: no crypto hash!\n")); return rv; } rv = hasher->Init(nsICryptoHash::SHA256); NS_ENSURE_SUCCESS(rv, rv); rv = hasher->Update((unsigned char*) aPlainText, strlen(aPlainText)); NS_ENSURE_SUCCESS(rv, rv); return hasher->Finish(false, aResult); } namespace mozilla { namespace net { nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &originHost, int32_t originPort, const nsACString &npnToken, const nsACString &username, nsProxyInfo *proxyInfo, const NeckoOriginAttributes &originAttributes, bool endToEndSSL) : mRoutedPort(443) { Init(originHost, originPort, npnToken, username, proxyInfo, originAttributes, endToEndSSL); } nsHttpConnectionInfo::nsHttpConnectionInfo(const nsACString &originHost, int32_t originPort, const nsACString &npnToken, const nsACString &username, nsProxyInfo *proxyInfo, const NeckoOriginAttributes &originAttributes, const nsACString &routedHost, int32_t routedPort) { mEndToEndSSL = true; // so DefaultPort() works mRoutedPort = routedPort == -1 ? DefaultPort() : routedPort; if (!originHost.Equals(routedHost) || (originPort != routedPort)) { mRoutedHost = routedHost; } Init(originHost, originPort, npnToken, username, proxyInfo, originAttributes, true); } void nsHttpConnectionInfo::Init(const nsACString &host, int32_t port, const nsACString &npnToken, const nsACString &username, nsProxyInfo* proxyInfo, const NeckoOriginAttributes &originAttributes, bool e2eSSL) { LOG(("Init nsHttpConnectionInfo @%p\n", this)); mUsername = username; mProxyInfo = proxyInfo; mEndToEndSSL = e2eSSL; mUsingConnect = false; mNPNToken = npnToken; mOriginAttributes = originAttributes; mUsingHttpsProxy = (proxyInfo && proxyInfo->IsHTTPS()); mUsingHttpProxy = mUsingHttpsProxy || (proxyInfo && proxyInfo->IsHTTP()); if (mUsingHttpProxy) { mUsingConnect = mEndToEndSSL; // SSL always uses CONNECT uint32_t resolveFlags = 0; if (NS_SUCCEEDED(mProxyInfo->GetResolveFlags(&resolveFlags)) && resolveFlags & nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL) { mUsingConnect = true; } } SetOriginServer(host, port); } void nsHttpConnectionInfo::SetNetworkInterfaceId(const nsACString& aNetworkInterfaceId) { mNetworkInterfaceId = aNetworkInterfaceId; BuildHashKey(); } void nsHttpConnectionInfo::BuildHashKey() { // // build hash key: // // the hash key uniquely identifies the connection type. two connections // are "equal" if they end up talking the same protocol to the same server // and are both used for anonymous or non-anonymous connection only; // anonymity of the connection is setup later from nsHttpChannel::AsyncOpen // where we know we use anonymous connection (LOAD_ANONYMOUS load flag) // const char *keyHost; int32_t keyPort; if (mUsingHttpProxy && !mUsingConnect) { keyHost = ProxyHost(); keyPort = ProxyPort(); } else { keyHost = Origin(); keyPort = OriginPort(); } // The hashkey has 4 fields followed by host connection info // byte 0 is P/T/. {P,T} for Plaintext/TLS Proxy over HTTP // byte 1 is S/. S is for end to end ssl such as https:// uris // byte 2 is A/. A is for an anonymous channel (no cookies, etc..) // byte 3 is P/. P is for a private browising channel // byte 4 is I/. I is for insecure scheme on TLS for http:// uris // byte 5 is X/. X is for disallow_spdy flag // byte 6 is C/. C is for be Conservative mHashKey.AssignLiteral("......."); mHashKey.Append(keyHost); if (!mNetworkInterfaceId.IsEmpty()) { mHashKey.Append('('); mHashKey.Append(mNetworkInterfaceId); mHashKey.Append(')'); } mHashKey.Append(':'); mHashKey.AppendInt(keyPort); if (!mUsername.IsEmpty()) { mHashKey.Append('['); mHashKey.Append(mUsername); mHashKey.Append(']'); } if (mUsingHttpsProxy) { mHashKey.SetCharAt('T', 0); } else if (mUsingHttpProxy) { mHashKey.SetCharAt('P', 0); } if (mEndToEndSSL) { mHashKey.SetCharAt('S', 1); } // NOTE: for transparent proxies (e.g., SOCKS) we need to encode the proxy // info in the hash key (this ensures that we will continue to speak the // right protocol even if our proxy preferences change). // // NOTE: for SSL tunnels add the proxy information to the cache key. // We cannot use the proxy as the host parameter (as we do for non SSL) // because this is a single host tunnel, but we need to include the proxy // information so that a change in proxy config will mean this connection // is not reused // NOTE: Adding the username and the password provides a means to isolate // keep-alive to the URL bar domain as well: If the username is the URL bar // domain, keep-alive connections are not reused by resources bound to // different URL bar domains as the respective hash keys are not matching. if ((!mUsingHttpProxy && ProxyHost()) || (mUsingHttpProxy && mUsingConnect)) { mHashKey.AppendLiteral(" ("); mHashKey.Append(ProxyType()); mHashKey.Append(':'); mHashKey.Append(ProxyHost()); mHashKey.Append(':'); mHashKey.AppendInt(ProxyPort()); mHashKey.Append(')'); mHashKey.Append('['); mHashKey.Append(ProxyUsername()); mHashKey.Append(':'); const char* password = ProxyPassword(); if (strlen(password) > 0) { nsAutoCString digestedPassword; nsresult rv = SHA256(password, digestedPassword); if (rv == NS_OK) { mHashKey.Append(digestedPassword); } } mHashKey.Append(']'); } if(!mRoutedHost.IsEmpty()) { mHashKey.AppendLiteral(" '); } if (!mNPNToken.IsEmpty()) { mHashKey.AppendLiteral(" {NPN-TOKEN "); mHashKey.Append(mNPNToken); mHashKey.AppendLiteral("}"); } nsAutoCString originAttributes; mOriginAttributes.CreateSuffix(originAttributes); mHashKey.Append(originAttributes); } void nsHttpConnectionInfo::SetOriginServer(const nsACString &host, int32_t port) { mOrigin = host; mOriginPort = port == -1 ? DefaultPort() : port; BuildHashKey(); } nsHttpConnectionInfo* nsHttpConnectionInfo::Clone() const { nsHttpConnectionInfo *clone; if (mRoutedHost.IsEmpty()) { clone = new nsHttpConnectionInfo(mOrigin, mOriginPort, mNPNToken, mUsername, mProxyInfo, mOriginAttributes, mEndToEndSSL); } else { MOZ_ASSERT(mEndToEndSSL); clone = new nsHttpConnectionInfo(mOrigin, mOriginPort, mNPNToken, mUsername, mProxyInfo, mOriginAttributes, mRoutedHost, mRoutedPort); } if (!mNetworkInterfaceId.IsEmpty()) { clone->SetNetworkInterfaceId(mNetworkInterfaceId); } // Make sure the anonymous, insecure-scheme, and private flags are transferred clone->SetAnonymous(GetAnonymous()); clone->SetPrivate(GetPrivate()); clone->SetInsecureScheme(GetInsecureScheme()); clone->SetNoSpdy(GetNoSpdy()); clone->SetBeConservative(GetBeConservative()); MOZ_ASSERT(clone->Equals(this)); return clone; } void nsHttpConnectionInfo::CloneAsDirectRoute(nsHttpConnectionInfo **outCI) { if (mRoutedHost.IsEmpty()) { *outCI = Clone(); return; } RefPtr clone = new nsHttpConnectionInfo(mOrigin, mOriginPort, EmptyCString(), mUsername, mProxyInfo, mOriginAttributes, mEndToEndSSL); // Make sure the anonymous, insecure-scheme, and private flags are transferred clone->SetAnonymous(GetAnonymous()); clone->SetPrivate(GetPrivate()); clone->SetInsecureScheme(GetInsecureScheme()); clone->SetNoSpdy(GetNoSpdy()); clone->SetBeConservative(GetBeConservative()); if (!mNetworkInterfaceId.IsEmpty()) { clone->SetNetworkInterfaceId(mNetworkInterfaceId); } clone.forget(outCI); } nsresult nsHttpConnectionInfo::CreateWildCard(nsHttpConnectionInfo **outParam) { // T???mozilla.org:443 (https:proxy.ducksong.com:3128) [specifc form] // TS??*:0 (https:proxy.ducksong.com:3128) [wildcard form] if (!mUsingHttpsProxy) { MOZ_ASSERT(false); return NS_ERROR_NOT_IMPLEMENTED; } RefPtr clone; clone = new nsHttpConnectionInfo(NS_LITERAL_CSTRING("*"), 0, mNPNToken, mUsername, mProxyInfo, mOriginAttributes, true); // Make sure the anonymous and private flags are transferred! clone->SetAnonymous(GetAnonymous()); clone->SetPrivate(GetPrivate()); clone.forget(outParam); return NS_OK; } bool nsHttpConnectionInfo::UsingProxy() { if (!mProxyInfo) return false; return !mProxyInfo->IsDirect(); } bool nsHttpConnectionInfo::HostIsLocalIPLiteral() const { PRNetAddr prAddr; // If the host/proxy host is not an IP address literal, return false. if (ProxyHost()) { if (PR_StringToNetAddr(ProxyHost(), &prAddr) != PR_SUCCESS) { return false; } } else if (PR_StringToNetAddr(Origin(), &prAddr) != PR_SUCCESS) { return false; } NetAddr netAddr; PRNetAddrToNetAddr(&prAddr, &netAddr); return IsIPAddrLocal(&netAddr); } } // namespace net } // namespace mozilla