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