Coverage Report

Created: 2018-09-25 14:53

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