1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set sw=4 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
9 
10 // Log on level :5, instead of default :4.
11 #undef LOG
12 #define LOG(args) LOG5(args)
13 #undef LOG_ENABLED
14 #define LOG_ENABLED() LOG5_ENABLED()
15 
16 #include "nsHttpConnectionInfo.h"
17 
18 #include "mozilla/net/DNS.h"
19 #include "nsComponentManagerUtils.h"
20 #include "nsICryptoHash.h"
21 #include "nsIProtocolProxyService.h"
22 #include "nsNetCID.h"
23 #include "prnetdb.h"
24 
SHA256(const char * aPlainText,nsAutoCString & aResult)25 static nsresult SHA256(const char *aPlainText, nsAutoCString &aResult) {
26   nsresult rv;
27   nsCOMPtr<nsICryptoHash> hasher =
28       do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
29   if (NS_FAILED(rv)) {
30     LOG(("nsHttpDigestAuth: no crypto hash!\n"));
31     return rv;
32   }
33   rv = hasher->Init(nsICryptoHash::SHA256);
34   NS_ENSURE_SUCCESS(rv, rv);
35   rv = hasher->Update((unsigned char *)aPlainText, strlen(aPlainText));
36   NS_ENSURE_SUCCESS(rv, rv);
37   return hasher->Finish(false, aResult);
38 }
39 
40 namespace mozilla {
41 namespace net {
42 
nsHttpConnectionInfo(const nsACString & originHost,int32_t originPort,const nsACString & npnToken,const nsACString & username,nsProxyInfo * proxyInfo,const OriginAttributes & originAttributes,bool endToEndSSL)43 nsHttpConnectionInfo::nsHttpConnectionInfo(
44     const nsACString &originHost, int32_t originPort,
45     const nsACString &npnToken, const nsACString &username,
46     nsProxyInfo *proxyInfo, const OriginAttributes &originAttributes,
47     bool endToEndSSL)
48     : mRoutedPort(443) {
49   Init(originHost, originPort, npnToken, username, proxyInfo, originAttributes,
50        endToEndSSL);
51 }
52 
nsHttpConnectionInfo(const nsACString & originHost,int32_t originPort,const nsACString & npnToken,const nsACString & username,nsProxyInfo * proxyInfo,const OriginAttributes & originAttributes,const nsACString & routedHost,int32_t routedPort)53 nsHttpConnectionInfo::nsHttpConnectionInfo(
54     const nsACString &originHost, int32_t originPort,
55     const nsACString &npnToken, const nsACString &username,
56     nsProxyInfo *proxyInfo, const OriginAttributes &originAttributes,
57     const nsACString &routedHost, int32_t routedPort) {
58   mEndToEndSSL = true;  // so DefaultPort() works
59   mRoutedPort = routedPort == -1 ? DefaultPort() : routedPort;
60 
61   if (!originHost.Equals(routedHost) || (originPort != routedPort)) {
62     mRoutedHost = routedHost;
63   }
64   Init(originHost, originPort, npnToken, username, proxyInfo, originAttributes,
65        true);
66 }
67 
Init(const nsACString & host,int32_t port,const nsACString & npnToken,const nsACString & username,nsProxyInfo * proxyInfo,const OriginAttributes & originAttributes,bool e2eSSL)68 void nsHttpConnectionInfo::Init(const nsACString &host, int32_t port,
69                                 const nsACString &npnToken,
70                                 const nsACString &username,
71                                 nsProxyInfo *proxyInfo,
72                                 const OriginAttributes &originAttributes,
73                                 bool e2eSSL) {
74   LOG(("Init nsHttpConnectionInfo @%p\n", this));
75 
76   mUsername = username;
77   mProxyInfo = proxyInfo;
78   mEndToEndSSL = e2eSSL;
79   mUsingConnect = false;
80   mNPNToken = npnToken;
81   mOriginAttributes = originAttributes;
82   mTlsFlags = 0x0;
83 
84   mUsingHttpsProxy = (proxyInfo && proxyInfo->IsHTTPS());
85   mUsingHttpProxy = mUsingHttpsProxy || (proxyInfo && proxyInfo->IsHTTP());
86 
87   if (mUsingHttpProxy) {
88     mUsingConnect = mEndToEndSSL;  // SSL always uses CONNECT
89     uint32_t resolveFlags = 0;
90     if (NS_SUCCEEDED(mProxyInfo->GetResolveFlags(&resolveFlags)) &&
91         resolveFlags & nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL) {
92       mUsingConnect = true;
93     }
94   }
95 
96   SetOriginServer(host, port);
97 }
98 
SetNetworkInterfaceId(const nsACString & aNetworkInterfaceId)99 void nsHttpConnectionInfo::SetNetworkInterfaceId(
100     const nsACString &aNetworkInterfaceId) {
101   mNetworkInterfaceId = aNetworkInterfaceId;
102   BuildHashKey();
103 }
104 
BuildHashKey()105 void nsHttpConnectionInfo::BuildHashKey() {
106   //
107   // build hash key:
108   //
109   // the hash key uniquely identifies the connection type.  two connections
110   // are "equal" if they end up talking the same protocol to the same server
111   // and are both used for anonymous or non-anonymous connection only;
112   // anonymity of the connection is setup later from nsHttpChannel::AsyncOpen
113   // where we know we use anonymous connection (LOAD_ANONYMOUS load flag)
114   //
115 
116   const char *keyHost;
117   int32_t keyPort;
118 
119   if (mUsingHttpProxy && !mUsingConnect) {
120     keyHost = ProxyHost();
121     keyPort = ProxyPort();
122   } else {
123     keyHost = Origin();
124     keyPort = OriginPort();
125   }
126 
127   // The hashkey has 4 fields followed by host connection info
128   // byte 0 is P/T/. {P,T} for Plaintext/TLS Proxy over HTTP
129   // byte 1 is S/. S is for end to end ssl such as https:// uris
130   // byte 2 is A/. A is for an anonymous channel (no cookies, etc..)
131   // byte 3 is P/. P is for a private browising channel
132   // byte 4 is I/. I is for insecure scheme on TLS for http:// uris
133   // byte 5 is X/. X is for disallow_spdy flag
134   // byte 6 is C/. C is for be Conservative
135 
136   mHashKey.AssignLiteral(".......[tlsflags0x00000000]");
137 
138   mHashKey.Append(keyHost);
139   if (!mNetworkInterfaceId.IsEmpty()) {
140     mHashKey.Append('(');
141     mHashKey.Append(mNetworkInterfaceId);
142     mHashKey.Append(')');
143   }
144   mHashKey.Append(':');
145   mHashKey.AppendInt(keyPort);
146   if (!mUsername.IsEmpty()) {
147     mHashKey.Append('[');
148     mHashKey.Append(mUsername);
149     mHashKey.Append(']');
150   }
151 
152   if (mUsingHttpsProxy) {
153     mHashKey.SetCharAt('T', 0);
154   } else if (mUsingHttpProxy) {
155     mHashKey.SetCharAt('P', 0);
156   }
157   if (mEndToEndSSL) {
158     mHashKey.SetCharAt('S', 1);
159   }
160 
161   // NOTE: for transparent proxies (e.g., SOCKS) we need to encode the proxy
162   // info in the hash key (this ensures that we will continue to speak the
163   // right protocol even if our proxy preferences change).
164   //
165   // NOTE: for SSL tunnels add the proxy information to the cache key.
166   // We cannot use the proxy as the host parameter (as we do for non SSL)
167   // because this is a single host tunnel, but we need to include the proxy
168   // information so that a change in proxy config will mean this connection
169   // is not reused
170 
171   // NOTE: Adding the username and the password provides a means to isolate
172   // keep-alive to the URL bar domain as well: If the username is the URL bar
173   // domain, keep-alive connections are not reused by resources bound to
174   // different URL bar domains as the respective hash keys are not matching.
175 
176   if ((!mUsingHttpProxy && ProxyHost()) || (mUsingHttpProxy && mUsingConnect)) {
177     mHashKey.AppendLiteral(" (");
178     mHashKey.Append(ProxyType());
179     mHashKey.Append(':');
180     mHashKey.Append(ProxyHost());
181     mHashKey.Append(':');
182     mHashKey.AppendInt(ProxyPort());
183     mHashKey.Append(')');
184     mHashKey.Append('[');
185     mHashKey.Append(ProxyUsername());
186     mHashKey.Append(':');
187     const char *password = ProxyPassword();
188     if (strlen(password) > 0) {
189       nsAutoCString digestedPassword;
190       nsresult rv = SHA256(password, digestedPassword);
191       if (rv == NS_OK) {
192         mHashKey.Append(digestedPassword);
193       }
194     }
195     mHashKey.Append(']');
196   }
197 
198   if (!mRoutedHost.IsEmpty()) {
199     mHashKey.AppendLiteral(" <ROUTE-via ");
200     mHashKey.Append(mRoutedHost);
201     mHashKey.Append(':');
202     mHashKey.AppendInt(mRoutedPort);
203     mHashKey.Append('>');
204   }
205 
206   if (!mNPNToken.IsEmpty()) {
207     mHashKey.AppendLiteral(" {NPN-TOKEN ");
208     mHashKey.Append(mNPNToken);
209     mHashKey.AppendLiteral("}");
210   }
211 
212   nsAutoCString originAttributes;
213   mOriginAttributes.CreateSuffix(originAttributes);
214   mHashKey.Append(originAttributes);
215 }
216 
SetOriginServer(const nsACString & host,int32_t port)217 void nsHttpConnectionInfo::SetOriginServer(const nsACString &host,
218                                            int32_t port) {
219   mOrigin = host;
220   mOriginPort = port == -1 ? DefaultPort() : port;
221   BuildHashKey();
222 }
223 
Clone() const224 nsHttpConnectionInfo *nsHttpConnectionInfo::Clone() const {
225   nsHttpConnectionInfo *clone;
226   if (mRoutedHost.IsEmpty()) {
227     clone =
228         new nsHttpConnectionInfo(mOrigin, mOriginPort, mNPNToken, mUsername,
229                                  mProxyInfo, mOriginAttributes, mEndToEndSSL);
230   } else {
231     MOZ_ASSERT(mEndToEndSSL);
232     clone = new nsHttpConnectionInfo(mOrigin, mOriginPort, mNPNToken, mUsername,
233                                      mProxyInfo, mOriginAttributes, mRoutedHost,
234                                      mRoutedPort);
235   }
236 
237   if (!mNetworkInterfaceId.IsEmpty()) {
238     clone->SetNetworkInterfaceId(mNetworkInterfaceId);
239   }
240 
241   // Make sure the anonymous, insecure-scheme, and private flags are transferred
242   clone->SetAnonymous(GetAnonymous());
243   clone->SetPrivate(GetPrivate());
244   clone->SetInsecureScheme(GetInsecureScheme());
245   clone->SetNoSpdy(GetNoSpdy());
246   clone->SetBeConservative(GetBeConservative());
247   clone->SetTlsFlags(GetTlsFlags());
248   MOZ_ASSERT(clone->Equals(this));
249 
250   return clone;
251 }
252 
CloneAsDirectRoute(nsHttpConnectionInfo ** outCI)253 void nsHttpConnectionInfo::CloneAsDirectRoute(nsHttpConnectionInfo **outCI) {
254   if (mRoutedHost.IsEmpty()) {
255     *outCI = Clone();
256     return;
257   }
258 
259   RefPtr<nsHttpConnectionInfo> clone =
260       new nsHttpConnectionInfo(mOrigin, mOriginPort, EmptyCString(), mUsername,
261                                mProxyInfo, mOriginAttributes, mEndToEndSSL);
262   // Make sure the anonymous, insecure-scheme, and private flags are transferred
263   clone->SetAnonymous(GetAnonymous());
264   clone->SetPrivate(GetPrivate());
265   clone->SetInsecureScheme(GetInsecureScheme());
266   clone->SetNoSpdy(GetNoSpdy());
267   clone->SetBeConservative(GetBeConservative());
268   clone->SetTlsFlags(GetTlsFlags());
269   if (!mNetworkInterfaceId.IsEmpty()) {
270     clone->SetNetworkInterfaceId(mNetworkInterfaceId);
271   }
272   clone.forget(outCI);
273 }
274 
CreateWildCard(nsHttpConnectionInfo ** outParam)275 nsresult nsHttpConnectionInfo::CreateWildCard(nsHttpConnectionInfo **outParam) {
276   // T???mozilla.org:443 (https:proxy.ducksong.com:3128) [specifc form]
277   // TS??*:0 (https:proxy.ducksong.com:3128)   [wildcard form]
278 
279   if (!mUsingHttpsProxy) {
280     MOZ_ASSERT(false);
281     return NS_ERROR_NOT_IMPLEMENTED;
282   }
283 
284   RefPtr<nsHttpConnectionInfo> clone;
285   clone =
286       new nsHttpConnectionInfo(NS_LITERAL_CSTRING("*"), 0, mNPNToken, mUsername,
287                                mProxyInfo, mOriginAttributes, true);
288   // Make sure the anonymous and private flags are transferred!
289   clone->SetAnonymous(GetAnonymous());
290   clone->SetPrivate(GetPrivate());
291   clone.forget(outParam);
292   return NS_OK;
293 }
294 
SetTlsFlags(uint32_t aTlsFlags)295 void nsHttpConnectionInfo::SetTlsFlags(uint32_t aTlsFlags) {
296   mTlsFlags = aTlsFlags;
297 
298   mHashKey.Replace(18, 8, nsPrintfCString("%08x", mTlsFlags));
299 }
300 
UsingProxy()301 bool nsHttpConnectionInfo::UsingProxy() {
302   if (!mProxyInfo) return false;
303   return !mProxyInfo->IsDirect();
304 }
305 
HostIsLocalIPLiteral() const306 bool nsHttpConnectionInfo::HostIsLocalIPLiteral() const {
307   PRNetAddr prAddr;
308   // If the host/proxy host is not an IP address literal, return false.
309   if (ProxyHost()) {
310     if (PR_StringToNetAddr(ProxyHost(), &prAddr) != PR_SUCCESS) {
311       return false;
312     }
313   } else if (PR_StringToNetAddr(Origin(), &prAddr) != PR_SUCCESS) {
314     return false;
315   }
316   NetAddr netAddr;
317   PRNetAddrToNetAddr(&prAddr, &netAddr);
318   return IsIPAddrLocal(&netAddr);
319 }
320 
321 }  // namespace net
322 }  // namespace mozilla
323