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