/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 |