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