1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 et cindent: */
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 #include "nsSocketTransport2.h"
8 
9 #include "mozilla/Attributes.h"
10 #include "mozilla/SyncRunnable.h"
11 #include "mozilla/Telemetry.h"
12 #include "nsIOService.h"
13 #include "nsStreamUtils.h"
14 #include "nsNetSegmentUtils.h"
15 #include "nsNetAddr.h"
16 #include "nsTransportUtils.h"
17 #include "nsProxyInfo.h"
18 #include "nsNetCID.h"
19 #include "nsNetUtil.h"
20 #include "nsCOMPtr.h"
21 #include "plstr.h"
22 #include "prerr.h"
23 #include "IOActivityMonitor.h"
24 #include "NSSErrorsService.h"
25 #include "mozilla/dom/ToJSValue.h"
26 #include "mozilla/net/NeckoChild.h"
27 #include "nsThreadUtils.h"
28 #include "nsSocketProviderService.h"
29 #include "nsISocketProvider.h"
30 #include "nsISSLSocketControl.h"
31 #include "nsIPipe.h"
32 #include "nsIClassInfoImpl.h"
33 #include "nsURLHelper.h"
34 #include "nsIDNSService.h"
35 #include "nsIDNSRecord.h"
36 #include "nsIDNSByTypeRecord.h"
37 #include "nsICancelable.h"
38 #include "NetworkDataCountLayer.h"
39 #include "QuicSocketControl.h"
40 #include <algorithm>
41 #include "sslexp.h"
42 #include "mozilla/net/SSLTokensCache.h"
43 #include "mozilla/StaticPrefs_network.h"
44 
45 #include "nsPrintfCString.h"
46 #include "xpcpublic.h"
47 
48 #if defined(FUZZING)
49 #  include "FuzzyLayer.h"
50 #  include "FuzzySecurityInfo.h"
51 #  include "mozilla/StaticPrefs_fuzzing.h"
52 #endif
53 
54 #if defined(XP_WIN)
55 #  include "ShutdownLayer.h"
56 #endif
57 
58 /* Following inclusions required for keepalive config not supported by NSPR. */
59 #include "private/pprio.h"
60 #if defined(XP_WIN)
61 #  include <winsock2.h>
62 #  include <mstcpip.h>
63 #elif defined(XP_UNIX)
64 #  include <errno.h>
65 #  include <netinet/tcp.h>
66 #endif
67 /* End keepalive config inclusions. */
68 
69 #define SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 0
70 #define UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 1
71 #define SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 2
72 #define UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 3
73 
74 //-----------------------------------------------------------------------------
75 
76 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
77 
78 //-----------------------------------------------------------------------------
79 
80 namespace mozilla {
81 namespace net {
82 
83 class nsSocketEvent : public Runnable {
84  public:
nsSocketEvent(nsSocketTransport * transport,uint32_t type,nsresult status=NS_OK,nsISupports * param=nullptr)85   nsSocketEvent(nsSocketTransport* transport, uint32_t type,
86                 nsresult status = NS_OK, nsISupports* param = nullptr)
87       : Runnable("net::nsSocketEvent"),
88         mTransport(transport),
89         mType(type),
90         mStatus(status),
91         mParam(param) {}
92 
Run()93   NS_IMETHOD Run() override {
94     mTransport->OnSocketEvent(mType, mStatus, mParam);
95     return NS_OK;
96   }
97 
98  private:
99   RefPtr<nsSocketTransport> mTransport;
100 
101   uint32_t mType;
102   nsresult mStatus;
103   nsCOMPtr<nsISupports> mParam;
104 };
105 
106 //-----------------------------------------------------------------------------
107 
108 //#define TEST_CONNECT_ERRORS
109 #ifdef TEST_CONNECT_ERRORS
110 #  include <stdlib.h>
RandomizeConnectError(PRErrorCode code)111 static PRErrorCode RandomizeConnectError(PRErrorCode code) {
112   //
113   // To test out these errors, load http://www.yahoo.com/.  It should load
114   // correctly despite the random occurrence of these errors.
115   //
116   int n = rand();
117   if (n > RAND_MAX / 2) {
118     struct {
119       PRErrorCode err_code;
120       const char* err_name;
121     } errors[] = {
122         //
123         // These errors should be recoverable provided there is another
124         // IP address in mDNSRecord.
125         //
126         {PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"},
127         {PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"},
128         //
129         // This error will cause this socket transport to error out;
130         // however, if the consumer is HTTP, then the HTTP transaction
131         // should be restarted when this error occurs.
132         //
133         {PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"},
134     };
135     n = n % (sizeof(errors) / sizeof(errors[0]));
136     code = errors[n].err_code;
137     SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
138   }
139   return code;
140 }
141 #endif
142 
143 //-----------------------------------------------------------------------------
144 
ErrorAccordingToNSPR(PRErrorCode errorCode)145 nsresult ErrorAccordingToNSPR(PRErrorCode errorCode) {
146   nsresult rv = NS_ERROR_FAILURE;
147   switch (errorCode) {
148     case PR_WOULD_BLOCK_ERROR:
149       rv = NS_BASE_STREAM_WOULD_BLOCK;
150       break;
151     case PR_CONNECT_ABORTED_ERROR:
152     case PR_CONNECT_RESET_ERROR:
153       rv = NS_ERROR_NET_RESET;
154       break;
155     case PR_END_OF_FILE_ERROR:  // XXX document this correlation
156       rv = NS_ERROR_NET_INTERRUPT;
157       break;
158     case PR_CONNECT_REFUSED_ERROR:
159     // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
160     // could get better diagnostics by adding distinct XPCOM error codes for
161     // each of these, but there are a lot of places in Gecko that check
162     // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
163     // be checked.
164     case PR_NETWORK_UNREACHABLE_ERROR:
165     case PR_HOST_UNREACHABLE_ERROR:
166     case PR_ADDRESS_NOT_AVAILABLE_ERROR:
167     // Treat EACCES as a soft error since (at least on Linux) connect() returns
168     // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
169     case PR_NO_ACCESS_RIGHTS_ERROR:
170       rv = NS_ERROR_CONNECTION_REFUSED;
171       break;
172     case PR_ADDRESS_NOT_SUPPORTED_ERROR:
173       rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
174       break;
175     case PR_IO_TIMEOUT_ERROR:
176     case PR_CONNECT_TIMEOUT_ERROR:
177       rv = NS_ERROR_NET_TIMEOUT;
178       break;
179     case PR_OUT_OF_MEMORY_ERROR:
180     // These really indicate that the descriptor table filled up, or that the
181     // kernel ran out of network buffers - but nobody really cares which part of
182     // the system ran out of memory.
183     case PR_PROC_DESC_TABLE_FULL_ERROR:
184     case PR_SYS_DESC_TABLE_FULL_ERROR:
185     case PR_INSUFFICIENT_RESOURCES_ERROR:
186       rv = NS_ERROR_OUT_OF_MEMORY;
187       break;
188     case PR_ADDRESS_IN_USE_ERROR:
189       rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
190       break;
191     // These filename-related errors can arise when using Unix-domain sockets.
192     case PR_FILE_NOT_FOUND_ERROR:
193       rv = NS_ERROR_FILE_NOT_FOUND;
194       break;
195     case PR_IS_DIRECTORY_ERROR:
196       rv = NS_ERROR_FILE_IS_DIRECTORY;
197       break;
198     case PR_LOOP_ERROR:
199       rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
200       break;
201     case PR_NAME_TOO_LONG_ERROR:
202       rv = NS_ERROR_FILE_NAME_TOO_LONG;
203       break;
204     case PR_NO_DEVICE_SPACE_ERROR:
205       rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
206       break;
207     case PR_NOT_DIRECTORY_ERROR:
208       rv = NS_ERROR_FILE_NOT_DIRECTORY;
209       break;
210     case PR_READ_ONLY_FILESYSTEM_ERROR:
211       rv = NS_ERROR_FILE_READ_ONLY;
212       break;
213     case PR_BAD_ADDRESS_ERROR:
214       rv = NS_ERROR_UNKNOWN_HOST;
215       break;
216     default:
217       if (psm::IsNSSErrorCode(errorCode)) {
218         rv = psm::GetXPCOMFromNSSError(errorCode);
219       }
220       break;
221 
222       // NSPR's socket code can return these, but they're not worth breaking out
223       // into their own error codes, distinct from NS_ERROR_FAILURE:
224       //
225       // PR_BAD_DESCRIPTOR_ERROR
226       // PR_INVALID_ARGUMENT_ERROR
227       // PR_NOT_SOCKET_ERROR
228       // PR_NOT_TCP_SOCKET_ERROR
229       //   These would indicate a bug internal to the component.
230       //
231       // PR_PROTOCOL_NOT_SUPPORTED_ERROR
232       //   This means that we can't use the given "protocol" (like
233       //   IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
234       //   above, this indicates an internal bug.
235       //
236       // PR_IS_CONNECTED_ERROR
237       //   This indicates that we've applied a system call like 'bind' or
238       //   'connect' to a socket that is already connected. The socket
239       //   components manage each file descriptor's state, and in some cases
240       //   handle this error result internally. We shouldn't be returning
241       //   this to our callers.
242       //
243       // PR_IO_ERROR
244       //   This is so vague that NS_ERROR_FAILURE is just as good.
245   }
246   SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%" PRIx32 "]\n", errorCode,
247               static_cast<uint32_t>(rv)));
248   return rv;
249 }
250 
251 //-----------------------------------------------------------------------------
252 // socket input stream impl
253 //-----------------------------------------------------------------------------
254 
nsSocketInputStream(nsSocketTransport * trans)255 nsSocketInputStream::nsSocketInputStream(nsSocketTransport* trans)
256     : mTransport(trans) {}
257 
258 // called on the socket transport thread...
259 //
260 //   condition : failure code if socket has been closed
261 //
OnSocketReady(nsresult condition)262 void nsSocketInputStream::OnSocketReady(nsresult condition) {
263   SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%" PRIx32 "]\n",
264               this, static_cast<uint32_t>(condition)));
265 
266   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
267 
268   nsCOMPtr<nsIInputStreamCallback> callback;
269   {
270     MutexAutoLock lock(mTransport->mLock);
271 
272     // update condition, but be careful not to erase an already
273     // existing error condition.
274     if (NS_SUCCEEDED(mCondition)) mCondition = condition;
275 
276     // ignore event if only waiting for closure and not closed.
277     if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
278       callback = std::move(mCallback);
279       mCallbackFlags = 0;
280     }
281   }
282 
283   if (callback) callback->OnInputStreamReady(this);
284 }
285 
NS_IMPL_QUERY_INTERFACE(nsSocketInputStream,nsIInputStream,nsIAsyncInputStream)286 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream, nsIInputStream,
287                         nsIAsyncInputStream)
288 
289 NS_IMETHODIMP_(MozExternalRefCountType)
290 nsSocketInputStream::AddRef() {
291   ++mReaderRefCnt;
292   return mTransport->AddRef();
293 }
294 
NS_IMETHODIMP_(MozExternalRefCountType)295 NS_IMETHODIMP_(MozExternalRefCountType)
296 nsSocketInputStream::Release() {
297   if (--mReaderRefCnt == 0) Close();
298   return mTransport->Release();
299 }
300 
301 NS_IMETHODIMP
Close()302 nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
303 
304 NS_IMETHODIMP
Available(uint64_t * avail)305 nsSocketInputStream::Available(uint64_t* avail) {
306   SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
307 
308   *avail = 0;
309 
310   PRFileDesc* fd;
311   {
312     MutexAutoLock lock(mTransport->mLock);
313 
314     if (NS_FAILED(mCondition)) return mCondition;
315 
316     fd = mTransport->GetFD_Locked();
317     if (!fd) return NS_OK;
318   }
319 
320   // cannot hold lock while calling NSPR.  (worried about the fact that PSM
321   // synchronously proxies notifications over to the UI thread, which could
322   // mistakenly try to re-enter this code.)
323   int32_t n = PR_Available(fd);
324 
325   // PSM does not implement PR_Available() so do a best approximation of it
326   // with MSG_PEEK
327   if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
328     char c;
329 
330     n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
331     SOCKET_LOG(
332         ("nsSocketInputStream::Available [this=%p] "
333          "using PEEK backup n=%d]\n",
334          this, n));
335   }
336 
337   nsresult rv;
338   {
339     MutexAutoLock lock(mTransport->mLock);
340 
341     mTransport->ReleaseFD_Locked(fd);
342 
343     if (n >= 0) {
344       *avail = n;
345     } else {
346       PRErrorCode code = PR_GetError();
347       if (code == PR_WOULD_BLOCK_ERROR) return NS_OK;
348       mCondition = ErrorAccordingToNSPR(code);
349     }
350     rv = mCondition;
351   }
352   if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
353   return rv;
354 }
355 
356 NS_IMETHODIMP
Read(char * buf,uint32_t count,uint32_t * countRead)357 nsSocketInputStream::Read(char* buf, uint32_t count, uint32_t* countRead) {
358   SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
359 
360   *countRead = 0;
361 
362   PRFileDesc* fd = nullptr;
363   {
364     MutexAutoLock lock(mTransport->mLock);
365 
366     if (NS_FAILED(mCondition)) {
367       return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
368     }
369 
370     fd = mTransport->GetFD_Locked();
371     if (!fd) return NS_BASE_STREAM_WOULD_BLOCK;
372   }
373 
374   SOCKET_LOG(("  calling PR_Read [count=%u]\n", count));
375 
376   // cannot hold lock while calling NSPR.  (worried about the fact that PSM
377   // synchronously proxies notifications over to the UI thread, which could
378   // mistakenly try to re-enter this code.)
379   int32_t n = PR_Read(fd, buf, count);
380 
381   SOCKET_LOG(("  PR_Read returned [n=%d]\n", n));
382 
383   nsresult rv = NS_OK;
384   {
385     MutexAutoLock lock(mTransport->mLock);
386 
387 #ifdef ENABLE_SOCKET_TRACING
388     if (n > 0) mTransport->TraceInBuf(buf, n);
389 #endif
390 
391     mTransport->ReleaseFD_Locked(fd);
392 
393     if (n > 0) {
394       mByteCount += (*countRead = n);
395     } else if (n < 0) {
396       PRErrorCode code = PR_GetError();
397       if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
398       mCondition = ErrorAccordingToNSPR(code);
399     }
400     rv = mCondition;
401   }
402   if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
403 
404   // only send this notification if we have indeed read some data.
405   // see bug 196827 for an example of why this is important.
406   if (n > 0) mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);
407   return rv;
408 }
409 
410 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun writer,void * closure,uint32_t count,uint32_t * countRead)411 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
412                                   uint32_t count, uint32_t* countRead) {
413   // socket stream is unbuffered
414   return NS_ERROR_NOT_IMPLEMENTED;
415 }
416 
417 NS_IMETHODIMP
IsNonBlocking(bool * nonblocking)418 nsSocketInputStream::IsNonBlocking(bool* nonblocking) {
419   *nonblocking = true;
420   return NS_OK;
421 }
422 
423 NS_IMETHODIMP
CloseWithStatus(nsresult reason)424 nsSocketInputStream::CloseWithStatus(nsresult reason) {
425   SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
426               "]\n",
427               this, static_cast<uint32_t>(reason)));
428 
429   // may be called from any thread
430 
431   nsresult rv;
432   {
433     MutexAutoLock lock(mTransport->mLock);
434 
435     if (NS_SUCCEEDED(mCondition)) {
436       rv = mCondition = reason;
437     } else {
438       rv = NS_OK;
439     }
440   }
441   if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
442   return NS_OK;
443 }
444 
445 NS_IMETHODIMP
AsyncWait(nsIInputStreamCallback * callback,uint32_t flags,uint32_t amount,nsIEventTarget * target)446 nsSocketInputStream::AsyncWait(nsIInputStreamCallback* callback, uint32_t flags,
447                                uint32_t amount, nsIEventTarget* target) {
448   SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
449 
450   bool hasError = false;
451   {
452     MutexAutoLock lock(mTransport->mLock);
453 
454     if (callback && target) {
455       //
456       // build event proxy
457       //
458       mCallback = NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
459                                               callback, target);
460     } else {
461       mCallback = callback;
462     }
463     mCallbackFlags = flags;
464 
465     hasError = NS_FAILED(mCondition);
466   }  // unlock mTransport->mLock
467 
468   if (hasError) {
469     // OnSocketEvent will call OnInputStreamReady with an error code after
470     // going through the event loop. We do this because most socket callers
471     // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
472     // callback.
473     mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
474   } else {
475     mTransport->OnInputPending();
476   }
477 
478   return NS_OK;
479 }
480 
481 //-----------------------------------------------------------------------------
482 // socket output stream impl
483 //-----------------------------------------------------------------------------
484 
nsSocketOutputStream(nsSocketTransport * trans)485 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport* trans)
486     : mTransport(trans) {}
487 
488 // called on the socket transport thread...
489 //
490 //   condition : failure code if socket has been closed
491 //
OnSocketReady(nsresult condition)492 void nsSocketOutputStream::OnSocketReady(nsresult condition) {
493   SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%" PRIx32
494               "]\n",
495               this, static_cast<uint32_t>(condition)));
496 
497   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
498 
499   nsCOMPtr<nsIOutputStreamCallback> callback;
500   {
501     MutexAutoLock lock(mTransport->mLock);
502 
503     // update condition, but be careful not to erase an already
504     // existing error condition.
505     if (NS_SUCCEEDED(mCondition)) mCondition = condition;
506 
507     // ignore event if only waiting for closure and not closed.
508     if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
509       callback = std::move(mCallback);
510       mCallbackFlags = 0;
511     }
512   }
513 
514   if (callback) callback->OnOutputStreamReady(this);
515 }
516 
NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream,nsIOutputStream,nsIAsyncOutputStream)517 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream, nsIOutputStream,
518                         nsIAsyncOutputStream)
519 
520 NS_IMETHODIMP_(MozExternalRefCountType)
521 nsSocketOutputStream::AddRef() {
522   ++mWriterRefCnt;
523   return mTransport->AddRef();
524 }
525 
NS_IMETHODIMP_(MozExternalRefCountType)526 NS_IMETHODIMP_(MozExternalRefCountType)
527 nsSocketOutputStream::Release() {
528   if (--mWriterRefCnt == 0) Close();
529   return mTransport->Release();
530 }
531 
532 NS_IMETHODIMP
Close()533 nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
534 
535 NS_IMETHODIMP
Flush()536 nsSocketOutputStream::Flush() { return NS_OK; }
537 
538 NS_IMETHODIMP
Write(const char * buf,uint32_t count,uint32_t * countWritten)539 nsSocketOutputStream::Write(const char* buf, uint32_t count,
540                             uint32_t* countWritten) {
541   SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count));
542 
543   *countWritten = 0;
544 
545   // A write of 0 bytes can be used to force the initial SSL handshake, so do
546   // not reject that.
547 
548   PRFileDesc* fd = nullptr;
549   {
550     MutexAutoLock lock(mTransport->mLock);
551 
552     if (NS_FAILED(mCondition)) return mCondition;
553 
554     fd = mTransport->GetFD_Locked();
555     if (!fd) return NS_BASE_STREAM_WOULD_BLOCK;
556   }
557 
558   SOCKET_LOG(("  calling PR_Write [count=%u]\n", count));
559 
560   // cannot hold lock while calling NSPR.  (worried about the fact that PSM
561   // synchronously proxies notifications over to the UI thread, which could
562   // mistakenly try to re-enter this code.)
563   int32_t n = PR_Write(fd, buf, count);
564 
565   SOCKET_LOG(("  PR_Write returned [n=%d]\n", n));
566 
567   nsresult rv = NS_OK;
568   {
569     MutexAutoLock lock(mTransport->mLock);
570 
571 #ifdef ENABLE_SOCKET_TRACING
572     if (n > 0) mTransport->TraceOutBuf(buf, n);
573 #endif
574 
575     mTransport->ReleaseFD_Locked(fd);
576 
577     if (n > 0) {
578       mByteCount += (*countWritten = n);
579     } else if (n < 0) {
580       PRErrorCode code = PR_GetError();
581       if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
582       mCondition = ErrorAccordingToNSPR(code);
583     }
584     rv = mCondition;
585   }
586   if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
587 
588   // only send this notification if we have indeed written some data.
589   // see bug 196827 for an example of why this is important.
590   if ((n > 0)) {
591     mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);
592   }
593 
594   return rv;
595 }
596 
597 NS_IMETHODIMP
WriteSegments(nsReadSegmentFun reader,void * closure,uint32_t count,uint32_t * countRead)598 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void* closure,
599                                     uint32_t count, uint32_t* countRead) {
600   // socket stream is unbuffered
601   return NS_ERROR_NOT_IMPLEMENTED;
602 }
603 
WriteFromSegments(nsIInputStream * input,void * closure,const char * fromSegment,uint32_t offset,uint32_t count,uint32_t * countRead)604 nsresult nsSocketOutputStream::WriteFromSegments(
605     nsIInputStream* input, void* closure, const char* fromSegment,
606     uint32_t offset, uint32_t count, uint32_t* countRead) {
607   nsSocketOutputStream* self = (nsSocketOutputStream*)closure;
608   return self->Write(fromSegment, count, countRead);
609 }
610 
611 NS_IMETHODIMP
WriteFrom(nsIInputStream * stream,uint32_t count,uint32_t * countRead)612 nsSocketOutputStream::WriteFrom(nsIInputStream* stream, uint32_t count,
613                                 uint32_t* countRead) {
614   return stream->ReadSegments(WriteFromSegments, this, count, countRead);
615 }
616 
617 NS_IMETHODIMP
IsNonBlocking(bool * nonblocking)618 nsSocketOutputStream::IsNonBlocking(bool* nonblocking) {
619   *nonblocking = true;
620   return NS_OK;
621 }
622 
623 NS_IMETHODIMP
CloseWithStatus(nsresult reason)624 nsSocketOutputStream::CloseWithStatus(nsresult reason) {
625   SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
626               "]\n",
627               this, static_cast<uint32_t>(reason)));
628 
629   // may be called from any thread
630 
631   nsresult rv;
632   {
633     MutexAutoLock lock(mTransport->mLock);
634 
635     if (NS_SUCCEEDED(mCondition)) {
636       rv = mCondition = reason;
637     } else {
638       rv = NS_OK;
639     }
640   }
641   if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
642   return NS_OK;
643 }
644 
645 NS_IMETHODIMP
AsyncWait(nsIOutputStreamCallback * callback,uint32_t flags,uint32_t amount,nsIEventTarget * target)646 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback* callback,
647                                 uint32_t flags, uint32_t amount,
648                                 nsIEventTarget* target) {
649   SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
650 
651   {
652     MutexAutoLock lock(mTransport->mLock);
653 
654     if (callback && target) {
655       //
656       // build event proxy
657       //
658       mCallback = NS_NewOutputStreamReadyEvent(callback, target);
659     } else {
660       mCallback = callback;
661     }
662 
663     mCallbackFlags = flags;
664   }
665   mTransport->OnOutputPending();
666   return NS_OK;
667 }
668 
669 //-----------------------------------------------------------------------------
670 // socket transport impl
671 //-----------------------------------------------------------------------------
672 
nsSocketTransport()673 nsSocketTransport::nsSocketTransport()
674     : mFD(this),
675       mSocketTransportService(gSocketTransportService),
676       mInput(this),
677       mOutput(this) {
678   SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
679 
680   mTimeouts[TIMEOUT_CONNECT] = UINT16_MAX;     // no timeout
681   mTimeouts[TIMEOUT_READ_WRITE] = UINT16_MAX;  // no timeout
682 }
683 
~nsSocketTransport()684 nsSocketTransport::~nsSocketTransport() {
685   SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
686 }
687 
Init(const nsTArray<nsCString> & types,const nsACString & host,uint16_t port,const nsACString & hostRoute,uint16_t portRoute,nsIProxyInfo * givenProxyInfo,nsIDNSRecord * dnsRecord)688 nsresult nsSocketTransport::Init(const nsTArray<nsCString>& types,
689                                  const nsACString& host, uint16_t port,
690                                  const nsACString& hostRoute,
691                                  uint16_t portRoute,
692                                  nsIProxyInfo* givenProxyInfo,
693                                  nsIDNSRecord* dnsRecord) {
694   nsCOMPtr<nsProxyInfo> proxyInfo;
695   if (givenProxyInfo) {
696     proxyInfo = do_QueryInterface(givenProxyInfo);
697     NS_ENSURE_ARG(proxyInfo);
698   }
699 
700   if (dnsRecord) {
701     mExternalDNSResolution = true;
702     mDNSRecord = do_QueryInterface(dnsRecord);
703   }
704 
705   // init socket type info
706 
707   mOriginHost = host;
708   mOriginPort = port;
709   if (!hostRoute.IsEmpty()) {
710     mHost = hostRoute;
711     mPort = portRoute;
712   } else {
713     mHost = host;
714     mPort = port;
715   }
716 
717   // A subtle check we don't enter this method more than once for the socket
718   // transport lifetime.  Disable on TSan builds to prevent race checking, we
719   // don't want an atomic here for perf reasons!
720 #ifndef MOZ_TSAN
721   MOZ_ASSERT(!mPortRemappingApplied);
722 #endif  // !MOZ_TSAN
723 
724   if (proxyInfo) {
725     mHttpsProxy = proxyInfo->IsHTTPS();
726   }
727 
728   const char* proxyType = nullptr;
729   mProxyInfo = proxyInfo;
730   if (proxyInfo) {
731     mProxyPort = proxyInfo->Port();
732     mProxyHost = proxyInfo->Host();
733     // grab proxy type (looking for "socks" for example)
734     proxyType = proxyInfo->Type();
735     if (proxyType && (proxyInfo->IsHTTP() || proxyInfo->IsHTTPS() ||
736                       proxyInfo->IsDirect() || !strcmp(proxyType, "unknown"))) {
737       proxyType = nullptr;
738     }
739   }
740 
741   SOCKET_LOG1(
742       ("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
743        "proxy=%s:%hu]\n",
744        this, mHost.get(), mPort, mOriginHost.get(), mOriginPort,
745        mProxyHost.get(), mProxyPort));
746 
747   // include proxy type as a socket type if proxy type is not "http"
748   uint32_t typeCount = types.Length() + (proxyType != nullptr);
749   if (!typeCount) return NS_OK;
750 
751   // if we have socket types, then the socket provider service had
752   // better exist!
753   nsresult rv;
754   nsCOMPtr<nsISocketProviderService> spserv =
755       nsSocketProviderService::GetOrCreate();
756 
757   if (!mTypes.SetCapacity(typeCount, fallible)) {
758     return NS_ERROR_OUT_OF_MEMORY;
759   }
760 
761   // now verify that each socket type has a registered socket provider.
762   for (uint32_t i = 0, type = 0; i < typeCount; ++i) {
763     // store socket types
764     if (i == 0 && proxyType) {
765       mTypes.AppendElement(proxyType);
766     } else {
767       mTypes.AppendElement(types[type++]);
768     }
769 
770     nsCOMPtr<nsISocketProvider> provider;
771     rv = spserv->GetSocketProvider(mTypes[i].get(), getter_AddRefs(provider));
772     if (NS_FAILED(rv)) {
773       NS_WARNING("no registered socket provider");
774       return rv;
775     }
776 
777     // note if socket type corresponds to a transparent proxy
778     // XXX don't hardcode SOCKS here (use proxy info's flags instead).
779     if (mTypes[i].EqualsLiteral("socks") || mTypes[i].EqualsLiteral("socks4")) {
780       mProxyTransparent = true;
781 
782       if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
783         // we want the SOCKS layer to send the hostname
784         // and port to the proxy and let it do the DNS.
785         mProxyTransparentResolvesHost = true;
786       }
787     }
788   }
789 
790   return NS_OK;
791 }
792 
793 #if defined(XP_UNIX)
InitWithFilename(const char * filename)794 nsresult nsSocketTransport::InitWithFilename(const char* filename) {
795   return InitWithName(filename, strlen(filename));
796 }
797 
InitWithName(const char * name,size_t length)798 nsresult nsSocketTransport::InitWithName(const char* name, size_t length) {
799   if (length > sizeof(mNetAddr.local.path) - 1) {
800     return NS_ERROR_FILE_NAME_TOO_LONG;
801   }
802 
803   if (!name[0] && length > 1) {
804     // name is abstract address name that is supported on Linux only
805 #  if defined(XP_LINUX)
806     mHost.Assign(name + 1, length - 1);
807 #  else
808     return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
809 #  endif
810   } else {
811     // The name isn't abstract socket address.  So this is Unix domain
812     // socket that has file path.
813     mHost.Assign(name, length);
814   }
815   mPort = 0;
816 
817   mNetAddr.local.family = AF_LOCAL;
818   memcpy(mNetAddr.local.path, name, length);
819   mNetAddr.local.path[length] = '\0';
820   mNetAddrIsSet = true;
821 
822   return NS_OK;
823 }
824 #endif
825 
InitWithConnectedSocket(PRFileDesc * fd,const NetAddr * addr)826 nsresult nsSocketTransport::InitWithConnectedSocket(PRFileDesc* fd,
827                                                     const NetAddr* addr) {
828   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
829   NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
830 
831   char buf[kNetAddrMaxCStrBufSize];
832   addr->ToStringBuffer(buf, sizeof(buf));
833   mHost.Assign(buf);
834 
835   uint16_t port;
836   if (addr->raw.family == AF_INET) {
837     port = addr->inet.port;
838   } else if (addr->raw.family == AF_INET6) {
839     port = addr->inet6.port;
840   } else {
841     port = 0;
842   }
843   mPort = ntohs(port);
844 
845   memcpy(&mNetAddr, addr, sizeof(NetAddr));
846 
847   mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
848   mState = STATE_TRANSFERRING;
849   SetSocketName(fd);
850   mNetAddrIsSet = true;
851 
852   {
853     MutexAutoLock lock(mLock);
854 
855     mFD = fd;
856     mFDref = 1;
857     mFDconnected = true;
858     mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
859   }
860 
861   // make sure new socket is non-blocking
862   PRSocketOptionData opt;
863   opt.option = PR_SockOpt_Nonblocking;
864   opt.value.non_blocking = true;
865   PR_SetSocketOption(fd, &opt);
866 
867   SOCKET_LOG(
868       ("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
869        this, mHost.get(), mPort));
870 
871   // jump to InitiateSocket to get ourselves attached to the STS poll list.
872   return PostEvent(MSG_RETRY_INIT_SOCKET);
873 }
874 
InitWithConnectedSocket(PRFileDesc * aFD,const NetAddr * aAddr,nsISupports * aSecInfo)875 nsresult nsSocketTransport::InitWithConnectedSocket(PRFileDesc* aFD,
876                                                     const NetAddr* aAddr,
877                                                     nsISupports* aSecInfo) {
878   mSecInfo = aSecInfo;
879   return InitWithConnectedSocket(aFD, aAddr);
880 }
881 
PostEvent(uint32_t type,nsresult status,nsISupports * param)882 nsresult nsSocketTransport::PostEvent(uint32_t type, nsresult status,
883                                       nsISupports* param) {
884   SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%" PRIx32
885               " param=%p]\n",
886               this, type, static_cast<uint32_t>(status), param));
887 
888   nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
889   if (!event) return NS_ERROR_OUT_OF_MEMORY;
890 
891   return mSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
892 }
893 
SendStatus(nsresult status)894 void nsSocketTransport::SendStatus(nsresult status) {
895   SOCKET_LOG1(("nsSocketTransport::SendStatus [this=%p status=%" PRIx32 "]\n",
896                this, static_cast<uint32_t>(status)));
897 
898   nsCOMPtr<nsITransportEventSink> sink;
899   uint64_t progress;
900   {
901     MutexAutoLock lock(mLock);
902     sink = mEventSink;
903     switch (status) {
904       case NS_NET_STATUS_SENDING_TO:
905         progress = mOutput.ByteCount();
906         break;
907       case NS_NET_STATUS_RECEIVING_FROM:
908         progress = mInput.ByteCount();
909         break;
910       default:
911         progress = 0;
912         break;
913     }
914   }
915   if (sink) {
916     sink->OnTransportStatus(this, status, progress, -1);
917   }
918 }
919 
ResolveHost()920 nsresult nsSocketTransport::ResolveHost() {
921   SOCKET_LOG((
922       "nsSocketTransport::ResolveHost [this=%p %s:%d%s] "
923       "mProxyTransparentResolvesHost=%d\n",
924       this, SocketHost().get(), SocketPort(),
925       mConnectionFlags & nsSocketTransport::BYPASS_CACHE ? " bypass cache" : "",
926       mProxyTransparentResolvesHost));
927 
928   nsresult rv;
929 
930   if (!mProxyHost.IsEmpty()) {
931     if (!mProxyTransparent || mProxyTransparentResolvesHost) {
932 #if defined(XP_UNIX)
933       MOZ_ASSERT(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
934                  "Unix domain sockets can't be used with proxies");
935 #endif
936       // When not resolving mHost locally, we still want to ensure that
937       // it only contains valid characters.  See bug 304904 for details.
938       // Sometimes the end host is not yet known and mHost is *
939       if (!net_IsValidHostName(mHost) && !mHost.EqualsLiteral("*")) {
940         SOCKET_LOG(("  invalid hostname %s\n", mHost.get()));
941         return NS_ERROR_UNKNOWN_HOST;
942       }
943     }
944     if (mProxyTransparentResolvesHost) {
945       // Name resolution is done on the server side.  Just pretend
946       // client resolution is complete, this will get picked up later.
947       // since we don't need to do DNS now, we bypass the resolving
948       // step by initializing mNetAddr to an empty address, but we
949       // must keep the port. The SOCKS IO layer will use the hostname
950       // we send it when it's created, rather than the empty address
951       // we send with the connect call.
952       mState = STATE_RESOLVING;
953       mNetAddr.raw.family = AF_INET;
954       mNetAddr.inet.port = htons(SocketPort());
955       mNetAddr.inet.ip = htonl(INADDR_ANY);
956       return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
957     }
958   }
959 
960   if (mExternalDNSResolution) {
961     MOZ_ASSERT(mDNSRecord);
962     mState = STATE_RESOLVING;
963     return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
964   }
965 
966   nsCOMPtr<nsIDNSService> dns = nullptr;
967   auto initTask = [&dns]() { dns = do_GetService(kDNSServiceCID); };
968   if (!NS_IsMainThread()) {
969     // Forward to the main thread synchronously.
970     RefPtr<nsIThread> mainThread = do_GetMainThread();
971     if (!mainThread) {
972       return NS_ERROR_FAILURE;
973     }
974 
975     SyncRunnable::DispatchToThread(
976         mainThread,
977         new SyncRunnable(NS_NewRunnableFunction(
978             "nsSocketTransport::ResolveHost->GetDNSService", initTask)));
979   } else {
980     initTask();
981   }
982   if (!dns) {
983     return NS_ERROR_FAILURE;
984   }
985 
986   mResolving = true;
987 
988   uint32_t dnsFlags = 0;
989   if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE) {
990     dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
991   }
992   if (mConnectionFlags & nsSocketTransport::REFRESH_CACHE) {
993     dnsFlags = nsIDNSService::RESOLVE_REFRESH_CACHE;
994   }
995   if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6) {
996     dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
997   }
998   if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4) {
999     dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
1000   }
1001   if (mConnectionFlags & nsSocketTransport::DISABLE_TRR) {
1002     dnsFlags |= nsIDNSService::RESOLVE_DISABLE_TRR;
1003   }
1004 
1005   if (mConnectionFlags & nsSocketTransport::USE_IP_HINT_ADDRESS) {
1006     dnsFlags |= nsIDNSService::RESOLVE_IP_HINT;
1007   }
1008 
1009   dnsFlags |= nsIDNSService::GetFlagsFromTRRMode(
1010       nsISocketTransport::GetTRRModeFromFlags(mConnectionFlags));
1011 
1012   // When we get here, we are not resolving using any configured proxy likely
1013   // because of individual proxy setting on the request or because the host is
1014   // excluded from proxying.  Hence, force resolution despite global proxy-DNS
1015   // configuration.
1016   dnsFlags |= nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS;
1017 
1018   NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
1019                    !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
1020                "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
1021 
1022   SendStatus(NS_NET_STATUS_RESOLVING_HOST);
1023 
1024   if (!SocketHost().Equals(mOriginHost)) {
1025     SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
1026                 mOriginHost.get(), SocketHost().get()));
1027   }
1028   rv =
1029       dns->AsyncResolveNative(SocketHost(), nsIDNSService::RESOLVE_TYPE_DEFAULT,
1030                               dnsFlags, nullptr, this, mSocketTransportService,
1031                               mOriginAttributes, getter_AddRefs(mDNSRequest));
1032 
1033   if (NS_SUCCEEDED(rv)) {
1034     SOCKET_LOG(("  advancing to STATE_RESOLVING\n"));
1035     mState = STATE_RESOLVING;
1036   }
1037   return rv;
1038 }
1039 
BuildSocket(PRFileDesc * & fd,bool & proxyTransparent,bool & usingSSL)1040 nsresult nsSocketTransport::BuildSocket(PRFileDesc*& fd, bool& proxyTransparent,
1041                                         bool& usingSSL) {
1042   SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1043 
1044   nsresult rv = NS_OK;
1045 
1046   proxyTransparent = false;
1047   usingSSL = false;
1048 
1049   if (mTypes.IsEmpty()) {
1050     fd = PR_OpenTCPSocket(mNetAddr.raw.family);
1051     if (!fd) {
1052       SOCKET_LOG(("  error creating TCP nspr socket [rv=%" PRIx32 "]\n",
1053                   static_cast<uint32_t>(rv)));
1054       return NS_ERROR_OUT_OF_MEMORY;
1055     }
1056     return NS_OK;
1057   }
1058 
1059 #if defined(XP_UNIX)
1060   MOZ_ASSERT(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
1061              "Unix domain sockets can't be used with socket types");
1062 #endif
1063 
1064   fd = nullptr;
1065 
1066   uint32_t controlFlags = 0;
1067   if (mProxyTransparentResolvesHost) {
1068     controlFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
1069   }
1070 
1071   if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT) {
1072     controlFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
1073   }
1074 
1075   if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE) {
1076     controlFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
1077   }
1078 
1079   if (mConnectionFlags & nsISocketTransport::BE_CONSERVATIVE) {
1080     controlFlags |= nsISocketProvider::BE_CONSERVATIVE;
1081   }
1082 
1083   if (mConnectionFlags &
1084       nsISocketTransport::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT) {
1085     controlFlags |= nsISocketProvider::ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT;
1086   }
1087 
1088   // by setting host to mOriginHost, instead of mHost we send the
1089   // SocketProvider (e.g. PSM) the origin hostname but can still do DNS
1090   // on an explicit alternate service host name
1091   const char* host = mOriginHost.get();
1092   int32_t port = (int32_t)mOriginPort;
1093 
1094   nsCOMPtr<nsISocketProviderService> spserv =
1095       nsSocketProviderService::GetOrCreate();
1096   nsCOMPtr<nsIProxyInfo> proxyInfo = mProxyInfo;
1097 
1098   uint32_t i;
1099   for (i = 0; i < mTypes.Length(); ++i) {
1100     nsCOMPtr<nsISocketProvider> provider;
1101 
1102     SOCKET_LOG(("  pushing io layer [%u:%s]\n", i, mTypes[i].get()));
1103 
1104     rv = spserv->GetSocketProvider(mTypes[i].get(), getter_AddRefs(provider));
1105     if (NS_FAILED(rv)) break;
1106 
1107     nsCOMPtr<nsISupports> secinfo;
1108     if (i == 0) {
1109       // if this is the first type, we'll want the
1110       // service to allocate a new socket
1111 
1112       // Most layers _ESPECIALLY_ PSM want the origin name here as they
1113       // will use it for secure checks, etc.. and any connection management
1114       // differences between the origin name and the routed name can be
1115       // taken care of via DNS. However, SOCKS is a special case as there is
1116       // no DNS. in the case of SOCKS and PSM the PSM is a separate layer
1117       // and receives the origin name.
1118       const char* socketProviderHost = host;
1119       int32_t socketProviderPort = port;
1120       if (mProxyTransparentResolvesHost &&
1121           (mTypes[0].EqualsLiteral("socks") ||
1122            mTypes[0].EqualsLiteral("socks4"))) {
1123         SOCKET_LOG(("SOCKS %d Host/Route override: %s:%d -> %s:%d\n",
1124                     mHttpsProxy, socketProviderHost, socketProviderPort,
1125                     mHost.get(), mPort));
1126         socketProviderHost = mHost.get();
1127         socketProviderPort = mPort;
1128       }
1129 
1130       // when https proxying we want to just connect to the proxy as if
1131       // it were the end host (i.e. expect the proxy's cert)
1132 
1133       rv = provider->NewSocket(
1134           mNetAddr.raw.family,
1135           mHttpsProxy ? mProxyHost.get() : socketProviderHost,
1136           mHttpsProxy ? mProxyPort : socketProviderPort, proxyInfo,
1137           mOriginAttributes, controlFlags, mTlsFlags, &fd,
1138           getter_AddRefs(secinfo));
1139 
1140       if (NS_SUCCEEDED(rv) && !fd) {
1141         MOZ_ASSERT_UNREACHABLE(
1142             "NewSocket succeeded but failed to "
1143             "create a PRFileDesc");
1144         rv = NS_ERROR_UNEXPECTED;
1145       }
1146     } else {
1147       // the socket has already been allocated,
1148       // so we just want the service to add itself
1149       // to the stack (such as pushing an io layer)
1150       rv = provider->AddToSocket(mNetAddr.raw.family, host, port, proxyInfo,
1151                                  mOriginAttributes, controlFlags, mTlsFlags, fd,
1152                                  getter_AddRefs(secinfo));
1153     }
1154 
1155     // controlFlags = 0; not used below this point...
1156     if (NS_FAILED(rv)) break;
1157 
1158     // if the service was ssl or starttls, we want to hold onto the socket
1159     // info
1160     bool isSSL = mTypes[i].EqualsLiteral("ssl");
1161     if (isSSL || mTypes[i].EqualsLiteral("starttls")) {
1162       // remember security info and give notification callbacks to PSM...
1163       nsCOMPtr<nsIInterfaceRequestor> callbacks;
1164       {
1165         MutexAutoLock lock(mLock);
1166         mSecInfo = secinfo;
1167         callbacks = mCallbacks;
1168         SOCKET_LOG(("  [secinfo=%p callbacks=%p]\n", mSecInfo.get(),
1169                     mCallbacks.get()));
1170       }
1171       // don't call into PSM while holding mLock!!
1172       nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
1173       if (secCtrl) secCtrl->SetNotificationCallbacks(callbacks);
1174       // remember if socket type is SSL so we can ProxyStartSSL if need be.
1175       usingSSL = isSSL;
1176     } else if (mTypes[i].EqualsLiteral("socks") ||
1177                mTypes[i].EqualsLiteral("socks4")) {
1178       // since socks is transparent, any layers above
1179       // it do not have to worry about proxy stuff
1180       proxyInfo = nullptr;
1181       proxyTransparent = true;
1182     }
1183   }
1184 
1185   if (NS_FAILED(rv)) {
1186     SOCKET_LOG(("  error pushing io layer [%u:%s rv=%" PRIx32 "]\n", i,
1187                 mTypes[i].get(), static_cast<uint32_t>(rv)));
1188     if (fd) {
1189       CloseSocket(
1190           fd, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1191     }
1192   }
1193   return rv;
1194 }
1195 
InitiateSocket()1196 nsresult nsSocketTransport::InitiateSocket() {
1197   SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1198 
1199   nsresult rv;
1200   bool isLocal;
1201   IsLocal(&isLocal);
1202 
1203   if (gIOService->IsNetTearingDown()) {
1204     return NS_ERROR_ABORT;
1205   }
1206   if (gIOService->IsOffline()) {
1207     if (!isLocal) return NS_ERROR_OFFLINE;
1208   } else if (!isLocal) {
1209 #ifdef DEBUG
1210     // all IP networking has to be done from the parent
1211     if (NS_SUCCEEDED(mCondition) && ((mNetAddr.raw.family == AF_INET) ||
1212                                      (mNetAddr.raw.family == AF_INET6))) {
1213       MOZ_ASSERT(!IsNeckoChild());
1214     }
1215 #endif
1216 
1217     if (NS_SUCCEEDED(mCondition) && xpc::AreNonLocalConnectionsDisabled() &&
1218         !(mNetAddr.IsIPAddrAny() || mNetAddr.IsIPAddrLocal() ||
1219           mNetAddr.IsIPAddrShared())) {
1220       nsAutoCString ipaddr;
1221       RefPtr<nsNetAddr> netaddr = new nsNetAddr(&mNetAddr);
1222       netaddr->GetAddress(ipaddr);
1223       fprintf_stderr(
1224           stderr,
1225           "FATAL ERROR: Non-local network connections are disabled and a "
1226           "connection "
1227           "attempt to %s (%s) was made.\nYou should only access hostnames "
1228           "available via the test networking proxy (if running mochitests) "
1229           "or from a test-specific httpd.js server (if running xpcshell "
1230           "tests). "
1231           "Browser services should be disabled or redirected to a local "
1232           "server.\n",
1233           mHost.get(), ipaddr.get());
1234       MOZ_CRASH("Attempting to connect to non-local address!");
1235     }
1236   }
1237 
1238   // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
1239   // connected - Bug 853423.
1240   if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
1241       mNetAddr.IsIPAddrLocal()) {
1242     if (SOCKET_LOG_ENABLED()) {
1243       nsAutoCString netAddrCString;
1244       netAddrCString.SetLength(kIPv6CStrBufSize);
1245       if (!mNetAddr.ToStringBuffer(netAddrCString.BeginWriting(),
1246                                    kIPv6CStrBufSize)) {
1247         netAddrCString = "<IP-to-string failed>"_ns;
1248       }
1249       SOCKET_LOG(
1250           ("nsSocketTransport::InitiateSocket skipping "
1251            "speculative connection for host [%s:%d] proxy "
1252            "[%s:%d] with Local IP address [%s]",
1253            mHost.get(), mPort, mProxyHost.get(), mProxyPort,
1254            netAddrCString.get()));
1255     }
1256     mCondition = NS_ERROR_CONNECTION_REFUSED;
1257     OnSocketDetached(nullptr);
1258     return mCondition;
1259   }
1260 
1261   //
1262   // find out if it is going to be ok to attach another socket to the STS.
1263   // if not then we have to wait for the STS to tell us that it is ok.
1264   // the notification is asynchronous, which means that when we could be
1265   // in a race to call AttachSocket once notified.  for this reason, when
1266   // we get notified, we just re-enter this function.  as a result, we are
1267   // sure to ask again before calling AttachSocket.  in this way we deal
1268   // with the race condition.  though it isn't the most elegant solution,
1269   // it is far simpler than trying to build a system that would guarantee
1270   // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
1271   // 194402 for more info.
1272   //
1273   if (!mSocketTransportService->CanAttachSocket()) {
1274     nsCOMPtr<nsIRunnable> event =
1275         new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
1276     if (!event) return NS_ERROR_OUT_OF_MEMORY;
1277     return mSocketTransportService->NotifyWhenCanAttachSocket(event);
1278   }
1279 
1280   //
1281   // if we already have a connected socket, then just attach and return.
1282   //
1283   if (mFD.IsInitialized()) {
1284     rv = mSocketTransportService->AttachSocket(mFD, this);
1285     if (NS_SUCCEEDED(rv)) mAttached = true;
1286     return rv;
1287   }
1288 
1289   //
1290   // create new socket fd, push io layers, etc.
1291   //
1292   PRFileDesc* fd;
1293   bool proxyTransparent;
1294   bool usingSSL;
1295 
1296   rv = BuildSocket(fd, proxyTransparent, usingSSL);
1297   if (NS_FAILED(rv)) {
1298     SOCKET_LOG(
1299         ("  BuildSocket failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
1300     return rv;
1301   }
1302 
1303   // create proxy via IOActivityMonitor
1304   IOActivityMonitor::MonitorSocket(fd);
1305 
1306 #ifdef FUZZING
1307   if (StaticPrefs::fuzzing_necko_enabled()) {
1308     rv = AttachFuzzyIOLayer(fd);
1309     if (NS_FAILED(rv)) {
1310       SOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32 "].\n",
1311                   static_cast<uint32_t>(rv)));
1312       return rv;
1313     }
1314     SOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
1315 
1316     if (usingSSL) {
1317       mSecInfo = static_cast<nsISupports*>(
1318           static_cast<nsISSLSocketControl*>(new FuzzySecurityInfo()));
1319     }
1320   }
1321 #endif
1322 
1323   PRStatus status;
1324 
1325   // Make the socket non-blocking...
1326   PRSocketOptionData opt;
1327   opt.option = PR_SockOpt_Nonblocking;
1328   opt.value.non_blocking = true;
1329   status = PR_SetSocketOption(fd, &opt);
1330   NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
1331 
1332   if (mReuseAddrPort) {
1333     SOCKET_LOG(("  Setting port/addr reuse socket options\n"));
1334 
1335     // Set ReuseAddr for TCP sockets to enable having several
1336     // sockets bound to same local IP and port
1337     PRSocketOptionData opt_reuseaddr;
1338     opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
1339     opt_reuseaddr.value.reuse_addr = PR_TRUE;
1340     status = PR_SetSocketOption(fd, &opt_reuseaddr);
1341     if (status != PR_SUCCESS) {
1342       SOCKET_LOG(("  Couldn't set reuse addr socket option: %d\n", status));
1343     }
1344 
1345     // And also set ReusePort for platforms supporting this socket option
1346     PRSocketOptionData opt_reuseport;
1347     opt_reuseport.option = PR_SockOpt_Reuseport;
1348     opt_reuseport.value.reuse_port = PR_TRUE;
1349     status = PR_SetSocketOption(fd, &opt_reuseport);
1350     if (status != PR_SUCCESS &&
1351         PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
1352       SOCKET_LOG(("  Couldn't set reuse port socket option: %d\n", status));
1353     }
1354   }
1355 
1356   // disable the nagle algorithm - if we rely on it to coalesce writes into
1357   // full packets the final packet of a multi segment POST/PUT or pipeline
1358   // sequence is delayed a full rtt
1359   opt.option = PR_SockOpt_NoDelay;
1360   opt.value.no_delay = true;
1361   PR_SetSocketOption(fd, &opt);
1362 
1363   // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1364   // The Windows default of 8KB is too small and as of vista sp1, autotuning
1365   // only applies to receive window
1366   int32_t sndBufferSize;
1367   mSocketTransportService->GetSendBufferSize(&sndBufferSize);
1368   if (sndBufferSize > 0) {
1369     opt.option = PR_SockOpt_SendBufferSize;
1370     opt.value.send_buffer_size = sndBufferSize;
1371     PR_SetSocketOption(fd, &opt);
1372   }
1373 
1374   if (mQoSBits) {
1375     opt.option = PR_SockOpt_IpTypeOfService;
1376     opt.value.tos = mQoSBits;
1377     PR_SetSocketOption(fd, &opt);
1378   }
1379 
1380 #if defined(XP_WIN)
1381   // The linger is turned off by default. This is not a hard close, but
1382   // closesocket should return immediately and operating system tries to send
1383   // remaining data for certain, implementation specific, amount of time.
1384   // https://msdn.microsoft.com/en-us/library/ms739165.aspx
1385   //
1386   // Turn the linger option on an set the interval to 0. This will cause hard
1387   // close of the socket.
1388   opt.option = PR_SockOpt_Linger;
1389   opt.value.linger.polarity = 1;
1390   opt.value.linger.linger = 0;
1391   PR_SetSocketOption(fd, &opt);
1392 #endif
1393 
1394   // inform socket transport about this newly created socket...
1395   rv = mSocketTransportService->AttachSocket(fd, this);
1396   if (NS_FAILED(rv)) {
1397     CloseSocket(fd,
1398                 mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1399     return rv;
1400   }
1401   mAttached = true;
1402 
1403   // assign mFD so that we can properly handle OnSocketDetached before we've
1404   // established a connection.
1405   {
1406     MutexAutoLock lock(mLock);
1407     mFD = fd;
1408     mFDref = 1;
1409     mFDconnected = false;
1410     mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1411   }
1412 
1413   SOCKET_LOG(("  advancing to STATE_CONNECTING\n"));
1414   mState = STATE_CONNECTING;
1415   SendStatus(NS_NET_STATUS_CONNECTING_TO);
1416 
1417   if (SOCKET_LOG_ENABLED()) {
1418     char buf[kNetAddrMaxCStrBufSize];
1419     mNetAddr.ToStringBuffer(buf, sizeof(buf));
1420     SOCKET_LOG(("  trying address: %s\n", buf));
1421   }
1422 
1423   //
1424   // Initiate the connect() to the host...
1425   //
1426   PRNetAddr prAddr;
1427   memset(&prAddr, 0, sizeof(prAddr));
1428   {
1429     if (mBindAddr) {
1430       MutexAutoLock lock(mLock);
1431       NetAddrToPRNetAddr(mBindAddr.get(), &prAddr);
1432       status = PR_Bind(fd, &prAddr);
1433       if (status != PR_SUCCESS) {
1434         return NS_ERROR_FAILURE;
1435       }
1436       mBindAddr = nullptr;
1437     }
1438   }
1439 
1440   NetAddrToPRNetAddr(&mNetAddr, &prAddr);
1441 
1442 #ifdef XP_WIN
1443   // Find the real tcp socket and set non-blocking once again!
1444   // Bug 1158189.
1445   PRFileDesc* bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
1446   if (bottom) {
1447     PROsfd osfd = PR_FileDesc2NativeHandle(bottom);
1448     u_long nonblocking = 1;
1449     if (ioctlsocket(osfd, FIONBIO, &nonblocking) != 0) {
1450       NS_WARNING("Socket could not be set non-blocking!");
1451       return NS_ERROR_FAILURE;
1452     }
1453   }
1454 #endif
1455 
1456   nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1457   if (secCtrl) {
1458     if (!mEchConfig.IsEmpty() &&
1459         !(mConnectionFlags & (DONT_TRY_ECH | BE_CONSERVATIVE))) {
1460       SOCKET_LOG(("nsSocketTransport::InitiateSocket set echconfig."));
1461       rv = secCtrl->SetEchConfig(mEchConfig);
1462       if (NS_FAILED(rv)) {
1463         return rv;
1464       }
1465       mEchConfigUsed = true;
1466     }
1467   }
1468 
1469   // We use PRIntervalTime here because we need
1470   // nsIOService::LastOfflineStateChange time and
1471   // nsIOService::LastConectivityChange time to be atomic.
1472   PRIntervalTime connectStarted = 0;
1473   if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
1474     connectStarted = PR_IntervalNow();
1475   }
1476 
1477   if (Telemetry::CanRecordPrereleaseData() ||
1478       Telemetry::CanRecordReleaseData()) {
1479     if (NS_FAILED(AttachNetworkDataCountLayer(fd))) {
1480       SOCKET_LOG(
1481           ("nsSocketTransport::InitiateSocket "
1482            "AttachNetworkDataCountLayer failed [this=%p]\n",
1483            this));
1484     }
1485   }
1486 
1487   bool connectCalled = true;  // This is only needed for telemetry.
1488   status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
1489   PRErrorCode code = PR_GetError();
1490   if (status == PR_SUCCESS) {
1491     PR_SetFDInheritable(fd, false);
1492   }
1493 
1494   if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
1495       connectStarted && connectCalled) {
1496     SendPRBlockingTelemetry(
1497         connectStarted, Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL,
1498         Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN,
1499         Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE,
1500         Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE,
1501         Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE);
1502   }
1503 
1504   if (status == PR_SUCCESS) {
1505     //
1506     // we are connected!
1507     //
1508     OnSocketConnected();
1509   } else {
1510 #if defined(TEST_CONNECT_ERRORS)
1511     code = RandomizeConnectError(code);
1512 #endif
1513     //
1514     // If the PR_Connect(...) would block, then poll for a connection.
1515     //
1516     if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
1517       mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1518       //
1519       // If the socket is already connected, then return success...
1520       //
1521     } else if (PR_IS_CONNECTED_ERROR == code) {
1522       //
1523       // we are connected!
1524       //
1525       OnSocketConnected();
1526 
1527       if (mSecInfo && !mProxyHost.IsEmpty() && proxyTransparent && usingSSL) {
1528         // if the connection phase is finished, and the ssl layer has
1529         // been pushed, and we were proxying (transparently; ie. nothing
1530         // has to happen in the protocol layer above us), it's time for
1531         // the ssl to start doing it's thing.
1532         nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1533         if (secCtrl) {
1534           SOCKET_LOG(("  calling ProxyStartSSL()\n"));
1535           secCtrl->ProxyStartSSL();
1536         }
1537         // XXX what if we were forced to poll on the socket for a successful
1538         // connection... wouldn't we need to call ProxyStartSSL after a call
1539         // to PR_ConnectContinue indicates that we are connected?
1540         //
1541         // XXX this appears to be what the old socket transport did.  why
1542         // isn't this broken?
1543       }
1544     }
1545     //
1546     // A SOCKS request was rejected; get the actual error code from
1547     // the OS error
1548     //
1549     else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
1550              !mProxyHost.IsEmpty()) {
1551       code = PR_GetOSError();
1552       rv = ErrorAccordingToNSPR(code);
1553     }
1554     //
1555     // The connection was refused...
1556     //
1557     else {
1558       if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
1559           connectStarted && connectCalled) {
1560         SendPRBlockingTelemetry(
1561             connectStarted, Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL,
1562             Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN,
1563             Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE,
1564             Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE,
1565             Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE);
1566       }
1567 
1568       rv = ErrorAccordingToNSPR(code);
1569       if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty()) {
1570         rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1571       }
1572     }
1573   }
1574   return rv;
1575 }
1576 
RecoverFromError()1577 bool nsSocketTransport::RecoverFromError() {
1578   NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
1579 
1580   SOCKET_LOG(
1581       ("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
1582        "]\n",
1583        this, mState, static_cast<uint32_t>(mCondition)));
1584 
1585   if (mDoNotRetryToConnect) {
1586     SOCKET_LOG(
1587         ("nsSocketTransport::RecoverFromError do not retry because "
1588          "mDoNotRetryToConnect is set [this=%p]\n",
1589          this));
1590     return false;
1591   }
1592 
1593 #if defined(XP_UNIX)
1594   // Unix domain connections don't have multiple addresses to try,
1595   // so the recovery techniques here don't apply.
1596   if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL) return false;
1597 #endif
1598 
1599   if ((mConnectionFlags & nsSocketTransport::USE_IP_HINT_ADDRESS) &&
1600       mCondition == NS_ERROR_UNKNOWN_HOST &&
1601       (mState == MSG_DNS_LOOKUP_COMPLETE || mState == MSG_ENSURE_CONNECT)) {
1602     SOCKET_LOG(("  try again without USE_IP_HINT_ADDRESS"));
1603     mConnectionFlags &= ~nsSocketTransport::USE_IP_HINT_ADDRESS;
1604     mState = STATE_CLOSED;
1605     return NS_SUCCEEDED(PostEvent(MSG_ENSURE_CONNECT, NS_OK));
1606   }
1607 
1608   // can only recover from errors in these states
1609   if (mState != STATE_RESOLVING && mState != STATE_CONNECTING) {
1610     SOCKET_LOG(("  not in a recoverable state"));
1611     return false;
1612   }
1613 
1614   nsresult rv;
1615 
1616   // OK to check this outside mLock
1617   NS_ASSERTION(!mFDconnected, "socket should not be connected");
1618 
1619   // all connection failures need to be reported to DNS so that the next
1620   // time we will use a different address if available.
1621   if (mState == STATE_CONNECTING && mDNSRecord) {
1622     mDNSRecord->ReportUnusable(SocketPort());
1623   }
1624 
1625   if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
1626       mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
1627       mCondition != NS_ERROR_NET_TIMEOUT &&
1628       mCondition != NS_ERROR_UNKNOWN_HOST &&
1629       mCondition != NS_ERROR_UNKNOWN_PROXY_HOST) {
1630     SOCKET_LOG(("  not a recoverable error %" PRIx32,
1631                 static_cast<uint32_t>(mCondition)));
1632     return false;
1633   }
1634 
1635   bool tryAgain = false;
1636 
1637   if ((mState == STATE_CONNECTING) && mDNSRecord) {
1638     if (mNetAddr.raw.family == AF_INET) {
1639       if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
1640         Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
1641                               UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
1642       }
1643     } else if (mNetAddr.raw.family == AF_INET6) {
1644       if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
1645         Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
1646                               UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
1647       }
1648     }
1649   }
1650 
1651   if (mConnectionFlags & RETRY_WITH_DIFFERENT_IP_FAMILY &&
1652       mCondition == NS_ERROR_UNKNOWN_HOST && mState == STATE_RESOLVING &&
1653       !mProxyTransparentResolvesHost) {
1654     SOCKET_LOG(("  trying lookup again with opposite ip family\n"));
1655     mConnectionFlags ^= (DISABLE_IPV6 | DISABLE_IPV4);
1656     mConnectionFlags &= ~RETRY_WITH_DIFFERENT_IP_FAMILY;
1657     // This will tell the consuming half-open to reset preference on the
1658     // connection entry
1659     mResetFamilyPreference = true;
1660     tryAgain = true;
1661   }
1662 
1663   // try next ip address only if past the resolver stage...
1664   if (mState == STATE_CONNECTING && mDNSRecord) {
1665     nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1666     mDNSRecord->IsTRR(&mResolvedByTRR);
1667     if (NS_SUCCEEDED(rv)) {
1668       SOCKET_LOG(("  trying again with next ip address\n"));
1669       tryAgain = true;
1670     } else if (mExternalDNSResolution) {
1671       mRetryDnsIfPossible = true;
1672       bool trrEnabled;
1673       mDNSRecord->IsTRR(&trrEnabled);
1674       // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
1675       // should intentionally not fallback to regular DNS.
1676       if (trrEnabled && !StaticPrefs::network_trr_fallback_on_zero_response() &&
1677           ((mNetAddr.raw.family == AF_INET && mNetAddr.inet.ip == 0) ||
1678            (mNetAddr.raw.family == AF_INET6 && mNetAddr.inet6.ip.u64[0] == 0 &&
1679             mNetAddr.inet6.ip.u64[1] == 0))) {
1680         SOCKET_LOG(("  TRR returned 0.0.0.0 and there are no other IPs"));
1681         mRetryDnsIfPossible = false;
1682       }
1683     } else if (mConnectionFlags & RETRY_WITH_DIFFERENT_IP_FAMILY) {
1684       SOCKET_LOG(("  failed to connect, trying with opposite ip family\n"));
1685       // Drop state to closed.  This will trigger new round of DNS
1686       // resolving bellow.
1687       mState = STATE_CLOSED;
1688       mConnectionFlags ^= (DISABLE_IPV6 | DISABLE_IPV4);
1689       mConnectionFlags &= ~RETRY_WITH_DIFFERENT_IP_FAMILY;
1690       // This will tell the consuming half-open to reset preference on the
1691       // connection entry
1692       mResetFamilyPreference = true;
1693       tryAgain = true;
1694     } else if (!(mConnectionFlags & DISABLE_TRR)) {
1695       bool trrEnabled;
1696       mDNSRecord->IsTRR(&trrEnabled);
1697 
1698       // Bug 1648147 - If the server responded with `0.0.0.0` or `::` then we
1699       // should intentionally not fallback to regular DNS.
1700       if (!StaticPrefs::network_trr_fallback_on_zero_response() &&
1701           ((mNetAddr.raw.family == AF_INET && mNetAddr.inet.ip == 0) ||
1702            (mNetAddr.raw.family == AF_INET6 && mNetAddr.inet6.ip.u64[0] == 0 &&
1703             mNetAddr.inet6.ip.u64[1] == 0))) {
1704         SOCKET_LOG(("  TRR returned 0.0.0.0 and there are no other IPs"));
1705       } else if (trrEnabled) {
1706         uint32_t trrMode = 0;
1707         mDNSRecord->GetEffectiveTRRMode(&trrMode);
1708         // If current trr mode is trr only, we should not retry.
1709         if (trrMode != 3) {
1710           // Drop state to closed.  This will trigger a new round of
1711           // DNS resolving. Bypass the cache this time since the
1712           // cached data came from TRR and failed already!
1713           SOCKET_LOG(("  failed to connect with TRR enabled, try w/o\n"));
1714           mState = STATE_CLOSED;
1715           mConnectionFlags |= DISABLE_TRR | BYPASS_CACHE | REFRESH_CACHE;
1716           tryAgain = true;
1717         }
1718       }
1719     }
1720   }
1721 
1722   // prepare to try again.
1723   if (tryAgain) {
1724     uint32_t msg;
1725 
1726     if (mState == STATE_CONNECTING) {
1727       mState = STATE_RESOLVING;
1728       msg = MSG_DNS_LOOKUP_COMPLETE;
1729     } else {
1730       mState = STATE_CLOSED;
1731       msg = MSG_ENSURE_CONNECT;
1732     }
1733 
1734     rv = PostEvent(msg, NS_OK);
1735     if (NS_FAILED(rv)) tryAgain = false;
1736   }
1737 
1738   return tryAgain;
1739 }
1740 
1741 // called on the socket thread only
OnMsgInputClosed(nsresult reason)1742 void nsSocketTransport::OnMsgInputClosed(nsresult reason) {
1743   SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%" PRIx32
1744               "]\n",
1745               this, static_cast<uint32_t>(reason)));
1746 
1747   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1748 
1749   mInputClosed = true;
1750   // check if event should affect entire transport
1751   if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED)) {
1752     mCondition = reason;  // XXX except if NS_FAILED(mCondition), right??
1753   } else if (mOutputClosed) {
1754     mCondition =
1755         NS_BASE_STREAM_CLOSED;  // XXX except if NS_FAILED(mCondition), right??
1756   } else {
1757     if (mState == STATE_TRANSFERRING) mPollFlags &= ~PR_POLL_READ;
1758     mInput.OnSocketReady(reason);
1759   }
1760 }
1761 
1762 // called on the socket thread only
OnMsgOutputClosed(nsresult reason)1763 void nsSocketTransport::OnMsgOutputClosed(nsresult reason) {
1764   SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%" PRIx32
1765               "]\n",
1766               this, static_cast<uint32_t>(reason)));
1767 
1768   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1769 
1770   mOutputClosed = true;
1771   // check if event should affect entire transport
1772   if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED)) {
1773     mCondition = reason;  // XXX except if NS_FAILED(mCondition), right??
1774   } else if (mInputClosed) {
1775     mCondition =
1776         NS_BASE_STREAM_CLOSED;  // XXX except if NS_FAILED(mCondition), right??
1777   } else {
1778     if (mState == STATE_TRANSFERRING) mPollFlags &= ~PR_POLL_WRITE;
1779     mOutput.OnSocketReady(reason);
1780   }
1781 }
1782 
OnSocketConnected()1783 void nsSocketTransport::OnSocketConnected() {
1784   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1785   SOCKET_LOG(("  advancing to STATE_TRANSFERRING\n"));
1786 
1787   mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
1788   mState = STATE_TRANSFERRING;
1789 
1790   // Set the m*AddrIsSet flags only when state has reached TRANSFERRING
1791   // because we need to make sure its value does not change due to failover
1792   mNetAddrIsSet = true;
1793 
1794   // assign mFD (must do this within the transport lock), but take care not
1795   // to trample over mFDref if mFD is already set.
1796   {
1797     MutexAutoLock lock(mLock);
1798     NS_ASSERTION(mFD.IsInitialized(), "no socket");
1799     NS_ASSERTION(mFDref == 1, "wrong socket ref count");
1800     SetSocketName(mFD);
1801     mFDconnected = true;
1802     mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1803   }
1804 
1805   // Ensure keepalive is configured correctly if previously enabled.
1806   if (mKeepaliveEnabled) {
1807     nsresult rv = SetKeepaliveEnabledInternal(true);
1808     if (NS_WARN_IF(NS_FAILED(rv))) {
1809       SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%" PRIx32 "]",
1810                   static_cast<uint32_t>(rv)));
1811     }
1812   }
1813 
1814   SendStatus(NS_NET_STATUS_CONNECTED_TO);
1815 }
1816 
SetSocketName(PRFileDesc * fd)1817 void nsSocketTransport::SetSocketName(PRFileDesc* fd) {
1818   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1819   if (mSelfAddrIsSet) {
1820     return;
1821   }
1822 
1823   PRNetAddr prAddr;
1824   memset(&prAddr, 0, sizeof(prAddr));
1825   if (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) {
1826     PRNetAddrToNetAddr(&prAddr, &mSelfAddr);
1827     mSelfAddrIsSet = true;
1828   }
1829 }
1830 
GetFD_Locked()1831 PRFileDesc* nsSocketTransport::GetFD_Locked() {
1832   mLock.AssertCurrentThreadOwns();
1833 
1834   // mFD is not available to the streams while disconnected.
1835   if (!mFDconnected) return nullptr;
1836 
1837   if (mFD.IsInitialized()) mFDref++;
1838 
1839   return mFD;
1840 }
1841 
1842 class ThunkPRClose : public Runnable {
1843  public:
ThunkPRClose(PRFileDesc * fd)1844   explicit ThunkPRClose(PRFileDesc* fd)
1845       : Runnable("net::ThunkPRClose"), mFD(fd) {}
1846 
Run()1847   NS_IMETHOD Run() override {
1848     nsSocketTransport::CloseSocket(
1849         mFD, gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1850     return NS_OK;
1851   }
1852 
1853  private:
1854   PRFileDesc* mFD;
1855 };
1856 
STS_PRCloseOnSocketTransport(PRFileDesc * fd,bool lingerPolarity,int16_t lingerTimeout)1857 void STS_PRCloseOnSocketTransport(PRFileDesc* fd, bool lingerPolarity,
1858                                   int16_t lingerTimeout) {
1859   if (gSocketTransportService) {
1860     // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1861     gSocketTransportService->Dispatch(new ThunkPRClose(fd), NS_DISPATCH_NORMAL);
1862   } else {
1863     // something horrible has happened
1864     NS_ASSERTION(gSocketTransportService, "No STS service");
1865   }
1866 }
1867 
ReleaseFD_Locked(PRFileDesc * fd)1868 void nsSocketTransport::ReleaseFD_Locked(PRFileDesc* fd) {
1869   mLock.AssertCurrentThreadOwns();
1870 
1871   NS_ASSERTION(mFD == fd, "wrong fd");
1872 
1873   if (--mFDref == 0) {
1874     if (gIOService->IsNetTearingDown() &&
1875         ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
1876          gSocketTransportService->MaxTimeForPrClosePref())) {
1877       // If shutdown last to long, let the socket leak and do not close it.
1878       SOCKET_LOG(("Intentional leak"));
1879     } else {
1880       if (mLingerPolarity || mLingerTimeout) {
1881         PRSocketOptionData socket_linger;
1882         socket_linger.option = PR_SockOpt_Linger;
1883         socket_linger.value.linger.polarity = mLingerPolarity;
1884         socket_linger.value.linger.linger = mLingerTimeout;
1885         PR_SetSocketOption(mFD, &socket_linger);
1886       }
1887       if (OnSocketThread()) {
1888         SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
1889         CloseSocket(
1890             mFD, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1891       } else {
1892         // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1893         STS_PRCloseOnSocketTransport(mFD, mLingerPolarity, mLingerTimeout);
1894       }
1895     }
1896     mFD = nullptr;
1897   }
1898 }
1899 
1900 //-----------------------------------------------------------------------------
1901 // socket event handler impl
1902 
OnSocketEvent(uint32_t type,nsresult status,nsISupports * param)1903 void nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status,
1904                                       nsISupports* param) {
1905   SOCKET_LOG(
1906       ("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%" PRIx32
1907        " param=%p]\n",
1908        this, type, static_cast<uint32_t>(status), param));
1909 
1910   if (NS_FAILED(mCondition)) {
1911     // block event since we're apparently already dead.
1912     SOCKET_LOG(("  blocking event [condition=%" PRIx32 "]\n",
1913                 static_cast<uint32_t>(mCondition)));
1914     //
1915     // notify input/output streams in case either has a pending notify.
1916     //
1917     mInput.OnSocketReady(mCondition);
1918     mOutput.OnSocketReady(mCondition);
1919     return;
1920   }
1921 
1922   switch (type) {
1923     case MSG_ENSURE_CONNECT:
1924       SOCKET_LOG(("  MSG_ENSURE_CONNECT\n"));
1925 
1926       // Apply port remapping here so that we do it on the socket thread and
1927       // before we process the resolved DNS name or create the socket the first
1928       // time.
1929       if (!mPortRemappingApplied) {
1930         mPortRemappingApplied = true;
1931 
1932         mSocketTransportService->ApplyPortRemap(&mPort);
1933         mSocketTransportService->ApplyPortRemap(&mOriginPort);
1934       }
1935 
1936       //
1937       // ensure that we have created a socket, attached it, and have a
1938       // connection.
1939       //
1940       if (mState == STATE_CLOSED) {
1941         // Unix domain sockets are ready to connect; mNetAddr is all we
1942         // need. Internet address families require a DNS lookup (or possibly
1943         // several) before we can connect.
1944 #if defined(XP_UNIX)
1945         if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL) {
1946           mCondition = InitiateSocket();
1947         } else {
1948 #else
1949         {
1950 #endif
1951           mCondition = ResolveHost();
1952         }
1953 
1954       } else {
1955         SOCKET_LOG(("  ignoring redundant event\n"));
1956       }
1957       break;
1958 
1959     case MSG_DNS_LOOKUP_COMPLETE:
1960       if (mDNSRequest) {  // only send this if we actually resolved anything
1961         SendStatus(NS_NET_STATUS_RESOLVED_HOST);
1962       }
1963 
1964       SOCKET_LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
1965       mDNSRequest = nullptr;
1966 
1967       if (mDNSRecord) {
1968         mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1969         mDNSRecord->IsTRR(&mResolvedByTRR);
1970       }
1971       // status contains DNS lookup status
1972       if (NS_FAILED(status)) {
1973         // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1974         // proxy host is not found, so we fixup the error code.
1975         // For SOCKS proxies (mProxyTransparent == true), the socket
1976         // transport resolves the real host here, so there's no fixup
1977         // (see bug 226943).
1978         if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyTransparent &&
1979             !mProxyHost.IsEmpty()) {
1980           mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
1981         } else {
1982           mCondition = status;
1983         }
1984       } else if (mState == STATE_RESOLVING) {
1985         mCondition = InitiateSocket();
1986       }
1987       break;
1988 
1989     case MSG_RETRY_INIT_SOCKET:
1990       mCondition = InitiateSocket();
1991       break;
1992 
1993     case MSG_INPUT_CLOSED:
1994       SOCKET_LOG(("  MSG_INPUT_CLOSED\n"));
1995       OnMsgInputClosed(status);
1996       break;
1997 
1998     case MSG_INPUT_PENDING:
1999       SOCKET_LOG(("  MSG_INPUT_PENDING\n"));
2000       OnMsgInputPending();
2001       break;
2002 
2003     case MSG_OUTPUT_CLOSED:
2004       SOCKET_LOG(("  MSG_OUTPUT_CLOSED\n"));
2005       OnMsgOutputClosed(status);
2006       break;
2007 
2008     case MSG_OUTPUT_PENDING:
2009       SOCKET_LOG(("  MSG_OUTPUT_PENDING\n"));
2010       OnMsgOutputPending();
2011       break;
2012     case MSG_TIMEOUT_CHANGED:
2013       SOCKET_LOG(("  MSG_TIMEOUT_CHANGED\n"));
2014       {
2015         MutexAutoLock lock(mLock);
2016         mPollTimeout =
2017             mTimeouts[(mState == STATE_TRANSFERRING) ? TIMEOUT_READ_WRITE
2018                                                      : TIMEOUT_CONNECT];
2019       }
2020       break;
2021     default:
2022       SOCKET_LOG(("  unhandled event!\n"));
2023   }
2024 
2025   if (NS_FAILED(mCondition)) {
2026     SOCKET_LOG(("  after event [this=%p cond=%" PRIx32 "]\n", this,
2027                 static_cast<uint32_t>(mCondition)));
2028     if (!mAttached) {  // need to process this error ourselves...
2029       OnSocketDetached(nullptr);
2030     }
2031   } else if (mPollFlags == PR_POLL_EXCEPT) {
2032     mPollFlags = 0;  // make idle
2033   }
2034 }
2035 
2036 //-----------------------------------------------------------------------------
2037 // socket handler impl
2038 
2039 void nsSocketTransport::OnSocketReady(PRFileDesc* fd, int16_t outFlags) {
2040   SOCKET_LOG1(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
2041                this, outFlags));
2042 
2043   if (outFlags == -1) {
2044     SOCKET_LOG(("socket timeout expired\n"));
2045     mCondition = NS_ERROR_NET_TIMEOUT;
2046     return;
2047   }
2048 
2049   if (mState == STATE_TRANSFERRING) {
2050     // if waiting to write and socket is writable or hit an exception.
2051     if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
2052       // assume that we won't need to poll any longer (the stream will
2053       // request that we poll again if it is still pending).
2054       mPollFlags &= ~PR_POLL_WRITE;
2055       mOutput.OnSocketReady(NS_OK);
2056     }
2057     // if waiting to read and socket is readable or hit an exception.
2058     if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
2059       // assume that we won't need to poll any longer (the stream will
2060       // request that we poll again if it is still pending).
2061       mPollFlags &= ~PR_POLL_READ;
2062       mInput.OnSocketReady(NS_OK);
2063     }
2064     // Update poll timeout in case it was changed
2065     {
2066       MutexAutoLock lock(mLock);
2067       mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
2068     }
2069   } else if ((mState == STATE_CONNECTING) && !gIOService->IsNetTearingDown()) {
2070     // We do not need to do PR_ConnectContinue when we are already
2071     // shutting down.
2072 
2073     // We use PRIntervalTime here because we need
2074     // nsIOService::LastOfflineStateChange time and
2075     // nsIOService::LastConectivityChange time to be atomic.
2076     PRIntervalTime connectStarted = 0;
2077     if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
2078       connectStarted = PR_IntervalNow();
2079     }
2080 
2081     PRStatus status = PR_ConnectContinue(fd, outFlags);
2082 
2083     if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
2084         connectStarted) {
2085       SendPRBlockingTelemetry(
2086           connectStarted, Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_NORMAL,
2087           Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_SHUTDOWN,
2088           Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_CONNECTIVITY_CHANGE,
2089           Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_LINK_CHANGE,
2090           Telemetry::PRCONNECTCONTINUE_BLOCKING_TIME_OFFLINE);
2091     }
2092 
2093     if (status == PR_SUCCESS) {
2094       //
2095       // we are connected!
2096       //
2097       OnSocketConnected();
2098 
2099       if (mNetAddr.raw.family == AF_INET) {
2100         if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
2101           Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
2102                                 SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS);
2103         }
2104       } else if (mNetAddr.raw.family == AF_INET6) {
2105         if (mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
2106           Telemetry::Accumulate(Telemetry::IPV4_AND_IPV6_ADDRESS_CONNECTIVITY,
2107                                 SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS);
2108         }
2109       }
2110     } else {
2111       PRErrorCode code = PR_GetError();
2112 #if defined(TEST_CONNECT_ERRORS)
2113       code = RandomizeConnectError(code);
2114 #endif
2115       //
2116       // If the connect is still not ready, then continue polling...
2117       //
2118       if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
2119         // Set up the select flags for connect...
2120         mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
2121         // Update poll timeout in case it was changed
2122         {
2123           MutexAutoLock lock(mLock);
2124           mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
2125         }
2126       }
2127       //
2128       // The SOCKS proxy rejected our request. Find out why.
2129       //
2130       else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
2131                !mProxyHost.IsEmpty()) {
2132         code = PR_GetOSError();
2133         mCondition = ErrorAccordingToNSPR(code);
2134       } else {
2135         //
2136         // else, the connection failed...
2137         //
2138         mCondition = ErrorAccordingToNSPR(code);
2139         if ((mCondition == NS_ERROR_CONNECTION_REFUSED) &&
2140             !mProxyHost.IsEmpty()) {
2141           mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
2142         }
2143         SOCKET_LOG(("  connection failed! [reason=%" PRIx32 "]\n",
2144                     static_cast<uint32_t>(mCondition)));
2145       }
2146     }
2147   } else if ((mState == STATE_CONNECTING) && gIOService->IsNetTearingDown()) {
2148     // We do not need to do PR_ConnectContinue when we are already
2149     // shutting down.
2150     SOCKET_LOG(
2151         ("We are in shutdown so skip PR_ConnectContinue and set "
2152          "and error.\n"));
2153     mCondition = NS_ERROR_ABORT;
2154   } else {
2155     NS_ERROR("unexpected socket state");
2156     mCondition = NS_ERROR_UNEXPECTED;
2157   }
2158 
2159   if (mPollFlags == PR_POLL_EXCEPT) mPollFlags = 0;  // make idle
2160 }
2161 
2162 // called on the socket thread only
2163 void nsSocketTransport::OnSocketDetached(PRFileDesc* fd) {
2164   SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%" PRIx32
2165               "]\n",
2166               this, static_cast<uint32_t>(mCondition)));
2167 
2168   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2169 
2170   mAttached = false;
2171 
2172   // if we didn't initiate this detach, then be sure to pass an error
2173   // condition up to our consumers.  (e.g., STS is shutting down.)
2174   if (NS_SUCCEEDED(mCondition)) {
2175     if (gIOService->IsOffline()) {
2176       mCondition = NS_ERROR_OFFLINE;
2177     } else {
2178       mCondition = NS_ERROR_ABORT;
2179     }
2180   }
2181 
2182   // If we are not shutting down try again.
2183   if (!gIOService->IsNetTearingDown() && RecoverFromError()) {
2184     mCondition = NS_OK;
2185   } else {
2186     mState = STATE_CLOSED;
2187 
2188     // make sure there isn't any pending DNS request
2189     if (mDNSRequest) {
2190       mDNSRequest->Cancel(NS_ERROR_ABORT);
2191       mDNSRequest = nullptr;
2192     }
2193 
2194     //
2195     // notify input/output streams
2196     //
2197     mInput.OnSocketReady(mCondition);
2198     mOutput.OnSocketReady(mCondition);
2199   }
2200 
2201   // break any potential reference cycle between the security info object
2202   // and ourselves by resetting its notification callbacks object.  see
2203   // bug 285991 for details.
2204   nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
2205   if (secCtrl) secCtrl->SetNotificationCallbacks(nullptr);
2206 
2207   // finally, release our reference to the socket (must do this within
2208   // the transport lock) possibly closing the socket. Also release our
2209   // listeners to break potential refcount cycles.
2210 
2211   // We should be careful not to release mEventSink and mCallbacks while
2212   // we're locked, because releasing it might require acquiring the lock
2213   // again, so we just null out mEventSink and mCallbacks while we're
2214   // holding the lock, and let the stack based objects' destuctors take
2215   // care of destroying it if needed.
2216   nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
2217   nsCOMPtr<nsITransportEventSink> ourEventSink;
2218   {
2219     MutexAutoLock lock(mLock);
2220     if (mFD.IsInitialized()) {
2221       ReleaseFD_Locked(mFD);
2222       // flag mFD as unusable; this prevents other consumers from
2223       // acquiring a reference to mFD.
2224       mFDconnected = false;
2225     }
2226 
2227     // We must release mCallbacks and mEventSink to avoid memory leak
2228     // but only when RecoverFromError() above failed. Otherwise we lose
2229     // link with UI and security callbacks on next connection attempt
2230     // round. That would lead e.g. to a broken certificate exception page.
2231     if (NS_FAILED(mCondition)) {
2232       mCallbacks.swap(ourCallbacks);
2233       mEventSink.swap(ourEventSink);
2234     }
2235   }
2236 }
2237 
2238 void nsSocketTransport::IsLocal(bool* aIsLocal) {
2239   {
2240     MutexAutoLock lock(mLock);
2241 
2242 #if defined(XP_UNIX)
2243     // Unix-domain sockets are always local.
2244     if (mNetAddr.raw.family == PR_AF_LOCAL) {
2245       *aIsLocal = true;
2246       return;
2247     }
2248 #endif
2249 
2250     *aIsLocal = mNetAddr.IsLoopbackAddr();
2251   }
2252 }
2253 
2254 //-----------------------------------------------------------------------------
2255 // xpcom api
2256 
2257 NS_IMPL_ISUPPORTS(nsSocketTransport, nsISocketTransport, nsITransport,
2258                   nsIDNSListener, nsIClassInfo, nsIInterfaceRequestor)
2259 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport, nsISocketTransport, nsITransport,
2260                             nsIDNSListener, nsIInterfaceRequestor)
2261 
2262 NS_IMETHODIMP
2263 nsSocketTransport::OpenInputStream(uint32_t flags, uint32_t segsize,
2264                                    uint32_t segcount,
2265                                    nsIInputStream** aResult) {
2266   SOCKET_LOG(
2267       ("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n", this, flags));
2268 
2269   NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
2270 
2271   nsresult rv;
2272   nsCOMPtr<nsIAsyncInputStream> pipeIn;
2273   nsCOMPtr<nsIInputStream> result;
2274 
2275   if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
2276     // XXX if the caller wants blocking, then the caller also gets buffered!
2277     // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2278     bool openBlocking = (flags & OPEN_BLOCKING);
2279 
2280     net_ResolveSegmentParams(segsize, segcount);
2281 
2282     // create a pipe
2283     nsCOMPtr<nsIAsyncOutputStream> pipeOut;
2284     rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
2285                      !openBlocking, true, segsize, segcount);
2286     if (NS_FAILED(rv)) return rv;
2287 
2288     // async copy from socket to pipe
2289     rv = NS_AsyncCopy(&mInput, pipeOut, mSocketTransportService,
2290                       NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
2291     if (NS_FAILED(rv)) return rv;
2292 
2293     result = pipeIn;
2294   } else {
2295     result = &mInput;
2296   }
2297 
2298   // flag input stream as open
2299   mInputClosed = false;
2300 
2301   rv = PostEvent(MSG_ENSURE_CONNECT);
2302   if (NS_FAILED(rv)) {
2303     return rv;
2304   }
2305 
2306   result.forget(aResult);
2307   return NS_OK;
2308 }
2309 
2310 NS_IMETHODIMP
2311 nsSocketTransport::OpenOutputStream(uint32_t flags, uint32_t segsize,
2312                                     uint32_t segcount,
2313                                     nsIOutputStream** aResult) {
2314   SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n", this,
2315               flags));
2316 
2317   NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
2318 
2319   nsresult rv;
2320   nsCOMPtr<nsIAsyncOutputStream> pipeOut;
2321   nsCOMPtr<nsIOutputStream> result;
2322   if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
2323     // XXX if the caller wants blocking, then the caller also gets buffered!
2324     // bool openBuffered = !(flags & OPEN_UNBUFFERED);
2325     bool openBlocking = (flags & OPEN_BLOCKING);
2326 
2327     net_ResolveSegmentParams(segsize, segcount);
2328 
2329     // create a pipe
2330     nsCOMPtr<nsIAsyncInputStream> pipeIn;
2331     rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true,
2332                      !openBlocking, segsize, segcount);
2333     if (NS_FAILED(rv)) return rv;
2334 
2335     // async copy from socket to pipe
2336     rv = NS_AsyncCopy(pipeIn, &mOutput, mSocketTransportService,
2337                       NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
2338     if (NS_FAILED(rv)) return rv;
2339 
2340     result = pipeOut;
2341   } else {
2342     result = &mOutput;
2343   }
2344 
2345   // flag output stream as open
2346   mOutputClosed = false;
2347 
2348   rv = PostEvent(MSG_ENSURE_CONNECT);
2349   if (NS_FAILED(rv)) return rv;
2350 
2351   result.forget(aResult);
2352   return NS_OK;
2353 }
2354 
2355 NS_IMETHODIMP
2356 nsSocketTransport::Close(nsresult reason) {
2357   SOCKET_LOG(("nsSocketTransport::Close %p reason=%" PRIx32, this,
2358               static_cast<uint32_t>(reason)));
2359 
2360   if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED;
2361 
2362   mDoNotRetryToConnect = true;
2363 
2364   mInput.CloseWithStatus(reason);
2365   mOutput.CloseWithStatus(reason);
2366   return NS_OK;
2367 }
2368 
2369 NS_IMETHODIMP
2370 nsSocketTransport::GetSecurityInfo(nsISupports** secinfo) {
2371   MutexAutoLock lock(mLock);
2372   NS_IF_ADDREF(*secinfo = mSecInfo);
2373   return NS_OK;
2374 }
2375 
2376 NS_IMETHODIMP
2377 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor** callbacks) {
2378   MutexAutoLock lock(mLock);
2379   NS_IF_ADDREF(*callbacks = mCallbacks);
2380   return NS_OK;
2381 }
2382 
2383 NS_IMETHODIMP
2384 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor* callbacks) {
2385   nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
2386   NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
2387                                          GetCurrentEventTarget(),
2388                                          getter_AddRefs(threadsafeCallbacks));
2389 
2390   nsCOMPtr<nsISupports> secinfo;
2391   {
2392     MutexAutoLock lock(mLock);
2393     mCallbacks = threadsafeCallbacks;
2394     SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n", mSecInfo.get(),
2395                 mCallbacks.get()));
2396 
2397     secinfo = mSecInfo;
2398   }
2399 
2400   // don't call into PSM while holding mLock!!
2401   nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
2402   if (secCtrl) secCtrl->SetNotificationCallbacks(threadsafeCallbacks);
2403 
2404   return NS_OK;
2405 }
2406 
2407 NS_IMETHODIMP
2408 nsSocketTransport::SetEventSink(nsITransportEventSink* sink,
2409                                 nsIEventTarget* target) {
2410   nsCOMPtr<nsITransportEventSink> temp;
2411   if (target) {
2412     nsresult rv =
2413         net_NewTransportEventSinkProxy(getter_AddRefs(temp), sink, target);
2414     if (NS_FAILED(rv)) return rv;
2415     sink = temp.get();
2416   }
2417 
2418   MutexAutoLock lock(mLock);
2419   mEventSink = sink;
2420   return NS_OK;
2421 }
2422 
2423 NS_IMETHODIMP
2424 nsSocketTransport::IsAlive(bool* result) {
2425   *result = false;
2426 
2427   nsresult conditionWhileLocked = NS_OK;
2428   PRFileDescAutoLock fd(this, &conditionWhileLocked);
2429   if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
2430     return NS_OK;
2431   }
2432 
2433   // XXX do some idle-time based checks??
2434 
2435   char c;
2436   int32_t rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
2437 
2438   if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR)) {
2439     *result = true;
2440   }
2441 
2442   return NS_OK;
2443 }
2444 
2445 NS_IMETHODIMP
2446 nsSocketTransport::GetHost(nsACString& host) {
2447   host = SocketHost();
2448   return NS_OK;
2449 }
2450 
2451 NS_IMETHODIMP
2452 nsSocketTransport::GetPort(int32_t* port) {
2453   *port = (int32_t)SocketPort();
2454   return NS_OK;
2455 }
2456 
2457 NS_IMETHODIMP
2458 nsSocketTransport::GetScriptableOriginAttributes(
2459     JSContext* aCx, JS::MutableHandle<JS::Value> aOriginAttributes) {
2460   if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aOriginAttributes))) {
2461     return NS_ERROR_FAILURE;
2462   }
2463   return NS_OK;
2464 }
2465 
2466 NS_IMETHODIMP
2467 nsSocketTransport::SetScriptableOriginAttributes(
2468     JSContext* aCx, JS::Handle<JS::Value> aOriginAttributes) {
2469   MutexAutoLock lock(mLock);
2470   NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
2471 
2472   OriginAttributes attrs;
2473   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
2474     return NS_ERROR_INVALID_ARG;
2475   }
2476 
2477   mOriginAttributes = attrs;
2478   return NS_OK;
2479 }
2480 
2481 nsresult nsSocketTransport::GetOriginAttributes(
2482     OriginAttributes* aOriginAttributes) {
2483   NS_ENSURE_ARG(aOriginAttributes);
2484   *aOriginAttributes = mOriginAttributes;
2485   return NS_OK;
2486 }
2487 
2488 nsresult nsSocketTransport::SetOriginAttributes(
2489     const OriginAttributes& aOriginAttributes) {
2490   MutexAutoLock lock(mLock);
2491   NS_ENSURE_FALSE(mFD.IsInitialized(), NS_ERROR_FAILURE);
2492 
2493   mOriginAttributes = aOriginAttributes;
2494   return NS_OK;
2495 }
2496 
2497 NS_IMETHODIMP
2498 nsSocketTransport::GetPeerAddr(NetAddr* addr) {
2499   // once we are in the connected state, mNetAddr will not change.
2500   // so if we can verify that we are in the connected state, then
2501   // we can freely access mNetAddr from any thread without being
2502   // inside a critical section.
2503 
2504   if (!mNetAddrIsSet) {
2505     SOCKET_LOG(
2506         ("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
2507          "NOT_AVAILABLE because not yet connected.",
2508          this, mState));
2509     return NS_ERROR_NOT_AVAILABLE;
2510   }
2511 
2512   memcpy(addr, &mNetAddr, sizeof(NetAddr));
2513   return NS_OK;
2514 }
2515 
2516 NS_IMETHODIMP
2517 nsSocketTransport::GetSelfAddr(NetAddr* addr) {
2518   // once we are in the connected state, mSelfAddr will not change.
2519   // so if we can verify that we are in the connected state, then
2520   // we can freely access mSelfAddr from any thread without being
2521   // inside a critical section.
2522 
2523   if (!mSelfAddrIsSet) {
2524     SOCKET_LOG(
2525         ("nsSocketTransport::GetSelfAddr [this=%p state=%d] "
2526          "NOT_AVAILABLE because not yet connected.",
2527          this, mState));
2528     return NS_ERROR_NOT_AVAILABLE;
2529   }
2530 
2531   memcpy(addr, &mSelfAddr, sizeof(NetAddr));
2532   return NS_OK;
2533 }
2534 
2535 NS_IMETHODIMP
2536 nsSocketTransport::Bind(NetAddr* aLocalAddr) {
2537   NS_ENSURE_ARG(aLocalAddr);
2538 
2539   MutexAutoLock lock(mLock);
2540   if (mAttached) {
2541     return NS_ERROR_FAILURE;
2542   }
2543 
2544   mBindAddr = MakeUnique<NetAddr>();
2545   memcpy(mBindAddr.get(), aLocalAddr, sizeof(NetAddr));
2546 
2547   return NS_OK;
2548 }
2549 
2550 NS_IMETHODIMP
2551 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr** addr) {
2552   NetAddr rawAddr;
2553 
2554   nsresult rv;
2555   rv = GetPeerAddr(&rawAddr);
2556   if (NS_FAILED(rv)) return rv;
2557 
2558   RefPtr<nsNetAddr> netaddr = new nsNetAddr(&rawAddr);
2559   netaddr.forget(addr);
2560   return NS_OK;
2561 }
2562 
2563 NS_IMETHODIMP
2564 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr** addr) {
2565   NetAddr rawAddr;
2566 
2567   nsresult rv;
2568   rv = GetSelfAddr(&rawAddr);
2569   if (NS_FAILED(rv)) return rv;
2570 
2571   RefPtr<nsNetAddr> netaddr = new nsNetAddr(&rawAddr);
2572   netaddr.forget(addr);
2573 
2574   return NS_OK;
2575 }
2576 
2577 NS_IMETHODIMP
2578 nsSocketTransport::GetTimeout(uint32_t type, uint32_t* value) {
2579   NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
2580   MutexAutoLock lock(mLock);
2581   *value = (uint32_t)mTimeouts[type];
2582   return NS_OK;
2583 }
2584 
2585 NS_IMETHODIMP
2586 nsSocketTransport::SetTimeout(uint32_t type, uint32_t value) {
2587   NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
2588 
2589   SOCKET_LOG(("nsSocketTransport::SetTimeout %p type=%u, value=%u", this, type,
2590               value));
2591 
2592   // truncate overly large timeout values.
2593   {
2594     MutexAutoLock lock(mLock);
2595     mTimeouts[type] = (uint16_t)std::min<uint32_t>(value, UINT16_MAX);
2596   }
2597   PostEvent(MSG_TIMEOUT_CHANGED);
2598   return NS_OK;
2599 }
2600 
2601 NS_IMETHODIMP
2602 nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort) {
2603   mReuseAddrPort = reuseAddrPort;
2604   return NS_OK;
2605 }
2606 
2607 NS_IMETHODIMP
2608 nsSocketTransport::SetLinger(bool aPolarity, int16_t aTimeout) {
2609   MutexAutoLock lock(mLock);
2610 
2611   mLingerPolarity = aPolarity;
2612   mLingerTimeout = aTimeout;
2613 
2614   return NS_OK;
2615 }
2616 
2617 NS_IMETHODIMP
2618 nsSocketTransport::SetQoSBits(uint8_t aQoSBits) {
2619   // Don't do any checking here of bits.  Why?  Because as of RFC-4594
2620   // several different Class Selector and Assured Forwarding values
2621   // have been defined, but that isn't to say more won't be added later.
2622   // In that case, any checking would be an impediment to interoperating
2623   // with newer QoS definitions.
2624 
2625   mQoSBits = aQoSBits;
2626   return NS_OK;
2627 }
2628 
2629 NS_IMETHODIMP
2630 nsSocketTransport::GetQoSBits(uint8_t* aQoSBits) {
2631   *aQoSBits = mQoSBits;
2632   return NS_OK;
2633 }
2634 
2635 NS_IMETHODIMP
2636 nsSocketTransport::GetRecvBufferSize(uint32_t* aSize) {
2637   PRFileDescAutoLock fd(this);
2638   if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
2639 
2640   nsresult rv = NS_OK;
2641   PRSocketOptionData opt;
2642   opt.option = PR_SockOpt_RecvBufferSize;
2643   if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS) {
2644     *aSize = opt.value.recv_buffer_size;
2645   } else {
2646     rv = NS_ERROR_FAILURE;
2647   }
2648 
2649   return rv;
2650 }
2651 
2652 NS_IMETHODIMP
2653 nsSocketTransport::GetSendBufferSize(uint32_t* aSize) {
2654   PRFileDescAutoLock fd(this);
2655   if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
2656 
2657   nsresult rv = NS_OK;
2658   PRSocketOptionData opt;
2659   opt.option = PR_SockOpt_SendBufferSize;
2660   if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS) {
2661     *aSize = opt.value.send_buffer_size;
2662   } else {
2663     rv = NS_ERROR_FAILURE;
2664   }
2665 
2666   return rv;
2667 }
2668 
2669 NS_IMETHODIMP
2670 nsSocketTransport::SetRecvBufferSize(uint32_t aSize) {
2671   PRFileDescAutoLock fd(this);
2672   if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
2673 
2674   nsresult rv = NS_OK;
2675   PRSocketOptionData opt;
2676   opt.option = PR_SockOpt_RecvBufferSize;
2677   opt.value.recv_buffer_size = aSize;
2678   if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS) rv = NS_ERROR_FAILURE;
2679 
2680   return rv;
2681 }
2682 
2683 NS_IMETHODIMP
2684 nsSocketTransport::SetSendBufferSize(uint32_t aSize) {
2685   PRFileDescAutoLock fd(this);
2686   if (!fd.IsInitialized()) return NS_ERROR_NOT_CONNECTED;
2687 
2688   nsresult rv = NS_OK;
2689   PRSocketOptionData opt;
2690   opt.option = PR_SockOpt_SendBufferSize;
2691   opt.value.send_buffer_size = aSize;
2692   if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS) rv = NS_ERROR_FAILURE;
2693 
2694   return rv;
2695 }
2696 
2697 NS_IMETHODIMP
2698 nsSocketTransport::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
2699                                     nsresult status) {
2700   SOCKET_LOG(("nsSocketTransport::OnLookupComplete: this=%p status %" PRIx32
2701               ".",
2702               this, static_cast<uint32_t>(status)));
2703 
2704   if (NS_SUCCEEDED(status)) {
2705     mDNSRecord = do_QueryInterface(rec);
2706     MOZ_ASSERT(mDNSRecord);
2707   }
2708 
2709   // flag host lookup complete for the benefit of the ResolveHost method.
2710   mResolving = false;
2711   nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, nullptr);
2712 
2713   // if posting a message fails, then we should assume that the socket
2714   // transport has been shutdown.  this should never happen!  if it does
2715   // it means that the socket transport service was shutdown before the
2716   // DNS service.
2717   if (NS_FAILED(rv)) {
2718     NS_WARNING("unable to post DNS lookup complete message");
2719   }
2720 
2721   return NS_OK;
2722 }
2723 
2724 // nsIInterfaceRequestor
2725 NS_IMETHODIMP
2726 nsSocketTransport::GetInterface(const nsIID& iid, void** result) {
2727   if (iid.Equals(NS_GET_IID(nsIDNSRecord)) ||
2728       iid.Equals(NS_GET_IID(nsIDNSAddrRecord))) {
2729     return mDNSRecord ? mDNSRecord->QueryInterface(iid, result)
2730                       : NS_ERROR_NO_INTERFACE;
2731   }
2732   return this->QueryInterface(iid, result);
2733 }
2734 
2735 NS_IMETHODIMP
2736 nsSocketTransport::GetInterfaces(nsTArray<nsIID>& array) {
2737   return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(array);
2738 }
2739 
2740 NS_IMETHODIMP
2741 nsSocketTransport::GetScriptableHelper(nsIXPCScriptable** _retval) {
2742   *_retval = nullptr;
2743   return NS_OK;
2744 }
2745 
2746 NS_IMETHODIMP
2747 nsSocketTransport::GetContractID(nsACString& aContractID) {
2748   aContractID.SetIsVoid(true);
2749   return NS_OK;
2750 }
2751 
2752 NS_IMETHODIMP
2753 nsSocketTransport::GetClassDescription(nsACString& aClassDescription) {
2754   aClassDescription.SetIsVoid(true);
2755   return NS_OK;
2756 }
2757 
2758 NS_IMETHODIMP
2759 nsSocketTransport::GetClassID(nsCID** aClassID) {
2760   *aClassID = nullptr;
2761   return NS_OK;
2762 }
2763 
2764 NS_IMETHODIMP
2765 nsSocketTransport::GetFlags(uint32_t* aFlags) {
2766   *aFlags = nsIClassInfo::THREADSAFE;
2767   return NS_OK;
2768 }
2769 
2770 NS_IMETHODIMP
2771 nsSocketTransport::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) {
2772   return NS_ERROR_NOT_AVAILABLE;
2773 }
2774 
2775 NS_IMETHODIMP
2776 nsSocketTransport::GetConnectionFlags(uint32_t* value) {
2777   *value = mConnectionFlags;
2778   return NS_OK;
2779 }
2780 
2781 NS_IMETHODIMP
2782 nsSocketTransport::SetConnectionFlags(uint32_t value) {
2783   SOCKET_LOG(
2784       ("nsSocketTransport::SetConnectionFlags %p flags=%u", this, value));
2785 
2786   mConnectionFlags = value;
2787   return NS_OK;
2788 }
2789 
2790 NS_IMETHODIMP
2791 nsSocketTransport::SetIsPrivate(bool aIsPrivate) {
2792   mIsPrivate = aIsPrivate;
2793   return NS_OK;
2794 }
2795 
2796 NS_IMETHODIMP
2797 nsSocketTransport::GetTlsFlags(uint32_t* value) {
2798   *value = mTlsFlags;
2799   return NS_OK;
2800 }
2801 
2802 NS_IMETHODIMP
2803 nsSocketTransport::SetTlsFlags(uint32_t value) {
2804   mTlsFlags = value;
2805   return NS_OK;
2806 }
2807 
2808 void nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled) {
2809   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2810 
2811   // The global pref toggles keepalive as a system feature; it only affects
2812   // an individual socket if keepalive has been specifically enabled for it.
2813   // So, ensure keepalive is configured correctly if previously enabled.
2814   if (mKeepaliveEnabled) {
2815     nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
2816     if (NS_WARN_IF(NS_FAILED(rv))) {
2817       SOCKET_LOG(("  SetKeepaliveEnabledInternal [%s] failed rv[0x%" PRIx32 "]",
2818                   aEnabled ? "enable" : "disable", static_cast<uint32_t>(rv)));
2819     }
2820   }
2821 }
2822 
2823 nsresult nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable) {
2824   MOZ_ASSERT(mKeepaliveIdleTimeS > 0 && mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
2825   MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 &&
2826              mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
2827   MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
2828              mKeepaliveProbeCount <= kMaxTCPKeepCount);
2829 
2830   PRFileDescAutoLock fd(this);
2831   if (NS_WARN_IF(!fd.IsInitialized())) {
2832     return NS_ERROR_NOT_INITIALIZED;
2833   }
2834 
2835   // Only enable if keepalives are globally enabled, but ensure other
2836   // options are set correctly on the fd.
2837   bool enable = aEnable && mSocketTransportService->IsKeepaliveEnabled();
2838   nsresult rv =
2839       fd.SetKeepaliveVals(enable, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
2840                           mKeepaliveProbeCount);
2841   if (NS_WARN_IF(NS_FAILED(rv))) {
2842     SOCKET_LOG(("  SetKeepaliveVals failed rv[0x%" PRIx32 "]",
2843                 static_cast<uint32_t>(rv)));
2844     return rv;
2845   }
2846   rv = fd.SetKeepaliveEnabled(enable);
2847   if (NS_WARN_IF(NS_FAILED(rv))) {
2848     SOCKET_LOG(("  SetKeepaliveEnabled failed rv[0x%" PRIx32 "]",
2849                 static_cast<uint32_t>(rv)));
2850     return rv;
2851   }
2852   return NS_OK;
2853 }
2854 
2855 NS_IMETHODIMP
2856 nsSocketTransport::GetKeepaliveEnabled(bool* aResult) {
2857   MOZ_ASSERT(aResult);
2858 
2859   *aResult = mKeepaliveEnabled;
2860   return NS_OK;
2861 }
2862 
2863 nsresult nsSocketTransport::EnsureKeepaliveValsAreInitialized() {
2864   nsresult rv = NS_OK;
2865   int32_t val = -1;
2866   if (mKeepaliveIdleTimeS == -1) {
2867     rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
2868     if (NS_WARN_IF(NS_FAILED(rv))) {
2869       return rv;
2870     }
2871     mKeepaliveIdleTimeS = val;
2872   }
2873   if (mKeepaliveRetryIntervalS == -1) {
2874     rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
2875     if (NS_WARN_IF(NS_FAILED(rv))) {
2876       return rv;
2877     }
2878     mKeepaliveRetryIntervalS = val;
2879   }
2880   if (mKeepaliveProbeCount == -1) {
2881     rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
2882     if (NS_WARN_IF(NS_FAILED(rv))) {
2883       return rv;
2884     }
2885     mKeepaliveProbeCount = val;
2886   }
2887   return NS_OK;
2888 }
2889 
2890 NS_IMETHODIMP
2891 nsSocketTransport::SetKeepaliveEnabled(bool aEnable) {
2892 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2893   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2894 
2895   if (aEnable == mKeepaliveEnabled) {
2896     SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.", this,
2897                 aEnable ? "enabled" : "disabled"));
2898     return NS_OK;
2899   }
2900 
2901   nsresult rv = NS_OK;
2902   if (aEnable) {
2903     rv = EnsureKeepaliveValsAreInitialized();
2904     if (NS_WARN_IF(NS_FAILED(rv))) {
2905       SOCKET_LOG(
2906           ("  SetKeepaliveEnabled [%p] "
2907            "error [0x%" PRIx32 "] initializing keepalive vals",
2908            this, static_cast<uint32_t>(rv)));
2909       return rv;
2910     }
2911   }
2912   SOCKET_LOG(
2913       ("nsSocketTransport::SetKeepaliveEnabled [%p] "
2914        "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
2915        "globally %s.",
2916        this, aEnable ? "enabled" : "disabled", mKeepaliveIdleTimeS,
2917        mKeepaliveRetryIntervalS, mKeepaliveProbeCount,
2918        mSocketTransportService->IsKeepaliveEnabled() ? "enabled" : "disabled"));
2919 
2920   // Set mKeepaliveEnabled here so that state is maintained; it is possible
2921   // that we're in between fds, e.g. the 1st IP address failed, so we're about
2922   // to retry on a 2nd from the DNS record.
2923   mKeepaliveEnabled = aEnable;
2924 
2925   rv = SetKeepaliveEnabledInternal(aEnable);
2926   if (NS_WARN_IF(NS_FAILED(rv))) {
2927     SOCKET_LOG(("  SetKeepaliveEnabledInternal failed rv[0x%" PRIx32 "]",
2928                 static_cast<uint32_t>(rv)));
2929     return rv;
2930   }
2931 
2932   return NS_OK;
2933 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
2934   SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
2935   return NS_ERROR_NOT_IMPLEMENTED;
2936 #endif
2937 }
2938 
2939 NS_IMETHODIMP
2940 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime, int32_t aRetryInterval) {
2941 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2942   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2943   if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
2944     return NS_ERROR_INVALID_ARG;
2945   }
2946   if (NS_WARN_IF(aRetryInterval <= 0 || kMaxTCPKeepIntvl < aRetryInterval)) {
2947     return NS_ERROR_INVALID_ARG;
2948   }
2949 
2950   if (aIdleTime == mKeepaliveIdleTimeS &&
2951       aRetryInterval == mKeepaliveRetryIntervalS) {
2952     SOCKET_LOG(
2953         ("nsSocketTransport::SetKeepaliveVals [%p] idle time "
2954          "already %ds and retry interval already %ds.",
2955          this, mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS));
2956     return NS_OK;
2957   }
2958   mKeepaliveIdleTimeS = aIdleTime;
2959   mKeepaliveRetryIntervalS = aRetryInterval;
2960 
2961   nsresult rv = NS_OK;
2962   if (mKeepaliveProbeCount == -1) {
2963     int32_t val = -1;
2964     nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
2965     if (NS_WARN_IF(NS_FAILED(rv))) {
2966       return rv;
2967     }
2968     mKeepaliveProbeCount = val;
2969   }
2970 
2971   SOCKET_LOG(
2972       ("nsSocketTransport::SetKeepaliveVals [%p] "
2973        "keepalive %s, idle time[%ds] retry interval[%ds] "
2974        "packet count[%d]",
2975        this, mKeepaliveEnabled ? "enabled" : "disabled", mKeepaliveIdleTimeS,
2976        mKeepaliveRetryIntervalS, mKeepaliveProbeCount));
2977 
2978   PRFileDescAutoLock fd(this);
2979   if (NS_WARN_IF(!fd.IsInitialized())) {
2980     return NS_ERROR_NULL_POINTER;
2981   }
2982 
2983   rv = fd.SetKeepaliveVals(mKeepaliveEnabled, mKeepaliveIdleTimeS,
2984                            mKeepaliveRetryIntervalS, mKeepaliveProbeCount);
2985   if (NS_WARN_IF(NS_FAILED(rv))) {
2986     return rv;
2987   }
2988   return NS_OK;
2989 #else
2990   SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
2991   return NS_ERROR_NOT_IMPLEMENTED;
2992 #endif
2993 }
2994 
2995 #ifdef ENABLE_SOCKET_TRACING
2996 
2997 #  include <stdio.h>
2998 #  include <ctype.h>
2999 #  include "prenv.h"
3000 
3001 static void DumpBytesToFile(const char* path, const char* header,
3002                             const char* buf, int32_t n) {
3003   FILE* fp = fopen(path, "a");
3004 
3005   fprintf(fp, "\n%s [%d bytes]\n", header, n);
3006 
3007   const unsigned char* p;
3008   while (n) {
3009     p = (const unsigned char*)buf;
3010 
3011     int32_t i, row_max = std::min(16, n);
3012 
3013     for (i = 0; i < row_max; ++i) fprintf(fp, "%02x  ", *p++);
3014     for (i = row_max; i < 16; ++i) fprintf(fp, "    ");
3015 
3016     p = (const unsigned char*)buf;
3017     for (i = 0; i < row_max; ++i, ++p) {
3018       if (isprint(*p))
3019         fprintf(fp, "%c", *p);
3020       else
3021         fprintf(fp, ".");
3022     }
3023 
3024     fprintf(fp, "\n");
3025     buf += row_max;
3026     n -= row_max;
3027   }
3028 
3029   fprintf(fp, "\n");
3030   fclose(fp);
3031 }
3032 
3033 void nsSocketTransport::TraceInBuf(const char* buf, int32_t n) {
3034   char* val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3035   if (!val || !*val) return;
3036 
3037   nsAutoCString header;
3038   header.AssignLiteral("Reading from: ");
3039   header.Append(mHost);
3040   header.Append(':');
3041   header.AppendInt(mPort);
3042 
3043   DumpBytesToFile(val, header.get(), buf, n);
3044 }
3045 
3046 void nsSocketTransport::TraceOutBuf(const char* buf, int32_t n) {
3047   char* val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
3048   if (!val || !*val) return;
3049 
3050   nsAutoCString header;
3051   header.AssignLiteral("Writing to: ");
3052   header.Append(mHost);
3053   header.Append(':');
3054   header.AppendInt(mPort);
3055 
3056   DumpBytesToFile(val, header.get(), buf, n);
3057 }
3058 
3059 #endif
3060 
3061 static void LogNSPRError(const char* aPrefix, const void* aObjPtr) {
3062 #if defined(DEBUG)
3063   PRErrorCode errCode = PR_GetError();
3064   int errLen = PR_GetErrorTextLength();
3065   nsAutoCString errStr;
3066   if (errLen > 0) {
3067     errStr.SetLength(errLen);
3068     PR_GetErrorText(errStr.BeginWriting());
3069   }
3070   NS_WARNING(
3071       nsPrintfCString("%s [%p] NSPR error[0x%x] %s.",
3072                       aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
3073                       errLen > 0 ? errStr.BeginReading() : "<no error text>")
3074           .get());
3075 #endif
3076 }
3077 
3078 nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(
3079     bool aEnable) {
3080   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3081   MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
3082              "Cannot enable keepalive if global pref is disabled!");
3083   if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
3084     return NS_ERROR_ILLEGAL_VALUE;
3085   }
3086 
3087   PRSocketOptionData opt;
3088 
3089   opt.option = PR_SockOpt_Keepalive;
3090   opt.value.keep_alive = aEnable;
3091   PRStatus status = PR_SetSocketOption(mFd, &opt);
3092   if (NS_WARN_IF(status != PR_SUCCESS)) {
3093     LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
3094                  mSocketTransport);
3095     return ErrorAccordingToNSPR(PR_GetError());
3096   }
3097   return NS_OK;
3098 }
3099 
3100 static void LogOSError(const char* aPrefix, const void* aObjPtr) {
3101 #if defined(DEBUG)
3102   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3103 
3104 #  ifdef XP_WIN
3105   DWORD errCode = WSAGetLastError();
3106   LPVOID errMessage;
3107   FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
3108                     FORMAT_MESSAGE_IGNORE_INSERTS,
3109                 NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
3110                 (LPTSTR)&errMessage, 0, NULL);
3111 #  else
3112   int errCode = errno;
3113   char* errMessage = strerror(errno);
3114 #  endif
3115   NS_WARNING(nsPrintfCString("%s [%p] OS error[0x%x] %s",
3116                              aPrefix ? aPrefix : "nsSocketTransport", aObjPtr,
3117                              errCode,
3118                              errMessage ? errMessage : "<no error text>")
3119                  .get());
3120 #  ifdef XP_WIN
3121   LocalFree(errMessage);
3122 #  endif
3123 #endif
3124 }
3125 
3126 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
3127  * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
3128  * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
3129  */
3130 
3131 nsresult nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(
3132     bool aEnabled, int aIdleTime, int aRetryInterval, int aProbeCount) {
3133 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
3134   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3135   if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
3136     return NS_ERROR_INVALID_ARG;
3137   }
3138   if (NS_WARN_IF(aRetryInterval <= 0 || kMaxTCPKeepIntvl < aRetryInterval)) {
3139     return NS_ERROR_INVALID_ARG;
3140   }
3141   if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
3142     return NS_ERROR_INVALID_ARG;
3143   }
3144 
3145   PROsfd sock = PR_FileDesc2NativeHandle(mFd);
3146   if (NS_WARN_IF(sock == -1)) {
3147     LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
3148                  mSocketTransport);
3149     return ErrorAccordingToNSPR(PR_GetError());
3150   }
3151 #endif
3152 
3153 #if defined(XP_WIN)
3154   // Windows allows idle time and retry interval to be set; NOT ping count.
3155   struct tcp_keepalive keepalive_vals = {(u_long)aEnabled,
3156                                          // Windows uses msec.
3157                                          (u_long)(aIdleTime * 1000UL),
3158                                          (u_long)(aRetryInterval * 1000UL)};
3159   DWORD bytes_returned;
3160   int err =
3161       WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
3162                sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL, NULL);
3163   if (NS_WARN_IF(err)) {
3164     LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
3165     return NS_ERROR_UNEXPECTED;
3166   }
3167   return NS_OK;
3168 
3169 #elif defined(XP_DARWIN)
3170   // Darwin uses sec; only supports idle time being set.
3171   int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE, &aIdleTime,
3172                        sizeof(aIdleTime));
3173   if (NS_WARN_IF(err)) {
3174     LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
3175                mSocketTransport);
3176     return NS_ERROR_UNEXPECTED;
3177   }
3178   return NS_OK;
3179 
3180 #elif defined(XP_UNIX)
3181   // Not all *nix OSes support the following setsockopt() options
3182   // ... but we assume they are supported in the Android kernel;
3183   // build errors will tell us if they are not.
3184 #  if defined(ANDROID) || defined(TCP_KEEPIDLE)
3185   // Idle time until first keepalive probe; interval between ack'd probes;
3186   // seconds.
3187   int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &aIdleTime,
3188                        sizeof(aIdleTime));
3189   if (NS_WARN_IF(err)) {
3190     LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
3191                mSocketTransport);
3192     return NS_ERROR_UNEXPECTED;
3193   }
3194 
3195 #  endif
3196 #  if defined(ANDROID) || defined(TCP_KEEPINTVL)
3197   // Interval between unack'd keepalive probes; seconds.
3198   err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &aRetryInterval,
3199                    sizeof(aRetryInterval));
3200   if (NS_WARN_IF(err)) {
3201     LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
3202                mSocketTransport);
3203     return NS_ERROR_UNEXPECTED;
3204   }
3205 
3206 #  endif
3207 #  if defined(ANDROID) || defined(TCP_KEEPCNT)
3208   // Number of unack'd keepalive probes before connection times out.
3209   err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &aProbeCount,
3210                    sizeof(aProbeCount));
3211   if (NS_WARN_IF(err)) {
3212     LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
3213                mSocketTransport);
3214     return NS_ERROR_UNEXPECTED;
3215   }
3216 
3217 #  endif
3218   return NS_OK;
3219 #else
3220   MOZ_ASSERT(false,
3221              "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
3222              "called on unsupported platform!");
3223   return NS_ERROR_UNEXPECTED;
3224 #endif
3225 }
3226 
3227 void nsSocketTransport::CloseSocket(PRFileDesc* aFd, bool aTelemetryEnabled) {
3228 #if defined(XP_WIN)
3229   AttachShutdownLayer(aFd);
3230 #endif
3231 
3232   // We use PRIntervalTime here because we need
3233   // nsIOService::LastOfflineStateChange time and
3234   // nsIOService::LastConectivityChange time to be atomic.
3235   PRIntervalTime closeStarted;
3236   if (aTelemetryEnabled) {
3237     closeStarted = PR_IntervalNow();
3238   }
3239 
3240   PR_Close(aFd);
3241 
3242   if (aTelemetryEnabled) {
3243     SendPRBlockingTelemetry(
3244         closeStarted, Telemetry::PRCLOSE_TCP_BLOCKING_TIME_NORMAL,
3245         Telemetry::PRCLOSE_TCP_BLOCKING_TIME_SHUTDOWN,
3246         Telemetry::PRCLOSE_TCP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
3247         Telemetry::PRCLOSE_TCP_BLOCKING_TIME_LINK_CHANGE,
3248         Telemetry::PRCLOSE_TCP_BLOCKING_TIME_OFFLINE);
3249   }
3250 }
3251 
3252 void nsSocketTransport::SendPRBlockingTelemetry(
3253     PRIntervalTime aStart, Telemetry::HistogramID aIDNormal,
3254     Telemetry::HistogramID aIDShutdown,
3255     Telemetry::HistogramID aIDConnectivityChange,
3256     Telemetry::HistogramID aIDLinkChange, Telemetry::HistogramID aIDOffline) {
3257   PRIntervalTime now = PR_IntervalNow();
3258   if (gIOService->IsNetTearingDown()) {
3259     Telemetry::Accumulate(aIDShutdown, PR_IntervalToMilliseconds(now - aStart));
3260 
3261   } else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange()) <
3262              60) {
3263     Telemetry::Accumulate(aIDConnectivityChange,
3264                           PR_IntervalToMilliseconds(now - aStart));
3265   } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange()) <
3266              60) {
3267     Telemetry::Accumulate(aIDLinkChange,
3268                           PR_IntervalToMilliseconds(now - aStart));
3269 
3270   } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange()) <
3271              60) {
3272     Telemetry::Accumulate(aIDOffline, PR_IntervalToMilliseconds(now - aStart));
3273   } else {
3274     Telemetry::Accumulate(aIDNormal, PR_IntervalToMilliseconds(now - aStart));
3275   }
3276 }
3277 
3278 NS_IMETHODIMP
3279 nsSocketTransport::GetResetIPFamilyPreference(bool* aReset) {
3280   *aReset = mResetFamilyPreference;
3281   return NS_OK;
3282 }
3283 
3284 NS_IMETHODIMP
3285 nsSocketTransport::GetEchConfigUsed(bool* aEchConfigUsed) {
3286   *aEchConfigUsed = mEchConfigUsed;
3287   return NS_OK;
3288 }
3289 
3290 NS_IMETHODIMP
3291 nsSocketTransport::SetEchConfig(const nsACString& aEchConfig) {
3292   mEchConfig = aEchConfig;
3293   return NS_OK;
3294 }
3295 
3296 NS_IMETHODIMP
3297 nsSocketTransport::ResolvedByTRR(bool* aResolvedByTRR) {
3298   *aResolvedByTRR = mResolvedByTRR;
3299   return NS_OK;
3300 }
3301 
3302 NS_IMETHODIMP
3303 nsSocketTransport::GetRetryDnsIfPossible(bool* aRetryDns) {
3304   *aRetryDns = mRetryDnsIfPossible;
3305   return NS_OK;
3306 }
3307 
3308 NS_IMETHODIMP
3309 nsSocketTransport::GetStatus(nsresult* aStatus) {
3310   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
3311 
3312   *aStatus = mCondition;
3313   return NS_OK;
3314 }
3315 
3316 }  // namespace net
3317 }  // namespace mozilla
3318