Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=4 sw=2 et cindent: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsSocketTransport2.h"
8
9
#include "mozilla/Attributes.h"
10
#include "mozilla/StaticPrefs.h"
11
#include "mozilla/Telemetry.h"
12
#include "nsIOService.h"
13
#include "nsStreamUtils.h"
14
#include "nsNetSegmentUtils.h"
15
#include "nsNetAddr.h"
16
#include "nsTransportUtils.h"
17
#include "nsProxyInfo.h"
18
#include "nsNetCID.h"
19
#include "nsNetUtil.h"
20
#include "nsAutoPtr.h"
21
#include "nsCOMPtr.h"
22
#include "plstr.h"
23
#include "prerr.h"
24
#include "IOActivityMonitor.h"
25
#include "NSSErrorsService.h"
26
#include "mozilla/dom/ToJSValue.h"
27
#include "mozilla/net/NeckoChild.h"
28
#include "nsThreadUtils.h"
29
#include "nsSocketProviderService.h"
30
#include "nsISocketProvider.h"
31
#include "nsISSLSocketControl.h"
32
#include "nsIPipe.h"
33
#include "nsIClassInfoImpl.h"
34
#include "nsURLHelper.h"
35
#include "nsIDNSService.h"
36
#include "nsIDNSRecord.h"
37
#include "nsIDNSByTypeRecord.h"
38
#include "nsICancelable.h"
39
#include "TCPFastOpenLayer.h"
40
#include <algorithm>
41
#include "sslexp.h"
42
#include "mozilla/net/SSLTokensCache.h"
43
44
#include "nsPrintfCString.h"
45
#include "xpcpublic.h"
46
47
#if defined(FUZZING)
48
# include "FuzzyLayer.h"
49
#endif
50
51
#if defined(XP_WIN)
52
# include "ShutdownLayer.h"
53
#endif
54
55
/* Following inclusions required for keepalive config not supported by NSPR. */
56
#include "private/pprio.h"
57
#if defined(XP_WIN)
58
# include <winsock2.h>
59
# include <mstcpip.h>
60
#elif defined(XP_UNIX)
61
# include <errno.h>
62
# include <netinet/tcp.h>
63
#endif
64
/* End keepalive config inclusions. */
65
66
#define SUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 0
67
#define UNSUCCESSFUL_CONNECTING_TO_IPV4_ADDRESS 1
68
#define SUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 2
69
#define UNSUCCESSFUL_CONNECTING_TO_IPV6_ADDRESS 3
70
71
//-----------------------------------------------------------------------------
72
73
static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
74
75
//-----------------------------------------------------------------------------
76
77
namespace mozilla {
78
namespace net {
79
80
class nsSocketEvent : public Runnable {
81
public:
82
nsSocketEvent(nsSocketTransport* transport, uint32_t type,
83
nsresult status = NS_OK, nsISupports* param = nullptr)
84
: Runnable("net::nsSocketEvent"),
85
mTransport(transport),
86
mType(type),
87
mStatus(status),
88
mParam(param) {}
89
90
NS_IMETHOD Run() override {
91
mTransport->OnSocketEvent(mType, mStatus, mParam);
92
return NS_OK;
93
}
94
95
private:
96
RefPtr<nsSocketTransport> mTransport;
97
98
uint32_t mType;
99
nsresult mStatus;
100
nsCOMPtr<nsISupports> mParam;
101
};
102
103
//-----------------------------------------------------------------------------
104
105
//#define TEST_CONNECT_ERRORS
106
#ifdef TEST_CONNECT_ERRORS
107
# include <stdlib.h>
108
static PRErrorCode RandomizeConnectError(PRErrorCode code) {
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
} errors[] = {
119
//
120
// These errors should be recoverable provided there is another
121
// IP address in mDNSRecord.
122
//
123
{PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR"},
124
{PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR"},
125
//
126
// This error will cause this socket transport to error out;
127
// however, if the consumer is HTTP, then the HTTP transaction
128
// should be restarted when this error occurs.
129
//
130
{PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR"},
131
};
132
n = n % (sizeof(errors) / sizeof(errors[0]));
133
code = errors[n].err_code;
134
SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
135
}
136
return code;
137
}
138
#endif
139
140
//-----------------------------------------------------------------------------
141
142
nsresult ErrorAccordingToNSPR(PRErrorCode errorCode) {
143
nsresult rv = NS_ERROR_FAILURE;
144
switch (errorCode) {
145
case PR_WOULD_BLOCK_ERROR:
146
rv = NS_BASE_STREAM_WOULD_BLOCK;
147
break;
148
case PR_CONNECT_ABORTED_ERROR:
149
case PR_CONNECT_RESET_ERROR:
150
rv = NS_ERROR_NET_RESET;
151
break;
152
case PR_END_OF_FILE_ERROR: // XXX document this correlation
153
rv = NS_ERROR_NET_INTERRUPT;
154
break;
155
case PR_CONNECT_REFUSED_ERROR:
156
// We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
157
// could get better diagnostics by adding distinct XPCOM error codes for
158
// each of these, but there are a lot of places in Gecko that check
159
// specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
160
// be checked.
161
case PR_NETWORK_UNREACHABLE_ERROR:
162
case PR_HOST_UNREACHABLE_ERROR:
163
case PR_ADDRESS_NOT_AVAILABLE_ERROR:
164
// Treat EACCES as a soft error since (at least on Linux) connect() returns
165
// EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
166
case PR_NO_ACCESS_RIGHTS_ERROR:
167
rv = NS_ERROR_CONNECTION_REFUSED;
168
break;
169
case PR_ADDRESS_NOT_SUPPORTED_ERROR:
170
rv = NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
171
break;
172
case PR_IO_TIMEOUT_ERROR:
173
case PR_CONNECT_TIMEOUT_ERROR:
174
rv = NS_ERROR_NET_TIMEOUT;
175
break;
176
case PR_OUT_OF_MEMORY_ERROR:
177
// These really indicate that the descriptor table filled up, or that the
178
// kernel ran out of network buffers - but nobody really cares which part of
179
// the system ran out of memory.
180
case PR_PROC_DESC_TABLE_FULL_ERROR:
181
case PR_SYS_DESC_TABLE_FULL_ERROR:
182
case PR_INSUFFICIENT_RESOURCES_ERROR:
183
rv = NS_ERROR_OUT_OF_MEMORY;
184
break;
185
case PR_ADDRESS_IN_USE_ERROR:
186
rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
187
break;
188
// These filename-related errors can arise when using Unix-domain sockets.
189
case PR_FILE_NOT_FOUND_ERROR:
190
rv = NS_ERROR_FILE_NOT_FOUND;
191
break;
192
case PR_IS_DIRECTORY_ERROR:
193
rv = NS_ERROR_FILE_IS_DIRECTORY;
194
break;
195
case PR_LOOP_ERROR:
196
rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
197
break;
198
case PR_NAME_TOO_LONG_ERROR:
199
rv = NS_ERROR_FILE_NAME_TOO_LONG;
200
break;
201
case PR_NO_DEVICE_SPACE_ERROR:
202
rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
203
break;
204
case PR_NOT_DIRECTORY_ERROR:
205
rv = NS_ERROR_FILE_NOT_DIRECTORY;
206
break;
207
case PR_READ_ONLY_FILESYSTEM_ERROR:
208
rv = NS_ERROR_FILE_READ_ONLY;
209
break;
210
case PR_BAD_ADDRESS_ERROR:
211
rv = NS_ERROR_UNKNOWN_HOST;
212
break;
213
default:
214
if (psm::IsNSSErrorCode(errorCode)) {
215
rv = psm::GetXPCOMFromNSSError(errorCode);
216
}
217
break;
218
219
// NSPR's socket code can return these, but they're not worth breaking out
220
// into their own error codes, distinct from NS_ERROR_FAILURE:
221
//
222
// PR_BAD_DESCRIPTOR_ERROR
223
// PR_INVALID_ARGUMENT_ERROR
224
// PR_NOT_SOCKET_ERROR
225
// PR_NOT_TCP_SOCKET_ERROR
226
// These would indicate a bug internal to the component.
227
//
228
// PR_PROTOCOL_NOT_SUPPORTED_ERROR
229
// This means that we can't use the given "protocol" (like
230
// IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
231
// above, this indicates an internal bug.
232
//
233
// PR_IS_CONNECTED_ERROR
234
// This indicates that we've applied a system call like 'bind' or
235
// 'connect' to a socket that is already connected. The socket
236
// components manage each file descriptor's state, and in some cases
237
// handle this error result internally. We shouldn't be returning
238
// this to our callers.
239
//
240
// PR_IO_ERROR
241
// This is so vague that NS_ERROR_FAILURE is just as good.
242
}
243
SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%" PRIx32 "]\n", errorCode,
244
static_cast<uint32_t>(rv)));
245
return rv;
246
}
247
248
//-----------------------------------------------------------------------------
249
// socket input stream impl
250
//-----------------------------------------------------------------------------
251
252
nsSocketInputStream::nsSocketInputStream(nsSocketTransport* trans)
253
: mTransport(trans),
254
mReaderRefCnt(0),
255
mCondition(NS_OK),
256
mCallbackFlags(0),
257
mByteCount(0) {}
258
259
// called on the socket transport thread...
260
//
261
// condition : failure code if socket has been closed
262
//
263
void nsSocketInputStream::OnSocketReady(nsresult condition) {
264
SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%p cond=%" PRIx32 "]\n",
265
this, static_cast<uint32_t>(condition)));
266
267
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
268
269
nsCOMPtr<nsIInputStreamCallback> callback;
270
{
271
MutexAutoLock lock(mTransport->mLock);
272
273
// update condition, but be careful not to erase an already
274
// existing error condition.
275
if (NS_SUCCEEDED(mCondition)) mCondition = condition;
276
277
// ignore event if only waiting for closure and not closed.
278
if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
279
callback = mCallback.forget();
280
mCallbackFlags = 0;
281
}
282
}
283
284
if (callback) callback->OnInputStreamReady(this);
285
}
286
287
NS_IMPL_QUERY_INTERFACE(nsSocketInputStream, nsIInputStream,
288
nsIAsyncInputStream)
289
290
NS_IMETHODIMP_(MozExternalRefCountType)
291
nsSocketInputStream::AddRef() {
292
++mReaderRefCnt;
293
return mTransport->AddRef();
294
}
295
296
NS_IMETHODIMP_(MozExternalRefCountType)
297
nsSocketInputStream::Release() {
298
if (--mReaderRefCnt == 0) Close();
299
return mTransport->Release();
300
}
301
302
NS_IMETHODIMP
303
nsSocketInputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
304
305
NS_IMETHODIMP
306
nsSocketInputStream::Available(uint64_t* avail) {
307
SOCKET_LOG(("nsSocketInputStream::Available [this=%p]\n", this));
308
309
*avail = 0;
310
311
PRFileDesc* fd;
312
{
313
MutexAutoLock lock(mTransport->mLock);
314
315
if (NS_FAILED(mCondition)) return mCondition;
316
317
fd = mTransport->GetFD_Locked();
318
if (!fd) return NS_OK;
319
}
320
321
// cannot hold lock while calling NSPR. (worried about the fact that PSM
322
// synchronously proxies notifications over to the UI thread, which could
323
// mistakenly try to re-enter this code.)
324
int32_t n = PR_Available(fd);
325
326
// PSM does not implement PR_Available() so do a best approximation of it
327
// with MSG_PEEK
328
if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
329
char c;
330
331
n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
332
SOCKET_LOG(
333
("nsSocketInputStream::Available [this=%p] "
334
"using PEEK backup n=%d]\n",
335
this, n));
336
}
337
338
nsresult rv;
339
{
340
MutexAutoLock lock(mTransport->mLock);
341
342
mTransport->ReleaseFD_Locked(fd);
343
344
if (n >= 0)
345
*avail = n;
346
else {
347
PRErrorCode code = PR_GetError();
348
if (code == PR_WOULD_BLOCK_ERROR) return NS_OK;
349
mCondition = ErrorAccordingToNSPR(code);
350
}
351
rv = mCondition;
352
}
353
if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
354
return rv;
355
}
356
357
NS_IMETHODIMP
358
nsSocketInputStream::Read(char* buf, uint32_t count, uint32_t* countRead) {
359
SOCKET_LOG(("nsSocketInputStream::Read [this=%p count=%u]\n", this, count));
360
361
*countRead = 0;
362
363
PRFileDesc* fd = nullptr;
364
{
365
MutexAutoLock lock(mTransport->mLock);
366
367
if (NS_FAILED(mCondition))
368
return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
369
370
fd = mTransport->GetFD_Locked();
371
if (!fd) return NS_BASE_STREAM_WOULD_BLOCK;
372
}
373
374
SOCKET_LOG((" calling PR_Read [count=%u]\n", count));
375
376
// cannot hold lock while calling NSPR. (worried about the fact that PSM
377
// synchronously proxies notifications over to the UI thread, which could
378
// mistakenly try to re-enter this code.)
379
int32_t n = PR_Read(fd, buf, count);
380
381
SOCKET_LOG((" PR_Read returned [n=%d]\n", n));
382
383
nsresult rv = NS_OK;
384
{
385
MutexAutoLock lock(mTransport->mLock);
386
387
#ifdef ENABLE_SOCKET_TRACING
388
if (n > 0) mTransport->TraceInBuf(buf, n);
389
#endif
390
391
mTransport->ReleaseFD_Locked(fd);
392
393
if (n > 0)
394
mByteCount += (*countRead = n);
395
else if (n < 0) {
396
PRErrorCode code = PR_GetError();
397
if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
398
mCondition = ErrorAccordingToNSPR(code);
399
}
400
rv = mCondition;
401
}
402
if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
403
404
// only send this notification if we have indeed read some data.
405
// see bug 196827 for an example of why this is important.
406
if (n > 0) mTransport->SendStatus(NS_NET_STATUS_RECEIVING_FROM);
407
return rv;
408
}
409
410
NS_IMETHODIMP
411
nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
412
uint32_t count, uint32_t* countRead) {
413
// socket stream is unbuffered
414
return NS_ERROR_NOT_IMPLEMENTED;
415
}
416
417
NS_IMETHODIMP
418
nsSocketInputStream::IsNonBlocking(bool* nonblocking) {
419
*nonblocking = true;
420
return NS_OK;
421
}
422
423
NS_IMETHODIMP
424
nsSocketInputStream::CloseWithStatus(nsresult reason) {
425
SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%p reason=%" PRIx32
426
"]\n",
427
this, static_cast<uint32_t>(reason)));
428
429
// may be called from any thread
430
431
nsresult rv;
432
{
433
MutexAutoLock lock(mTransport->mLock);
434
435
if (NS_SUCCEEDED(mCondition))
436
rv = mCondition = reason;
437
else
438
rv = NS_OK;
439
}
440
if (NS_FAILED(rv)) mTransport->OnInputClosed(rv);
441
return NS_OK;
442
}
443
444
NS_IMETHODIMP
445
nsSocketInputStream::AsyncWait(nsIInputStreamCallback* callback, uint32_t flags,
446
uint32_t amount, nsIEventTarget* target) {
447
SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
448
449
bool hasError = false;
450
{
451
MutexAutoLock lock(mTransport->mLock);
452
453
if (callback && target) {
454
//
455
// build event proxy
456
//
457
mCallback = NS_NewInputStreamReadyEvent("nsSocketInputStream::AsyncWait",
458
callback, target);
459
} else
460
mCallback = callback;
461
mCallbackFlags = flags;
462
463
hasError = NS_FAILED(mCondition);
464
} // unlock mTransport->mLock
465
466
if (hasError) {
467
// OnSocketEvent will call OnInputStreamReady with an error code after
468
// going through the event loop. We do this because most socket callers
469
// do not expect AsyncWait() to synchronously execute the OnInputStreamReady
470
// callback.
471
mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
472
} else {
473
mTransport->OnInputPending();
474
}
475
476
return NS_OK;
477
}
478
479
//-----------------------------------------------------------------------------
480
// socket output stream impl
481
//-----------------------------------------------------------------------------
482
483
nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport* trans)
484
: mTransport(trans),
485
mWriterRefCnt(0),
486
mCondition(NS_OK),
487
mCallbackFlags(0),
488
mByteCount(0) {}
489
490
// called on the socket transport thread...
491
//
492
// condition : failure code if socket has been closed
493
//
494
void nsSocketOutputStream::OnSocketReady(nsresult condition) {
495
SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%p cond=%" PRIx32
496
"]\n",
497
this, static_cast<uint32_t>(condition)));
498
499
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
500
501
nsCOMPtr<nsIOutputStreamCallback> callback;
502
{
503
MutexAutoLock lock(mTransport->mLock);
504
505
// update condition, but be careful not to erase an already
506
// existing error condition.
507
if (NS_SUCCEEDED(mCondition)) mCondition = condition;
508
509
// ignore event if only waiting for closure and not closed.
510
if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
511
callback = mCallback.forget();
512
mCallbackFlags = 0;
513
}
514
}
515
516
if (callback) callback->OnOutputStreamReady(this);
517
}
518
519
NS_IMPL_QUERY_INTERFACE(nsSocketOutputStream, nsIOutputStream,
520
nsIAsyncOutputStream)
521
522
NS_IMETHODIMP_(MozExternalRefCountType)
523
nsSocketOutputStream::AddRef() {
524
++mWriterRefCnt;
525
return mTransport->AddRef();
526
}
527
528
NS_IMETHODIMP_(MozExternalRefCountType)
529
nsSocketOutputStream::Release() {
530
if (--mWriterRefCnt == 0) Close();
531
return mTransport->Release();
532
}
533
534
NS_IMETHODIMP
535
nsSocketOutputStream::Close() { return CloseWithStatus(NS_BASE_STREAM_CLOSED); }
536
537
NS_IMETHODIMP
538
nsSocketOutputStream::Flush() { return NS_OK; }
539
540
NS_IMETHODIMP
541
nsSocketOutputStream::Write(const char* buf, uint32_t count,
542
uint32_t* countWritten) {
543
SOCKET_LOG(("nsSocketOutputStream::Write [this=%p count=%u]\n", this, count));
544
545
*countWritten = 0;
546
547
// A write of 0 bytes can be used to force the initial SSL handshake, so do
548
// not reject that.
549
550
PRFileDesc* fd = nullptr;
551
bool fastOpenInProgress;
552
{
553
MutexAutoLock lock(mTransport->mLock);
554
555
if (NS_FAILED(mCondition)) return mCondition;
556
557
fd = mTransport->GetFD_LockedAlsoDuringFastOpen();
558
if (!fd) return NS_BASE_STREAM_WOULD_BLOCK;
559
560
fastOpenInProgress = mTransport->FastOpenInProgress();
561
}
562
563
if (fastOpenInProgress) {
564
// If we are in the fast open phase, we should not write more data
565
// than TCPFastOpenLayer can accept. If we write more data, this data
566
// will be buffered in tls and we want to avoid that.
567
uint32_t availableSpace = TCPFastOpenGetBufferSizeLeft(fd);
568
count = (count > availableSpace) ? availableSpace : count;
569
if (!count) {
570
{
571
MutexAutoLock lock(mTransport->mLock);
572
mTransport->ReleaseFD_Locked(fd);
573
}
574
return NS_BASE_STREAM_WOULD_BLOCK;
575
}
576
}
577
578
SOCKET_LOG((" calling PR_Write [count=%u]\n", count));
579
580
// cannot hold lock while calling NSPR. (worried about the fact that PSM
581
// synchronously proxies notifications over to the UI thread, which could
582
// mistakenly try to re-enter this code.)
583
int32_t n = PR_Write(fd, buf, count);
584
585
SOCKET_LOG((" PR_Write returned [n=%d]\n", n));
586
587
nsresult rv = NS_OK;
588
{
589
MutexAutoLock lock(mTransport->mLock);
590
591
#ifdef ENABLE_SOCKET_TRACING
592
if (n > 0) mTransport->TraceOutBuf(buf, n);
593
#endif
594
595
mTransport->ReleaseFD_Locked(fd);
596
597
if (n > 0)
598
mByteCount += (*countWritten = n);
599
else if (n < 0) {
600
PRErrorCode code = PR_GetError();
601
if (code == PR_WOULD_BLOCK_ERROR) return NS_BASE_STREAM_WOULD_BLOCK;
602
mCondition = ErrorAccordingToNSPR(code);
603
}
604
rv = mCondition;
605
}
606
if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
607
608
// only send this notification if we have indeed written some data.
609
// see bug 196827 for an example of why this is important.
610
// During a fast open we are actually not sending data, the data will be
611
// only buffered in the TCPFastOpenLayer. Therefore we will call
612
// SendStatus(NS_NET_STATUS_SENDING_TO) when we really send data (i.e. when
613
// TCPFastOpenFinish is called.
614
if ((n > 0) && !fastOpenInProgress) {
615
mTransport->SendStatus(NS_NET_STATUS_SENDING_TO);
616
}
617
618
return rv;
619
}
620
621
NS_IMETHODIMP
622
nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void* closure,
623
uint32_t count, uint32_t* countRead) {
624
// socket stream is unbuffered
625
return NS_ERROR_NOT_IMPLEMENTED;
626
}
627
628
nsresult nsSocketOutputStream::WriteFromSegments(
629
nsIInputStream* input, void* closure, const char* fromSegment,
630
uint32_t offset, uint32_t count, uint32_t* countRead) {
631
nsSocketOutputStream* self = (nsSocketOutputStream*)closure;
632
return self->Write(fromSegment, count, countRead);
633
}
634
635
NS_IMETHODIMP
636
nsSocketOutputStream::WriteFrom(nsIInputStream* stream, uint32_t count,
637
uint32_t* countRead) {
638
return stream->ReadSegments(WriteFromSegments, this, count, countRead);
639
}
640
641
NS_IMETHODIMP
642
nsSocketOutputStream::IsNonBlocking(bool* nonblocking) {
643
*nonblocking = true;
644
return NS_OK;
645
}
646
647
NS_IMETHODIMP
648
nsSocketOutputStream::CloseWithStatus(nsresult reason) {
649
SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%p reason=%" PRIx32
650
"]\n",
651
this, static_cast<uint32_t>(reason)));
652
653
// may be called from any thread
654
655
nsresult rv;
656
{
657
MutexAutoLock lock(mTransport->mLock);
658
659
if (NS_SUCCEEDED(mCondition))
660
rv = mCondition = reason;
661
else
662
rv = NS_OK;
663
}
664
if (NS_FAILED(rv)) mTransport->OnOutputClosed(rv);
665
return NS_OK;
666
}
667
668
NS_IMETHODIMP
669
nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback* callback,
670
uint32_t flags, uint32_t amount,
671
nsIEventTarget* target) {
672
SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%p]\n", this));
673
674
{
675
MutexAutoLock lock(mTransport->mLock);
676
677
if (callback && target) {
678
//
679
// build event proxy
680
//
681
mCallback = NS_NewOutputStreamReadyEvent(callback, target);
682
} else
683
mCallback = callback;
684
685
mCallbackFlags = flags;
686
}
687
mTransport->OnOutputPending();
688
return NS_OK;
689
}
690
691
//-----------------------------------------------------------------------------
692
// socket transport impl
693
//-----------------------------------------------------------------------------
694
695
nsSocketTransport::nsSocketTransport()
696
: mPort(0),
697
mProxyPort(0),
698
mOriginPort(0),
699
mProxyTransparent(false),
700
mProxyTransparentResolvesHost(false),
701
mHttpsProxy(false),
702
mConnectionFlags(0),
703
mResetFamilyPreference(false),
704
mTlsFlags(0),
705
mReuseAddrPort(false),
706
mState(STATE_CLOSED),
707
mAttached(false),
708
mInputClosed(true),
709
mOutputClosed(true),
710
mResolving(false),
711
mDNSLookupStatus(NS_OK),
712
mDNSARequestFinished(0),
713
mEsniQueried(false),
714
mEsniUsed(false),
715
mResolvedByTRR(false),
716
mNetAddrIsSet(false),
717
mSelfAddrIsSet(false),
718
mLock("nsSocketTransport.mLock"),
719
mFD(this),
720
mFDref(0),
721
mFDconnected(false),
722
mFDFastOpenInProgress(false),
723
mSocketTransportService(gSocketTransportService),
724
mInput(this),
725
mOutput(this),
726
mLingerPolarity(false),
727
mLingerTimeout(0),
728
mQoSBits(0x00),
729
mKeepaliveEnabled(false),
730
mKeepaliveIdleTimeS(-1),
731
mKeepaliveRetryIntervalS(-1),
732
mKeepaliveProbeCount(-1),
733
mFastOpenCallback(nullptr),
734
mFastOpenLayerHasBufferedData(false),
735
mFastOpenStatus(TFO_NOT_SET),
736
mFirstRetryError(NS_OK),
737
mDoNotRetryToConnect(false),
738
mSSLCallbackSet(false) {
739
this->mNetAddr.raw.family = 0;
740
this->mNetAddr.inet = {};
741
this->mSelfAddr.raw.family = 0;
742
this->mSelfAddr.inet = {};
743
SOCKET_LOG(("creating nsSocketTransport @%p\n", this));
744
745
mTimeouts[TIMEOUT_CONNECT] = UINT16_MAX; // no timeout
746
mTimeouts[TIMEOUT_READ_WRITE] = UINT16_MAX; // no timeout
747
}
748
749
nsSocketTransport::~nsSocketTransport() {
750
SOCKET_LOG(("destroying nsSocketTransport @%p\n", this));
751
}
752
753
nsresult nsSocketTransport::Init(const nsTArray<nsCString>& types,
754
const nsACString& host, uint16_t port,
755
const nsACString& hostRoute,
756
uint16_t portRoute,
757
nsIProxyInfo* givenProxyInfo) {
758
nsCOMPtr<nsProxyInfo> proxyInfo;
759
if (givenProxyInfo) {
760
proxyInfo = do_QueryInterface(givenProxyInfo);
761
NS_ENSURE_ARG(proxyInfo);
762
}
763
764
// init socket type info
765
766
mOriginHost = host;
767
mOriginPort = port;
768
if (!hostRoute.IsEmpty()) {
769
mHost = hostRoute;
770
mPort = portRoute;
771
} else {
772
mHost = host;
773
mPort = port;
774
}
775
776
if (proxyInfo) {
777
mHttpsProxy = proxyInfo->IsHTTPS();
778
}
779
780
const char* proxyType = nullptr;
781
mProxyInfo = proxyInfo;
782
if (proxyInfo) {
783
mProxyPort = proxyInfo->Port();
784
mProxyHost = proxyInfo->Host();
785
// grab proxy type (looking for "socks" for example)
786
proxyType = proxyInfo->Type();
787
if (proxyType && (proxyInfo->IsHTTP() || proxyInfo->IsHTTPS() ||
788
proxyInfo->IsDirect() || !strcmp(proxyType, "unknown"))) {
789
proxyType = nullptr;
790
}
791
}
792
793
SOCKET_LOG1(
794
("nsSocketTransport::Init [this=%p host=%s:%hu origin=%s:%d "
795
"proxy=%s:%hu]\n",
796
this, mHost.get(), mPort, mOriginHost.get(), mOriginPort,
797
mProxyHost.get(), mProxyPort));
798
799
// include proxy type as a socket type if proxy type is not "http"
800
uint32_t typeCount = types.Length() + (proxyType != nullptr);
801
if (!typeCount) return NS_OK;
802
803
// if we have socket types, then the socket provider service had
804
// better exist!
805
nsresult rv;
806
nsCOMPtr<nsISocketProviderService> spserv =
807
nsSocketProviderService::GetOrCreate();
808
809
if (!mTypes.SetCapacity(typeCount, fallible)) {
810
return NS_ERROR_OUT_OF_MEMORY;
811
}
812
813
// now verify that each socket type has a registered socket provider.
814
for (uint32_t i = 0, type = 0; i < typeCount; ++i) {
815
// store socket types
816
if (i == 0 && proxyType)
817
mTypes.AppendElement(proxyType);
818
else
819
mTypes.AppendElement(types[type++]);
820
821
nsCOMPtr<nsISocketProvider> provider;
822
rv = spserv->GetSocketProvider(mTypes[i].get(), getter_AddRefs(provider));
823
if (NS_FAILED(rv)) {
824
NS_WARNING("no registered socket provider");
825
return rv;
826
}
827
828
// note if socket type corresponds to a transparent proxy
829
// XXX don't hardcode SOCKS here (use proxy info's flags instead).
830
if (mTypes[i].EqualsLiteral("socks") || mTypes[i].EqualsLiteral("socks4")) {
831
mProxyTransparent = true;
832
833
if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
834
// we want the SOCKS layer to send the hostname
835
// and port to the proxy and let it do the DNS.
836
mProxyTransparentResolvesHost = true;
837
}
838
}
839
}
840
841
return NS_OK;
842
}
843
844
#if defined(XP_UNIX)
845
nsresult nsSocketTransport::InitWithFilename(const char* filename) {
846
return InitWithName(filename, strlen(filename));
847
}
848
849
nsresult nsSocketTransport::InitWithName(const char* name, size_t length) {
850
if (length > sizeof(mNetAddr.local.path) - 1) {
851
return NS_ERROR_FILE_NAME_TOO_LONG;
852
}
853
854
if (!name[0] && length > 1) {
855
// name is abstract address name that is supported on Linux only
856
# if defined(XP_LINUX)
857
mHost.Assign(name + 1, length - 1);
858
# else
859
return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
860
# endif
861
} else {
862
// The name isn't abstract socket address. So this is Unix domain
863
// socket that has file path.
864
mHost.Assign(name, length);
865
}
866
mPort = 0;
867
868
mNetAddr.local.family = AF_LOCAL;
869
memcpy(mNetAddr.local.path, name, length);
870
mNetAddr.local.path[length] = '\0';
871
mNetAddrIsSet = true;
872
873
return NS_OK;
874
}
875
#endif
876
877
nsresult nsSocketTransport::InitWithConnectedSocket(PRFileDesc* fd,
878
const NetAddr* addr) {
879
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
880
NS_ASSERTION(!mFD.IsInitialized(), "already initialized");
881
882
char buf[kNetAddrMaxCStrBufSize];
883
NetAddrToString(addr, buf, sizeof(buf));
884
mHost.Assign(buf);
885
886
uint16_t port;
887
if (addr->raw.family == AF_INET)
888
port = addr->inet.port;
889
else if (addr->raw.family == AF_INET6)
890
port = addr->inet6.port;
891
else
892
port = 0;
893
mPort = ntohs(port);
894
895
memcpy(&mNetAddr, addr, sizeof(NetAddr));
896
897
mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
898
mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
899
mState = STATE_TRANSFERRING;
900
SetSocketName(fd);
901
mNetAddrIsSet = true;
902
903
{
904
MutexAutoLock lock(mLock);
905
906
mFD = fd;
907
mFDref = 1;
908
mFDconnected = true;
909
}
910
911
// make sure new socket is non-blocking
912
PRSocketOptionData opt;
913
opt.option = PR_SockOpt_Nonblocking;
914
opt.value.non_blocking = true;
915
PR_SetSocketOption(fd, &opt);
916
917
SOCKET_LOG(
918
("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
919
this, mHost.get(), mPort));
920
921
// jump to InitiateSocket to get ourselves attached to the STS poll list.
922
return PostEvent(MSG_RETRY_INIT_SOCKET);
923
}
924
925
nsresult nsSocketTransport::InitWithConnectedSocket(PRFileDesc* aFD,
926
const NetAddr* aAddr,
927
nsISupports* aSecInfo) {
928
mSecInfo = aSecInfo;
929
return InitWithConnectedSocket(aFD, aAddr);
930
}
931
932
nsresult nsSocketTransport::PostEvent(uint32_t type, nsresult status,
933
nsISupports* param) {
934
SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%" PRIx32
935
" param=%p]\n",
936
this, type, static_cast<uint32_t>(status), param));
937
938
nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
939
if (!event) return NS_ERROR_OUT_OF_MEMORY;
940
941
return mSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
942
}
943
944
void nsSocketTransport::SendStatus(nsresult status) {
945
SOCKET_LOG1(("nsSocketTransport::SendStatus [this=%p status=%" PRIx32 "]\n",
946
this, static_cast<uint32_t>(status)));
947
948
nsCOMPtr<nsITransportEventSink> sink;
949
uint64_t progress;
950
{
951
MutexAutoLock lock(mLock);
952
sink = mEventSink;
953
switch (status) {
954
case NS_NET_STATUS_SENDING_TO:
955
progress = mOutput.ByteCount();
956
// If Fast Open is used, we buffer some data in TCPFastOpenLayer,
957
// This data can be only tls data or application data as well.
958
// socketTransport should send status only if it really has sent
959
// application data. socketTransport cannot query transaction for
960
// that info but it can know if transaction has send data if
961
// mOutput.ByteCount() is > 0.
962
if (progress == 0) {
963
return;
964
}
965
break;
966
case NS_NET_STATUS_RECEIVING_FROM:
967
progress = mInput.ByteCount();
968
break;
969
default:
970
progress = 0;
971
break;
972
}
973
}
974
if (sink) {
975
sink->OnTransportStatus(this, status, progress, -1);
976
}
977
}
978
979
nsresult nsSocketTransport::ResolveHost() {
980
SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n", this,
981
SocketHost().get(), SocketPort(),
982
mConnectionFlags & nsSocketTransport::BYPASS_CACHE
983
? " bypass cache"
984
: ""));
985
986
nsresult rv;
987
988
if (!mProxyHost.IsEmpty()) {
989
if (!mProxyTransparent || mProxyTransparentResolvesHost) {
990
#if defined(XP_UNIX)
991
MOZ_ASSERT(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
992
"Unix domain sockets can't be used with proxies");
993
#endif
994
// When not resolving mHost locally, we still want to ensure that
995
// it only contains valid characters. See bug 304904 for details.
996
// Sometimes the end host is not yet known and mHost is *
997
if (!net_IsValidHostName(mHost) && !mHost.EqualsLiteral("*")) {
998
SOCKET_LOG((" invalid hostname %s\n", mHost.get()));
999
return NS_ERROR_UNKNOWN_HOST;
1000
}
1001
}
1002
if (mProxyTransparentResolvesHost) {
1003
// Name resolution is done on the server side. Just pretend
1004
// client resolution is complete, this will get picked up later.
1005
// since we don't need to do DNS now, we bypass the resolving
1006
// step by initializing mNetAddr to an empty address, but we
1007
// must keep the port. The SOCKS IO layer will use the hostname
1008
// we send it when it's created, rather than the empty address
1009
// we send with the connect call.
1010
mState = STATE_RESOLVING;
1011
mNetAddr.raw.family = AF_INET;
1012
mNetAddr.inet.port = htons(SocketPort());
1013
mNetAddr.inet.ip = htonl(INADDR_ANY);
1014
return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr);
1015
}
1016
}
1017
1018
nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
1019
if (NS_FAILED(rv)) return rv;
1020
1021
mResolving = true;
1022
1023
uint32_t dnsFlags = 0;
1024
if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
1025
dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
1026
if (mConnectionFlags & nsSocketTransport::REFRESH_CACHE)
1027
dnsFlags = nsIDNSService::RESOLVE_REFRESH_CACHE;
1028
if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
1029
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
1030
if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
1031
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
1032
if (mConnectionFlags & nsSocketTransport::DISABLE_TRR)
1033
dnsFlags |= nsIDNSService::RESOLVE_DISABLE_TRR;
1034
1035
NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
1036
!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
1037
"Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
1038
1039
SendStatus(NS_NET_STATUS_RESOLVING_HOST);
1040
1041
if (!SocketHost().Equals(mOriginHost)) {
1042
SOCKET_LOG(("nsSocketTransport %p origin %s doing dns for %s\n", this,
1043
mOriginHost.get(), SocketHost().get()));
1044
}
1045
rv = dns->AsyncResolveNative(SocketHost(), dnsFlags, this,
1046
mSocketTransportService, mOriginAttributes,
1047
getter_AddRefs(mDNSRequest));
1048
mEsniQueried = false;
1049
if (mSocketTransportService->IsEsniEnabled() && NS_SUCCEEDED(rv) &&
1050
!(mConnectionFlags & (DONT_TRY_ESNI | BE_CONSERVATIVE))) {
1051
bool isSSL = false;
1052
for (unsigned int i = 0; i < mTypes.Length(); ++i) {
1053
if (mTypes[i].EqualsLiteral("ssl")) {
1054
isSSL = true;
1055
break;
1056
}
1057
}
1058
if (isSSL) {
1059
SOCKET_LOG((" look for esni txt record"));
1060
nsAutoCString esniHost;
1061
esniHost.Append("_esni.");
1062
// This might end up being the SocketHost
1064
esniHost.Append(mOriginHost);
1065
rv = dns->AsyncResolveByTypeNative(
1066
esniHost, nsIDNSService::RESOLVE_TYPE_TXT, dnsFlags, this,
1067
mSocketTransportService, mOriginAttributes,
1068
getter_AddRefs(mDNSTxtRequest));
1069
if (NS_FAILED(rv)) {
1070
SOCKET_LOG((" dns request by type failed."));
1071
mDNSTxtRequest = nullptr;
1072
rv = NS_OK;
1073
} else {
1074
mEsniQueried = true;
1075
}
1076
}
1077
}
1078
1079
if (NS_SUCCEEDED(rv)) {
1080
SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
1081
mState = STATE_RESOLVING;
1082
}
1083
return rv;
1084
}
1085
1086
nsresult nsSocketTransport::BuildSocket(PRFileDesc*& fd, bool& proxyTransparent,
1087
bool& usingSSL) {
1088
SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%p]\n", this));
1089
1090
nsresult rv;
1091
1092
proxyTransparent = false;
1093
usingSSL = false;
1094
1095
if (mTypes.IsEmpty()) {
1096
fd = PR_OpenTCPSocket(mNetAddr.raw.family);
1097
rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1098
} else {
1099
#if defined(XP_UNIX)
1100
MOZ_ASSERT(!mNetAddrIsSet || mNetAddr.raw.family != AF_LOCAL,
1101
"Unix domain sockets can't be used with socket types");
1102
#endif
1103
1104
fd = nullptr;
1105
1106
nsCOMPtr<nsISocketProviderService> spserv =
1107
nsSocketProviderService::GetOrCreate();
1108
1109
// by setting host to mOriginHost, instead of mHost we send the
1110
// SocketProvider (e.g. PSM) the origin hostname but can still do DNS
1111
// on an explicit alternate service host name
1112
const char* host = mOriginHost.get();
1113
int32_t port = (int32_t)mOriginPort;
1114
nsCOMPtr<nsIProxyInfo> proxyInfo = mProxyInfo;
1115
uint32_t controlFlags = 0;
1116
1117
uint32_t i;
1118
for (i = 0; i < mTypes.Length(); ++i) {
1119
nsCOMPtr<nsISocketProvider> provider;
1120
1121
SOCKET_LOG((" pushing io layer [%u:%s]\n", i, mTypes[i].get()));
1122
1123
rv = spserv->GetSocketProvider(mTypes[i].get(), getter_AddRefs(provider));
1124
if (NS_FAILED(rv)) break;
1125
1126
if (mProxyTransparentResolvesHost)
1127
controlFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
1128
1129
if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT)
1130
controlFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
1131
1132
if (mConnectionFlags & nsISocketTransport::NO_PERMANENT_STORAGE)
1133
controlFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
1134
1135
if (mConnectionFlags & nsISocketTransport::MITM_OK)
1136
controlFlags |= nsISocketProvider::MITM_OK;
1137
1138
if (mConnectionFlags & nsISocketTransport::BE_CONSERVATIVE)
1139
controlFlags |= nsISocketProvider::BE_CONSERVATIVE;
1140
1141
nsCOMPtr<nsISupports> secinfo;
1142
if (i == 0) {
1143
// if this is the first type, we'll want the
1144
// service to allocate a new socket
1145
1146
// Most layers _ESPECIALLY_ PSM want the origin name here as they
1147
// will use it for secure checks, etc.. and any connection management
1148
// differences between the origin name and the routed name can be
1149
// taken care of via DNS. However, SOCKS is a special case as there is
1150
// no DNS. in the case of SOCKS and PSM the PSM is a separate layer
1151
// and receives the origin name.
1152
const char* socketProviderHost = host;
1153
int32_t socketProviderPort = port;
1154
if (mProxyTransparentResolvesHost &&
1155
(mTypes[0].EqualsLiteral("socks") ||
1156
mTypes[0].EqualsLiteral("socks4"))) {
1157
SOCKET_LOG(("SOCKS %d Host/Route override: %s:%d -> %s:%d\n",
1158
mHttpsProxy, socketProviderHost, socketProviderPort,
1159
mHost.get(), mPort));
1160
socketProviderHost = mHost.get();
1161
socketProviderPort = mPort;
1162
}
1163
1164
// when https proxying we want to just connect to the proxy as if
1165
// it were the end host (i.e. expect the proxy's cert)
1166
1167
rv = provider->NewSocket(
1168
mNetAddr.raw.family,
1169
mHttpsProxy ? mProxyHost.get() : socketProviderHost,
1170
mHttpsProxy ? mProxyPort : socketProviderPort, proxyInfo,
1171
mOriginAttributes, controlFlags, mTlsFlags, &fd,
1172
getter_AddRefs(secinfo));
1173
1174
if (NS_SUCCEEDED(rv) && !fd) {
1175
MOZ_ASSERT_UNREACHABLE(
1176
"NewSocket succeeded but failed to "
1177
"create a PRFileDesc");
1178
rv = NS_ERROR_UNEXPECTED;
1179
}
1180
} else {
1181
// the socket has already been allocated,
1182
// so we just want the service to add itself
1183
// to the stack (such as pushing an io layer)
1184
rv = provider->AddToSocket(mNetAddr.raw.family, host, port, proxyInfo,
1185
mOriginAttributes, controlFlags, mTlsFlags,
1186
fd, getter_AddRefs(secinfo));
1187
}
1188
1189
// controlFlags = 0; not used below this point...
1190
if (NS_FAILED(rv)) break;
1191
1192
// if the service was ssl or starttls, we want to hold onto the socket
1193
// info
1194
bool isSSL = mTypes[i].EqualsLiteral("ssl");
1195
if (isSSL || mTypes[i].EqualsLiteral("starttls")) {
1196
// remember security info and give notification callbacks to PSM...
1197
nsCOMPtr<nsIInterfaceRequestor> callbacks;
1198
{
1199
MutexAutoLock lock(mLock);
1200
mSecInfo = secinfo;
1201
callbacks = mCallbacks;
1202
SOCKET_LOG((" [secinfo=%p callbacks=%p]\n", mSecInfo.get(),
1203
mCallbacks.get()));
1204
}
1205
// don't call into PSM while holding mLock!!
1206
nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
1207
if (secCtrl) secCtrl->SetNotificationCallbacks(callbacks);
1208
// remember if socket type is SSL so we can ProxyStartSSL if need be.
1209
usingSSL = isSSL;
1210
} else if (mTypes[i].EqualsLiteral("socks") ||
1211
mTypes[i].EqualsLiteral("socks4")) {
1212
// since socks is transparent, any layers above
1213
// it do not have to worry about proxy stuff
1214
proxyInfo = nullptr;
1215
proxyTransparent = true;
1216
}
1217
}
1218
1219
if (NS_FAILED(rv)) {
1220
SOCKET_LOG((" error pushing io layer [%u:%s rv=%" PRIx32 "]\n", i,
1221
mTypes[i].get(), static_cast<uint32_t>(rv)));
1222
if (fd) {
1223
CloseSocket(
1224
fd, mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1225
}
1226
}
1227
}
1228
1229
return rv;
1230
}
1231
1232
// static
1233
SECStatus nsSocketTransport::StoreResumptionToken(
1234
PRFileDesc* fd, const PRUint8* resumptionToken, unsigned int len,
1235
void* ctx) {
1236
PRIntn val;
1237
if (SSL_OptionGet(fd, SSL_ENABLE_SESSION_TICKETS, &val) != SECSuccess ||
1238
val == 0) {
1239
return SECFailure;
1240
}
1241
1242
SSLTokensCache::Put(static_cast<nsSocketTransport*>(ctx)->mHost,
1243
resumptionToken, len);
1244
1245
return SECSuccess;
1246
}
1247
1248
nsresult nsSocketTransport::InitiateSocket() {
1249
SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%p]\n", this));
1250
1251
nsresult rv;
1252
bool isLocal;
1253
IsLocal(&isLocal);
1254
1255
if (gIOService->IsNetTearingDown()) {
1256
return NS_ERROR_ABORT;
1257
}
1258
if (gIOService->IsOffline()) {
1259
if (!isLocal) return NS_ERROR_OFFLINE;
1260
} else if (!isLocal) {
1261
#ifdef DEBUG
1262
// all IP networking has to be done from the parent
1263
if (NS_SUCCEEDED(mCondition) && ((mNetAddr.raw.family == AF_INET) ||
1264
(mNetAddr.raw.family == AF_INET6))) {
1265
MOZ_ASSERT(!IsNeckoChild());
1266
}
1267
#endif
1268
1269
if (NS_SUCCEEDED(mCondition) && xpc::AreNonLocalConnectionsDisabled() &&
1270
!(IsIPAddrAny(&mNetAddr) || IsIPAddrLocal(&mNetAddr))) {
1271
nsAutoCString ipaddr;
1272
RefPtr<nsNetAddr> netaddr = new nsNetAddr(&mNetAddr);
1273
netaddr->GetAddress(ipaddr);
1274
fprintf_stderr(
1275
stderr,
1276
"FATAL ERROR: Non-local network connections are disabled and a "
1277
"connection "
1278
"attempt to %s (%s) was made.\nYou should only access hostnames "
1279
"available via the test networking proxy (if running mochitests) "
1280
"or from a test-specific httpd.js server (if running xpcshell "
1281
"tests). "
1282
"Browser services should be disabled or redirected to a local "
1283
"server.\n",
1284
mHost.get(), ipaddr.get());
1285
MOZ_CRASH("Attempting to connect to non-local address!");
1286
}
1287
}
1288
1289
// Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
1290
// connected - Bug 853423.
1291
if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
1292
IsIPAddrLocal(&mNetAddr)) {
1293
if (SOCKET_LOG_ENABLED()) {
1294
nsAutoCString netAddrCString;
1295
netAddrCString.SetLength(kIPv6CStrBufSize);
1296
if (!NetAddrToString(&mNetAddr, netAddrCString.BeginWriting(),
1297
kIPv6CStrBufSize))
1298
netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
1299
SOCKET_LOG(
1300
("nsSocketTransport::InitiateSocket skipping "
1301
"speculative connection for host [%s:%d] proxy "
1302
"[%s:%d] with Local IP address [%s]",
1303
mHost.get(), mPort, mProxyHost.get(), mProxyPort,
1304
netAddrCString.get()));
1305
}
1306
mCondition = NS_ERROR_CONNECTION_REFUSED;
1307
OnSocketDetached(nullptr);
1308
return mCondition;
1309
}
1310
1311
//
1312
// find out if it is going to be ok to attach another socket to the STS.
1313
// if not then we have to wait for the STS to tell us that it is ok.
1314
// the notification is asynchronous, which means that when we could be
1315
// in a race to call AttachSocket once notified. for this reason, when
1316
// we get notified, we just re-enter this function. as a result, we are
1317
// sure to ask again before calling AttachSocket. in this way we deal
1318
// with the race condition. though it isn't the most elegant solution,
1319
// it is far simpler than trying to build a system that would guarantee
1320
// FIFO ordering (which wouldn't even be that valuable IMO). see bug
1321
// 194402 for more info.
1322
//
1323
if (!mSocketTransportService->CanAttachSocket()) {
1324
nsCOMPtr<nsIRunnable> event =
1325
new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
1326
if (!event) return NS_ERROR_OUT_OF_MEMORY;
1327
return mSocketTransportService->NotifyWhenCanAttachSocket(event);
1328
}
1329
1330
//
1331
// if we already have a connected socket, then just attach and return.
1332
//
1333
if (mFD.IsInitialized()) {
1334
rv = mSocketTransportService->AttachSocket(mFD, this);
1335
if (NS_SUCCEEDED(rv)) mAttached = true;
1336
return rv;
1337
}
1338
1339
//
1340
// create new socket fd, push io layers, etc.
1341
//
1342
PRFileDesc* fd;
1343
bool proxyTransparent;
1344
bool usingSSL;
1345
1346
rv = BuildSocket(fd, proxyTransparent, usingSSL);
1347
if (NS_FAILED(rv)) {
1348
SOCKET_LOG(
1349
(" BuildSocket failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
1350
return rv;
1351
}
1352
1353
// create proxy via IOActivityMonitor
1354
IOActivityMonitor::MonitorSocket(fd);
1355
1356
#ifdef FUZZING
1357
if (StaticPrefs::fuzzing_necko_enabled()) {
1358
rv = AttachFuzzyIOLayer(fd);
1359
if (NS_FAILED(rv)) {
1360
SOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32 "].\n",
1361
static_cast<uint32_t>(rv)));
1362
return rv;
1363
}
1364
SOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
1365
}
1366
#endif
1367
1368
PRStatus status;
1369
1370
// Make the socket non-blocking...
1371
PRSocketOptionData opt;
1372
opt.option = PR_SockOpt_Nonblocking;
1373
opt.value.non_blocking = true;
1374
status = PR_SetSocketOption(fd, &opt);
1375
NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
1376
1377
if (mReuseAddrPort) {
1378
SOCKET_LOG((" Setting port/addr reuse socket options\n"));
1379
1380
// Set ReuseAddr for TCP sockets to enable having several
1381
// sockets bound to same local IP and port
1382
PRSocketOptionData opt_reuseaddr;
1383
opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
1384
opt_reuseaddr.value.reuse_addr = PR_TRUE;
1385
status = PR_SetSocketOption(fd, &opt_reuseaddr);
1386
if (status != PR_SUCCESS) {
1387
SOCKET_LOG((" Couldn't set reuse addr socket option: %d\n", status));
1388
}
1389
1390
// And also set ReusePort for platforms supporting this socket option
1391
PRSocketOptionData opt_reuseport;
1392
opt_reuseport.option = PR_SockOpt_Reuseport;
1393
opt_reuseport.value.reuse_port = PR_TRUE;
1394
status = PR_SetSocketOption(fd, &opt_reuseport);
1395
if (status != PR_SUCCESS &&
1396
PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
1397
SOCKET_LOG((" Couldn't set reuse port socket option: %d\n", status));
1398
}
1399
}
1400
1401
// disable the nagle algorithm - if we rely on it to coalesce writes into
1402
// full packets the final packet of a multi segment POST/PUT or pipeline
1403
// sequence is delayed a full rtt
1404
opt.option = PR_SockOpt_NoDelay;
1405
opt.value.no_delay = true;
1406
PR_SetSocketOption(fd, &opt);
1407
1408
// if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1409
// The Windows default of 8KB is too small and as of vista sp1, autotuning
1410
// only applies to receive window
1411
int32_t sndBufferSize;
1412
mSocketTransportService->GetSendBufferSize(&sndBufferSize);
1413
if (sndBufferSize > 0) {
1414
opt.option = PR_SockOpt_SendBufferSize;
1415
opt.value.send_buffer_size = sndBufferSize;
1416
PR_SetSocketOption(fd, &opt);
1417
}
1418
1419
if (mQoSBits) {
1420
opt.option = PR_SockOpt_IpTypeOfService;
1421
opt.value.tos = mQoSBits;
1422
PR_SetSocketOption(fd, &opt);
1423
}
1424
1425
#if defined(XP_WIN)
1426
// The linger is turned off by default. This is not a hard close, but
1427
// closesocket should return immediately and operating system tries to send
1428
// remaining data for certain, implementation specific, amount of time.
1430
//
1431
// Turn the linger option on an set the interval to 0. This will cause hard
1432
// close of the socket.
1433
opt.option = PR_SockOpt_Linger;
1434
opt.value.linger.polarity = 1;
1435
opt.value.linger.linger = 0;
1436
PR_SetSocketOption(fd, &opt);
1437
#endif
1438
1439
// inform socket transport about this newly created socket...
1440
rv = mSocketTransportService->AttachSocket(fd, this);
1441
if (NS_FAILED(rv)) {
1442
CloseSocket(fd,
1443
mSocketTransportService->IsTelemetryEnabledAndNotSleepPhase());
1444
return rv;
1445
}
1446
mAttached = true;
1447
1448
// assign mFD so that we can properly handle OnSocketDetached before we've
1449
// established a connection.
1450
{
1451
MutexAutoLock lock(mLock);
1452
mFD = fd;
1453
mFDref = 1;
1454
mFDconnected = false;
1455
}
1456
1457
SOCKET_LOG((" advancing to STATE_CONNECTING\n"));
1458
mState = STATE_CONNECTING;
1459
mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1460
SendStatus(NS_NET_STATUS_CONNECTING_TO);
1461
1462
if (SOCKET_LOG_ENABLED()) {
1463
char buf[kNetAddrMaxCStrBufSize];
1464
NetAddrToString(&mNetAddr, buf, sizeof(buf));
1465
SOCKET_LOG((" trying address: %s\n", buf));
1466
}
1467
1468
//
1469
// Initiate the connect() to the host...
1470
//
1471
PRNetAddr prAddr;
1472
{
1473
if (mBindAddr) {
1474
MutexAutoLock lock(mLock);
1475
NetAddrToPRNetAddr(mBindAddr.get(), &prAddr);
1476
status = PR_Bind(fd, &prAddr);
1477
if (status != PR_SUCCESS) {
1478
return NS_ERROR_FAILURE;
1479
}
1480
mBindAddr = nullptr;
1481
}
1482
}
1483
1484
NetAddrToPRNetAddr(&mNetAddr, &prAddr);
1485
1486
#ifdef XP_WIN
1487
// Find the real tcp socket and set non-blocking once again!
1488
// Bug 1158189.
1489
PRFileDesc* bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
1490
if (bottom) {
1491
PROsfd osfd = PR_FileDesc2NativeHandle(bottom);
1492
u_long nonblocking = 1;
1493
if (ioctlsocket(osfd, FIONBIO, &nonblocking) != 0) {
1494
NS_WARNING("Socket could not be set non-blocking!");
1495
return NS_ERROR_FAILURE;
1496
}
1497
}
1498
#endif
1499
1500
if (!mDNSRecordTxt.IsEmpty() && mSecInfo) {
1501
nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1502
if (secCtrl) {
1503
SOCKET_LOG(("nsSocketTransport::InitiateSocket set esni keys."));
1504
rv = secCtrl->SetEsniTxt(mDNSRecordTxt);
1505
if (NS_FAILED(rv)) {
1506
return rv;
1507
}
1508
mEsniUsed = true;
1509
}
1510
}
1511
1512
// We use PRIntervalTime here because we need
1513
// nsIOService::LastOfflineStateChange time and
1514
// nsIOService::LastConectivityChange time to be atomic.
1515
PRIntervalTime connectStarted = 0;
1516
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
1517
connectStarted = PR_IntervalNow();
1518
}
1519
1520
bool tfo = false;
1521
if (!mProxyTransparent && mFastOpenCallback &&
1522
mFastOpenCallback->FastOpenEnabled()) {
1523
if (NS_SUCCEEDED(AttachTCPFastOpenIOLayer(fd))) {
1524
tfo = true;
1525
SOCKET_LOG(
1526
("nsSocketTransport::InitiateSocket TCP Fast Open "
1527
"started [this=%p]\n",
1528
this));
1529
}
1530
}
1531
1532
if (usingSSL && SSLTokensCache::IsEnabled()) {
1533
PRIntn val;
1534
// If SSL_NO_CACHE option was set, we must not use the cache
1535
if (SSL_OptionGet(fd, SSL_NO_CACHE, &val) == SECSuccess && val == 0) {
1536
nsTArray<uint8_t> token;
1537
nsresult rv2 = SSLTokensCache::Get(mHost, token);
1538
if (NS_SUCCEEDED(rv2) && token.Length() != 0) {
1539
SECStatus srv =
1540
SSL_SetResumptionToken(fd, token.Elements(), token.Length());
1541
if (srv == SECFailure) {
1542
SOCKET_LOG(("Setting token failed with NSS error %d [host=%s]",
1543
PORT_GetError(), PromiseFlatCString(mHost).get()));
1544
SSLTokensCache::Remove(mHost);
1545
}
1546
}
1547
}
1548
1549
SSL_SetResumptionTokenCallback(fd, &StoreResumptionToken, this);
1550
mSSLCallbackSet = true;
1551
}
1552
1553
bool connectCalled = true; // This is only needed for telemetry.
1554
status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT);
1555
PRErrorCode code = PR_GetError();
1556
if (status == PR_SUCCESS) {
1557
PR_SetFDInheritable(fd, false);
1558
}
1559
if ((status == PR_SUCCESS) && tfo) {
1560
{
1561
MutexAutoLock lock(mLock);
1562
mFDFastOpenInProgress = true;
1563
}
1564
SOCKET_LOG(("Using TCP Fast Open."));
1565
rv = mFastOpenCallback->StartFastOpen();
1566
if (NS_FAILED(rv)) {
1567
if (NS_SUCCEEDED(mCondition)) {
1568
mCondition = rv;
1569
}
1570
mFastOpenCallback = nullptr;
1571
MutexAutoLock lock(mLock);
1572
mFDFastOpenInProgress = false;
1573
return rv;
1574
}
1575
status = PR_FAILURE;
1576
connectCalled = false;
1577
bool fastOpenNotSupported = false;
1578
TCPFastOpenFinish(fd, code, fastOpenNotSupported, mFastOpenStatus);
1579
1580
// If we have sent data, trigger a socket status event.
1581
if (mFastOpenStatus == TFO_DATA_SENT) {
1582
SendStatus(NS_NET_STATUS_SENDING_TO);
1583
}
1584
1585
// If we have still some data buffered this data must be flush before
1586
// mOutput.OnSocketReady(NS_OK) is called in
1587
// nsSocketTransport::OnSocketReady, partially to keep socket status
1588
// event in order.
1589
mFastOpenLayerHasBufferedData = TCPFastOpenGetCurrentBufferSize(fd);
1590
1591
MOZ_ASSERT((mFastOpenStatus == TFO_NOT_TRIED) ||
1592
(mFastOpenStatus == TFO_DISABLED) ||
1593
(mFastOpenStatus == TFO_DATA_SENT) ||
1594
(mFastOpenStatus == TFO_TRIED));
1595
mFastOpenCallback->SetFastOpenStatus(mFastOpenStatus);
1596
SOCKET_LOG(
1597
("called StartFastOpen - code=%d; fastOpen is %s "
1598
"supported.\n",
1599
code, fastOpenNotSupported ? "not" : ""));
1600
SOCKET_LOG(("TFO status %d\n", mFastOpenStatus));
1601
1602
if (fastOpenNotSupported) {
1603
// When TCP_FastOpen is turned off on the local host
1604
// SendTo will return PR_NOT_TCP_SOCKET_ERROR. This is only
1605
// on Linux.
1606
// If a windows version does not support Fast Open, the return value
1607
// will be PR_NOT_IMPLEMENTED_ERROR. This is only for windows 10
1608
// versions older than version 1607, because we do not have subverion
1609
// to check, we need to call PR_SendTo to check if it is supported.
1610
mFastOpenCallback->FastOpenNotSupported();
1611
// FastOpenNotSupported will set Fast Open as not supported globally.
1612
// For this connection we will pretend that we still use fast open,
1613
// because of the fallback mechanism in case we need to restart the
1614
// attached transaction.
1615
connectCalled = true;
1616
}
1617
} else {
1618
mFastOpenCallback = nullptr;
1619
}
1620
1621
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
1622
connectStarted && connectCalled) {
1623
SendPRBlockingTelemetry(
1624
connectStarted, Telemetry::PRCONNECT_BLOCKING_TIME_NORMAL,
1625
Telemetry::PRCONNECT_BLOCKING_TIME_SHUTDOWN,
1626
Telemetry::PRCONNECT_BLOCKING_TIME_CONNECTIVITY_CHANGE,
1627
Telemetry::PRCONNECT_BLOCKING_TIME_LINK_CHANGE,
1628
Telemetry::PRCONNECT_BLOCKING_TIME_OFFLINE);
1629
}
1630
1631
if (status == PR_SUCCESS) {
1632
//
1633
// we are connected!
1634
//
1635
OnSocketConnected();
1636
} else {
1637
#if defined(TEST_CONNECT_ERRORS)
1638
code = RandomizeConnectError(code);
1639
#endif
1640
//
1641
// If the PR_Connect(...) would block, then poll for a connection.
1642
//
1643
if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
1644
mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1645
//
1646
// If the socket is already connected, then return success...
1647
//
1648
else if (PR_IS_CONNECTED_ERROR == code) {
1649
//
1650
// we are connected!
1651
//
1652
OnSocketConnected();
1653
1654
if (mSecInfo && !mProxyHost.IsEmpty() && proxyTransparent && usingSSL) {
1655
// if the connection phase is finished, and the ssl layer has
1656
// been pushed, and we were proxying (transparently; ie. nothing
1657
// has to happen in the protocol layer above us), it's time for
1658
// the ssl to start doing it's thing.
1659
nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1660
if (secCtrl) {
1661
SOCKET_LOG((" calling ProxyStartSSL()\n"));
1662
secCtrl->ProxyStartSSL();
1663
}
1664
// XXX what if we were forced to poll on the socket for a successful
1665
// connection... wouldn't we need to call ProxyStartSSL after a call
1666
// to PR_ConnectContinue indicates that we are connected?
1667
//
1668
// XXX this appears to be what the old socket transport did. why
1669
// isn't this broken?
1670
}
1671
}
1672
//
1673
// A SOCKS request was rejected; get the actual error code from
1674
// the OS error
1675
//
1676
else if (PR_UNKNOWN_ERROR == code && mProxyTransparent &&
1677
!mProxyHost.IsEmpty()) {
1678
code = PR_GetOSError();
1679
rv = ErrorAccordingToNSPR(code);
1680
}
1681
//
1682
// The connection was refused...
1683
//
1684
else {
1685
if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase() &&
1686
connectStarted && connectStarted) {
1687
SendPRBlockingTelemetry(
1688
connectStarted, Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_NORMAL,
1689
Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_SHUTDOWN,
1690
Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_CONNECTIVITY_CHANGE,
1691
Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_LINK_CHANGE,
1692
Telemetry::PRCONNECT_FAIL_BLOCKING_TIME_OFFLINE);
1693
}
1694
1695
rv = ErrorAccordingToNSPR(code);
1696
if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
1697
rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1698
}
1699
}
1700
return rv;
1701
}
1702
1703
bool nsSocketTransport::RecoverFromError() {
1704
NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
1705
1706
SOCKET_LOG(
1707
("nsSocketTransport::RecoverFromError [this=%p state=%x cond=%" PRIx32
1708
"]\n",
1709
this, mState, static_cast<uint32_t>(mCondition)));
1710
1711
if (mDoNotRetryToConnect) {
1712
SOCKET_LOG(
1713
("nsSocketTransport::RecoverFromError do not retry because "
1714
"mDoNotRetryToConnect is set [this=%p]\n",
1715
this));
1716
return false;
1717
}
1718
1719
#if defined(XP_UNIX)
1720
// Unix domain connections don't have multiple addresses to try,
1721
// so the recovery techniques here don't apply.
1722
if (mNetAddrIsSet && mNetAddr.raw.family == AF_LOCAL) return false;
1723
#endif
1724
1725
// can only recover from errors in these states
1726
if (mState != STATE_RESOLVING && mState != STATE_CONNECTING) {
1727
SOCKET_LOG((" not in a recoverable state"));
1728
return false;
1729
}
1730
1731
nsresult rv;
1732
1733
// OK to check this outside mLock
1734
NS_ASSERTION(!mFDconnected, "socket should not be connected");
1735
1736
// all connection failures need to be reported to DNS so that the next
1737
// time we will use a different address if available.
1738
// Skip conditions that can be cause by TCP Fast Open.
1739
if ((!mFDFastOpenInProgress ||
1740
((mCondition != NS_ERROR_CONNECTION_REFUSED) &&
1741
(mCondition != NS_ERROR_NET_TIMEOUT) &&
1742
(mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED))) &&
1743<