Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 #ifdef MOZ_LOGGING
8 #define FORCE_PR_LOG
9 #endif
11 #include "nsSocketTransport2.h"
13 #include "mozilla/Attributes.h"
14 #include "nsIOService.h"
15 #include "nsStreamUtils.h"
16 #include "nsNetSegmentUtils.h"
17 #include "nsNetAddr.h"
18 #include "nsTransportUtils.h"
19 #include "nsProxyInfo.h"
20 #include "nsNetCID.h"
21 #include "nsNetUtil.h"
22 #include "nsAutoPtr.h"
23 #include "nsCOMPtr.h"
24 #include "plstr.h"
25 #include "prerr.h"
26 #include "NetworkActivityMonitor.h"
27 #include "mozilla/VisualEventTracer.h"
28 #include "nsThreadUtils.h"
29 #include "nsISocketProviderService.h"
30 #include "nsISocketProvider.h"
31 #include "nsISSLSocketControl.h"
32 #include "nsINSSErrorsService.h"
33 #include "nsIPipe.h"
34 #include "nsIProgrammingLanguage.h"
35 #include "nsIClassInfoImpl.h"
36 #include "nsURLHelper.h"
37 #include "nsIDNSService.h"
38 #include "nsIDNSRecord.h"
39 #include "nsICancelable.h"
40 #include <algorithm>
42 #include "nsPrintfCString.h"
44 #if defined(XP_WIN)
45 #include "nsNativeConnectionHelper.h"
46 #endif
48 /* Following inclusions required for keepalive config not supported by NSPR. */
49 #include "private/pprio.h"
50 #if defined(XP_WIN)
51 #include <winsock2.h>
52 #include <mstcpip.h>
53 #elif defined(XP_UNIX)
54 #include <errno.h>
55 #include <netinet/tcp.h>
56 #endif
57 /* End keepalive config inclusions. */
59 using namespace mozilla;
60 using namespace mozilla::net;
62 //-----------------------------------------------------------------------------
64 static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
65 static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
67 //-----------------------------------------------------------------------------
69 class nsSocketEvent : public nsRunnable
70 {
71 public:
72 nsSocketEvent(nsSocketTransport *transport, uint32_t type,
73 nsresult status = NS_OK, nsISupports *param = nullptr)
74 : mTransport(transport)
75 , mType(type)
76 , mStatus(status)
77 , mParam(param)
78 {}
80 NS_IMETHOD Run()
81 {
82 mTransport->OnSocketEvent(mType, mStatus, mParam);
83 return NS_OK;
84 }
86 private:
87 nsRefPtr<nsSocketTransport> mTransport;
89 uint32_t mType;
90 nsresult mStatus;
91 nsCOMPtr<nsISupports> mParam;
92 };
94 //-----------------------------------------------------------------------------
96 //#define TEST_CONNECT_ERRORS
97 #ifdef TEST_CONNECT_ERRORS
98 #include <stdlib.h>
99 static PRErrorCode RandomizeConnectError(PRErrorCode code)
100 {
101 //
102 // To test out these errors, load http://www.yahoo.com/. It should load
103 // correctly despite the random occurrence of these errors.
104 //
105 int n = rand();
106 if (n > RAND_MAX/2) {
107 struct {
108 PRErrorCode err_code;
109 const char *err_name;
110 }
111 errors[] = {
112 //
113 // These errors should be recoverable provided there is another
114 // IP address in mDNSRecord.
115 //
116 { PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR" },
117 { PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR" },
118 //
119 // This error will cause this socket transport to error out;
120 // however, if the consumer is HTTP, then the HTTP transaction
121 // should be restarted when this error occurs.
122 //
123 { PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR" },
124 };
125 n = n % (sizeof(errors)/sizeof(errors[0]));
126 code = errors[n].err_code;
127 SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
128 }
129 return code;
130 }
131 #endif
133 //-----------------------------------------------------------------------------
135 static bool
136 IsNSSErrorCode(PRErrorCode code)
137 {
138 return
139 ((code >= nsINSSErrorsService::NSS_SEC_ERROR_BASE) &&
140 (code < nsINSSErrorsService::NSS_SEC_ERROR_LIMIT))
141 ||
142 ((code >= nsINSSErrorsService::NSS_SSL_ERROR_BASE) &&
143 (code < nsINSSErrorsService::NSS_SSL_ERROR_LIMIT));
144 }
146 // this logic is duplicated from the implementation of
147 // nsINSSErrorsService::getXPCOMFromNSSError
148 // It might have been better to implement that interface here...
149 static nsresult
150 GetXPCOMFromNSSError(PRErrorCode code)
151 {
152 // XXX Don't make up nsresults, it's supposed to be an enum (bug 778113)
153 return (nsresult)NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY,
154 -1 * code);
155 }
157 nsresult
158 ErrorAccordingToNSPR(PRErrorCode errorCode)
159 {
160 nsresult rv = NS_ERROR_FAILURE;
161 switch (errorCode) {
162 case PR_WOULD_BLOCK_ERROR:
163 rv = NS_BASE_STREAM_WOULD_BLOCK;
164 break;
165 case PR_CONNECT_ABORTED_ERROR:
166 case PR_CONNECT_RESET_ERROR:
167 rv = NS_ERROR_NET_RESET;
168 break;
169 case PR_END_OF_FILE_ERROR: // XXX document this correlation
170 rv = NS_ERROR_NET_INTERRUPT;
171 break;
172 case PR_CONNECT_REFUSED_ERROR:
173 // We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
174 // could get better diagnostics by adding distinct XPCOM error codes for
175 // each of these, but there are a lot of places in Gecko that check
176 // specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
177 // be checked.
178 case PR_NETWORK_UNREACHABLE_ERROR:
179 case PR_HOST_UNREACHABLE_ERROR:
180 case PR_ADDRESS_NOT_AVAILABLE_ERROR:
181 // Treat EACCES as a soft error since (at least on Linux) connect() returns
182 // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
183 case PR_NO_ACCESS_RIGHTS_ERROR:
184 rv = NS_ERROR_CONNECTION_REFUSED;
185 break;
186 case PR_ADDRESS_NOT_SUPPORTED_ERROR:
187 rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
188 break;
189 case PR_IO_TIMEOUT_ERROR:
190 case PR_CONNECT_TIMEOUT_ERROR:
191 rv = NS_ERROR_NET_TIMEOUT;
192 break;
193 case PR_OUT_OF_MEMORY_ERROR:
194 // These really indicate that the descriptor table filled up, or that the
195 // kernel ran out of network buffers - but nobody really cares which part of
196 // the system ran out of memory.
197 case PR_PROC_DESC_TABLE_FULL_ERROR:
198 case PR_SYS_DESC_TABLE_FULL_ERROR:
199 case PR_INSUFFICIENT_RESOURCES_ERROR:
200 rv = NS_ERROR_OUT_OF_MEMORY;
201 break;
202 case PR_ADDRESS_IN_USE_ERROR:
203 rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
204 break;
205 // These filename-related errors can arise when using Unix-domain sockets.
206 case PR_FILE_NOT_FOUND_ERROR:
207 rv = NS_ERROR_FILE_NOT_FOUND;
208 break;
209 case PR_IS_DIRECTORY_ERROR:
210 rv = NS_ERROR_FILE_IS_DIRECTORY;
211 break;
212 case PR_LOOP_ERROR:
213 rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
214 break;
215 case PR_NAME_TOO_LONG_ERROR:
216 rv = NS_ERROR_FILE_NAME_TOO_LONG;
217 break;
218 case PR_NO_DEVICE_SPACE_ERROR:
219 rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
220 break;
221 case PR_NOT_DIRECTORY_ERROR:
222 rv = NS_ERROR_FILE_NOT_DIRECTORY;
223 break;
224 case PR_READ_ONLY_FILESYSTEM_ERROR:
225 rv = NS_ERROR_FILE_READ_ONLY;
226 break;
227 default:
228 if (IsNSSErrorCode(errorCode))
229 rv = GetXPCOMFromNSSError(errorCode);
230 break;
232 // NSPR's socket code can return these, but they're not worth breaking out
233 // into their own error codes, distinct from NS_ERROR_FAILURE:
234 //
235 // PR_BAD_DESCRIPTOR_ERROR
236 // PR_INVALID_ARGUMENT_ERROR
237 // PR_NOT_SOCKET_ERROR
238 // PR_NOT_TCP_SOCKET_ERROR
239 // These would indicate a bug internal to the component.
240 //
241 // PR_PROTOCOL_NOT_SUPPORTED_ERROR
242 // This means that we can't use the given "protocol" (like
243 // IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
244 // above, this indicates an internal bug.
245 //
246 // PR_IS_CONNECTED_ERROR
247 // This indicates that we've applied a system call like 'bind' or
248 // 'connect' to a socket that is already connected. The socket
249 // components manage each file descriptor's state, and in some cases
250 // handle this error result internally. We shouldn't be returning
251 // this to our callers.
252 //
253 // PR_IO_ERROR
254 // This is so vague that NS_ERROR_FAILURE is just as good.
255 }
256 SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
257 return rv;
258 }
260 //-----------------------------------------------------------------------------
261 // socket input stream impl
262 //-----------------------------------------------------------------------------
264 nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
265 : mTransport(trans)
266 , mReaderRefCnt(0)
267 , mCondition(NS_OK)
268 , mCallbackFlags(0)
269 , mByteCount(0)
270 {
271 }
273 nsSocketInputStream::~nsSocketInputStream()
274 {
275 }
277 // called on the socket transport thread...
278 //
279 // condition : failure code if socket has been closed
280 //
281 void
282 nsSocketInputStream::OnSocketReady(nsresult condition)
283 {
284 SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%x]\n",
285 this, condition));
287 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
289 nsCOMPtr<nsIInputStreamCallback> callback;
290 {
291 MutexAutoLock lock(mTransport->mLock);
293 // update condition, but be careful not to erase an already
294 // existing error condition.
295 if (NS_SUCCEEDED(mCondition))
296 mCondition = condition;
298 // ignore event if only waiting for closure and not closed.
299 if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
300 callback = mCallback;
301 mCallback = nullptr;
302 mCallbackFlags = 0;
303 }
304 }
306 if (callback)
307 callback->OnInputStreamReady(this);
308 }
310 NS_IMPL_QUERY_INTERFACE(nsSocketInputStream,
311 nsIInputStream,
312 nsIAsyncInputStream)
314 NS_IMETHODIMP_(MozExternalRefCountType)
315 nsSocketInputStream::AddRef()
316 {
317 ++mReaderRefCnt;
318 return mTransport->AddRef();
319 }
321 NS_IMETHODIMP_(MozExternalRefCountType)
322 nsSocketInputStream::Release()
323 {
324 if (--mReaderRefCnt == 0)
325 Close();
326 return mTransport->Release();
327 }
329 NS_IMETHODIMP
330 nsSocketInputStream::Close()
331 {
332 return CloseWithStatus(NS_BASE_STREAM_CLOSED);
333 }
335 NS_IMETHODIMP
336 nsSocketInputStream::Available(uint64_t *avail)
337 {
338 SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
340 *avail = 0;
342 PRFileDesc *fd;
343 {
344 MutexAutoLock lock(mTransport->mLock);
346 if (NS_FAILED(mCondition))
347 return mCondition;
349 fd = mTransport->GetFD_Locked();
350 if (!fd)
351 return NS_OK;
352 }
354 // cannot hold lock while calling NSPR. (worried about the fact that PSM
355 // synchronously proxies notifications over to the UI thread, which could
356 // mistakenly try to re-enter this code.)
357 int32_t n = PR_Available(fd);
359 // PSM does not implement PR_Available() so do a best approximation of it
360 // with MSG_PEEK
361 if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
362 char c;
364 n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
365 SOCKET_LOG(("nsSocketInputStream::Available [this=%p] "
366 "using PEEK backup n=%d]\n", this, n));
367 }
369 nsresult rv;
370 {
371 MutexAutoLock lock(mTransport->mLock);
373 mTransport->ReleaseFD_Locked(fd);
375 if (n >= 0)
376 *avail = n;
377 else {
378 PRErrorCode code = PR_GetError();
379 if (code == PR_WOULD_BLOCK_ERROR)
380 return NS_OK;
381 mCondition = ErrorAccordingToNSPR(code);
382 }
383 rv = mCondition;
384 }
385 if (NS_FAILED(rv))
386 mTransport->OnInputClosed(rv);
387 return rv;
388 }
390 NS_IMETHODIMP
391 nsSocketInputStream::Read(char *buf, uint32_t count, uint32_t *countRead)
392 {
393 SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
395 *countRead = 0;
397 PRFileDesc* fd = nullptr;
398 {
399 MutexAutoLock lock(mTransport->mLock);
401 if (NS_FAILED(mCondition))
402 return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
404 fd = mTransport->GetFD_Locked();
405 if (!fd)
406 return NS_BASE_STREAM_WOULD_BLOCK;
407 }
409 SOCKET_LOG((" calling PR_Read [count=%u]\n", count));
411 // cannot hold lock while calling NSPR. (worried about the fact that PSM
412 // synchronously proxies notifications over to the UI thread, which could
413 // mistakenly try to re-enter this code.)
414 int32_t n = PR_Read(fd, buf, count);
416 SOCKET_LOG((" PR_Read returned [n=%d]\n", n));
418 nsresult rv = NS_OK;
419 {
420 MutexAutoLock lock(mTransport->mLock);
422 #ifdef ENABLE_SOCKET_TRACING
423 if (n > 0)
424 mTransport->TraceInBuf(buf, n);
425 #endif
427 mTransport->ReleaseFD_Locked(fd);
429 if (n > 0)
430 mByteCount += (*countRead = n);
431 else if (n < 0) {
432 PRErrorCode code = PR_GetError();
433 if (code == PR_WOULD_BLOCK_ERROR)
434 return NS_BASE_STREAM_WOULD_BLOCK;
435 mCondition = ErrorAccordingToNSPR(code);
436 }
437 rv = mCondition;
438 }
439 if (NS_FAILED(rv))
440 mTransport->OnInputClosed(rv);
442 // only send this notification if we have indeed read some data.
443 // see bug 196827 for an example of why this is important.
444 if (n > 0)
445 mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);
446 return rv;
447 }
449 NS_IMETHODIMP
450 nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
451 uint32_t count, uint32_t *countRead)
452 {
453 // socket stream is unbuffered
454 return NS_ERROR_NOT_IMPLEMENTED;
455 }
457 NS_IMETHODIMP
458 nsSocketInputStream::IsNonBlocking(bool *nonblocking)
459 {
460 *nonblocking = true;
461 return NS_OK;
462 }
464 NS_IMETHODIMP
465 nsSocketInputStream::CloseWithStatus(nsresult reason)
466 {
467 SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%x]\n", this, reason));
469 // may be called from any thread
471 nsresult rv;
472 {
473 MutexAutoLock lock(mTransport->mLock);
475 if (NS_SUCCEEDED(mCondition))
476 rv = mCondition = reason;
477 else
478 rv = NS_OK;
479 }
480 if (NS_FAILED(rv))
481 mTransport->OnInputClosed(rv);
482 return NS_OK;
483 }
485 NS_IMETHODIMP
486 nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
487 uint32_t flags,
488 uint32_t amount,
489 nsIEventTarget *target)
490 {
491 SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
493 bool hasError = false;
494 {
495 MutexAutoLock lock(mTransport->mLock);
497 if (callback && target) {
498 //
499 // build event proxy
500 //
501 mCallback = NS_NewInputStreamReadyEvent(callback, target);
502 }
503 else
504 mCallback = callback;
505 mCallbackFlags = flags;
507 hasError = NS_FAILED(mCondition);
508 } // unlock mTransport->mLock
510 if (hasError) {
511 // OnSocketEvent will call OnInputStreamReady with an error code after
512 // going through the event loop. We do this because most socket callers
513 // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
514 // callback.
515 mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
516 } else {
517 mTransport->OnInputPending();
518 }
520 return NS_OK;
521 }
523 //-----------------------------------------------------------------------------
524 // socket output stream impl
525 //-----------------------------------------------------------------------------
527 nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
528 : mTransport(trans)
529 , mWriterRefCnt(0)
530 , mCondition(NS_OK)
531 , mCallbackFlags(0)
532 , mByteCount(0)
533 {
534 }
536 nsSocketOutputStream::~nsSocketOutputStream()
537 {
538 }
540 // called on the socket transport thread...
541 //
542 // condition : failure code if socket has been closed
543 //
544 void
545 nsSocketOutputStream::OnSocketReady(nsresult condition)
546 {
547 SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%x]\n",
548 this, condition));
550 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
552 nsCOMPtr<nsIOutputStreamCallback> callback;
553 {
554 MutexAutoLock lock(mTransport->mLock);
556 // update condition, but be careful not to erase an already
557 // existing error condition.
558 if (NS_SUCCEEDED(mCondition))
559 mCondition = condition;
561 // ignore event if only waiting for closure and not closed.
562 if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
563 callback = mCallback;
564 mCallback = nullptr;
565 mCallbackFlags = 0;
566 }
567 }
569 if (callback)
570 callback->OnOutputStreamReady(this);
571 }
573 NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream,
574 nsIOutputStream,
575 nsIAsyncOutputStream)
577 NS_IMETHODIMP_(MozExternalRefCountType)
578 nsSocketOutputStream::AddRef()
579 {
580 ++mWriterRefCnt;
581 return mTransport->AddRef();
582 }
584 NS_IMETHODIMP_(MozExternalRefCountType)
585 nsSocketOutputStream::Release()
586 {
587 if (--mWriterRefCnt == 0)
588 Close();
589 return mTransport->Release();
590 }
592 NS_IMETHODIMP
593 nsSocketOutputStream::Close()
594 {
595 return CloseWithStatus(NS_BASE_STREAM_CLOSED);
596 }
598 NS_IMETHODIMP
599 nsSocketOutputStream::Flush()
600 {
601 return NS_OK;
602 }
604 NS_IMETHODIMP
605 nsSocketOutputStream::Write(const char *buf, uint32_t count, uint32_t *countWritten)
606 {
607 SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count));
609 *countWritten = 0;
611 // A write of 0 bytes can be used to force the initial SSL handshake, so do
612 // not reject that.
614 PRFileDesc* fd = nullptr;
615 {
616 MutexAutoLock lock(mTransport->mLock);
618 if (NS_FAILED(mCondition))
619 return mCondition;
621 fd = mTransport->GetFD_Locked();
622 if (!fd)
623 return NS_BASE_STREAM_WOULD_BLOCK;
624 }
626 SOCKET_LOG((" calling PR_Write [count=%u]\n", count));
628 // cannot hold lock while calling NSPR. (worried about the fact that PSM
629 // synchronously proxies notifications over to the UI thread, which could
630 // mistakenly try to re-enter this code.)
631 int32_t n = PR_Write(fd, buf, count);
633 SOCKET_LOG((" PR_Write returned [n=%d]\n", n));
635 nsresult rv = NS_OK;
636 {
637 MutexAutoLock lock(mTransport->mLock);
639 #ifdef ENABLE_SOCKET_TRACING
640 if (n > 0)
641 mTransport->TraceOutBuf(buf, n);
642 #endif
644 mTransport->ReleaseFD_Locked(fd);
646 if (n > 0)
647 mByteCount += (*countWritten = n);
648 else if (n < 0) {
649 PRErrorCode code = PR_GetError();
650 if (code == PR_WOULD_BLOCK_ERROR)
651 return NS_BASE_STREAM_WOULD_BLOCK;
652 mCondition = ErrorAccordingToNSPR(code);
653 }
654 rv = mCondition;
655 }
656 if (NS_FAILED(rv))
657 mTransport->OnOutputClosed(rv);
659 // only send this notification if we have indeed written some data.
660 // see bug 196827 for an example of why this is important.
661 if (n > 0)
662 mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);
663 return rv;
664 }
666 NS_IMETHODIMP
667 nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
668 uint32_t count, uint32_t *countRead)
669 {
670 // socket stream is unbuffered
671 return NS_ERROR_NOT_IMPLEMENTED;
672 }
674 NS_METHOD
675 nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
676 void *closure,
677 const char *fromSegment,
678 uint32_t offset,
679 uint32_t count,
680 uint32_t *countRead)
681 {
682 nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
683 return self->Write(fromSegment, count, countRead);
684 }
686 NS_IMETHODIMP
687 nsSocketOutputStream::WriteFrom(nsIInputStream *stream, uint32_t count, uint32_t *countRead)
688 {
689 return stream->ReadSegments(WriteFromSegments, this, count, countRead);
690 }
692 NS_IMETHODIMP
693 nsSocketOutputStream::IsNonBlocking(bool *nonblocking)
694 {
695 *nonblocking = true;
696 return NS_OK;
697 }
699 NS_IMETHODIMP
700 nsSocketOutputStream::CloseWithStatus(nsresult reason)
701 {
702 SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%x]\n", this, reason));
704 // may be called from any thread
706 nsresult rv;
707 {
708 MutexAutoLock lock(mTransport->mLock);
710 if (NS_SUCCEEDED(mCondition))
711 rv = mCondition = reason;
712 else
713 rv = NS_OK;
714 }
715 if (NS_FAILED(rv))
716 mTransport->OnOutputClosed(rv);
717 return NS_OK;
718 }
720 NS_IMETHODIMP
721 nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
722 uint32_t flags,
723 uint32_t amount,
724 nsIEventTarget *target)
725 {
726 SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
728 {
729 MutexAutoLock lock(mTransport->mLock);
731 if (callback && target) {
732 //
733 // build event proxy
734 //
735 mCallback = NS_NewOutputStreamReadyEvent(callback, target);
736 }
737 else
738 mCallback = callback;
740 mCallbackFlags = flags;
741 }
742 mTransport->OnOutputPending();
743 return NS_OK;
744 }
746 //-----------------------------------------------------------------------------
747 // socket transport impl
748 //-----------------------------------------------------------------------------
750 nsSocketTransport::nsSocketTransport()
751 : mTypes(nullptr)
752 , mTypeCount(0)
753 , mPort(0)
754 , mHttpsProxy(false)
755 , mProxyUse(false)
756 , mProxyTransparent(false)
757 , mProxyTransparentResolvesHost(false)
758 , mConnectionFlags(0)
759 , mState(STATE_CLOSED)
760 , mAttached(false)
761 , mInputClosed(true)
762 , mOutputClosed(true)
763 , mResolving(false)
764 , mNetAddrIsSet(false)
765 , mLock("nsSocketTransport.mLock")
766 , mFD(MOZ_THIS_IN_INITIALIZER_LIST())
767 , mFDref(0)
768 , mFDconnected(false)
769 , mSocketTransportService(gSocketTransportService)
770 , mInput(MOZ_THIS_IN_INITIALIZER_LIST())
771 , mOutput(MOZ_THIS_IN_INITIALIZER_LIST())
772 , mQoSBits(0x00)
773 , mKeepaliveEnabled(false)
774 , mKeepaliveIdleTimeS(-1)
775 , mKeepaliveRetryIntervalS(-1)
776 , mKeepaliveProbeCount(-1)
777 {
778 SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
780 mTimeouts[TIMEOUT_CONNECT] = UINT16_MAX; // no timeout
781 mTimeouts[TIMEOUT_READ_WRITE] = UINT16_MAX; // no timeout
782 }
784 nsSocketTransport::~nsSocketTransport()
785 {
786 SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
788 // cleanup socket type info
789 if (mTypes) {
790 uint32_t i;
791 for (i=0; i<mTypeCount; ++i)
792 PL_strfree(mTypes[i]);
793 free(mTypes);
794 }
795 }
797 nsresult
798 nsSocketTransport::Init(const char **types, uint32_t typeCount,
799 const nsACString &host, uint16_t port,
800 nsIProxyInfo *givenProxyInfo)
801 {
802 MOZ_EVENT_TRACER_NAME_OBJECT(this, host.BeginReading());
804 nsCOMPtr<nsProxyInfo> proxyInfo;
805 if (givenProxyInfo) {
806 proxyInfo = do_QueryInterface(givenProxyInfo);
807 NS_ENSURE_ARG(proxyInfo);
808 }
810 // init socket type info
812 mPort = port;
813 mHost = host;
815 const char *proxyType = nullptr;
816 if (proxyInfo) {
817 mProxyInfo = proxyInfo;
818 // grab proxy type (looking for "socks" for example)
819 proxyType = proxyInfo->Type();
820 if (proxyType && (strcmp(proxyType, "http") == 0 ||
821 strcmp(proxyType, "direct") == 0 ||
822 strcmp(proxyType, "unknown") == 0))
823 proxyType = nullptr;
825 mProxyUse = true;
826 // check that we don't have a proxyInfo without proxy
827 nsCString proxyHost;
828 proxyInfo->GetHost(proxyHost);
829 if (!proxyType || proxyHost.IsEmpty()) {
830 mProxyUse = false;
831 }
832 }
834 SOCKET_LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s]\n",
835 this, mHost.get(), mPort, mProxyUse ? "yes" : "no"));
837 // include proxy type as a socket type if proxy type is not "http"
838 mTypeCount = typeCount + (proxyType != nullptr);
839 if (!mTypeCount)
840 return NS_OK;
842 // if we have socket types, then the socket provider service had
843 // better exist!
844 nsresult rv;
845 nsCOMPtr<nsISocketProviderService> spserv =
846 do_GetService(kSocketProviderServiceCID, &rv);
847 if (NS_FAILED(rv)) return rv;
849 mTypes = (char **) malloc(mTypeCount * sizeof(char *));
850 if (!mTypes)
851 return NS_ERROR_OUT_OF_MEMORY;
853 // now verify that each socket type has a registered socket provider.
854 for (uint32_t i = 0, type = 0; i < mTypeCount; ++i) {
855 // store socket types
856 if (i == 0 && proxyType)
857 mTypes[i] = PL_strdup(proxyType);
858 else
859 mTypes[i] = PL_strdup(types[type++]);
861 if (!mTypes[i]) {
862 mTypeCount = i;
863 return NS_ERROR_OUT_OF_MEMORY;
864 }
865 nsCOMPtr<nsISocketProvider> provider;
866 rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
867 if (NS_FAILED(rv)) {
868 NS_WARNING("no registered socket provider");
869 return rv;
870 }
872 // note if socket type corresponds to a transparent proxy
873 // XXX don't hardcode SOCKS here (use proxy info's flags instead).
874 if ((strcmp(mTypes[i], "socks") == 0) ||
875 (strcmp(mTypes[i], "socks4") == 0)) {
876 mProxyTransparent = true;
878 if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
879 // we want the SOCKS layer to send the hostname
880 // and port to the proxy and let it do the DNS.
881 mProxyTransparentResolvesHost = true;
882 }
883 }
884 }
886 return NS_OK;
887 }
889 nsresult
890 nsSocketTransport::InitWithFilename(const char *filename)
891 {
892 #if defined(XP_UNIX)
893 size_t filenameLength = strlen(filename);
895 if (filenameLength > sizeof(mNetAddr.local.path) - 1)
896 return NS_ERROR_FILE_NAME_TOO_LONG;
898 mHost.Assign(filename);
899 mPort = 0;
900 mTypeCount = 0;
902 mNetAddr.local.family = AF_LOCAL;
903 memcpy(mNetAddr.local.path, filename, filenameLength);
904 mNetAddr.local.path[filenameLength] = '\0';
905 mNetAddrIsSet = true;
907 return NS_OK;
908 #else
909 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
910 #endif
911 }
913 nsresult
914 nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr)
915 {
916 NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
918 char buf[kNetAddrMaxCStrBufSize];
919 NetAddrToString(addr, buf, sizeof(buf));
920 mHost.Assign(buf);
922 uint16_t port;
923 if (addr->raw.family == AF_INET)
924 port = addr->inet.port;
925 else if (addr->raw.family == AF_INET6)
926 port = addr->inet6.port;
927 else
928 port = 0;
929 mPort = ntohs(port);
931 memcpy(&mNetAddr, addr, sizeof(NetAddr));
933 mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
934 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
935 mState = STATE_TRANSFERRING;
936 mNetAddrIsSet = true;
938 {
939 MutexAutoLock lock(mLock);
941 mFD = fd;
942 mFDref = 1;
943 mFDconnected = 1;
944 }
946 // make sure new socket is non-blocking
947 PRSocketOptionData opt;
948 opt.option = PR_SockOpt_Nonblocking;
949 opt.value.non_blocking = true;
950 PR_SetSocketOption(fd, &opt);
952 SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
953 this, mHost.get(), mPort));
955 // jump to InitiateSocket to get ourselves attached to the STS poll list.
956 return PostEvent(MSG_RETRY_INIT_SOCKET);
957 }
959 nsresult
960 nsSocketTransport::PostEvent(uint32_t type, nsresult status, nsISupports *param)
961 {
962 SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
963 this, type, status, param));
965 nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
966 if (!event)
967 return NS_ERROR_OUT_OF_MEMORY;
969 return mSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
970 }
972 void
973 nsSocketTransport::SendStatus(nsresult status)
974 {
975 SOCKET_LOG(("nsSocketTransport::SendStatus [this=%p status=%x]\n", this, status));
977 nsCOMPtr<nsITransportEventSink> sink;
978 uint64_t progress;
979 {
980 MutexAutoLock lock(mLock);
981 sink = mEventSink;
982 switch (status) {
983 case NS_NET_STATUS_SENDING_TO:
984 progress = mOutput.ByteCount();
985 break;
986 case NS_NET_STATUS_RECEIVING_FROM:
987 progress = mInput.ByteCount();
988 break;
989 default:
990 progress = 0;
991 break;
992 }
993 }
994 if (sink)
995 sink->OnTransportStatus(this, status, progress, UINT64_MAX);
996 }
998 nsresult
999 nsSocketTransport::ResolveHost()
1000 {
1001 SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n",
1002 this, SocketHost().get(), SocketPort(),
1003 mConnectionFlags & nsSocketTransport::BYPASS_CACHE ?
1004 " bypass cache" : ""));
1006 nsresult rv;
1008 if (mProxyUse) {
1009 if (!mProxyTransparent || mProxyTransparentResolvesHost) {
1010 #if defined(XP_UNIX)
1011 NS_ABORT_IF_FALSE(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
1012 "Unix domain sockets can't be used with proxies");
1013 #endif
1014 // When not resolving mHost locally, we still want to ensure that
1015 // it only contains valid characters. See bug 304904 for details.
1016 if (!net_IsValidHostName(mHost))
1017 return NS_ERROR_UNKNOWN_HOST;
1018 }
1019 if (mProxyTransparentResolvesHost) {
1020 // Name resolution is done on the server side. Just pretend
1021 // client resolution is complete, this will get picked up later.
1022 // since we don't need to do DNS now, we bypass the resolving
1023 // step by initializing mNetAddr to an empty address, but we
1024 // must keep the port. The SOCKS IO layer will use the hostname
1025 // we send it when it's created, rather than the empty address
1026 // we send with the connect call.
1027 mState = STATE_RESOLVING;
1028 mNetAddr.raw.family = AF_INET;
1029 mNetAddr.inet.port = htons(SocketPort());
1030 mNetAddr.inet.ip = htonl(INADDR_ANY);
1031 return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
1032 }
1033 }
1035 nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
1036 if (NS_FAILED(rv)) return rv;
1038 mResolving = true;
1040 uint32_t dnsFlags = 0;
1041 if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
1042 dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
1043 if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
1044 dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
1045 if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
1046 dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
1048 NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
1049 !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
1050 "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
1052 SendStatus(NS_NET_STATUS_RESOLVING_HOST);
1053 rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nullptr,
1054 getter_AddRefs(mDNSRequest));
1055 if (NS_SUCCEEDED(rv)) {
1056 SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
1057 mState = STATE_RESOLVING;
1058 }
1059 return rv;
1060 }
1062 nsresult
1063 nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &usingSSL)
1064 {
1065 SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1067 nsresult rv;
1069 proxyTransparent = false;
1070 usingSSL = false;
1072 if (mTypeCount == 0) {
1073 fd = PR_OpenTCPSocket(mNetAddr.raw.family);
1074 rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1075 }
1076 else {
1077 #if defined(XP_UNIX)
1078 NS_ABORT_IF_FALSE(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
1079 "Unix domain sockets can't be used with socket types");
1080 #endif
1082 fd = nullptr;
1084 nsCOMPtr<nsISocketProviderService> spserv =
1085 do_GetService(kSocketProviderServiceCID, &rv);
1086 if (NS_FAILED(rv)) return rv;
1088 const char *host = mHost.get();
1089 int32_t port = (int32_t) mPort;
1090 uint32_t proxyFlags = 0;
1091 nsCOMPtr<nsIProxyInfo> proxy = mProxyInfo;
1093 uint32_t i;
1094 for (i=0; i<mTypeCount; ++i) {
1095 nsCOMPtr<nsISocketProvider> provider;
1097 SOCKET_LOG((" pushing io layer [%u:%s]\n", i, mTypes[i]));
1099 rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
1100 if (NS_FAILED(rv))
1101 break;
1103 if (mProxyTransparentResolvesHost)
1104 proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
1106 if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT)
1107 proxyFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
1109 if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE)
1110 proxyFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
1113 nsCOMPtr<nsISupports> secinfo;
1114 if (i == 0) {
1115 // if this is the first type, we'll want the
1116 // service to allocate a new socket
1117 nsCString proxyHost;
1118 GetHost(proxyHost);
1119 int32_t proxyPort;
1120 GetPort(&proxyPort);
1121 rv = provider->NewSocket(mNetAddr.raw.family,
1122 mHttpsProxy ? proxyHost.get() : host,
1123 mHttpsProxy ? proxyPort : port,
1124 proxy,
1125 proxyFlags, &fd,
1126 getter_AddRefs(secinfo));
1128 if (NS_SUCCEEDED(rv) && !fd) {
1129 NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
1130 rv = NS_ERROR_UNEXPECTED;
1131 }
1132 }
1133 else {
1134 // the socket has already been allocated,
1135 // so we just want the service to add itself
1136 // to the stack (such as pushing an io layer)
1137 rv = provider->AddToSocket(mNetAddr.raw.family,
1138 host, port, proxy,
1139 proxyFlags, fd,
1140 getter_AddRefs(secinfo));
1141 }
1142 // proxyFlags = 0; not used below this point...
1143 if (NS_FAILED(rv))
1144 break;
1146 // if the service was ssl or starttls, we want to hold onto the socket info
1147 bool isSSL = (strcmp(mTypes[i], "ssl") == 0);
1148 if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
1149 // remember security info and give notification callbacks to PSM...
1150 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1151 {
1152 MutexAutoLock lock(mLock);
1153 mSecInfo = secinfo;
1154 callbacks = mCallbacks;
1155 SOCKET_LOG((" [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
1156 }
1157 // don't call into PSM while holding mLock!!
1158 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
1159 if (secCtrl)
1160 secCtrl->SetNotificationCallbacks(callbacks);
1161 // remember if socket type is SSL so we can ProxyStartSSL if need be.
1162 usingSSL = isSSL;
1163 }
1164 else if ((strcmp(mTypes[i], "socks") == 0) ||
1165 (strcmp(mTypes[i], "socks4") == 0)) {
1166 // since socks is transparent, any layers above
1167 // it do not have to worry about proxy stuff
1168 proxy = nullptr;
1169 proxyTransparent = true;
1170 }
1171 }
1173 if (NS_FAILED(rv)) {
1174 SOCKET_LOG((" error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
1175 if (fd)
1176 PR_Close(fd);
1177 }
1178 }
1180 return rv;
1181 }
1183 nsresult
1184 nsSocketTransport::InitiateSocket()
1185 {
1186 SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1188 static bool crashOnNonLocalConnections = !!getenv("MOZ_DISABLE_NONLOCAL_CONNECTIONS");
1190 nsresult rv;
1191 bool isLocal;
1192 IsLocal(&isLocal);
1194 if (gIOService->IsOffline()) {
1195 if (!isLocal)
1196 return NS_ERROR_OFFLINE;
1197 } else if (!isLocal) {
1198 if (NS_SUCCEEDED(mCondition) &&
1199 crashOnNonLocalConnections &&
1200 !(IsIPAddrAny(&mNetAddr) || IsIPAddrLocal(&mNetAddr))) {
1201 nsAutoCString ipaddr;
1202 nsRefPtr<nsNetAddr> netaddr = new nsNetAddr(&mNetAddr);
1203 netaddr->GetAddress(ipaddr);
1204 fprintf_stderr(stderr,
1205 "FATAL ERROR: Non-local network connections are disabled and a connection "
1206 "attempt to %s (%s) was made.\nYou should only access hostnames "
1207 "available via the test networking proxy (if running mochitests) "
1208 "or from a test-specific httpd.js server (if running xpcshell tests). "
1209 "Browser services should be disabled or redirected to a local server.\n",
1210 mHost.get(), ipaddr.get());
1211 MOZ_CRASH("Attempting to connect to non-local address!");
1212 }
1213 }
1215 // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
1216 // connected - Bug 853423.
1217 if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
1218 IsIPAddrLocal(&mNetAddr)) {
1219 #ifdef PR_LOGGING
1220 if (SOCKET_LOG_ENABLED()) {
1221 nsAutoCString netAddrCString;
1222 netAddrCString.SetCapacity(kIPv6CStrBufSize);
1223 if (!NetAddrToString(&mNetAddr,
1224 netAddrCString.BeginWriting(),
1225 kIPv6CStrBufSize))
1226 netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
1227 nsCString proxyHost;
1228 GetHost(proxyHost);
1229 int32_t proxyPort;
1230 GetPort(&proxyPort);
1231 SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
1232 "speculative connection for host [%s:%d] proxy "
1233 "[%s:%d] with Local IP address [%s]",
1234 mHost.get(), mPort, proxyHost.get(), proxyPort,
1235 netAddrCString.get()));
1236 }
1237 #endif
1238 return NS_ERROR_CONNECTION_REFUSED;
1239 }
1241 //
1242 // find out if it is going to be ok to attach another socket to the STS.
1243 // if not then we have to wait for the STS to tell us that it is ok.
1244 // the notification is asynchronous, which means that when we could be
1245 // in a race to call AttachSocket once notified. for this reason, when
1246 // we get notified, we just re-enter this function. as a result, we are
1247 // sure to ask again before calling AttachSocket. in this way we deal
1248 // with the race condition. though it isn't the most elegant solution,
1249 // it is far simpler than trying to build a system that would guarantee
1250 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1251 // 194402 for more info.
1252 //
1253 if (!mSocketTransportService->CanAttachSocket()) {
1254 nsCOMPtr<nsIRunnable> event =
1255 new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
1256 if (!event)
1257 return NS_ERROR_OUT_OF_MEMORY;
1258 return mSocketTransportService->NotifyWhenCanAttachSocket(event);
1259 }
1261 //
1262 // if we already have a connected socket, then just attach and return.
1263 //
1264 if (mFD.IsInitialized()) {
1265 rv = mSocketTransportService->AttachSocket(mFD, this);
1266 if (NS_SUCCEEDED(rv))
1267 mAttached = true;
1268 return rv;
1269 }
1271 //
1272 // create new socket fd, push io layers, etc.
1273 //
1274 PRFileDesc *fd;
1275 bool proxyTransparent;
1276 bool usingSSL;
1278 rv = BuildSocket(fd, proxyTransparent, usingSSL);
1279 if (NS_FAILED(rv)) {
1280 SOCKET_LOG((" BuildSocket failed [rv=%x]\n", rv));
1281 return rv;
1282 }
1284 // Attach network activity monitor
1285 mozilla::net::NetworkActivityMonitor::AttachIOLayer(fd);
1287 PRStatus status;
1289 // Make the socket non-blocking...
1290 PRSocketOptionData opt;
1291 opt.option = PR_SockOpt_Nonblocking;
1292 opt.value.non_blocking = true;
1293 status = PR_SetSocketOption(fd, &opt);
1294 NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
1296 // disable the nagle algorithm - if we rely on it to coalesce writes into
1297 // full packets the final packet of a multi segment POST/PUT or pipeline
1298 // sequence is delayed a full rtt
1299 opt.option = PR_SockOpt_NoDelay;
1300 opt.value.no_delay = true;
1301 PR_SetSocketOption(fd, &opt);
1303 // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1304 // The Windows default of 8KB is too small and as of vista sp1, autotuning
1305 // only applies to receive window
1306 int32_t sndBufferSize;
1307 mSocketTransportService->GetSendBufferSize(&sndBufferSize);
1308 if (sndBufferSize > 0) {
1309 opt.option = PR_SockOpt_SendBufferSize;
1310 opt.value.send_buffer_size = sndBufferSize;
1311 PR_SetSocketOption(fd, &opt);
1312 }
1314 if (mQoSBits) {
1315 opt.option = PR_SockOpt_IpTypeOfService;
1316 opt.value.tos = mQoSBits;
1317 PR_SetSocketOption(fd, &opt);
1318 }
1320 // inform socket transport about this newly created socket...
1321 rv = mSocketTransportService->AttachSocket(fd, this);
1322 if (NS_FAILED(rv)) {
1323 PR_Close(fd);
1324 return rv;
1325 }
1326 mAttached = true;
1328 // assign mFD so that we can properly handle OnSocketDetached before we've
1329 // established a connection.
1330 {
1331 MutexAutoLock lock(mLock);
1332 mFD = fd;
1333 mFDref = 1;
1334 mFDconnected = false;
1335 }
1337 SOCKET_LOG((" advancing to STATE_CONNECTING\n"));
1338 mState = STATE_CONNECTING;
1339 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1340 SendStatus(NS_NET_STATUS_CONNECTING_TO);
1342 #if defined(PR_LOGGING)
1343 if (SOCKET_LOG_ENABLED()) {
1344 char buf[kNetAddrMaxCStrBufSize];
1345 NetAddrToString(&mNetAddr, buf, sizeof(buf));
1346 SOCKET_LOG((" trying address: %s\n", buf));
1347 }
1348 #endif
1350 //
1351 // Initiate the connect() to the host...
1352 //
1353 PRNetAddr prAddr;
1354 NetAddrToPRNetAddr(&mNetAddr, &prAddr);
1356 MOZ_EVENT_TRACER_EXEC(this, "net::tcp::connect");
1357 status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
1358 if (status == PR_SUCCESS) {
1359 //
1360 // we are connected!
1361 //
1362 OnSocketConnected();
1363 }
1364 else {
1365 PRErrorCode code = PR_GetError();
1366 #if defined(TEST_CONNECT_ERRORS)
1367 code = RandomizeConnectError(code);
1368 #endif
1369 //
1370 // If the PR_Connect(...) would block, then poll for a connection.
1371 //
1372 if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
1373 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1374 //
1375 // If the socket is already connected, then return success...
1376 //
1377 else if (PR_IS_CONNECTED_ERROR == code) {
1378 //
1379 // we are connected!
1380 //
1381 OnSocketConnected();
1383 if (mSecInfo && mProxyUse && proxyTransparent && usingSSL) {
1384 // if the connection phase is finished, and the ssl layer has
1385 // been pushed, and we were proxying (transparently; ie. nothing
1386 // has to happen in the protocol layer above us), it's time for
1387 // the ssl to start doing it's thing.
1388 nsCOMPtr<nsISSLSocketControl> secCtrl =
1389 do_QueryInterface(mSecInfo);
1390 if (secCtrl) {
1391 SOCKET_LOG((" calling ProxyStartSSL()\n"));
1392 secCtrl->ProxyStartSSL();
1393 }
1394 // XXX what if we were forced to poll on the socket for a successful
1395 // connection... wouldn't we need to call ProxyStartSSL after a call
1396 // to PR_ConnectContinue indicates that we are connected?
1397 //
1398 // XXX this appears to be what the old socket transport did. why
1399 // isn't this broken?
1400 }
1401 }
1402 //
1403 // A SOCKS request was rejected; get the actual error code from
1404 // the OS error
1405 //
1406 else if (PR_UNKNOWN_ERROR == code &&
1407 mProxyUse && mProxyTransparent) {
1408 code = PR_GetOSError();
1409 rv = ErrorAccordingToNSPR(code);
1410 }
1411 //
1412 // The connection was refused...
1413 //
1414 else {
1415 rv = ErrorAccordingToNSPR(code);
1416 if (rv == NS_ERROR_CONNECTION_REFUSED && mProxyUse)
1417 rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1418 }
1419 }
1420 return rv;
1421 }
1423 bool
1424 nsSocketTransport::RecoverFromError()
1425 {
1426 NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
1428 SOCKET_LOG(("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%x]\n",
1429 this, mState, mCondition));
1431 #if defined(XP_UNIX)
1432 // Unix domain connections don't have multiple addresses to try,
1433 // so the recovery techniques here don't apply.
1434 if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL)
1435 return false;
1436 #endif
1438 // can only recover from errors in these states
1439 if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
1440 return false;
1442 nsresult rv;
1444 // OK to check this outside mLock
1445 NS_ASSERTION(!mFDconnected, "socket should not be connected");
1447 // all connection failures need to be reported to DNS so that the next
1448 // time we will use a different address if available.
1449 if (mState == STATE_CONNECTING && mDNSRecord) {
1450 mDNSRecord->ReportUnusable(SocketPort());
1451 }
1453 // can only recover from these errors
1454 if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
1455 mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
1456 mCondition != NS_ERROR_NET_TIMEOUT &&
1457 mCondition != NS_ERROR_UNKNOWN_HOST &&
1458 mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
1459 return false;
1461 bool tryAgain = false;
1463 if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4) &&
1464 mCondition == NS_ERROR_UNKNOWN_HOST &&
1465 mState == STATE_RESOLVING &&
1466 !mProxyTransparentResolvesHost) {
1467 SOCKET_LOG((" trying lookup again with both ipv4/ipv6 enabled\n"));
1468 mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
1469 tryAgain = true;
1470 }
1472 // try next ip address only if past the resolver stage...
1473 if (mState == STATE_CONNECTING && mDNSRecord) {
1474 nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1475 if (NS_SUCCEEDED(rv)) {
1476 SOCKET_LOG((" trying again with next ip address\n"));
1477 tryAgain = true;
1478 }
1479 else if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4)) {
1480 // Drop state to closed. This will trigger new round of DNS
1481 // resolving bellow.
1482 // XXX Could be optimized to only switch the flags to save duplicate
1483 // connection attempts.
1484 SOCKET_LOG((" failed to connect all ipv4-only or ipv6-only hosts,"
1485 " trying lookup/connect again with both ipv4/ipv6\n"));
1486 mState = STATE_CLOSED;
1487 mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4);
1488 tryAgain = true;
1489 }
1490 }
1492 #if defined(XP_WIN)
1493 // If not trying next address, try to make a connection using dialup.
1494 // Retry if that connection is made.
1495 if (!tryAgain) {
1496 bool autodialEnabled;
1497 mSocketTransportService->GetAutodialEnabled(&autodialEnabled);
1498 if (autodialEnabled) {
1499 tryAgain = nsNativeConnectionHelper::OnConnectionFailed(
1500 NS_ConvertUTF8toUTF16(SocketHost()).get());
1501 }
1502 }
1503 #endif
1505 // prepare to try again.
1506 if (tryAgain) {
1507 uint32_t msg;
1509 if (mState == STATE_CONNECTING) {
1510 mState = STATE_RESOLVING;
1511 msg = MSG_DNS_LOOKUP_COMPLETE;
1512 }
1513 else {
1514 mState = STATE_CLOSED;
1515 msg = MSG_ENSURE_CONNECT;
1516 }
1518 rv = PostEvent(msg, NS_OK);
1519 if (NS_FAILED(rv))
1520 tryAgain = false;
1521 }
1523 return tryAgain;
1524 }
1526 // called on the socket thread only
1527 void
1528 nsSocketTransport::OnMsgInputClosed(nsresult reason)
1529 {
1530 SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%p reason=%x]\n",
1531 this, reason));
1533 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1535 mInputClosed = true;
1536 // check if event should affect entire transport
1537 if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
1538 mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
1539 else if (mOutputClosed)
1540 mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1541 else {
1542 if (mState == STATE_TRANSFERRING)
1543 mPollFlags &= ~PR_POLL_READ;
1544 mInput.OnSocketReady(reason);
1545 }
1546 }
1548 // called on the socket thread only
1549 void
1550 nsSocketTransport::OnMsgOutputClosed(nsresult reason)
1551 {
1552 SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%p reason=%x]\n",
1553 this, reason));
1555 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1557 mOutputClosed = true;
1558 // check if event should affect entire transport
1559 if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
1560 mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
1561 else if (mInputClosed)
1562 mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1563 else {
1564 if (mState == STATE_TRANSFERRING)
1565 mPollFlags &= ~PR_POLL_WRITE;
1566 mOutput.OnSocketReady(reason);
1567 }
1568 }
1570 void
1571 nsSocketTransport::OnSocketConnected()
1572 {
1573 SOCKET_LOG((" advancing to STATE_TRANSFERRING\n"));
1575 mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
1576 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1577 mState = STATE_TRANSFERRING;
1579 // Set the mNetAddrIsSet flag only when state has reached TRANSFERRING
1580 // because we need to make sure its value does not change due to failover
1581 mNetAddrIsSet = true;
1583 // assign mFD (must do this within the transport lock), but take care not
1584 // to trample over mFDref if mFD is already set.
1585 {
1586 MutexAutoLock lock(mLock);
1587 NS_ASSERTION(mFD.IsInitialized(), "no socket");
1588 NS_ASSERTION(mFDref == 1, "wrong socket ref count");
1589 mFDconnected = true;
1590 }
1592 // Ensure keepalive is configured correctly if previously enabled.
1593 if (mKeepaliveEnabled) {
1594 nsresult rv = SetKeepaliveEnabledInternal(true);
1595 if (NS_WARN_IF(NS_FAILED(rv))) {
1596 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
1597 }
1598 }
1600 MOZ_EVENT_TRACER_DONE(this, "net::tcp::connect");
1602 SendStatus(NS_NET_STATUS_CONNECTED_TO);
1603 }
1605 PRFileDesc *
1606 nsSocketTransport::GetFD_Locked()
1607 {
1608 mLock.AssertCurrentThreadOwns();
1610 // mFD is not available to the streams while disconnected.
1611 if (!mFDconnected)
1612 return nullptr;
1614 if (mFD.IsInitialized())
1615 mFDref++;
1617 return mFD;
1618 }
1620 class ThunkPRClose : public nsRunnable
1621 {
1622 public:
1623 ThunkPRClose(PRFileDesc *fd) : mFD(fd) {}
1625 NS_IMETHOD Run()
1626 {
1627 PR_Close(mFD);
1628 return NS_OK;
1629 }
1630 private:
1631 PRFileDesc *mFD;
1632 };
1634 void
1635 STS_PRCloseOnSocketTransport(PRFileDesc *fd)
1636 {
1637 if (gSocketTransportService) {
1638 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1639 // FIX - Should use RUN_ON_THREAD once it's generally available
1640 // RUN_ON_THREAD(gSocketThread,WrapRunnableNM(&PR_Close, mFD);
1641 gSocketTransportService->Dispatch(new ThunkPRClose(fd), NS_DISPATCH_NORMAL);
1642 } else {
1643 // something horrible has happened
1644 NS_ASSERTION(gSocketTransportService, "No STS service");
1645 }
1646 }
1648 void
1649 nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
1650 {
1651 mLock.AssertCurrentThreadOwns();
1653 NS_ASSERTION(mFD == fd, "wrong fd");
1654 SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %d\n", mFDref));
1656 if (--mFDref == 0) {
1657 if (PR_GetCurrentThread() == gSocketThread) {
1658 SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
1659 PR_Close(mFD);
1660 } else {
1661 // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
1662 STS_PRCloseOnSocketTransport(mFD);
1663 }
1664 mFD = nullptr;
1665 }
1666 }
1668 //-----------------------------------------------------------------------------
1669 // socket event handler impl
1671 void
1672 nsSocketTransport::OnSocketEvent(uint32_t type, nsresult status, nsISupports *param)
1673 {
1674 SOCKET_LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
1675 this, type, status, param));
1677 if (NS_FAILED(mCondition)) {
1678 // block event since we're apparently already dead.
1679 SOCKET_LOG((" blocking event [condition=%x]\n", mCondition));
1680 //
1681 // notify input/output streams in case either has a pending notify.
1682 //
1683 mInput.OnSocketReady(mCondition);
1684 mOutput.OnSocketReady(mCondition);
1685 return;
1686 }
1688 switch (type) {
1689 case MSG_ENSURE_CONNECT:
1690 SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));
1691 //
1692 // ensure that we have created a socket, attached it, and have a
1693 // connection.
1694 //
1695 if (mState == STATE_CLOSED) {
1696 // Unix domain sockets are ready to connect; mNetAddr is all we
1697 // need. Internet address families require a DNS lookup (or possibly
1698 // several) before we can connect.
1699 #if defined(XP_UNIX)
1700 if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL)
1701 mCondition = InitiateSocket();
1702 else
1703 #endif
1704 mCondition = ResolveHost();
1706 } else {
1707 SOCKET_LOG((" ignoring redundant event\n"));
1708 }
1709 break;
1711 case MSG_DNS_LOOKUP_COMPLETE:
1712 if (mDNSRequest) // only send this if we actually resolved anything
1713 SendStatus(NS_NET_STATUS_RESOLVED_HOST);
1715 SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1716 mDNSRequest = 0;
1717 if (param) {
1718 mDNSRecord = static_cast<nsIDNSRecord *>(param);
1719 mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1720 }
1721 // status contains DNS lookup status
1722 if (NS_FAILED(status)) {
1723 // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1724 // proxy host is not found, so we fixup the error code.
1725 // For SOCKS proxies (mProxyTransparent == true), the socket
1726 // transport resolves the real host here, so there's no fixup
1727 // (see bug 226943).
1728 if (status == NS_ERROR_UNKNOWN_HOST && !mProxyTransparent &&
1729 mProxyUse)
1730 mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
1731 else
1732 mCondition = status;
1733 }
1734 else if (mState == STATE_RESOLVING)
1735 mCondition = InitiateSocket();
1736 break;
1738 case MSG_RETRY_INIT_SOCKET:
1739 mCondition = InitiateSocket();
1740 break;
1742 case MSG_INPUT_CLOSED:
1743 SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
1744 OnMsgInputClosed(status);
1745 break;
1747 case MSG_INPUT_PENDING:
1748 SOCKET_LOG((" MSG_INPUT_PENDING\n"));
1749 OnMsgInputPending();
1750 break;
1752 case MSG_OUTPUT_CLOSED:
1753 SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
1754 OnMsgOutputClosed(status);
1755 break;
1757 case MSG_OUTPUT_PENDING:
1758 SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
1759 OnMsgOutputPending();
1760 break;
1761 case MSG_TIMEOUT_CHANGED:
1762 SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
1763 mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
1764 ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
1765 break;
1766 default:
1767 SOCKET_LOG((" unhandled event!\n"));
1768 }
1770 if (NS_FAILED(mCondition)) {
1771 SOCKET_LOG((" after event [this=%p cond=%x]\n", this, mCondition));
1772 if (!mAttached) // need to process this error ourselves...
1773 OnSocketDetached(nullptr);
1774 }
1775 else if (mPollFlags == PR_POLL_EXCEPT)
1776 mPollFlags = 0; // make idle
1777 }
1779 //-----------------------------------------------------------------------------
1780 // socket handler impl
1782 void
1783 nsSocketTransport::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
1784 {
1785 SOCKET_LOG(("nsSocketTransport::OnSocketReady [this=%p outFlags=%hd]\n",
1786 this, outFlags));
1788 if (outFlags == -1) {
1789 SOCKET_LOG(("socket timeout expired\n"));
1790 mCondition = NS_ERROR_NET_TIMEOUT;
1791 return;
1792 }
1794 if (mState == STATE_TRANSFERRING) {
1795 // if waiting to write and socket is writable or hit an exception.
1796 if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
1797 // assume that we won't need to poll any longer (the stream will
1798 // request that we poll again if it is still pending).
1799 mPollFlags &= ~PR_POLL_WRITE;
1800 mOutput.OnSocketReady(NS_OK);
1801 }
1802 // if waiting to read and socket is readable or hit an exception.
1803 if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
1804 // assume that we won't need to poll any longer (the stream will
1805 // request that we poll again if it is still pending).
1806 mPollFlags &= ~PR_POLL_READ;
1807 mInput.OnSocketReady(NS_OK);
1808 }
1809 // Update poll timeout in case it was changed
1810 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1811 }
1813 //STATE_SENDINGGET: handshake proceeded to state "sent connect"
1814 //one more poll to OnSocketReady will trigger the get request, and state STATE_SENTGET
1815 //STATE_SENTGET: continue and finish handshake
1816 else if (mState == STATE_SENDINGGET) {
1817 if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
1818 mOutput.OnSocketReady(NS_OK);
1819 }
1820 mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1821 mState = STATE_SENTGET;
1822 }
1824 else if (mState == STATE_CONNECTING || mState == STATE_SENTGET) {
1825 PRStatus status = PR_ConnectContinue(fd, outFlags);
1826 if (status == PR_SUCCESS && mState == STATE_CONNECTING) {
1827 OnSocketConnected();
1828 mState = STATE_SENDINGGET;
1829 }
1830 else if (status == PR_SUCCESS && mState == STATE_SENTGET) {
1831 //
1832 // we are connected!
1833 //
1834 OnSocketConnected();
1835 }
1836 else {
1837 PRErrorCode code = PR_GetError();
1838 #if defined(TEST_CONNECT_ERRORS)
1839 code = RandomizeConnectError(code);
1840 #endif
1841 //
1842 // If the connect is still not ready, then continue polling...
1843 //
1844 if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
1845 // Set up the select flags for connect...
1846 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1847 // Update poll timeout in case it was changed
1848 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1849 }
1850 //
1851 // The SOCKS proxy rejected our request. Find out why.
1852 //
1853 else if (PR_UNKNOWN_ERROR == code &&
1854 mProxyUse && mProxyTransparent) {
1855 code = PR_GetOSError();
1856 mCondition = ErrorAccordingToNSPR(code);
1857 }
1858 else {
1859 //
1860 // else, the connection failed...
1861 //
1862 mCondition = ErrorAccordingToNSPR(code);
1863 if (mCondition == NS_ERROR_CONNECTION_REFUSED && mProxyUse)
1864 mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
1865 SOCKET_LOG((" connection failed! [reason=%x]\n", mCondition));
1866 }
1867 }
1868 }
1869 else {
1870 NS_ERROR("unexpected socket state");
1871 mCondition = NS_ERROR_UNEXPECTED;
1872 }
1874 if (mPollFlags == PR_POLL_EXCEPT)
1875 mPollFlags = 0; // make idle
1876 }
1878 // called on the socket thread only
1879 void
1880 nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
1881 {
1882 SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%p cond=%x]\n",
1883 this, mCondition));
1885 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1887 // if we didn't initiate this detach, then be sure to pass an error
1888 // condition up to our consumers. (e.g., STS is shutting down.)
1889 if (NS_SUCCEEDED(mCondition)) {
1890 if (gIOService->IsOffline()) {
1891 mCondition = NS_ERROR_OFFLINE;
1892 }
1893 else {
1894 mCondition = NS_ERROR_ABORT;
1895 }
1896 }
1898 if (RecoverFromError())
1899 mCondition = NS_OK;
1900 else {
1901 mState = STATE_CLOSED;
1903 // make sure there isn't any pending DNS request
1904 if (mDNSRequest) {
1905 mDNSRequest->Cancel(NS_ERROR_ABORT);
1906 mDNSRequest = 0;
1907 }
1909 //
1910 // notify input/output streams
1911 //
1912 mInput.OnSocketReady(mCondition);
1913 mOutput.OnSocketReady(mCondition);
1914 }
1916 // break any potential reference cycle between the security info object
1917 // and ourselves by resetting its notification callbacks object. see
1918 // bug 285991 for details.
1919 nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1920 if (secCtrl)
1921 secCtrl->SetNotificationCallbacks(nullptr);
1923 // finally, release our reference to the socket (must do this within
1924 // the transport lock) possibly closing the socket. Also release our
1925 // listeners to break potential refcount cycles.
1927 // We should be careful not to release mEventSink and mCallbacks while
1928 // we're locked, because releasing it might require acquiring the lock
1929 // again, so we just null out mEventSink and mCallbacks while we're
1930 // holding the lock, and let the stack based objects' destuctors take
1931 // care of destroying it if needed.
1932 nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
1933 nsCOMPtr<nsITransportEventSink> ourEventSink;
1934 {
1935 MutexAutoLock lock(mLock);
1936 if (mFD.IsInitialized()) {
1937 ReleaseFD_Locked(mFD);
1938 // flag mFD as unusable; this prevents other consumers from
1939 // acquiring a reference to mFD.
1940 mFDconnected = false;
1941 }
1943 // We must release mCallbacks and mEventSink to avoid memory leak
1944 // but only when RecoverFromError() above failed. Otherwise we lose
1945 // link with UI and security callbacks on next connection attempt
1946 // round. That would lead e.g. to a broken certificate exception page.
1947 if (NS_FAILED(mCondition)) {
1948 mCallbacks.swap(ourCallbacks);
1949 mEventSink.swap(ourEventSink);
1950 }
1951 }
1952 }
1954 void
1955 nsSocketTransport::IsLocal(bool *aIsLocal)
1956 {
1957 {
1958 MutexAutoLock lock(mLock);
1960 #if defined(XP_UNIX)
1961 // Unix-domain sockets are always local.
1962 if (mNetAddr.raw.family == PR_AF_LOCAL)
1963 {
1964 *aIsLocal = true;
1965 return;
1966 }
1967 #endif
1969 *aIsLocal = IsLoopBackAddress(&mNetAddr);
1970 }
1971 }
1973 //-----------------------------------------------------------------------------
1974 // xpcom api
1976 NS_IMPL_ISUPPORTS(nsSocketTransport,
1977 nsISocketTransport,
1978 nsITransport,
1979 nsIDNSListener,
1980 nsIClassInfo)
1981 NS_IMPL_CI_INTERFACE_GETTER(nsSocketTransport,
1982 nsISocketTransport,
1983 nsITransport,
1984 nsIDNSListener)
1986 NS_IMETHODIMP
1987 nsSocketTransport::OpenInputStream(uint32_t flags,
1988 uint32_t segsize,
1989 uint32_t segcount,
1990 nsIInputStream **result)
1991 {
1992 SOCKET_LOG(("nsSocketTransport::OpenInputStream [this=%p flags=%x]\n",
1993 this, flags));
1995 NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
1997 nsresult rv;
1998 nsCOMPtr<nsIAsyncInputStream> pipeIn;
2000 if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
2001 // XXX if the caller wants blocking, then the caller also gets buffered!
2002 //bool openBuffered = !(flags & OPEN_UNBUFFERED);
2003 bool openBlocking = (flags & OPEN_BLOCKING);
2005 net_ResolveSegmentParams(segsize, segcount);
2007 // create a pipe
2008 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
2009 rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
2010 !openBlocking, true, segsize, segcount);
2011 if (NS_FAILED(rv)) return rv;
2013 // async copy from socket to pipe
2014 rv = NS_AsyncCopy(&mInput, pipeOut, mSocketTransportService,
2015 NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
2016 if (NS_FAILED(rv)) return rv;
2018 *result = pipeIn;
2019 }
2020 else
2021 *result = &mInput;
2023 // flag input stream as open
2024 mInputClosed = false;
2026 rv = PostEvent(MSG_ENSURE_CONNECT);
2027 if (NS_FAILED(rv)) return rv;
2029 NS_ADDREF(*result);
2030 return NS_OK;
2031 }
2033 NS_IMETHODIMP
2034 nsSocketTransport::OpenOutputStream(uint32_t flags,
2035 uint32_t segsize,
2036 uint32_t segcount,
2037 nsIOutputStream **result)
2038 {
2039 SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%p flags=%x]\n",
2040 this, flags));
2042 NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
2044 nsresult rv;
2045 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
2046 if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
2047 // XXX if the caller wants blocking, then the caller also gets buffered!
2048 //bool openBuffered = !(flags & OPEN_UNBUFFERED);
2049 bool openBlocking = (flags & OPEN_BLOCKING);
2051 net_ResolveSegmentParams(segsize, segcount);
2053 // create a pipe
2054 nsCOMPtr<nsIAsyncInputStream> pipeIn;
2055 rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
2056 true, !openBlocking, segsize, segcount);
2057 if (NS_FAILED(rv)) return rv;
2059 // async copy from socket to pipe
2060 rv = NS_AsyncCopy(pipeIn, &mOutput, mSocketTransportService,
2061 NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
2062 if (NS_FAILED(rv)) return rv;
2064 *result = pipeOut;
2065 }
2066 else
2067 *result = &mOutput;
2069 // flag output stream as open
2070 mOutputClosed = false;
2072 rv = PostEvent(MSG_ENSURE_CONNECT);
2073 if (NS_FAILED(rv)) return rv;
2075 NS_ADDREF(*result);
2076 return NS_OK;
2077 }
2079 NS_IMETHODIMP
2080 nsSocketTransport::Close(nsresult reason)
2081 {
2082 if (NS_SUCCEEDED(reason))
2083 reason = NS_BASE_STREAM_CLOSED;
2085 mInput.CloseWithStatus(reason);
2086 mOutput.CloseWithStatus(reason);
2087 return NS_OK;
2088 }
2090 NS_IMETHODIMP
2091 nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
2092 {
2093 MutexAutoLock lock(mLock);
2094 NS_IF_ADDREF(*secinfo = mSecInfo);
2095 return NS_OK;
2096 }
2098 NS_IMETHODIMP
2099 nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
2100 {
2101 MutexAutoLock lock(mLock);
2102 NS_IF_ADDREF(*callbacks = mCallbacks);
2103 return NS_OK;
2104 }
2106 NS_IMETHODIMP
2107 nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
2108 {
2109 nsCOMPtr<nsIInterfaceRequestor> threadsafeCallbacks;
2110 NS_NewNotificationCallbacksAggregation(callbacks, nullptr,
2111 NS_GetCurrentThread(),
2112 getter_AddRefs(threadsafeCallbacks));
2114 nsCOMPtr<nsISupports> secinfo;
2115 {
2116 MutexAutoLock lock(mLock);
2117 mCallbacks = threadsafeCallbacks;
2118 SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",
2119 mSecInfo.get(), mCallbacks.get()));
2121 secinfo = mSecInfo;
2122 }
2124 // don't call into PSM while holding mLock!!
2125 nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
2126 if (secCtrl)
2127 secCtrl->SetNotificationCallbacks(threadsafeCallbacks);
2129 return NS_OK;
2130 }
2132 NS_IMETHODIMP
2133 nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
2134 nsIEventTarget *target)
2135 {
2136 nsCOMPtr<nsITransportEventSink> temp;
2137 if (target) {
2138 nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
2139 sink, target);
2140 if (NS_FAILED(rv))
2141 return rv;
2142 sink = temp.get();
2143 }
2145 MutexAutoLock lock(mLock);
2146 mEventSink = sink;
2147 return NS_OK;
2148 }
2150 NS_IMETHODIMP
2151 nsSocketTransport::IsAlive(bool *result)
2152 {
2153 *result = false;
2155 nsresult conditionWhileLocked = NS_OK;
2156 PRFileDescAutoLock fd(this, &conditionWhileLocked);
2157 if (NS_FAILED(conditionWhileLocked) || !fd.IsInitialized()) {
2158 return NS_OK;
2159 }
2161 // XXX do some idle-time based checks??
2163 char c;
2164 int32_t rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
2166 if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
2167 *result = true;
2169 return NS_OK;
2170 }
2172 NS_IMETHODIMP
2173 nsSocketTransport::GetHost(nsACString &host)
2174 {
2175 host = SocketHost();
2176 return NS_OK;
2177 }
2179 NS_IMETHODIMP
2180 nsSocketTransport::GetPort(int32_t *port)
2181 {
2182 *port = (int32_t) SocketPort();
2183 return NS_OK;
2184 }
2186 const nsCString &
2187 nsSocketTransport::SocketHost()
2188 {
2189 if (mProxyInfo && !mProxyTransparent) {
2190 if (mProxyHostCache.IsEmpty()) {
2191 mProxyInfo->GetHost(mProxyHostCache);
2192 }
2193 return mProxyHostCache;
2194 }
2195 else
2196 return mHost;
2197 }
2199 uint16_t
2200 nsSocketTransport::SocketPort()
2201 {
2202 if (mProxyInfo && !mProxyTransparent) {
2203 int32_t result;
2204 mProxyInfo->GetPort(&result);
2205 return (uint16_t) result;
2206 }
2207 else
2208 return mPort;
2209 }
2211 NS_IMETHODIMP
2212 nsSocketTransport::GetPeerAddr(NetAddr *addr)
2213 {
2214 // once we are in the connected state, mNetAddr will not change.
2215 // so if we can verify that we are in the connected state, then
2216 // we can freely access mNetAddr from any thread without being
2217 // inside a critical section.
2219 if (!mNetAddrIsSet) {
2220 SOCKET_LOG(("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
2221 "NOT_AVAILABLE because not yet connected.", this, mState));
2222 return NS_ERROR_NOT_AVAILABLE;
2223 }
2225 memcpy(addr, &mNetAddr, sizeof(NetAddr));
2226 return NS_OK;
2227 }
2229 NS_IMETHODIMP
2230 nsSocketTransport::GetSelfAddr(NetAddr *addr)
2231 {
2232 // we must not call any PR methods on our file descriptor
2233 // while holding mLock since those methods might re-enter
2234 // socket transport code.
2236 PRFileDescAutoLock fd(this);
2237 if (!fd.IsInitialized()) {
2238 return NS_ERROR_NOT_CONNECTED;
2239 }
2241 PRNetAddr prAddr;
2243 // NSPR doesn't tell us the socket address's length (as provided by
2244 // the 'getsockname' system call), so we can't distinguish between
2245 // named, unnamed, and abstract Unix domain socket names. (Server
2246 // sockets are never unnamed, obviously, but client sockets can use
2247 // any kind of address.) Clear prAddr first, so that the path for
2248 // unnamed and abstract addresses will at least be reliably empty,
2249 // and not garbage for unnamed sockets.
2250 memset(&prAddr, 0, sizeof(prAddr));
2252 nsresult rv =
2253 (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
2254 PRNetAddrToNetAddr(&prAddr, addr);
2256 return rv;
2257 }
2259 /* nsINetAddr getScriptablePeerAddr (); */
2260 NS_IMETHODIMP
2261 nsSocketTransport::GetScriptablePeerAddr(nsINetAddr * *addr)
2262 {
2263 NetAddr rawAddr;
2265 nsresult rv;
2266 rv = GetPeerAddr(&rawAddr);
2267 if (NS_FAILED(rv))
2268 return rv;
2270 NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
2272 return NS_OK;
2273 }
2275 /* nsINetAddr getScriptableSelfAddr (); */
2276 NS_IMETHODIMP
2277 nsSocketTransport::GetScriptableSelfAddr(nsINetAddr * *addr)
2278 {
2279 NetAddr rawAddr;
2281 nsresult rv;
2282 rv = GetSelfAddr(&rawAddr);
2283 if (NS_FAILED(rv))
2284 return rv;
2286 NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
2288 return NS_OK;
2289 }
2291 NS_IMETHODIMP
2292 nsSocketTransport::GetTimeout(uint32_t type, uint32_t *value)
2293 {
2294 NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
2295 *value = (uint32_t) mTimeouts[type];
2296 return NS_OK;
2297 }
2299 NS_IMETHODIMP
2300 nsSocketTransport::SetTimeout(uint32_t type, uint32_t value)
2301 {
2302 NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
2303 // truncate overly large timeout values.
2304 mTimeouts[type] = (uint16_t) std::min<uint32_t>(value, UINT16_MAX);
2305 PostEvent(MSG_TIMEOUT_CHANGED);
2306 return NS_OK;
2307 }
2309 NS_IMETHODIMP
2310 nsSocketTransport::SetQoSBits(uint8_t aQoSBits)
2311 {
2312 // Don't do any checking here of bits. Why? Because as of RFC-4594
2313 // several different Class Selector and Assured Forwarding values
2314 // have been defined, but that isn't to say more won't be added later.
2315 // In that case, any checking would be an impediment to interoperating
2316 // with newer QoS definitions.
2318 mQoSBits = aQoSBits;
2319 return NS_OK;
2320 }
2322 NS_IMETHODIMP
2323 nsSocketTransport::GetQoSBits(uint8_t *aQoSBits)
2324 {
2325 *aQoSBits = mQoSBits;
2326 return NS_OK;
2327 }
2329 NS_IMETHODIMP
2330 nsSocketTransport::GetRecvBufferSize(uint32_t *aSize)
2331 {
2332 PRFileDescAutoLock fd(this);
2333 if (!fd.IsInitialized())
2334 return NS_ERROR_NOT_CONNECTED;
2336 nsresult rv = NS_OK;
2337 PRSocketOptionData opt;
2338 opt.option = PR_SockOpt_RecvBufferSize;
2339 if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
2340 *aSize = opt.value.recv_buffer_size;
2341 else
2342 rv = NS_ERROR_FAILURE;
2344 return rv;
2345 }
2347 NS_IMETHODIMP
2348 nsSocketTransport::GetSendBufferSize(uint32_t *aSize)
2349 {
2350 PRFileDescAutoLock fd(this);
2351 if (!fd.IsInitialized())
2352 return NS_ERROR_NOT_CONNECTED;
2354 nsresult rv = NS_OK;
2355 PRSocketOptionData opt;
2356 opt.option = PR_SockOpt_SendBufferSize;
2357 if (PR_GetSocketOption(fd, &opt) == PR_SUCCESS)
2358 *aSize = opt.value.send_buffer_size;
2359 else
2360 rv = NS_ERROR_FAILURE;
2362 return rv;
2363 }
2365 NS_IMETHODIMP
2366 nsSocketTransport::SetRecvBufferSize(uint32_t aSize)
2367 {
2368 PRFileDescAutoLock fd(this);
2369 if (!fd.IsInitialized())
2370 return NS_ERROR_NOT_CONNECTED;
2372 nsresult rv = NS_OK;
2373 PRSocketOptionData opt;
2374 opt.option = PR_SockOpt_RecvBufferSize;
2375 opt.value.recv_buffer_size = aSize;
2376 if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
2377 rv = NS_ERROR_FAILURE;
2379 return rv;
2380 }
2382 NS_IMETHODIMP
2383 nsSocketTransport::SetSendBufferSize(uint32_t aSize)
2384 {
2385 PRFileDescAutoLock fd(this);
2386 if (!fd.IsInitialized())
2387 return NS_ERROR_NOT_CONNECTED;
2389 nsresult rv = NS_OK;
2390 PRSocketOptionData opt;
2391 opt.option = PR_SockOpt_SendBufferSize;
2392 opt.value.send_buffer_size = aSize;
2393 if (PR_SetSocketOption(fd, &opt) != PR_SUCCESS)
2394 rv = NS_ERROR_FAILURE;
2396 return rv;
2397 }
2399 NS_IMETHODIMP
2400 nsSocketTransport::OnLookupComplete(nsICancelable *request,
2401 nsIDNSRecord *rec,
2402 nsresult status)
2403 {
2404 // flag host lookup complete for the benefit of the ResolveHost method.
2405 mResolving = false;
2407 MOZ_EVENT_TRACER_WAIT(this, "net::tcp::connect");
2408 nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
2410 // if posting a message fails, then we should assume that the socket
2411 // transport has been shutdown. this should never happen! if it does
2412 // it means that the socket transport service was shutdown before the
2413 // DNS service.
2414 if (NS_FAILED(rv))
2415 NS_WARNING("unable to post DNS lookup complete message");
2417 return NS_OK;
2418 }
2420 NS_IMETHODIMP
2421 nsSocketTransport::GetInterfaces(uint32_t *count, nsIID * **array)
2422 {
2423 return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
2424 }
2426 NS_IMETHODIMP
2427 nsSocketTransport::GetHelperForLanguage(uint32_t language, nsISupports **_retval)
2428 {
2429 *_retval = nullptr;
2430 return NS_OK;
2431 }
2433 NS_IMETHODIMP
2434 nsSocketTransport::GetContractID(char * *aContractID)
2435 {
2436 *aContractID = nullptr;
2437 return NS_OK;
2438 }
2440 NS_IMETHODIMP
2441 nsSocketTransport::GetClassDescription(char * *aClassDescription)
2442 {
2443 *aClassDescription = nullptr;
2444 return NS_OK;
2445 }
2447 NS_IMETHODIMP
2448 nsSocketTransport::GetClassID(nsCID * *aClassID)
2449 {
2450 *aClassID = nullptr;
2451 return NS_OK;
2452 }
2454 NS_IMETHODIMP
2455 nsSocketTransport::GetImplementationLanguage(uint32_t *aImplementationLanguage)
2456 {
2457 *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
2458 return NS_OK;
2459 }
2461 NS_IMETHODIMP
2462 nsSocketTransport::GetFlags(uint32_t *aFlags)
2463 {
2464 *aFlags = nsIClassInfo::THREADSAFE;
2465 return NS_OK;
2466 }
2468 NS_IMETHODIMP
2469 nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
2470 {
2471 return NS_ERROR_NOT_AVAILABLE;
2472 }
2475 NS_IMETHODIMP
2476 nsSocketTransport::GetConnectionFlags(uint32_t *value)
2477 {
2478 *value = mConnectionFlags;
2479 return NS_OK;
2480 }
2482 NS_IMETHODIMP
2483 nsSocketTransport::SetConnectionFlags(uint32_t value)
2484 {
2485 mConnectionFlags = value;
2486 mIsPrivate = value & nsISocketTransport::NO_PERMANENT_STORAGE;
2487 return NS_OK;
2488 }
2490 void
2491 nsSocketTransport::OnKeepaliveEnabledPrefChange(bool aEnabled)
2492 {
2493 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2495 // The global pref toggles keepalive as a system feature; it only affects
2496 // an individual socket if keepalive has been specifically enabled for it.
2497 // So, ensure keepalive is configured correctly if previously enabled.
2498 if (mKeepaliveEnabled) {
2499 nsresult rv = SetKeepaliveEnabledInternal(aEnabled);
2500 if (NS_WARN_IF(NS_FAILED(rv))) {
2501 SOCKET_LOG((" SetKeepaliveEnabledInternal [%s] failed rv[0x%x]",
2502 aEnabled ? "enable" : "disable", rv));
2503 }
2504 }
2505 }
2507 nsresult
2508 nsSocketTransport::SetKeepaliveEnabledInternal(bool aEnable)
2509 {
2510 MOZ_ASSERT(mKeepaliveIdleTimeS > 0 &&
2511 mKeepaliveIdleTimeS <= kMaxTCPKeepIdle);
2512 MOZ_ASSERT(mKeepaliveRetryIntervalS > 0 &&
2513 mKeepaliveRetryIntervalS <= kMaxTCPKeepIntvl);
2514 MOZ_ASSERT(mKeepaliveProbeCount > 0 &&
2515 mKeepaliveProbeCount <= kMaxTCPKeepCount);
2517 PRFileDescAutoLock fd(this);
2518 if (NS_WARN_IF(!fd.IsInitialized())) {
2519 return NS_ERROR_NOT_INITIALIZED;
2520 }
2522 // Only enable if keepalives are globally enabled, but ensure other
2523 // options are set correctly on the fd.
2524 bool enable = aEnable && mSocketTransportService->IsKeepaliveEnabled();
2525 nsresult rv = fd.SetKeepaliveVals(enable,
2526 mKeepaliveIdleTimeS,
2527 mKeepaliveRetryIntervalS,
2528 mKeepaliveProbeCount);
2529 if (NS_WARN_IF(NS_FAILED(rv))) {
2530 SOCKET_LOG((" SetKeepaliveVals failed rv[0x%x]", rv));
2531 return rv;
2532 }
2533 rv = fd.SetKeepaliveEnabled(enable);
2534 if (NS_WARN_IF(NS_FAILED(rv))) {
2535 SOCKET_LOG((" SetKeepaliveEnabled failed rv[0x%x]", rv));
2536 return rv;
2537 }
2538 return NS_OK;
2539 }
2541 NS_IMETHODIMP
2542 nsSocketTransport::GetKeepaliveEnabled(bool *aResult)
2543 {
2544 MOZ_ASSERT(aResult);
2546 *aResult = mKeepaliveEnabled;
2547 return NS_OK;
2548 }
2550 nsresult
2551 nsSocketTransport::EnsureKeepaliveValsAreInitialized()
2552 {
2553 nsresult rv = NS_OK;
2554 int32_t val = -1;
2555 if (mKeepaliveIdleTimeS == -1) {
2556 rv = mSocketTransportService->GetKeepaliveIdleTime(&val);
2557 if (NS_WARN_IF(NS_FAILED(rv))) {
2558 return rv;
2559 }
2560 mKeepaliveIdleTimeS = val;
2561 }
2562 if (mKeepaliveRetryIntervalS == -1) {
2563 rv = mSocketTransportService->GetKeepaliveRetryInterval(&val);
2564 if (NS_WARN_IF(NS_FAILED(rv))) {
2565 return rv;
2566 }
2567 mKeepaliveRetryIntervalS = val;
2568 }
2569 if (mKeepaliveProbeCount == -1) {
2570 rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
2571 if (NS_WARN_IF(NS_FAILED(rv))) {
2572 return rv;
2573 }
2574 mKeepaliveProbeCount = val;
2575 }
2576 return NS_OK;
2577 }
2579 NS_IMETHODIMP
2580 nsSocketTransport::SetKeepaliveEnabled(bool aEnable)
2581 {
2582 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2583 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2585 if (aEnable == mKeepaliveEnabled) {
2586 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] already %s.",
2587 this, aEnable ? "enabled" : "disabled"));
2588 return NS_OK;
2589 }
2591 nsresult rv = NS_OK;
2592 if (aEnable) {
2593 rv = EnsureKeepaliveValsAreInitialized();
2594 if (NS_WARN_IF(NS_FAILED(rv))) {
2595 SOCKET_LOG((" SetKeepaliveEnabled [%p] "
2596 "error [0x%x] initializing keepalive vals",
2597 this, rv));
2598 return rv;
2599 }
2600 }
2601 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled [%p] "
2602 "%s, idle time[%ds] retry interval[%ds] packet count[%d]: "
2603 "globally %s.",
2604 this, aEnable ? "enabled" : "disabled",
2605 mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
2606 mKeepaliveProbeCount,
2607 mSocketTransportService->IsKeepaliveEnabled() ?
2608 "enabled" : "disabled"));
2610 // Set mKeepaliveEnabled here so that state is maintained; it is possible
2611 // that we're in between fds, e.g. the 1st IP address failed, so we're about
2612 // to retry on a 2nd from the DNS record.
2613 mKeepaliveEnabled = aEnable;
2615 rv = SetKeepaliveEnabledInternal(aEnable);
2616 if (NS_WARN_IF(NS_FAILED(rv))) {
2617 SOCKET_LOG((" SetKeepaliveEnabledInternal failed rv[0x%x]", rv));
2618 return rv;
2619 }
2621 return NS_OK;
2622 #else /* !(defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)) */
2623 SOCKET_LOG(("nsSocketTransport::SetKeepaliveEnabled unsupported platform"));
2624 return NS_ERROR_NOT_IMPLEMENTED;
2625 #endif
2626 }
2628 NS_IMETHODIMP
2629 nsSocketTransport::SetKeepaliveVals(int32_t aIdleTime,
2630 int32_t aRetryInterval)
2631 {
2632 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2633 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2634 if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
2635 return NS_ERROR_INVALID_ARG;
2636 }
2637 if (NS_WARN_IF(aRetryInterval <= 0 ||
2638 kMaxTCPKeepIntvl < aRetryInterval)) {
2639 return NS_ERROR_INVALID_ARG;
2640 }
2642 if (aIdleTime == mKeepaliveIdleTimeS &&
2643 aRetryInterval == mKeepaliveRetryIntervalS) {
2644 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] idle time "
2645 "already %ds and retry interval already %ds.",
2646 this, mKeepaliveIdleTimeS,
2647 mKeepaliveRetryIntervalS));
2648 return NS_OK;
2649 }
2650 mKeepaliveIdleTimeS = aIdleTime;
2651 mKeepaliveRetryIntervalS = aRetryInterval;
2653 nsresult rv = NS_OK;
2654 if (mKeepaliveProbeCount == -1) {
2655 int32_t val = -1;
2656 nsresult rv = mSocketTransportService->GetKeepaliveProbeCount(&val);
2657 if (NS_WARN_IF(NS_FAILED(rv))) {
2658 return rv;
2659 }
2660 mKeepaliveProbeCount = val;
2661 }
2663 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals [%p] "
2664 "keepalive %s, idle time[%ds] retry interval[%ds] "
2665 "packet count[%d]",
2666 this, mKeepaliveEnabled ? "enabled" : "disabled",
2667 mKeepaliveIdleTimeS, mKeepaliveRetryIntervalS,
2668 mKeepaliveProbeCount));
2670 PRFileDescAutoLock fd(this);
2671 if (NS_WARN_IF(!fd.IsInitialized())) {
2672 return NS_ERROR_NULL_POINTER;
2673 }
2675 rv = fd.SetKeepaliveVals(mKeepaliveEnabled,
2676 mKeepaliveIdleTimeS,
2677 mKeepaliveRetryIntervalS,
2678 mKeepaliveProbeCount);
2679 if (NS_WARN_IF(NS_FAILED(rv))) {
2680 return rv;
2681 }
2682 return NS_OK;
2683 #else
2684 SOCKET_LOG(("nsSocketTransport::SetKeepaliveVals unsupported platform"));
2685 return NS_ERROR_NOT_IMPLEMENTED;
2686 #endif
2687 }
2689 #ifdef ENABLE_SOCKET_TRACING
2691 #include <stdio.h>
2692 #include <ctype.h>
2693 #include "prenv.h"
2695 static void
2696 DumpBytesToFile(const char *path, const char *header, const char *buf, int32_t n)
2697 {
2698 FILE *fp = fopen(path, "a");
2700 fprintf(fp, "\n%s [%d bytes]\n", header, n);
2702 const unsigned char *p;
2703 while (n) {
2704 p = (const unsigned char *) buf;
2706 int32_t i, row_max = std::min(16, n);
2708 for (i = 0; i < row_max; ++i)
2709 fprintf(fp, "%02x ", *p++);
2710 for (i = row_max; i < 16; ++i)
2711 fprintf(fp, " ");
2713 p = (const unsigned char *) buf;
2714 for (i = 0; i < row_max; ++i, ++p) {
2715 if (isprint(*p))
2716 fprintf(fp, "%c", *p);
2717 else
2718 fprintf(fp, ".");
2719 }
2721 fprintf(fp, "\n");
2722 buf += row_max;
2723 n -= row_max;
2724 }
2726 fprintf(fp, "\n");
2727 fclose(fp);
2728 }
2730 void
2731 nsSocketTransport::TraceInBuf(const char *buf, int32_t n)
2732 {
2733 char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2734 if (!val || !*val)
2735 return;
2737 nsAutoCString header;
2738 header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
2739 header.Append(':');
2740 header.AppendInt(mPort);
2742 DumpBytesToFile(val, header.get(), buf, n);
2743 }
2745 void
2746 nsSocketTransport::TraceOutBuf(const char *buf, int32_t n)
2747 {
2748 char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2749 if (!val || !*val)
2750 return;
2752 nsAutoCString header;
2753 header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
2754 header.Append(':');
2755 header.AppendInt(mPort);
2757 DumpBytesToFile(val, header.get(), buf, n);
2758 }
2760 #endif
2762 static void LogNSPRError(const char* aPrefix, const void *aObjPtr)
2763 {
2764 #if defined(PR_LOGGING) && defined(DEBUG)
2765 PRErrorCode errCode = PR_GetError();
2766 int errLen = PR_GetErrorTextLength();
2767 nsAutoCString errStr;
2768 if (errLen > 0) {
2769 errStr.SetLength(errLen);
2770 PR_GetErrorText(errStr.BeginWriting());
2771 }
2772 NS_WARNING(nsPrintfCString(
2773 "%s [%p] NSPR error[0x%x] %s.",
2774 aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
2775 errLen > 0 ? errStr.BeginReading() : "<no error text>").get());
2776 #endif
2777 }
2779 nsresult
2780 nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled(bool aEnable)
2781 {
2782 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2783 MOZ_ASSERT(!(aEnable && !gSocketTransportService->IsKeepaliveEnabled()),
2784 "Cannot enable keepalive if global pref is disabled!");
2785 if (aEnable && !gSocketTransportService->IsKeepaliveEnabled()) {
2786 return NS_ERROR_ILLEGAL_VALUE;
2787 }
2789 PRSocketOptionData opt;
2791 opt.option = PR_SockOpt_Keepalive;
2792 opt.value.keep_alive = aEnable;
2793 PRStatus status = PR_SetSocketOption(mFd, &opt);
2794 if (NS_WARN_IF(status != PR_SUCCESS)) {
2795 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveEnabled",
2796 mSocketTransport);
2797 return ErrorAccordingToNSPR(PR_GetError());
2798 }
2799 return NS_OK;
2800 }
2802 static void LogOSError(const char *aPrefix, const void *aObjPtr)
2803 {
2804 #if defined(PR_LOGGING) && defined(DEBUG)
2805 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2807 #ifdef XP_WIN
2808 DWORD errCode = WSAGetLastError();
2809 LPVOID errMessage;
2810 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
2811 FORMAT_MESSAGE_FROM_SYSTEM |
2812 FORMAT_MESSAGE_IGNORE_INSERTS,
2813 NULL,
2814 errCode,
2815 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2816 (LPTSTR) &errMessage,
2817 0, NULL);
2818 #else
2819 int errCode = errno;
2820 char *errMessage = strerror(errno);
2821 #endif
2822 NS_WARNING(nsPrintfCString(
2823 "%s [%p] OS error[0x%x] %s",
2824 aPrefix ? aPrefix : "nsSocketTransport", aObjPtr, errCode,
2825 errMessage ? errMessage : "<no error text>").get());
2826 #ifdef XP_WIN
2827 LocalFree(errMessage);
2828 #endif
2829 #endif
2830 }
2832 /* XXX PR_SetSockOpt does not support setting keepalive values, so native
2833 * handles and platform specific apis (setsockopt, WSAIOCtl) are used in this
2834 * file. Requires inclusion of NSPR private/pprio.h, and platform headers.
2835 */
2837 nsresult
2838 nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled,
2839 int aIdleTime,
2840 int aRetryInterval,
2841 int aProbeCount)
2842 {
2843 #if defined(XP_WIN) || defined(XP_UNIX) || defined(XP_MACOSX)
2844 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread, "wrong thread");
2845 if (NS_WARN_IF(aIdleTime <= 0 || kMaxTCPKeepIdle < aIdleTime)) {
2846 return NS_ERROR_INVALID_ARG;
2847 }
2848 if (NS_WARN_IF(aRetryInterval <= 0 ||
2849 kMaxTCPKeepIntvl < aRetryInterval)) {
2850 return NS_ERROR_INVALID_ARG;
2851 }
2852 if (NS_WARN_IF(aProbeCount <= 0 || kMaxTCPKeepCount < aProbeCount)) {
2853 return NS_ERROR_INVALID_ARG;
2854 }
2856 PROsfd sock = PR_FileDesc2NativeHandle(mFd);
2857 if (NS_WARN_IF(sock == -1)) {
2858 LogNSPRError("nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals",
2859 mSocketTransport);
2860 return ErrorAccordingToNSPR(PR_GetError());
2861 }
2862 #endif
2864 #if defined(XP_WIN)
2865 // Windows allows idle time and retry interval to be set; NOT ping count.
2866 struct tcp_keepalive keepalive_vals = {
2867 (int)aEnabled,
2868 // Windows uses msec.
2869 aIdleTime * 1000,
2870 aRetryInterval * 1000
2871 };
2872 DWORD bytes_returned;
2873 int err = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals,
2874 sizeof(keepalive_vals), NULL, 0, &bytes_returned, NULL,
2875 NULL);
2876 if (NS_WARN_IF(err)) {
2877 LogOSError("nsSocketTransport WSAIoctl failed", mSocketTransport);
2878 return NS_ERROR_UNEXPECTED;
2879 }
2880 return NS_OK;
2882 #elif defined(XP_MACOSX)
2883 // OS X uses sec; only supports idle time being set.
2884 int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPALIVE,
2885 &aIdleTime, sizeof(aIdleTime));
2886 if (NS_WARN_IF(err)) {
2887 LogOSError("nsSocketTransport Failed setting TCP_KEEPALIVE",
2888 mSocketTransport);
2889 return NS_ERROR_UNEXPECTED;
2890 }
2891 return NS_OK;
2893 #elif defined(XP_UNIX)
2894 // Not all *nix OSes support the following setsockopt() options
2895 // ... but we assume they are supported in the Android kernel;
2896 // build errors will tell us if they are not.
2897 #if defined(ANDROID) || defined(TCP_KEEPIDLE)
2898 // Idle time until first keepalive probe; interval between ack'd probes; seconds.
2899 int err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE,
2900 &aIdleTime, sizeof(aIdleTime));
2901 if (NS_WARN_IF(err)) {
2902 LogOSError("nsSocketTransport Failed setting TCP_KEEPIDLE",
2903 mSocketTransport);
2904 return NS_ERROR_UNEXPECTED;
2905 }
2907 #endif
2908 #if defined(ANDROID) || defined(TCP_KEEPINTVL)
2909 // Interval between unack'd keepalive probes; seconds.
2910 err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL,
2911 &aRetryInterval, sizeof(aRetryInterval));
2912 if (NS_WARN_IF(err)) {
2913 LogOSError("nsSocketTransport Failed setting TCP_KEEPINTVL",
2914 mSocketTransport);
2915 return NS_ERROR_UNEXPECTED;
2916 }
2918 #endif
2919 #if defined(ANDROID) || defined(TCP_KEEPCNT)
2920 // Number of unack'd keepalive probes before connection times out.
2921 err = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT,
2922 &aProbeCount, sizeof(aProbeCount));
2923 if (NS_WARN_IF(err)) {
2924 LogOSError("nsSocketTransport Failed setting TCP_KEEPCNT",
2925 mSocketTransport);
2926 return NS_ERROR_UNEXPECTED;
2927 }
2929 #endif
2930 return NS_OK;
2931 #else
2932 MOZ_ASSERT(false, "nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals "
2933 "called on unsupported platform!");
2934 return NS_ERROR_UNEXPECTED;
2935 #endif
2936 }