/src/mozilla-central/netwerk/base/nsSocketTransport2.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #ifndef nsSocketTransport2_h__ |
6 | | #define nsSocketTransport2_h__ |
7 | | |
8 | | #ifdef DEBUG_darinf |
9 | | #define ENABLE_SOCKET_TRACING |
10 | | #endif |
11 | | |
12 | | #include "mozilla/Mutex.h" |
13 | | #include "nsSocketTransportService2.h" |
14 | | #include "nsString.h" |
15 | | #include "nsCOMPtr.h" |
16 | | |
17 | | #include "nsIInterfaceRequestor.h" |
18 | | #include "nsISocketTransport.h" |
19 | | #include "nsIAsyncInputStream.h" |
20 | | #include "nsIAsyncOutputStream.h" |
21 | | #include "nsIDNSListener.h" |
22 | | #include "nsIClassInfo.h" |
23 | | #include "TCPFastOpen.h" |
24 | | #include "mozilla/net/DNS.h" |
25 | | #include "nsASocketHandler.h" |
26 | | #include "mozilla/Telemetry.h" |
27 | | |
28 | | #include "prerror.h" |
29 | | #include "nsAutoPtr.h" |
30 | | |
31 | | class nsICancelable; |
32 | | class nsIDNSRecord; |
33 | | class nsIInterfaceRequestor; |
34 | | |
35 | | //----------------------------------------------------------------------------- |
36 | | |
37 | | // after this short interval, we will return to PR_Poll |
38 | 0 | #define NS_SOCKET_CONNECT_TIMEOUT PR_MillisecondsToInterval(20) |
39 | | |
40 | | //----------------------------------------------------------------------------- |
41 | | |
42 | | namespace mozilla { |
43 | | namespace net { |
44 | | |
45 | | nsresult |
46 | | ErrorAccordingToNSPR(PRErrorCode errorCode); |
47 | | |
48 | | class nsSocketTransport; |
49 | | |
50 | | class nsSocketInputStream : public nsIAsyncInputStream |
51 | | { |
52 | | public: |
53 | | NS_DECL_ISUPPORTS_INHERITED |
54 | | NS_DECL_NSIINPUTSTREAM |
55 | | NS_DECL_NSIASYNCINPUTSTREAM |
56 | | |
57 | | explicit nsSocketInputStream(nsSocketTransport *); |
58 | 0 | virtual ~nsSocketInputStream() = default; |
59 | | |
60 | 0 | bool IsReferenced() { return mReaderRefCnt > 0; } |
61 | 0 | nsresult Condition() { return mCondition; } |
62 | 0 | uint64_t ByteCount() { return mByteCount; } |
63 | | |
64 | | // called by the socket transport on the socket thread... |
65 | | void OnSocketReady(nsresult condition); |
66 | | |
67 | | private: |
68 | | nsSocketTransport *mTransport; |
69 | | ThreadSafeAutoRefCnt mReaderRefCnt; |
70 | | |
71 | | // access to these is protected by mTransport->mLock |
72 | | nsresult mCondition; |
73 | | nsCOMPtr<nsIInputStreamCallback> mCallback; |
74 | | uint32_t mCallbackFlags; |
75 | | uint64_t mByteCount; |
76 | | }; |
77 | | |
78 | | //----------------------------------------------------------------------------- |
79 | | |
80 | | class nsSocketOutputStream : public nsIAsyncOutputStream |
81 | | { |
82 | | public: |
83 | | NS_DECL_ISUPPORTS_INHERITED |
84 | | NS_DECL_NSIOUTPUTSTREAM |
85 | | NS_DECL_NSIASYNCOUTPUTSTREAM |
86 | | |
87 | | explicit nsSocketOutputStream(nsSocketTransport *); |
88 | 0 | virtual ~nsSocketOutputStream() = default; |
89 | | |
90 | 0 | bool IsReferenced() { return mWriterRefCnt > 0; } |
91 | 0 | nsresult Condition() { return mCondition; } |
92 | 0 | uint64_t ByteCount() { return mByteCount; } |
93 | | |
94 | | // called by the socket transport on the socket thread... |
95 | | void OnSocketReady(nsresult condition); |
96 | | |
97 | | private: |
98 | | static nsresult WriteFromSegments(nsIInputStream *, void *, |
99 | | const char *, uint32_t offset, |
100 | | uint32_t count, uint32_t *countRead); |
101 | | |
102 | | nsSocketTransport *mTransport; |
103 | | ThreadSafeAutoRefCnt mWriterRefCnt; |
104 | | |
105 | | // access to these is protected by mTransport->mLock |
106 | | nsresult mCondition; |
107 | | nsCOMPtr<nsIOutputStreamCallback> mCallback; |
108 | | uint32_t mCallbackFlags; |
109 | | uint64_t mByteCount; |
110 | | }; |
111 | | |
112 | | //----------------------------------------------------------------------------- |
113 | | |
114 | | class nsSocketTransport final : public nsASocketHandler |
115 | | , public nsISocketTransport |
116 | | , public nsIDNSListener |
117 | | , public nsIClassInfo |
118 | | , public nsIInterfaceRequestor |
119 | | { |
120 | | public: |
121 | | NS_DECL_THREADSAFE_ISUPPORTS |
122 | | NS_DECL_NSITRANSPORT |
123 | | NS_DECL_NSISOCKETTRANSPORT |
124 | | NS_DECL_NSIDNSLISTENER |
125 | | NS_DECL_NSICLASSINFO |
126 | | NS_DECL_NSIINTERFACEREQUESTOR |
127 | | |
128 | | nsSocketTransport(); |
129 | | |
130 | | // this method instructs the socket transport to open a socket of the |
131 | | // given type(s) to the given host or proxy. |
132 | | nsresult Init(const char **socketTypes, uint32_t typeCount, |
133 | | const nsACString &host, uint16_t port, |
134 | | const nsACString &hostRoute, uint16_t portRoute, |
135 | | nsIProxyInfo *proxyInfo); |
136 | | |
137 | | // this method instructs the socket transport to use an already connected |
138 | | // socket with the given address. |
139 | | nsresult InitWithConnectedSocket(PRFileDesc *socketFD, |
140 | | const NetAddr *addr); |
141 | | |
142 | | // this method instructs the socket transport to use an already connected |
143 | | // socket with the given address, and additionally supplies security info. |
144 | | nsresult InitWithConnectedSocket(PRFileDesc* aSocketFD, |
145 | | const NetAddr* aAddr, |
146 | | nsISupports* aSecInfo); |
147 | | |
148 | | #ifdef XP_UNIX |
149 | | // This method instructs the socket transport to open a socket |
150 | | // connected to the given Unix domain address. We can only create |
151 | | // unlayered, simple, stream sockets. |
152 | | nsresult InitWithFilename(const char *filename); |
153 | | |
154 | | // This method instructs the socket transport to open a socket |
155 | | // connected to the given Unix domain address that includes abstract |
156 | | // socket address. If using abstract socket address, first character of |
157 | | // name parameter has to be \0. |
158 | | // We can only create unlayered, simple, stream sockets. |
159 | | nsresult InitWithName(const char *name, size_t len); |
160 | | #endif |
161 | | |
162 | | // nsASocketHandler methods: |
163 | | void OnSocketReady(PRFileDesc *, int16_t outFlags) override; |
164 | | void OnSocketDetached(PRFileDesc *) override; |
165 | | void IsLocal(bool *aIsLocal) override; |
166 | | void OnKeepaliveEnabledPrefChange(bool aEnabled) final; |
167 | | |
168 | | // called when a socket event is handled |
169 | | void OnSocketEvent(uint32_t type, nsresult status, nsISupports *param); |
170 | | |
171 | 0 | uint64_t ByteCountReceived() override { return mInput.ByteCount(); } |
172 | 0 | uint64_t ByteCountSent() override { return mOutput.ByteCount(); } |
173 | | static void CloseSocket(PRFileDesc *aFd, bool aTelemetryEnabled); |
174 | | static void SendPRBlockingTelemetry(PRIntervalTime aStart, |
175 | | Telemetry::HistogramID aIDNormal, |
176 | | Telemetry::HistogramID aIDShutdown, |
177 | | Telemetry::HistogramID aIDConnectivityChange, |
178 | | Telemetry::HistogramID aIDLinkChange, |
179 | | Telemetry::HistogramID aIDOffline); |
180 | | protected: |
181 | | |
182 | | virtual ~nsSocketTransport(); |
183 | | void CleanupTypes(); |
184 | | |
185 | | private: |
186 | | |
187 | | // event types |
188 | | enum { |
189 | | MSG_ENSURE_CONNECT, |
190 | | MSG_DNS_LOOKUP_COMPLETE, |
191 | | MSG_RETRY_INIT_SOCKET, |
192 | | MSG_TIMEOUT_CHANGED, |
193 | | MSG_INPUT_CLOSED, |
194 | | MSG_INPUT_PENDING, |
195 | | MSG_OUTPUT_CLOSED, |
196 | | MSG_OUTPUT_PENDING |
197 | | }; |
198 | | nsresult PostEvent(uint32_t type, nsresult status = NS_OK, nsISupports *param = nullptr); |
199 | | |
200 | | enum { |
201 | | STATE_CLOSED, |
202 | | STATE_IDLE, |
203 | | STATE_RESOLVING, |
204 | | STATE_CONNECTING, |
205 | | STATE_TRANSFERRING |
206 | | }; |
207 | | |
208 | | // Safer way to get and automatically release PRFileDesc objects. |
209 | | class MOZ_STACK_CLASS PRFileDescAutoLock |
210 | | { |
211 | | public: |
212 | | explicit PRFileDescAutoLock(nsSocketTransport *aSocketTransport, |
213 | | bool aAlsoDuringFastOpen, |
214 | | nsresult *aConditionWhileLocked = nullptr) |
215 | | : mSocketTransport(aSocketTransport) |
216 | | , mFd(nullptr) |
217 | 0 | { |
218 | 0 | MOZ_ASSERT(aSocketTransport); |
219 | 0 | MutexAutoLock lock(mSocketTransport->mLock); |
220 | 0 | if (aConditionWhileLocked) { |
221 | 0 | *aConditionWhileLocked = mSocketTransport->mCondition; |
222 | 0 | if (NS_FAILED(mSocketTransport->mCondition)) { |
223 | 0 | return; |
224 | 0 | } |
225 | 0 | } |
226 | 0 | if (!aAlsoDuringFastOpen) { |
227 | 0 | mFd = mSocketTransport->GetFD_Locked(); |
228 | 0 | } else { |
229 | 0 | mFd = mSocketTransport->GetFD_LockedAlsoDuringFastOpen(); |
230 | 0 | } |
231 | 0 | } |
232 | 0 | ~PRFileDescAutoLock() { |
233 | 0 | MutexAutoLock lock(mSocketTransport->mLock); |
234 | 0 | if (mFd) { |
235 | 0 | mSocketTransport->ReleaseFD_Locked(mFd); |
236 | 0 | } |
237 | 0 | } |
238 | 0 | bool IsInitialized() { |
239 | 0 | return mFd; |
240 | 0 | } |
241 | 0 | operator PRFileDesc*() { |
242 | 0 | return mFd; |
243 | 0 | } |
244 | | nsresult SetKeepaliveEnabled(bool aEnable); |
245 | | nsresult SetKeepaliveVals(bool aEnabled, int aIdleTime, |
246 | | int aRetryInterval, int aProbeCount); |
247 | | private: |
248 | 0 | operator PRFileDescAutoLock*() { return nullptr; } |
249 | | |
250 | | // Weak ptr to nsSocketTransport since this is a stack class only. |
251 | | nsSocketTransport *mSocketTransport; |
252 | | PRFileDesc *mFd; |
253 | | }; |
254 | | friend class PRFileDescAutoLock; |
255 | | |
256 | | class LockedPRFileDesc |
257 | | { |
258 | | public: |
259 | | explicit LockedPRFileDesc(nsSocketTransport *aSocketTransport) |
260 | | : mSocketTransport(aSocketTransport) |
261 | | , mFd(nullptr) |
262 | 0 | { |
263 | 0 | MOZ_ASSERT(aSocketTransport); |
264 | 0 | } |
265 | | ~LockedPRFileDesc() = default; |
266 | 0 | bool IsInitialized() { |
267 | 0 | return mFd; |
268 | 0 | } |
269 | 0 | LockedPRFileDesc& operator=(PRFileDesc *aFd) { |
270 | 0 | mSocketTransport->mLock.AssertCurrentThreadOwns(); |
271 | 0 | mFd = aFd; |
272 | 0 | return *this; |
273 | 0 | } |
274 | 0 | operator PRFileDesc*() { |
275 | 0 | if (mSocketTransport->mAttached) { |
276 | 0 | mSocketTransport->mLock.AssertCurrentThreadOwns(); |
277 | 0 | } |
278 | 0 | return mFd; |
279 | 0 | } |
280 | 0 | bool operator==(PRFileDesc *aFd) { |
281 | 0 | mSocketTransport->mLock.AssertCurrentThreadOwns(); |
282 | 0 | return mFd == aFd; |
283 | 0 | } |
284 | | private: |
285 | 0 | operator LockedPRFileDesc*() { return nullptr; } |
286 | | // Weak ptr to nsSocketTransport since it owns this class. |
287 | | nsSocketTransport *mSocketTransport; |
288 | | PRFileDesc *mFd; |
289 | | }; |
290 | | friend class LockedPRFileDesc; |
291 | | |
292 | | //------------------------------------------------------------------------- |
293 | | // these members are "set" at initialization time and are never modified |
294 | | // afterwards. this allows them to be safely accessed from any thread. |
295 | | //------------------------------------------------------------------------- |
296 | | |
297 | | // socket type info: |
298 | | char **mTypes; |
299 | | uint32_t mTypeCount; |
300 | | nsCString mHost; |
301 | | nsCString mProxyHost; |
302 | | nsCString mOriginHost; |
303 | | uint16_t mPort; |
304 | | nsCOMPtr<nsIProxyInfo> mProxyInfo; |
305 | | uint16_t mProxyPort; |
306 | | uint16_t mOriginPort; |
307 | | bool mProxyTransparent; |
308 | | bool mProxyTransparentResolvesHost; |
309 | | bool mHttpsProxy; |
310 | | uint32_t mConnectionFlags; |
311 | | // When we fail to connect using a prefered IP family, we tell the consumer to reset |
312 | | // the IP family preference on the connection entry. |
313 | | bool mResetFamilyPreference; |
314 | | uint32_t mTlsFlags; |
315 | | bool mReuseAddrPort; |
316 | | |
317 | | // The origin attributes are used to create sockets. The first party domain |
318 | | // will eventually be used to isolate OCSP cache and is only non-empty when |
319 | | // "privacy.firstparty.isolate" is enabled. Setting this is the only way to |
320 | | // carry origin attributes down to NSPR layers which are final consumers. |
321 | | // It must be set before the socket transport is built. |
322 | | OriginAttributes mOriginAttributes; |
323 | | |
324 | 0 | uint16_t SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; } |
325 | 0 | const nsCString &SocketHost() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; } |
326 | | |
327 | | //------------------------------------------------------------------------- |
328 | | // members accessible only on the socket transport thread: |
329 | | // (the exception being initialization/shutdown time) |
330 | | //------------------------------------------------------------------------- |
331 | | |
332 | | // socket state vars: |
333 | | uint32_t mState; // STATE_??? flags |
334 | | bool mAttached; |
335 | | bool mInputClosed; |
336 | | bool mOutputClosed; |
337 | | |
338 | | // this flag is used to determine if the results of a host lookup arrive |
339 | | // recursively or not. this flag is not protected by any lock. |
340 | | bool mResolving; |
341 | | |
342 | | nsCOMPtr<nsICancelable> mDNSRequest; |
343 | | nsCOMPtr<nsIDNSRecord> mDNSRecord; |
344 | | |
345 | | nsresult mDNSLookupStatus; |
346 | | PRIntervalTime mDNSARequestFinished; |
347 | | nsCOMPtr<nsICancelable> mDNSTxtRequest; |
348 | | nsCString mDNSRecordTxt; |
349 | | bool mEsniQueried; |
350 | | bool mEsniUsed; |
351 | | |
352 | | // mNetAddr/mSelfAddr is valid from GetPeerAddr()/GetSelfAddr() once we have |
353 | | // reached STATE_TRANSFERRING. It must not change after that. |
354 | | void SetSocketName(PRFileDesc *fd); |
355 | | NetAddr mNetAddr; |
356 | | NetAddr mSelfAddr; // getsockname() |
357 | | Atomic<bool, Relaxed> mNetAddrIsSet; |
358 | | Atomic<bool, Relaxed> mSelfAddrIsSet; |
359 | | |
360 | | nsAutoPtr<NetAddr> mBindAddr; |
361 | | |
362 | | // socket methods (these can only be called on the socket thread): |
363 | | |
364 | | void SendStatus(nsresult status); |
365 | | nsresult ResolveHost(); |
366 | | nsresult BuildSocket(PRFileDesc *&, bool &, bool &); |
367 | | nsresult InitiateSocket(); |
368 | | bool RecoverFromError(); |
369 | | |
370 | | void OnMsgInputPending() |
371 | 0 | { |
372 | 0 | if (mState == STATE_TRANSFERRING) |
373 | 0 | mPollFlags |= (PR_POLL_READ | PR_POLL_EXCEPT); |
374 | 0 | } |
375 | | void OnMsgOutputPending() |
376 | 0 | { |
377 | 0 | if (mState == STATE_TRANSFERRING) |
378 | 0 | mPollFlags |= (PR_POLL_WRITE | PR_POLL_EXCEPT); |
379 | 0 | } |
380 | | void OnMsgInputClosed(nsresult reason); |
381 | | void OnMsgOutputClosed(nsresult reason); |
382 | | |
383 | | // called when the socket is connected |
384 | | void OnSocketConnected(); |
385 | | |
386 | | //------------------------------------------------------------------------- |
387 | | // socket input/output objects. these may be accessed on any thread with |
388 | | // the exception of some specific methods (XXX). |
389 | | |
390 | | Mutex mLock; // protects members in this section. |
391 | | LockedPRFileDesc mFD; |
392 | | nsrefcnt mFDref; // mFD is closed when mFDref goes to zero. |
393 | | bool mFDconnected; // mFD is available to consumer when TRUE. |
394 | | bool mFDFastOpenInProgress; // Fast Open is in progress, so |
395 | | // socket available for some |
396 | | // operations. |
397 | | |
398 | | // A delete protector reference to gSocketTransportService held for lifetime |
399 | | // of 'this'. Sometimes used interchangably with gSocketTransportService due |
400 | | // to scoping. |
401 | | RefPtr<nsSocketTransportService> mSocketTransportService; |
402 | | |
403 | | nsCOMPtr<nsIInterfaceRequestor> mCallbacks; |
404 | | nsCOMPtr<nsITransportEventSink> mEventSink; |
405 | | nsCOMPtr<nsISupports> mSecInfo; |
406 | | |
407 | | nsSocketInputStream mInput; |
408 | | nsSocketOutputStream mOutput; |
409 | | |
410 | | friend class nsSocketInputStream; |
411 | | friend class nsSocketOutputStream; |
412 | | |
413 | | // socket timeouts are not protected by any lock. |
414 | | uint16_t mTimeouts[2]; |
415 | | |
416 | | // QoS setting for socket |
417 | | uint8_t mQoSBits; |
418 | | |
419 | | // |
420 | | // mFD access methods: called with mLock held. |
421 | | // |
422 | | PRFileDesc *GetFD_Locked(); |
423 | | PRFileDesc *GetFD_LockedAlsoDuringFastOpen(); |
424 | | void ReleaseFD_Locked(PRFileDesc *fd); |
425 | | bool FastOpenInProgress(); |
426 | | |
427 | | // |
428 | | // stream state changes (called outside mLock): |
429 | | // |
430 | | void OnInputClosed(nsresult reason) |
431 | 0 | { |
432 | 0 | // no need to post an event if called on the socket thread |
433 | 0 | if (OnSocketThread()) |
434 | 0 | OnMsgInputClosed(reason); |
435 | 0 | else |
436 | 0 | PostEvent(MSG_INPUT_CLOSED, reason); |
437 | 0 | } |
438 | | void OnInputPending() |
439 | 0 | { |
440 | 0 | // no need to post an event if called on the socket thread |
441 | 0 | if (OnSocketThread()) |
442 | 0 | OnMsgInputPending(); |
443 | 0 | else |
444 | 0 | PostEvent(MSG_INPUT_PENDING); |
445 | 0 | } |
446 | | void OnOutputClosed(nsresult reason) |
447 | 0 | { |
448 | 0 | // no need to post an event if called on the socket thread |
449 | 0 | if (OnSocketThread()) |
450 | 0 | OnMsgOutputClosed(reason); // XXX need to not be inside lock! |
451 | 0 | else |
452 | 0 | PostEvent(MSG_OUTPUT_CLOSED, reason); |
453 | 0 | } |
454 | | void OnOutputPending() |
455 | 0 | { |
456 | 0 | // no need to post an event if called on the socket thread |
457 | 0 | if (OnSocketThread()) |
458 | 0 | OnMsgOutputPending(); |
459 | 0 | else |
460 | 0 | PostEvent(MSG_OUTPUT_PENDING); |
461 | 0 | } |
462 | | |
463 | | #ifdef ENABLE_SOCKET_TRACING |
464 | | void TraceInBuf(const char *buf, int32_t n); |
465 | | void TraceOutBuf(const char *buf, int32_t n); |
466 | | #endif |
467 | | |
468 | | // Reads prefs to get default keepalive config. |
469 | | nsresult EnsureKeepaliveValsAreInitialized(); |
470 | | |
471 | | // Groups calls to fd.SetKeepaliveEnabled and fd.SetKeepaliveVals. |
472 | | nsresult SetKeepaliveEnabledInternal(bool aEnable); |
473 | | |
474 | | // True if keepalive has been enabled by the socket owner. Note: Keepalive |
475 | | // must also be enabled globally for it to be enabled in TCP. |
476 | | bool mKeepaliveEnabled; |
477 | | |
478 | | // Keepalive config (support varies by platform). |
479 | | int32_t mKeepaliveIdleTimeS; |
480 | | int32_t mKeepaliveRetryIntervalS; |
481 | | int32_t mKeepaliveProbeCount; |
482 | | |
483 | | // A Fast Open callback. |
484 | | TCPFastOpen *mFastOpenCallback; |
485 | | bool mFastOpenLayerHasBufferedData; |
486 | | uint8_t mFastOpenStatus; |
487 | | nsresult mFirstRetryError; |
488 | | |
489 | | bool mDoNotRetryToConnect; |
490 | | }; |
491 | | |
492 | | } // namespace net |
493 | | } // namespace mozilla |
494 | | |
495 | | #endif // !nsSocketTransport_h__ |