1 : /* vim:set ts=4 sw=4 et cindent: */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com>
24 : * Malcolm Smith <malsmith@cs.rmit.edu.au>
25 : * Andreas Otte <andreas.otte@debitel.net>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifdef MOZ_LOGGING
42 : #define FORCE_PR_LOG
43 : #endif
44 :
45 : #include "nsSocketTransport2.h"
46 : #include "nsAtomicRefcnt.h"
47 : #include "nsIOService.h"
48 : #include "nsStreamUtils.h"
49 : #include "nsNetSegmentUtils.h"
50 : #include "nsNetAddr.h"
51 : #include "nsTransportUtils.h"
52 : #include "nsProxyInfo.h"
53 : #include "nsNetCID.h"
54 : #include "nsAutoPtr.h"
55 : #include "nsCOMPtr.h"
56 : #include "netCore.h"
57 : #include "prmem.h"
58 : #include "plstr.h"
59 : #include "prnetdb.h"
60 : #include "prerror.h"
61 : #include "prerr.h"
62 :
63 : #include "nsIServiceManager.h"
64 : #include "nsISocketProviderService.h"
65 : #include "nsISocketProvider.h"
66 : #include "nsISSLSocketControl.h"
67 : #include "nsINSSErrorsService.h"
68 : #include "nsIPipe.h"
69 : #include "nsIProgrammingLanguage.h"
70 : #include "nsIClassInfoImpl.h"
71 :
72 : #if defined(XP_WIN) || defined(MOZ_PLATFORM_MAEMO)
73 : #include "nsNativeConnectionHelper.h"
74 : #endif
75 :
76 : using namespace mozilla;
77 :
78 : //-----------------------------------------------------------------------------
79 :
80 : static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
81 : static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
82 :
83 : //-----------------------------------------------------------------------------
84 :
85 : class nsSocketEvent : public nsRunnable
86 70592 : {
87 : public:
88 17648 : nsSocketEvent(nsSocketTransport *transport, PRUint32 type,
89 : nsresult status = NS_OK, nsISupports *param = nsnull)
90 : : mTransport(transport)
91 : , mType(type)
92 : , mStatus(status)
93 17648 : , mParam(param)
94 17648 : {}
95 :
96 17648 : NS_IMETHOD Run()
97 : {
98 17648 : mTransport->OnSocketEvent(mType, mStatus, mParam);
99 17648 : return NS_OK;
100 : }
101 :
102 : private:
103 : nsRefPtr<nsSocketTransport> mTransport;
104 :
105 : PRUint32 mType;
106 : nsresult mStatus;
107 : nsCOMPtr<nsISupports> mParam;
108 : };
109 :
110 : //-----------------------------------------------------------------------------
111 :
112 : //#define TEST_CONNECT_ERRORS
113 : #ifdef TEST_CONNECT_ERRORS
114 : #include <stdlib.h>
115 : static PRErrorCode RandomizeConnectError(PRErrorCode code)
116 : {
117 : //
118 : // To test out these errors, load http://www.yahoo.com/. It should load
119 : // correctly despite the random occurrence of these errors.
120 : //
121 : int n = rand();
122 : if (n > RAND_MAX/2) {
123 : struct {
124 : PRErrorCode err_code;
125 : const char *err_name;
126 : }
127 : errors[] = {
128 : //
129 : // These errors should be recoverable provided there is another
130 : // IP address in mDNSRecord.
131 : //
132 : { PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR" },
133 : { PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR" },
134 : //
135 : // This error will cause this socket transport to error out;
136 : // however, if the consumer is HTTP, then the HTTP transaction
137 : // should be restarted when this error occurs.
138 : //
139 : { PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR" },
140 : };
141 : n = n % (sizeof(errors)/sizeof(errors[0]));
142 : code = errors[n].err_code;
143 : SOCKET_LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
144 : }
145 : return code;
146 : }
147 : #endif
148 :
149 : //-----------------------------------------------------------------------------
150 :
151 : static bool
152 0 : IsNSSErrorCode(PRErrorCode code)
153 : {
154 : return
155 : ((code >= nsINSSErrorsService::NSS_SEC_ERROR_BASE) &&
156 : (code < nsINSSErrorsService::NSS_SEC_ERROR_LIMIT))
157 : ||
158 : ((code >= nsINSSErrorsService::NSS_SSL_ERROR_BASE) &&
159 0 : (code < nsINSSErrorsService::NSS_SSL_ERROR_LIMIT));
160 : }
161 :
162 : // this logic is duplicated from the implementation of
163 : // nsINSSErrorsService::getXPCOMFromNSSError
164 : // It might have been better to implement that interface here...
165 : static nsresult
166 0 : GetXPCOMFromNSSError(PRErrorCode code)
167 : {
168 0 : return NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_SECURITY, -1 * code);
169 : }
170 :
171 : static nsresult
172 139 : ErrorAccordingToNSPR(PRErrorCode errorCode)
173 : {
174 139 : nsresult rv = NS_ERROR_FAILURE;
175 139 : switch (errorCode) {
176 : case PR_WOULD_BLOCK_ERROR:
177 0 : rv = NS_BASE_STREAM_WOULD_BLOCK;
178 0 : break;
179 : case PR_CONNECT_ABORTED_ERROR:
180 : case PR_CONNECT_RESET_ERROR:
181 3 : rv = NS_ERROR_NET_RESET;
182 3 : break;
183 : case PR_END_OF_FILE_ERROR: // XXX document this correlation
184 0 : rv = NS_ERROR_NET_INTERRUPT;
185 0 : break;
186 : case PR_CONNECT_REFUSED_ERROR:
187 : case PR_NETWORK_UNREACHABLE_ERROR: // XXX need new nsresult for this!
188 : case PR_HOST_UNREACHABLE_ERROR: // XXX and this!
189 : case PR_ADDRESS_NOT_AVAILABLE_ERROR:
190 : // Treat EACCES as a soft error since (at least on Linux) connect() returns
191 : // EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
192 : case PR_ADDRESS_NOT_SUPPORTED_ERROR:
193 : case PR_NO_ACCESS_RIGHTS_ERROR:
194 136 : rv = NS_ERROR_CONNECTION_REFUSED;
195 136 : break;
196 : case PR_IO_TIMEOUT_ERROR:
197 : case PR_CONNECT_TIMEOUT_ERROR:
198 0 : rv = NS_ERROR_NET_TIMEOUT;
199 0 : break;
200 : default:
201 0 : if (IsNSSErrorCode(errorCode))
202 0 : rv = GetXPCOMFromNSSError(errorCode);
203 0 : break;
204 : }
205 139 : SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
206 139 : return rv;
207 : }
208 :
209 : //-----------------------------------------------------------------------------
210 : // socket input stream impl
211 : //-----------------------------------------------------------------------------
212 :
213 5883 : nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
214 : : mTransport(trans)
215 : , mReaderRefCnt(0)
216 : , mCondition(NS_OK)
217 : , mCallbackFlags(0)
218 5883 : , mByteCount(0)
219 : {
220 5883 : }
221 :
222 5882 : nsSocketInputStream::~nsSocketInputStream()
223 : {
224 11764 : }
225 :
226 : // called on the socket transport thread...
227 : //
228 : // condition : failure code if socket has been closed
229 : //
230 : void
231 20764 : nsSocketInputStream::OnSocketReady(nsresult condition)
232 : {
233 20764 : SOCKET_LOG(("nsSocketInputStream::OnSocketReady [this=%x cond=%x]\n",
234 : this, condition));
235 :
236 20764 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
237 :
238 41528 : nsCOMPtr<nsIInputStreamCallback> callback;
239 : {
240 41528 : MutexAutoLock lock(mTransport->mLock);
241 :
242 : // update condition, but be careful not to erase an already
243 : // existing error condition.
244 20764 : if (NS_SUCCEEDED(mCondition))
245 12150 : mCondition = condition;
246 :
247 : // ignore event if only waiting for closure and not closed.
248 20764 : if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
249 20738 : callback = mCallback;
250 20738 : mCallback = nsnull;
251 20738 : mCallbackFlags = 0;
252 : }
253 : }
254 :
255 20764 : if (callback)
256 9217 : callback->OnInputStreamReady(this);
257 20764 : }
258 :
259 17716 : NS_IMPL_QUERY_INTERFACE2(nsSocketInputStream,
260 : nsIInputStream,
261 : nsIAsyncInputStream)
262 :
263 : NS_IMETHODIMP_(nsrefcnt)
264 26574 : nsSocketInputStream::AddRef()
265 : {
266 26574 : NS_AtomicIncrementRefcnt(mReaderRefCnt);
267 26574 : return mTransport->AddRef();
268 : }
269 :
270 : NS_IMETHODIMP_(nsrefcnt)
271 26574 : nsSocketInputStream::Release()
272 : {
273 26574 : if (NS_AtomicDecrementRefcnt(mReaderRefCnt) == 0)
274 5882 : Close();
275 26574 : return mTransport->Release();
276 : }
277 :
278 : NS_IMETHODIMP
279 5882 : nsSocketInputStream::Close()
280 : {
281 5882 : return CloseWithStatus(NS_BASE_STREAM_CLOSED);
282 : }
283 :
284 : NS_IMETHODIMP
285 6 : nsSocketInputStream::Available(PRUint32 *avail)
286 : {
287 6 : SOCKET_LOG(("nsSocketInputStream::Available [this=%x]\n", this));
288 :
289 6 : *avail = 0;
290 :
291 : PRFileDesc *fd;
292 : {
293 12 : MutexAutoLock lock(mTransport->mLock);
294 :
295 6 : if (NS_FAILED(mCondition))
296 0 : return mCondition;
297 :
298 6 : fd = mTransport->GetFD_Locked();
299 6 : if (!fd)
300 0 : return NS_OK;
301 : }
302 :
303 : // cannot hold lock while calling NSPR. (worried about the fact that PSM
304 : // synchronously proxies notifications over to the UI thread, which could
305 : // mistakenly try to re-enter this code.)
306 6 : PRInt32 n = PR_Available(fd);
307 :
308 : // PSM does not implement PR_Available() so do a best approximation of it
309 : // with MSG_PEEK
310 6 : if ((n == -1) && (PR_GetError() == PR_NOT_IMPLEMENTED_ERROR)) {
311 : char c;
312 :
313 4 : n = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
314 4 : SOCKET_LOG(("nsSocketInputStream::Available [this=%x] "
315 : "using PEEK backup n=%d]\n", this, n));
316 : }
317 :
318 : nsresult rv;
319 : {
320 12 : MutexAutoLock lock(mTransport->mLock);
321 :
322 6 : mTransport->ReleaseFD_Locked(fd);
323 :
324 6 : if (n >= 0)
325 2 : *avail = n;
326 : else {
327 4 : PRErrorCode code = PR_GetError();
328 4 : if (code == PR_WOULD_BLOCK_ERROR)
329 4 : return NS_OK;
330 0 : mCondition = ErrorAccordingToNSPR(code);
331 : }
332 8 : rv = mCondition;
333 : }
334 2 : if (NS_FAILED(rv))
335 0 : mTransport->OnInputClosed(rv);
336 2 : return rv;
337 : }
338 :
339 : NS_IMETHODIMP
340 18590 : nsSocketInputStream::Read(char *buf, PRUint32 count, PRUint32 *countRead)
341 : {
342 18590 : SOCKET_LOG(("nsSocketInputStream::Read [this=%x count=%u]\n", this, count));
343 :
344 18590 : *countRead = 0;
345 :
346 : PRFileDesc *fd;
347 : {
348 37180 : MutexAutoLock lock(mTransport->mLock);
349 :
350 18590 : if (NS_FAILED(mCondition))
351 2 : return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
352 :
353 18588 : fd = mTransport->GetFD_Locked();
354 18588 : if (!fd)
355 36 : return NS_BASE_STREAM_WOULD_BLOCK;
356 : }
357 :
358 18552 : SOCKET_LOG((" calling PR_Read [count=%u]\n", count));
359 :
360 : // cannot hold lock while calling NSPR. (worried about the fact that PSM
361 : // synchronously proxies notifications over to the UI thread, which could
362 : // mistakenly try to re-enter this code.)
363 18552 : PRInt32 n = PR_Read(fd, buf, count);
364 :
365 18552 : SOCKET_LOG((" PR_Read returned [n=%d]\n", n));
366 :
367 : nsresult rv;
368 : {
369 37104 : MutexAutoLock lock(mTransport->mLock);
370 :
371 : #ifdef ENABLE_SOCKET_TRACING
372 : if (n > 0)
373 : mTransport->TraceInBuf(buf, n);
374 : #endif
375 :
376 18552 : mTransport->ReleaseFD_Locked(fd);
377 :
378 18552 : if (n > 0)
379 9349 : mByteCount += (*countRead = n);
380 9203 : else if (n < 0) {
381 6347 : PRErrorCode code = PR_GetError();
382 6347 : if (code == PR_WOULD_BLOCK_ERROR)
383 6347 : return NS_BASE_STREAM_WOULD_BLOCK;
384 0 : mCondition = ErrorAccordingToNSPR(code);
385 : }
386 30757 : rv = mCondition;
387 : }
388 12205 : if (NS_FAILED(rv))
389 0 : mTransport->OnInputClosed(rv);
390 :
391 : // only send this notification if we have indeed read some data.
392 : // see bug 196827 for an example of why this is important.
393 12205 : if (n > 0)
394 9349 : mTransport->SendStatus(nsISocketTransport::STATUS_RECEIVING_FROM);
395 12205 : return rv;
396 : }
397 :
398 : NS_IMETHODIMP
399 0 : nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
400 : PRUint32 count, PRUint32 *countRead)
401 : {
402 : // socket stream is unbuffered
403 0 : return NS_ERROR_NOT_IMPLEMENTED;
404 : }
405 :
406 : NS_IMETHODIMP
407 0 : nsSocketInputStream::IsNonBlocking(bool *nonblocking)
408 : {
409 0 : *nonblocking = true;
410 0 : return NS_OK;
411 : }
412 :
413 : NS_IMETHODIMP
414 11926 : nsSocketInputStream::CloseWithStatus(nsresult reason)
415 : {
416 11926 : SOCKET_LOG(("nsSocketInputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason));
417 :
418 : // may be called from any thread
419 :
420 : nsresult rv;
421 : {
422 23852 : MutexAutoLock lock(mTransport->mLock);
423 :
424 11926 : if (NS_SUCCEEDED(mCondition))
425 8581 : rv = mCondition = reason;
426 : else
427 3345 : rv = NS_OK;
428 : }
429 11926 : if (NS_FAILED(rv))
430 5739 : mTransport->OnInputClosed(rv);
431 11926 : return NS_OK;
432 : }
433 :
434 : NS_IMETHODIMP
435 9253 : nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
436 : PRUint32 flags,
437 : PRUint32 amount,
438 : nsIEventTarget *target)
439 : {
440 9253 : SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%x]\n", this));
441 :
442 : // This variable will be non-null when we want to call the callback
443 : // directly from this function, but outside the lock.
444 : // (different from callback when target is not null)
445 18506 : nsCOMPtr<nsIInputStreamCallback> directCallback;
446 : {
447 18506 : MutexAutoLock lock(mTransport->mLock);
448 :
449 9253 : if (callback && target) {
450 : //
451 : // build event proxy
452 : //
453 : // failure to create an event proxy (most likely out of memory)
454 : // shouldn't alter the state of the transport.
455 : //
456 0 : nsCOMPtr<nsIInputStreamCallback> temp;
457 0 : nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(temp),
458 0 : callback, target);
459 0 : if (NS_FAILED(rv)) return rv;
460 0 : mCallback = temp;
461 : }
462 : else
463 9253 : mCallback = callback;
464 :
465 9253 : if (NS_FAILED(mCondition))
466 0 : directCallback.swap(mCallback);
467 : else
468 9253 : mCallbackFlags = flags;
469 : }
470 9253 : if (directCallback)
471 0 : directCallback->OnInputStreamReady(this);
472 : else
473 9253 : mTransport->OnInputPending();
474 :
475 9253 : return NS_OK;
476 : }
477 :
478 : //-----------------------------------------------------------------------------
479 : // socket output stream impl
480 : //-----------------------------------------------------------------------------
481 :
482 5883 : nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
483 : : mTransport(trans)
484 : , mWriterRefCnt(0)
485 : , mCondition(NS_OK)
486 : , mCallbackFlags(0)
487 5883 : , mByteCount(0)
488 : {
489 5883 : }
490 :
491 5882 : nsSocketOutputStream::~nsSocketOutputStream()
492 : {
493 11764 : }
494 :
495 : // called on the socket transport thread...
496 : //
497 : // condition : failure code if socket has been closed
498 : //
499 : void
500 20903 : nsSocketOutputStream::OnSocketReady(nsresult condition)
501 : {
502 20903 : SOCKET_LOG(("nsSocketOutputStream::OnSocketReady [this=%x cond=%x]\n",
503 : this, condition));
504 :
505 20903 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
506 :
507 41806 : nsCOMPtr<nsIOutputStreamCallback> callback;
508 : {
509 41806 : MutexAutoLock lock(mTransport->mLock);
510 :
511 : // update condition, but be careful not to erase an already
512 : // existing error condition.
513 20903 : if (NS_SUCCEEDED(mCondition))
514 15127 : mCondition = condition;
515 :
516 : // ignore event if only waiting for closure and not closed.
517 20903 : if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) {
518 11684 : callback = mCallback;
519 11684 : mCallback = nsnull;
520 11684 : mCallbackFlags = 0;
521 : }
522 : }
523 :
524 20903 : if (callback)
525 5911 : callback->OnOutputStreamReady(this);
526 20903 : }
527 :
528 17718 : NS_IMPL_QUERY_INTERFACE2(nsSocketOutputStream,
529 : nsIOutputStream,
530 : nsIAsyncOutputStream)
531 :
532 : NS_IMETHODIMP_(nsrefcnt)
533 26577 : nsSocketOutputStream::AddRef()
534 : {
535 26577 : NS_AtomicIncrementRefcnt(mWriterRefCnt);
536 26577 : return mTransport->AddRef();
537 : }
538 :
539 : NS_IMETHODIMP_(nsrefcnt)
540 26575 : nsSocketOutputStream::Release()
541 : {
542 26575 : if (NS_AtomicDecrementRefcnt(mWriterRefCnt) == 0)
543 5882 : Close();
544 26575 : return mTransport->Release();
545 : }
546 :
547 : NS_IMETHODIMP
548 5882 : nsSocketOutputStream::Close()
549 : {
550 5882 : return CloseWithStatus(NS_BASE_STREAM_CLOSED);
551 : }
552 :
553 : NS_IMETHODIMP
554 0 : nsSocketOutputStream::Flush()
555 : {
556 0 : return NS_OK;
557 : }
558 :
559 : NS_IMETHODIMP
560 9520 : nsSocketOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *countWritten)
561 : {
562 9520 : SOCKET_LOG(("nsSocketOutputStream::Write [this=%x count=%u]\n", this, count));
563 :
564 9520 : *countWritten = 0;
565 :
566 : // A write of 0 bytes can be used to force the initial SSL handshake.
567 9520 : if (count == 0 && mByteCount)
568 0 : return NS_OK;
569 :
570 : PRFileDesc *fd;
571 : {
572 19040 : MutexAutoLock lock(mTransport->mLock);
573 :
574 9520 : if (NS_FAILED(mCondition))
575 145 : return mCondition;
576 :
577 9375 : fd = mTransport->GetFD_Locked();
578 9375 : if (!fd)
579 0 : return NS_BASE_STREAM_WOULD_BLOCK;
580 : }
581 :
582 9375 : SOCKET_LOG((" calling PR_Write [count=%u]\n", count));
583 :
584 : // cannot hold lock while calling NSPR. (worried about the fact that PSM
585 : // synchronously proxies notifications over to the UI thread, which could
586 : // mistakenly try to re-enter this code.)
587 9375 : PRInt32 n = PR_Write(fd, buf, count);
588 :
589 9375 : SOCKET_LOG((" PR_Write returned [n=%d]\n", n));
590 :
591 : nsresult rv;
592 : {
593 18750 : MutexAutoLock lock(mTransport->mLock);
594 :
595 : #ifdef ENABLE_SOCKET_TRACING
596 : if (n > 0)
597 : mTransport->TraceOutBuf(buf, n);
598 : #endif
599 :
600 9375 : mTransport->ReleaseFD_Locked(fd);
601 :
602 9375 : if (n > 0)
603 9344 : mByteCount += (*countWritten = n);
604 31 : else if (n < 0) {
605 27 : PRErrorCode code = PR_GetError();
606 27 : if (code == PR_WOULD_BLOCK_ERROR)
607 24 : return NS_BASE_STREAM_WOULD_BLOCK;
608 3 : mCondition = ErrorAccordingToNSPR(code);
609 : }
610 18726 : rv = mCondition;
611 : }
612 9351 : if (NS_FAILED(rv))
613 3 : mTransport->OnOutputClosed(rv);
614 :
615 : // only send this notification if we have indeed written some data.
616 : // see bug 196827 for an example of why this is important.
617 9351 : if (n > 0)
618 9344 : mTransport->SendStatus(nsISocketTransport::STATUS_SENDING_TO);
619 9351 : return rv;
620 : }
621 :
622 : NS_IMETHODIMP
623 0 : nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
624 : PRUint32 count, PRUint32 *countRead)
625 : {
626 : // socket stream is unbuffered
627 0 : return NS_ERROR_NOT_IMPLEMENTED;
628 : }
629 :
630 : NS_METHOD
631 0 : nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
632 : void *closure,
633 : const char *fromSegment,
634 : PRUint32 offset,
635 : PRUint32 count,
636 : PRUint32 *countRead)
637 : {
638 0 : nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
639 0 : return self->Write(fromSegment, count, countRead);
640 : }
641 :
642 : NS_IMETHODIMP
643 0 : nsSocketOutputStream::WriteFrom(nsIInputStream *stream, PRUint32 count, PRUint32 *countRead)
644 : {
645 0 : return stream->ReadSegments(WriteFromSegments, this, count, countRead);
646 : }
647 :
648 : NS_IMETHODIMP
649 0 : nsSocketOutputStream::IsNonBlocking(bool *nonblocking)
650 : {
651 0 : *nonblocking = true;
652 0 : return NS_OK;
653 : }
654 :
655 : NS_IMETHODIMP
656 11926 : nsSocketOutputStream::CloseWithStatus(nsresult reason)
657 : {
658 11926 : SOCKET_LOG(("nsSocketOutputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason));
659 :
660 : // may be called from any thread
661 :
662 : nsresult rv;
663 : {
664 23852 : MutexAutoLock lock(mTransport->mLock);
665 :
666 11926 : if (NS_SUCCEEDED(mCondition))
667 8631 : rv = mCondition = reason;
668 : else
669 3295 : rv = NS_OK;
670 : }
671 11926 : if (NS_FAILED(rv))
672 5733 : mTransport->OnOutputClosed(rv);
673 11926 : return NS_OK;
674 : }
675 :
676 : NS_IMETHODIMP
677 12235 : nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback,
678 : PRUint32 flags,
679 : PRUint32 amount,
680 : nsIEventTarget *target)
681 : {
682 12235 : SOCKET_LOG(("nsSocketOutputStream::AsyncWait [this=%x]\n", this));
683 :
684 : {
685 24470 : MutexAutoLock lock(mTransport->mLock);
686 :
687 12235 : if (callback && target) {
688 : //
689 : // build event proxy
690 : //
691 : // failure to create an event proxy (most likely out of memory)
692 : // shouldn't alter the state of the transport.
693 : //
694 0 : nsCOMPtr<nsIOutputStreamCallback> temp;
695 0 : nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(temp),
696 0 : callback, target);
697 0 : if (NS_FAILED(rv)) return rv;
698 0 : mCallback = temp;
699 : }
700 : else
701 12235 : mCallback = callback;
702 :
703 24470 : mCallbackFlags = flags;
704 : }
705 12235 : mTransport->OnOutputPending();
706 12235 : return NS_OK;
707 : }
708 :
709 : //-----------------------------------------------------------------------------
710 : // socket transport impl
711 : //-----------------------------------------------------------------------------
712 :
713 5883 : nsSocketTransport::nsSocketTransport()
714 : : mTypes(nsnull)
715 : , mTypeCount(0)
716 : , mPort(0)
717 : , mProxyPort(0)
718 : , mProxyTransparent(false)
719 : , mProxyTransparentResolvesHost(false)
720 : , mConnectionFlags(0)
721 : , mState(STATE_CLOSED)
722 : , mAttached(false)
723 : , mInputClosed(true)
724 : , mOutputClosed(true)
725 : , mResolving(false)
726 : , mNetAddrIsSet(false)
727 : , mLock("nsSocketTransport.mLock")
728 : , mFD(nsnull)
729 : , mFDref(0)
730 : , mFDconnected(false)
731 : , mInput(this)
732 : , mOutput(this)
733 5883 : , mQoSBits(0x00)
734 : {
735 5883 : SOCKET_LOG(("creating nsSocketTransport @%x\n", this));
736 :
737 5883 : NS_ADDREF(gSocketTransportService);
738 :
739 5883 : mTimeouts[TIMEOUT_CONNECT] = PR_UINT16_MAX; // no timeout
740 5883 : mTimeouts[TIMEOUT_READ_WRITE] = PR_UINT16_MAX; // no timeout
741 5883 : }
742 :
743 17646 : nsSocketTransport::~nsSocketTransport()
744 : {
745 5882 : SOCKET_LOG(("destroying nsSocketTransport @%x\n", this));
746 :
747 : // cleanup socket type info
748 5882 : if (mTypes) {
749 : PRUint32 i;
750 8 : for (i=0; i<mTypeCount; ++i)
751 4 : PL_strfree(mTypes[i]);
752 4 : free(mTypes);
753 : }
754 :
755 5882 : nsSocketTransportService *serv = gSocketTransportService;
756 5882 : NS_RELEASE(serv); // nulls argument
757 23528 : }
758 :
759 : nsresult
760 3013 : nsSocketTransport::Init(const char **types, PRUint32 typeCount,
761 : const nsACString &host, PRUint16 port,
762 : nsIProxyInfo *givenProxyInfo)
763 : {
764 6026 : nsCOMPtr<nsProxyInfo> proxyInfo;
765 3013 : if (givenProxyInfo) {
766 27 : proxyInfo = do_QueryInterface(givenProxyInfo);
767 27 : NS_ENSURE_ARG(proxyInfo);
768 : }
769 :
770 : // init socket type info
771 :
772 3013 : mPort = port;
773 3013 : mHost = host;
774 :
775 3013 : const char *proxyType = nsnull;
776 3013 : if (proxyInfo) {
777 27 : mProxyPort = proxyInfo->Port();
778 27 : mProxyHost = proxyInfo->Host();
779 : // grab proxy type (looking for "socks" for example)
780 27 : proxyType = proxyInfo->Type();
781 27 : if (proxyType && (strcmp(proxyType, "http") == 0 ||
782 0 : strcmp(proxyType, "direct") == 0 ||
783 0 : strcmp(proxyType, "unknown") == 0))
784 27 : proxyType = nsnull;
785 : }
786 :
787 3013 : SOCKET_LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s:%hu]\n",
788 : this, mHost.get(), mPort, mProxyHost.get(), mProxyPort));
789 :
790 : // include proxy type as a socket type if proxy type is not "http"
791 3013 : mTypeCount = typeCount + (proxyType != nsnull);
792 3013 : if (!mTypeCount)
793 3009 : return NS_OK;
794 :
795 : // if we have socket types, then the socket provider service had
796 : // better exist!
797 : nsresult rv;
798 : nsCOMPtr<nsISocketProviderService> spserv =
799 8 : do_GetService(kSocketProviderServiceCID, &rv);
800 4 : if (NS_FAILED(rv)) return rv;
801 :
802 4 : mTypes = (char **) malloc(mTypeCount * sizeof(char *));
803 4 : if (!mTypes)
804 0 : return NS_ERROR_OUT_OF_MEMORY;
805 :
806 : // now verify that each socket type has a registered socket provider.
807 8 : for (PRUint32 i = 0, type = 0; i < mTypeCount; ++i) {
808 : // store socket types
809 4 : if (i == 0 && proxyType)
810 0 : mTypes[i] = PL_strdup(proxyType);
811 : else
812 4 : mTypes[i] = PL_strdup(types[type++]);
813 :
814 4 : if (!mTypes[i]) {
815 0 : mTypeCount = i;
816 0 : return NS_ERROR_OUT_OF_MEMORY;
817 : }
818 8 : nsCOMPtr<nsISocketProvider> provider;
819 4 : rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
820 4 : if (NS_FAILED(rv)) {
821 0 : NS_WARNING("no registered socket provider");
822 0 : return rv;
823 : }
824 :
825 : // note if socket type corresponds to a transparent proxy
826 : // XXX don't hardcode SOCKS here (use proxy info's flags instead).
827 8 : if ((strcmp(mTypes[i], "socks") == 0) ||
828 4 : (strcmp(mTypes[i], "socks4") == 0)) {
829 0 : mProxyTransparent = true;
830 :
831 0 : if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
832 : // we want the SOCKS layer to send the hostname
833 : // and port to the proxy and let it do the DNS.
834 0 : mProxyTransparentResolvesHost = true;
835 : }
836 : }
837 : }
838 :
839 4 : return NS_OK;
840 : }
841 :
842 : nsresult
843 2870 : nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const PRNetAddr *addr)
844 : {
845 2870 : NS_ASSERTION(!mFD, "already initialized");
846 :
847 : char buf[64];
848 2870 : PR_NetAddrToString(addr, buf, sizeof(buf));
849 2870 : mHost.Assign(buf);
850 :
851 : PRUint16 port;
852 2870 : if (addr->raw.family == PR_AF_INET)
853 2870 : port = addr->inet.port;
854 : else
855 0 : port = addr->ipv6.port;
856 2870 : mPort = PR_ntohs(port);
857 :
858 2870 : memcpy(&mNetAddr, addr, sizeof(PRNetAddr));
859 :
860 2870 : mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
861 2870 : mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
862 2870 : mState = STATE_TRANSFERRING;
863 2870 : mNetAddrIsSet = true;
864 :
865 2870 : mFD = fd;
866 2870 : mFDref = 1;
867 2870 : mFDconnected = 1;
868 :
869 : // make sure new socket is non-blocking
870 : PRSocketOptionData opt;
871 2870 : opt.option = PR_SockOpt_Nonblocking;
872 2870 : opt.value.non_blocking = true;
873 2870 : PR_SetSocketOption(mFD, &opt);
874 :
875 2870 : SOCKET_LOG(("nsSocketTransport::InitWithConnectedSocket [this=%p addr=%s:%hu]\n",
876 : this, mHost.get(), mPort));
877 :
878 : // jump to InitiateSocket to get ourselves attached to the STS poll list.
879 2870 : return PostEvent(MSG_RETRY_INIT_SOCKET);
880 : }
881 :
882 : nsresult
883 17648 : nsSocketTransport::PostEvent(PRUint32 type, nsresult status, nsISupports *param)
884 : {
885 17648 : SOCKET_LOG(("nsSocketTransport::PostEvent [this=%p type=%u status=%x param=%p]\n",
886 : this, type, status, param));
887 :
888 35296 : nsCOMPtr<nsIRunnable> event = new nsSocketEvent(this, type, status, param);
889 17648 : if (!event)
890 0 : return NS_ERROR_OUT_OF_MEMORY;
891 :
892 17648 : return gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
893 : }
894 :
895 : void
896 30595 : nsSocketTransport::SendStatus(nsresult status)
897 : {
898 30595 : SOCKET_LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status));
899 :
900 61190 : nsCOMPtr<nsITransportEventSink> sink;
901 : PRUint64 progress;
902 : {
903 61190 : MutexAutoLock lock(mLock);
904 30595 : sink = mEventSink;
905 30595 : switch (status) {
906 : case STATUS_SENDING_TO:
907 9344 : progress = mOutput.ByteCount();
908 9344 : break;
909 : case STATUS_RECEIVING_FROM:
910 9349 : progress = mInput.ByteCount();
911 9349 : break;
912 : default:
913 11902 : progress = 0;
914 11902 : break;
915 : }
916 : }
917 30595 : if (sink)
918 20313 : sink->OnTransportStatus(this, status, progress, LL_MAXUINT);
919 30595 : }
920 :
921 : nsresult
922 3013 : nsSocketTransport::ResolveHost()
923 : {
924 3013 : SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
925 :
926 : nsresult rv;
927 :
928 3013 : if (!mProxyHost.IsEmpty()) {
929 27 : if (!mProxyTransparent || mProxyTransparentResolvesHost) {
930 : // When not resolving mHost locally, we still want to ensure that
931 : // it only contains valid characters. See bug 304904 for details.
932 27 : if (!net_IsValidHostName(mHost))
933 0 : return NS_ERROR_UNKNOWN_HOST;
934 : }
935 27 : if (mProxyTransparentResolvesHost) {
936 : // Name resolution is done on the server side. Just pretend
937 : // client resolution is complete, this will get picked up later.
938 : // since we don't need to do DNS now, we bypass the resolving
939 : // step by initializing mNetAddr to an empty address, but we
940 : // must keep the port. The SOCKS IO layer will use the hostname
941 : // we send it when it's created, rather than the empty address
942 : // we send with the connect call.
943 0 : mState = STATE_RESOLVING;
944 0 : PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, SocketPort(), &mNetAddr);
945 0 : return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nsnull);
946 : }
947 : }
948 :
949 6026 : nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
950 3013 : if (NS_FAILED(rv)) return rv;
951 :
952 3013 : mResolving = true;
953 :
954 3013 : PRUint32 dnsFlags = 0;
955 3013 : if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
956 1923 : dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
957 3013 : if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
958 0 : dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
959 :
960 3013 : SendStatus(STATUS_RESOLVING);
961 6026 : rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nsnull,
962 6026 : getter_AddRefs(mDNSRequest));
963 3013 : if (NS_SUCCEEDED(rv)) {
964 3013 : SOCKET_LOG((" advancing to STATE_RESOLVING\n"));
965 3013 : mState = STATE_RESOLVING;
966 : }
967 3013 : return rv;
968 : }
969 :
970 : nsresult
971 3006 : nsSocketTransport::BuildSocket(PRFileDesc *&fd, bool &proxyTransparent, bool &usingSSL)
972 : {
973 3006 : SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
974 :
975 : nsresult rv;
976 :
977 3006 : proxyTransparent = false;
978 3006 : usingSSL = false;
979 :
980 3006 : if (mTypeCount == 0) {
981 3002 : fd = PR_OpenTCPSocket(mNetAddr.raw.family);
982 3002 : rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
983 : }
984 : else {
985 4 : fd = nsnull;
986 :
987 : nsCOMPtr<nsISocketProviderService> spserv =
988 8 : do_GetService(kSocketProviderServiceCID, &rv);
989 4 : if (NS_FAILED(rv)) return rv;
990 :
991 4 : const char *host = mHost.get();
992 4 : PRInt32 port = (PRInt32) mPort;
993 4 : const char *proxyHost = mProxyHost.IsEmpty() ? nsnull : mProxyHost.get();
994 4 : PRInt32 proxyPort = (PRInt32) mProxyPort;
995 4 : PRUint32 proxyFlags = 0;
996 :
997 : PRUint32 i;
998 8 : for (i=0; i<mTypeCount; ++i) {
999 8 : nsCOMPtr<nsISocketProvider> provider;
1000 :
1001 4 : SOCKET_LOG((" pushing io layer [%u:%s]\n", i, mTypes[i]));
1002 :
1003 4 : rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
1004 4 : if (NS_FAILED(rv))
1005 : break;
1006 :
1007 4 : if (mProxyTransparentResolvesHost)
1008 0 : proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
1009 :
1010 4 : if (mConnectionFlags & nsISocketTransport::ANONYMOUS_CONNECT)
1011 0 : proxyFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
1012 :
1013 8 : nsCOMPtr<nsISupports> secinfo;
1014 4 : if (i == 0) {
1015 : // if this is the first type, we'll want the
1016 : // service to allocate a new socket
1017 4 : rv = provider->NewSocket(mNetAddr.raw.family,
1018 : host, port, proxyHost, proxyPort,
1019 : proxyFlags, &fd,
1020 4 : getter_AddRefs(secinfo));
1021 :
1022 4 : if (NS_SUCCEEDED(rv) && !fd) {
1023 0 : NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
1024 0 : rv = NS_ERROR_UNEXPECTED;
1025 : }
1026 : }
1027 : else {
1028 : // the socket has already been allocated,
1029 : // so we just want the service to add itself
1030 : // to the stack (such as pushing an io layer)
1031 0 : rv = provider->AddToSocket(mNetAddr.raw.family,
1032 : host, port, proxyHost, proxyPort,
1033 : proxyFlags, fd,
1034 0 : getter_AddRefs(secinfo));
1035 : }
1036 : // proxyFlags = 0; not used below this point...
1037 4 : if (NS_FAILED(rv))
1038 : break;
1039 :
1040 : // if the service was ssl or starttls, we want to hold onto the socket info
1041 4 : bool isSSL = (strcmp(mTypes[i], "ssl") == 0);
1042 4 : if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
1043 : // remember security info and give notification callbacks to PSM...
1044 8 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
1045 : {
1046 8 : MutexAutoLock lock(mLock);
1047 4 : mSecInfo = secinfo;
1048 4 : callbacks = mCallbacks;
1049 4 : SOCKET_LOG((" [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
1050 : }
1051 : // don't call into PSM while holding mLock!!
1052 8 : nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
1053 4 : if (secCtrl)
1054 4 : secCtrl->SetNotificationCallbacks(callbacks);
1055 : // remember if socket type is SSL so we can ProxyStartSSL if need be.
1056 8 : usingSSL = isSSL;
1057 : }
1058 0 : else if ((strcmp(mTypes[i], "socks") == 0) ||
1059 0 : (strcmp(mTypes[i], "socks4") == 0)) {
1060 : // since socks is transparent, any layers above
1061 : // it do not have to worry about proxy stuff
1062 0 : proxyHost = nsnull;
1063 0 : proxyPort = -1;
1064 0 : proxyTransparent = true;
1065 : }
1066 : }
1067 :
1068 4 : if (NS_FAILED(rv)) {
1069 0 : SOCKET_LOG((" error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
1070 0 : if (fd)
1071 0 : PR_Close(fd);
1072 : }
1073 : }
1074 :
1075 3006 : return rv;
1076 : }
1077 :
1078 : nsresult
1079 5876 : nsSocketTransport::InitiateSocket()
1080 : {
1081 5876 : SOCKET_LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
1082 :
1083 : nsresult rv;
1084 :
1085 : //
1086 : // find out if it is going to be ok to attach another socket to the STS.
1087 : // if not then we have to wait for the STS to tell us that it is ok.
1088 : // the notification is asynchronous, which means that when we could be
1089 : // in a race to call AttachSocket once notified. for this reason, when
1090 : // we get notified, we just re-enter this function. as a result, we are
1091 : // sure to ask again before calling AttachSocket. in this way we deal
1092 : // with the race condition. though it isn't the most elegant solution,
1093 : // it is far simpler than trying to build a system that would guarantee
1094 : // FIFO ordering (which wouldn't even be that valuable IMO). see bug
1095 : // 194402 for more info.
1096 : //
1097 5876 : if (!gSocketTransportService->CanAttachSocket()) {
1098 : nsCOMPtr<nsIRunnable> event =
1099 0 : new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET);
1100 0 : if (!event)
1101 0 : return NS_ERROR_OUT_OF_MEMORY;
1102 0 : return gSocketTransportService->NotifyWhenCanAttachSocket(event);
1103 : }
1104 :
1105 : //
1106 : // if we already have a connected socket, then just attach and return.
1107 : //
1108 5876 : if (mFD) {
1109 2870 : rv = gSocketTransportService->AttachSocket(mFD, this);
1110 2870 : if (NS_SUCCEEDED(rv))
1111 2870 : mAttached = true;
1112 2870 : return rv;
1113 : }
1114 :
1115 : //
1116 : // create new socket fd, push io layers, etc.
1117 : //
1118 : PRFileDesc *fd;
1119 : bool proxyTransparent;
1120 : bool usingSSL;
1121 :
1122 3006 : rv = BuildSocket(fd, proxyTransparent, usingSSL);
1123 3006 : if (NS_FAILED(rv)) {
1124 0 : SOCKET_LOG((" BuildSocket failed [rv=%x]\n", rv));
1125 0 : return rv;
1126 : }
1127 :
1128 : PRStatus status;
1129 :
1130 : // Make the socket non-blocking...
1131 : PRSocketOptionData opt;
1132 3006 : opt.option = PR_SockOpt_Nonblocking;
1133 3006 : opt.value.non_blocking = true;
1134 3006 : status = PR_SetSocketOption(fd, &opt);
1135 3006 : NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
1136 :
1137 : // disable the nagle algorithm - if we rely on it to coalesce writes into
1138 : // full packets the final packet of a multi segment POST/PUT or pipeline
1139 : // sequence is delayed a full rtt
1140 3006 : opt.option = PR_SockOpt_NoDelay;
1141 3006 : opt.value.no_delay = true;
1142 3006 : PR_SetSocketOption(fd, &opt);
1143 :
1144 : // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
1145 : // The Windows default of 8KB is too small and as of vista sp1, autotuning
1146 : // only applies to receive window
1147 : PRInt32 sndBufferSize;
1148 3006 : gSocketTransportService->GetSendBufferSize(&sndBufferSize);
1149 3006 : if (sndBufferSize > 0) {
1150 0 : opt.option = PR_SockOpt_SendBufferSize;
1151 0 : opt.value.send_buffer_size = sndBufferSize;
1152 0 : PR_SetSocketOption(fd, &opt);
1153 : }
1154 :
1155 3006 : if (mQoSBits) {
1156 0 : opt.option = PR_SockOpt_IpTypeOfService;
1157 0 : opt.value.tos = mQoSBits;
1158 0 : PR_SetSocketOption(fd, &opt);
1159 : }
1160 :
1161 : // inform socket transport about this newly created socket...
1162 3006 : rv = gSocketTransportService->AttachSocket(fd, this);
1163 3006 : if (NS_FAILED(rv)) {
1164 0 : PR_Close(fd);
1165 0 : return rv;
1166 : }
1167 3006 : mAttached = true;
1168 :
1169 : // assign mFD so that we can properly handle OnSocketDetached before we've
1170 : // established a connection.
1171 : {
1172 6012 : MutexAutoLock lock(mLock);
1173 3006 : mFD = fd;
1174 3006 : mFDref = 1;
1175 3006 : mFDconnected = false;
1176 : }
1177 :
1178 3006 : SOCKET_LOG((" advancing to STATE_CONNECTING\n"));
1179 3006 : mState = STATE_CONNECTING;
1180 3006 : mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1181 3006 : SendStatus(STATUS_CONNECTING_TO);
1182 :
1183 : #if defined(PR_LOGGING)
1184 3006 : if (SOCKET_LOG_ENABLED()) {
1185 : char buf[64];
1186 0 : PR_NetAddrToString(&mNetAddr, buf, sizeof(buf));
1187 0 : SOCKET_LOG((" trying address: %s\n", buf));
1188 : }
1189 : #endif
1190 :
1191 : //
1192 : // Initiate the connect() to the host...
1193 : //
1194 3006 : status = PR_Connect(fd, &mNetAddr, NS_SOCKET_CONNECT_TIMEOUT);
1195 3006 : if (status == PR_SUCCESS) {
1196 : //
1197 : // we are connected!
1198 : //
1199 0 : OnSocketConnected();
1200 : }
1201 : else {
1202 3006 : PRErrorCode code = PR_GetError();
1203 : #if defined(TEST_CONNECT_ERRORS)
1204 : code = RandomizeConnectError(code);
1205 : #endif
1206 : //
1207 : // If the PR_Connect(...) would block, then poll for a connection.
1208 : //
1209 3006 : if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
1210 3006 : mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1211 : //
1212 : // If the socket is already connected, then return success...
1213 : //
1214 0 : else if (PR_IS_CONNECTED_ERROR == code) {
1215 : //
1216 : // we are connected!
1217 : //
1218 0 : OnSocketConnected();
1219 :
1220 0 : if (mSecInfo && !mProxyHost.IsEmpty() && proxyTransparent && usingSSL) {
1221 : // if the connection phase is finished, and the ssl layer has
1222 : // been pushed, and we were proxying (transparently; ie. nothing
1223 : // has to happen in the protocol layer above us), it's time for
1224 : // the ssl to start doing it's thing.
1225 : nsCOMPtr<nsISSLSocketControl> secCtrl =
1226 0 : do_QueryInterface(mSecInfo);
1227 0 : if (secCtrl) {
1228 0 : SOCKET_LOG((" calling ProxyStartSSL()\n"));
1229 0 : secCtrl->ProxyStartSSL();
1230 : }
1231 : // XXX what if we were forced to poll on the socket for a successful
1232 : // connection... wouldn't we need to call ProxyStartSSL after a call
1233 : // to PR_ConnectContinue indicates that we are connected?
1234 : //
1235 : // XXX this appears to be what the old socket transport did. why
1236 : // isn't this broken?
1237 : }
1238 : }
1239 : //
1240 : // A SOCKS request was rejected; get the actual error code from
1241 : // the OS error
1242 : //
1243 0 : else if (PR_UNKNOWN_ERROR == code &&
1244 : mProxyTransparent &&
1245 0 : !mProxyHost.IsEmpty()) {
1246 0 : code = PR_GetOSError();
1247 0 : rv = ErrorAccordingToNSPR(code);
1248 : }
1249 : //
1250 : // The connection was refused...
1251 : //
1252 : else {
1253 0 : rv = ErrorAccordingToNSPR(code);
1254 0 : if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
1255 0 : rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
1256 : }
1257 : }
1258 3006 : return rv;
1259 : }
1260 :
1261 : bool
1262 5883 : nsSocketTransport::RecoverFromError()
1263 : {
1264 5883 : NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
1265 :
1266 5883 : SOCKET_LOG(("nsSocketTransport::RecoverFromError [this=%x state=%x cond=%x]\n",
1267 : this, mState, mCondition));
1268 :
1269 : // can only recover from errors in these states
1270 5883 : if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
1271 5740 : return false;
1272 :
1273 : nsresult rv;
1274 :
1275 : // OK to check this outside mLock
1276 143 : NS_ASSERTION(!mFDconnected, "socket should not be connected");
1277 :
1278 : // can only recover from these errors
1279 143 : if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
1280 : mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
1281 : mCondition != NS_ERROR_NET_TIMEOUT &&
1282 : mCondition != NS_ERROR_UNKNOWN_HOST &&
1283 : mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
1284 0 : return false;
1285 :
1286 143 : bool tryAgain = false;
1287 :
1288 143 : if (mConnectionFlags & DISABLE_IPV6 &&
1289 : mCondition == NS_ERROR_UNKNOWN_HOST &&
1290 : mState == STATE_RESOLVING &&
1291 0 : !mProxyTransparentResolvesHost) {
1292 0 : SOCKET_LOG((" trying lookup again with both ipv4/ipv6 enabled\n"));
1293 0 : mConnectionFlags &= ~DISABLE_IPV6;
1294 0 : tryAgain = true;
1295 : }
1296 :
1297 : // try next ip address only if past the resolver stage...
1298 143 : if (mState == STATE_CONNECTING && mDNSRecord) {
1299 136 : mDNSRecord->ReportUnusable(SocketPort());
1300 :
1301 136 : nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1302 136 : if (NS_SUCCEEDED(rv)) {
1303 0 : SOCKET_LOG((" trying again with next ip address\n"));
1304 0 : tryAgain = true;
1305 : }
1306 136 : else if (mConnectionFlags & DISABLE_IPV6) {
1307 : // Drop state to closed. This will trigger new round of DNS
1308 : // resolving bellow.
1309 : // XXX Here should idealy be set now non-existing flag DISABLE_IPV4
1310 0 : SOCKET_LOG((" failed to connect all ipv4 hosts,"
1311 : " trying lookup/connect again with both ipv4/ipv6\n"));
1312 0 : mState = STATE_CLOSED;
1313 0 : mConnectionFlags &= ~DISABLE_IPV6;
1314 0 : tryAgain = true;
1315 : }
1316 : }
1317 :
1318 : #if defined(XP_WIN) || defined(MOZ_PLATFORM_MAEMO)
1319 : // If not trying next address, try to make a connection using dialup.
1320 : // Retry if that connection is made.
1321 : if (!tryAgain) {
1322 : bool autodialEnabled;
1323 : gSocketTransportService->GetAutodialEnabled(&autodialEnabled);
1324 : if (autodialEnabled) {
1325 : tryAgain = nsNativeConnectionHelper::OnConnectionFailed(
1326 : NS_ConvertUTF8toUTF16(SocketHost()).get());
1327 : }
1328 : }
1329 : #endif
1330 :
1331 : // prepare to try again.
1332 143 : if (tryAgain) {
1333 : PRUint32 msg;
1334 :
1335 0 : if (mState == STATE_CONNECTING) {
1336 0 : mState = STATE_RESOLVING;
1337 0 : msg = MSG_DNS_LOOKUP_COMPLETE;
1338 : }
1339 : else {
1340 0 : mState = STATE_CLOSED;
1341 0 : msg = MSG_ENSURE_CONNECT;
1342 : }
1343 :
1344 0 : rv = PostEvent(msg, NS_OK);
1345 0 : if (NS_FAILED(rv))
1346 0 : tryAgain = false;
1347 : }
1348 :
1349 143 : return tryAgain;
1350 : }
1351 :
1352 : // called on the socket thread only
1353 : void
1354 5739 : nsSocketTransport::OnMsgInputClosed(nsresult reason)
1355 : {
1356 5739 : SOCKET_LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
1357 : this, reason));
1358 :
1359 5739 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1360 :
1361 5739 : mInputClosed = true;
1362 : // check if event should affect entire transport
1363 5739 : if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
1364 2835 : mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
1365 2904 : else if (mOutputClosed)
1366 36 : mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1367 : else {
1368 2868 : if (mState == STATE_TRANSFERRING)
1369 2868 : mPollFlags &= ~PR_POLL_READ;
1370 2868 : mInput.OnSocketReady(reason);
1371 : }
1372 5739 : }
1373 :
1374 : // called on the socket thread only
1375 : void
1376 5736 : nsSocketTransport::OnMsgOutputClosed(nsresult reason)
1377 : {
1378 5736 : SOCKET_LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
1379 : this, reason));
1380 :
1381 5736 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1382 :
1383 5736 : mOutputClosed = true;
1384 : // check if event should affect entire transport
1385 5736 : if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
1386 2838 : mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
1387 2898 : else if (mInputClosed)
1388 2865 : mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
1389 : else {
1390 33 : if (mState == STATE_TRANSFERRING)
1391 33 : mPollFlags &= ~PR_POLL_WRITE;
1392 33 : mOutput.OnSocketReady(reason);
1393 : }
1394 5736 : }
1395 :
1396 : void
1397 2870 : nsSocketTransport::OnSocketConnected()
1398 : {
1399 2870 : SOCKET_LOG((" advancing to STATE_TRANSFERRING\n"));
1400 :
1401 2870 : mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
1402 2870 : mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1403 2870 : mState = STATE_TRANSFERRING;
1404 :
1405 : // Set the mNetAddrIsSet flag only when state has reached TRANSFERRING
1406 : // because we need to make sure its value does not change due to failover
1407 2870 : mNetAddrIsSet = true;
1408 :
1409 : // assign mFD (must do this within the transport lock), but take care not
1410 : // to trample over mFDref if mFD is already set.
1411 : {
1412 5740 : MutexAutoLock lock(mLock);
1413 2870 : NS_ASSERTION(mFD, "no socket");
1414 2870 : NS_ASSERTION(mFDref == 1, "wrong socket ref count");
1415 2870 : mFDconnected = true;
1416 : }
1417 :
1418 2870 : SendStatus(STATUS_CONNECTED_TO);
1419 2870 : }
1420 :
1421 : PRFileDesc *
1422 30908 : nsSocketTransport::GetFD_Locked()
1423 : {
1424 : // mFD is not available to the streams while disconnected.
1425 30908 : if (!mFDconnected)
1426 39 : return nsnull;
1427 :
1428 30869 : if (mFD)
1429 30869 : mFDref++;
1430 :
1431 30869 : return mFD;
1432 : }
1433 :
1434 : void
1435 36745 : nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
1436 : {
1437 36745 : NS_ASSERTION(mFD == fd, "wrong fd");
1438 :
1439 36745 : if (--mFDref == 0) {
1440 5876 : SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%x]\n", this));
1441 5876 : PR_Close(mFD);
1442 5876 : mFD = nsnull;
1443 : }
1444 36745 : }
1445 :
1446 : //-----------------------------------------------------------------------------
1447 : // socket event handler impl
1448 :
1449 : void
1450 17648 : nsSocketTransport::OnSocketEvent(PRUint32 type, nsresult status, nsISupports *param)
1451 : {
1452 17648 : SOCKET_LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n",
1453 : this, type, status, param));
1454 :
1455 17648 : if (NS_FAILED(mCondition)) {
1456 : // block event since we're apparently already dead.
1457 7 : SOCKET_LOG((" blocking event [condition=%x]\n", mCondition));
1458 : //
1459 : // notify input/output streams in case either has a pending notify.
1460 : //
1461 7 : mInput.OnSocketReady(mCondition);
1462 7 : mOutput.OnSocketReady(mCondition);
1463 7 : return;
1464 : }
1465 :
1466 17641 : switch (type) {
1467 : case MSG_ENSURE_CONNECT:
1468 11758 : SOCKET_LOG((" MSG_ENSURE_CONNECT\n"));
1469 : //
1470 : // ensure that we have created a socket, attached it, and have a
1471 : // connection.
1472 : //
1473 11758 : if (mState == STATE_CLOSED)
1474 3013 : mCondition = ResolveHost();
1475 : else
1476 8745 : SOCKET_LOG((" ignoring redundant event\n"));
1477 11758 : break;
1478 :
1479 : case MSG_DNS_LOOKUP_COMPLETE:
1480 3013 : if (mDNSRequest) // only send this if we actually resolved anything
1481 3013 : SendStatus(STATUS_RESOLVED);
1482 :
1483 3013 : SOCKET_LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
1484 3013 : mDNSRequest = 0;
1485 3013 : if (param) {
1486 3006 : mDNSRecord = static_cast<nsIDNSRecord *>(param);
1487 3006 : mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
1488 : }
1489 : // status contains DNS lookup status
1490 3013 : if (NS_FAILED(status)) {
1491 : // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP
1492 : // proxy host is not found, so we fixup the error code.
1493 : // For SOCKS proxies (mProxyTransparent == true), the socket
1494 : // transport resolves the real host here, so there's no fixup
1495 : // (see bug 226943).
1496 14 : if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyTransparent &&
1497 7 : !mProxyHost.IsEmpty())
1498 2 : mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
1499 : else
1500 5 : mCondition = status;
1501 : }
1502 3006 : else if (mState == STATE_RESOLVING)
1503 3006 : mCondition = InitiateSocket();
1504 3013 : break;
1505 :
1506 : case MSG_RETRY_INIT_SOCKET:
1507 2870 : mCondition = InitiateSocket();
1508 2870 : break;
1509 :
1510 : case MSG_INPUT_CLOSED:
1511 0 : SOCKET_LOG((" MSG_INPUT_CLOSED\n"));
1512 0 : OnMsgInputClosed(status);
1513 0 : break;
1514 :
1515 : case MSG_INPUT_PENDING:
1516 0 : SOCKET_LOG((" MSG_INPUT_PENDING\n"));
1517 0 : OnMsgInputPending();
1518 0 : break;
1519 :
1520 : case MSG_OUTPUT_CLOSED:
1521 0 : SOCKET_LOG((" MSG_OUTPUT_CLOSED\n"));
1522 0 : OnMsgOutputClosed(status);
1523 0 : break;
1524 :
1525 : case MSG_OUTPUT_PENDING:
1526 0 : SOCKET_LOG((" MSG_OUTPUT_PENDING\n"));
1527 0 : OnMsgOutputPending();
1528 0 : break;
1529 : case MSG_TIMEOUT_CHANGED:
1530 0 : SOCKET_LOG((" MSG_TIMEOUT_CHANGED\n"));
1531 : mPollTimeout = mTimeouts[(mState == STATE_TRANSFERRING)
1532 0 : ? TIMEOUT_READ_WRITE : TIMEOUT_CONNECT];
1533 0 : break;
1534 : default:
1535 0 : SOCKET_LOG((" unhandled event!\n"));
1536 : }
1537 :
1538 17641 : if (NS_FAILED(mCondition)) {
1539 7 : SOCKET_LOG((" after event [this=%x cond=%x]\n", this, mCondition));
1540 7 : if (!mAttached) // need to process this error ourselves...
1541 7 : OnSocketDetached(nsnull);
1542 : }
1543 17634 : else if (mPollFlags == PR_POLL_EXCEPT)
1544 0 : mPollFlags = 0; // make idle
1545 : }
1546 :
1547 : //-----------------------------------------------------------------------------
1548 : // socket handler impl
1549 :
1550 : void
1551 29887 : nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
1552 : {
1553 29887 : SOCKET_LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
1554 : this, outFlags));
1555 :
1556 29887 : if (outFlags == -1) {
1557 0 : SOCKET_LOG(("socket timeout expired\n"));
1558 0 : mCondition = NS_ERROR_NET_TIMEOUT;
1559 0 : return;
1560 : }
1561 :
1562 29887 : if (mState == STATE_TRANSFERRING) {
1563 : // if waiting to write and socket is writable or hit an exception.
1564 26881 : if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
1565 : // assume that we won't need to poll any longer (the stream will
1566 : // request that we poll again if it is still pending).
1567 14980 : mPollFlags &= ~PR_POLL_WRITE;
1568 14980 : mOutput.OnSocketReady(NS_OK);
1569 : }
1570 : // if waiting to read and socket is readable or hit an exception.
1571 26881 : if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
1572 : // assume that we won't need to poll any longer (the stream will
1573 : // request that we poll again if it is still pending).
1574 12006 : mPollFlags &= ~PR_POLL_READ;
1575 12006 : mInput.OnSocketReady(NS_OK);
1576 : }
1577 : // Update poll timeout in case it was changed
1578 26881 : mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE];
1579 : }
1580 3006 : else if (mState == STATE_CONNECTING) {
1581 3006 : PRStatus status = PR_ConnectContinue(fd, outFlags);
1582 3006 : if (status == PR_SUCCESS) {
1583 : //
1584 : // we are connected!
1585 : //
1586 2870 : OnSocketConnected();
1587 : }
1588 : else {
1589 136 : PRErrorCode code = PR_GetError();
1590 : #if defined(TEST_CONNECT_ERRORS)
1591 : code = RandomizeConnectError(code);
1592 : #endif
1593 : //
1594 : // If the connect is still not ready, then continue polling...
1595 : //
1596 136 : if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
1597 : // Set up the select flags for connect...
1598 0 : mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
1599 : // Update poll timeout in case it was changed
1600 0 : mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
1601 : }
1602 : //
1603 : // The SOCKS proxy rejected our request. Find out why.
1604 : //
1605 136 : else if (PR_UNKNOWN_ERROR == code &&
1606 : mProxyTransparent &&
1607 0 : !mProxyHost.IsEmpty()) {
1608 0 : code = PR_GetOSError();
1609 0 : mCondition = ErrorAccordingToNSPR(code);
1610 : }
1611 : else {
1612 : //
1613 : // else, the connection failed...
1614 : //
1615 136 : mCondition = ErrorAccordingToNSPR(code);
1616 136 : if ((mCondition == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
1617 0 : mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
1618 136 : SOCKET_LOG((" connection failed! [reason=%x]\n", mCondition));
1619 : }
1620 : }
1621 : }
1622 : else {
1623 0 : NS_ERROR("unexpected socket state");
1624 0 : mCondition = NS_ERROR_UNEXPECTED;
1625 : }
1626 :
1627 29887 : if (mPollFlags == PR_POLL_EXCEPT)
1628 9101 : mPollFlags = 0; // make idle
1629 : }
1630 :
1631 : // called on the socket thread only
1632 : void
1633 5883 : nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
1634 : {
1635 5883 : SOCKET_LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
1636 : this, mCondition));
1637 :
1638 5883 : NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
1639 :
1640 : // if we didn't initiate this detach, then be sure to pass an error
1641 : // condition up to our consumers. (e.g., STS is shutting down.)
1642 5883 : if (NS_SUCCEEDED(mCondition))
1643 1 : mCondition = NS_ERROR_ABORT;
1644 :
1645 5883 : if (RecoverFromError())
1646 0 : mCondition = NS_OK;
1647 : else {
1648 5883 : mState = STATE_CLOSED;
1649 :
1650 : // make sure there isn't any pending DNS request
1651 5883 : if (mDNSRequest) {
1652 0 : mDNSRequest->Cancel(NS_ERROR_ABORT);
1653 0 : mDNSRequest = 0;
1654 : }
1655 :
1656 : //
1657 : // notify input/output streams
1658 : //
1659 5883 : mInput.OnSocketReady(mCondition);
1660 5883 : mOutput.OnSocketReady(mCondition);
1661 : }
1662 :
1663 : // break any potential reference cycle between the security info object
1664 : // and ourselves by resetting its notification callbacks object. see
1665 : // bug 285991 for details.
1666 11766 : nsCOMPtr<nsISSLSocketControl> secCtrl = do_QueryInterface(mSecInfo);
1667 5883 : if (secCtrl)
1668 4 : secCtrl->SetNotificationCallbacks(nsnull);
1669 :
1670 : // finally, release our reference to the socket (must do this within
1671 : // the transport lock) possibly closing the socket. Also release our
1672 : // listeners to break potential refcount cycles.
1673 :
1674 : // We should be careful not to release mEventSink and mCallbacks while
1675 : // we're locked, because releasing it might require acquiring the lock
1676 : // again, so we just null out mEventSink and mCallbacks while we're
1677 : // holding the lock, and let the stack based objects' destuctors take
1678 : // care of destroying it if needed.
1679 11766 : nsCOMPtr<nsIInterfaceRequestor> ourCallbacks;
1680 11766 : nsCOMPtr<nsITransportEventSink> ourEventSink;
1681 : {
1682 11766 : MutexAutoLock lock(mLock);
1683 5883 : if (mFD) {
1684 5876 : ReleaseFD_Locked(mFD);
1685 : // flag mFD as unusable; this prevents other consumers from
1686 : // acquiring a reference to mFD.
1687 5876 : mFDconnected = false;
1688 : }
1689 :
1690 : // We must release mCallbacks and mEventSink to avoid memory leak
1691 : // but only when RecoverFromError() above failed. Otherwise we lose
1692 : // link with UI and security callbacks on next connection attempt
1693 : // round. That would lead e.g. to a broken certificate exception page.
1694 5883 : if (NS_FAILED(mCondition)) {
1695 5883 : mCallbacks.swap(ourCallbacks);
1696 5883 : mEventSink.swap(ourEventSink);
1697 : }
1698 : }
1699 5883 : }
1700 :
1701 : //-----------------------------------------------------------------------------
1702 : // xpcom api
1703 :
1704 313570 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport,
1705 : nsISocketTransport,
1706 : nsITransport,
1707 : nsIDNSListener,
1708 : nsIClassInfo)
1709 2907 : NS_IMPL_CI_INTERFACE_GETTER3(nsSocketTransport,
1710 : nsISocketTransport,
1711 : nsITransport,
1712 2907 : nsIDNSListener)
1713 :
1714 : NS_IMETHODIMP
1715 5882 : nsSocketTransport::OpenInputStream(PRUint32 flags,
1716 : PRUint32 segsize,
1717 : PRUint32 segcount,
1718 : nsIInputStream **result)
1719 : {
1720 5882 : SOCKET_LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
1721 : this, flags));
1722 :
1723 5882 : NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
1724 :
1725 : nsresult rv;
1726 11764 : nsCOMPtr<nsIAsyncInputStream> pipeIn;
1727 :
1728 5882 : if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
1729 : // XXX if the caller wants blocking, then the caller also gets buffered!
1730 : //bool openBuffered = !(flags & OPEN_UNBUFFERED);
1731 2906 : bool openBlocking = (flags & OPEN_BLOCKING);
1732 :
1733 2906 : net_ResolveSegmentParams(segsize, segcount);
1734 :
1735 : // create a pipe
1736 5812 : nsCOMPtr<nsIAsyncOutputStream> pipeOut;
1737 2906 : rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
1738 2906 : !openBlocking, true, segsize, segcount);
1739 2906 : if (NS_FAILED(rv)) return rv;
1740 :
1741 : // async copy from socket to pipe
1742 : rv = NS_AsyncCopy(&mInput, pipeOut, gSocketTransportService,
1743 2906 : NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
1744 2906 : if (NS_FAILED(rv)) return rv;
1745 :
1746 5812 : *result = pipeIn;
1747 : }
1748 : else
1749 2976 : *result = &mInput;
1750 :
1751 : // flag input stream as open
1752 5882 : mInputClosed = false;
1753 :
1754 5882 : rv = PostEvent(MSG_ENSURE_CONNECT);
1755 5882 : if (NS_FAILED(rv)) return rv;
1756 :
1757 5882 : NS_ADDREF(*result);
1758 5882 : return NS_OK;
1759 : }
1760 :
1761 : NS_IMETHODIMP
1762 5883 : nsSocketTransport::OpenOutputStream(PRUint32 flags,
1763 : PRUint32 segsize,
1764 : PRUint32 segcount,
1765 : nsIOutputStream **result)
1766 : {
1767 5883 : SOCKET_LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
1768 : this, flags));
1769 :
1770 5883 : NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
1771 :
1772 : nsresult rv;
1773 11766 : nsCOMPtr<nsIAsyncOutputStream> pipeOut;
1774 5883 : if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
1775 : // XXX if the caller wants blocking, then the caller also gets buffered!
1776 : //bool openBuffered = !(flags & OPEN_UNBUFFERED);
1777 2907 : bool openBlocking = (flags & OPEN_BLOCKING);
1778 :
1779 2907 : net_ResolveSegmentParams(segsize, segcount);
1780 :
1781 : // create a pipe
1782 5814 : nsCOMPtr<nsIAsyncInputStream> pipeIn;
1783 2907 : rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
1784 2907 : true, !openBlocking, segsize, segcount);
1785 2907 : if (NS_FAILED(rv)) return rv;
1786 :
1787 : // async copy from socket to pipe
1788 : rv = NS_AsyncCopy(pipeIn, &mOutput, gSocketTransportService,
1789 2907 : NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
1790 2907 : if (NS_FAILED(rv)) return rv;
1791 :
1792 5814 : *result = pipeOut;
1793 : }
1794 : else
1795 2976 : *result = &mOutput;
1796 :
1797 : // flag output stream as open
1798 5883 : mOutputClosed = false;
1799 :
1800 5883 : rv = PostEvent(MSG_ENSURE_CONNECT);
1801 5883 : if (NS_FAILED(rv)) return rv;
1802 :
1803 5883 : NS_ADDREF(*result);
1804 5883 : return NS_OK;
1805 : }
1806 :
1807 : NS_IMETHODIMP
1808 3138 : nsSocketTransport::Close(nsresult reason)
1809 : {
1810 3138 : if (NS_SUCCEEDED(reason))
1811 1 : reason = NS_BASE_STREAM_CLOSED;
1812 :
1813 3138 : mInput.CloseWithStatus(reason);
1814 3138 : mOutput.CloseWithStatus(reason);
1815 3138 : return NS_OK;
1816 : }
1817 :
1818 : NS_IMETHODIMP
1819 2995 : nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
1820 : {
1821 5990 : MutexAutoLock lock(mLock);
1822 2995 : NS_IF_ADDREF(*secinfo = mSecInfo);
1823 2995 : return NS_OK;
1824 : }
1825 :
1826 : NS_IMETHODIMP
1827 0 : nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
1828 : {
1829 0 : MutexAutoLock lock(mLock);
1830 0 : NS_IF_ADDREF(*callbacks = mCallbacks);
1831 0 : return NS_OK;
1832 : }
1833 :
1834 : NS_IMETHODIMP
1835 9089 : nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
1836 : {
1837 18178 : nsCOMPtr<nsISupports> secinfo;
1838 : {
1839 18178 : MutexAutoLock lock(mLock);
1840 9089 : mCallbacks = callbacks;
1841 9089 : SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",
1842 : mSecInfo.get(), mCallbacks.get()));
1843 :
1844 9089 : secinfo = mSecInfo;
1845 : }
1846 :
1847 : // don't call into PSM while holding mLock!!
1848 18178 : nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
1849 9089 : if (secCtrl)
1850 8 : secCtrl->SetNotificationCallbacks(callbacks);
1851 :
1852 9089 : return NS_OK;
1853 : }
1854 :
1855 : NS_IMETHODIMP
1856 9089 : nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
1857 : nsIEventTarget *target)
1858 : {
1859 18178 : nsCOMPtr<nsITransportEventSink> temp;
1860 9089 : if (target) {
1861 0 : nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp),
1862 0 : sink, target);
1863 0 : if (NS_FAILED(rv))
1864 0 : return rv;
1865 0 : sink = temp.get();
1866 : }
1867 :
1868 18178 : MutexAutoLock lock(mLock);
1869 9089 : mEventSink = sink;
1870 9089 : return NS_OK;
1871 : }
1872 :
1873 : NS_IMETHODIMP
1874 6 : nsSocketTransport::IsAlive(bool *result)
1875 : {
1876 6 : *result = false;
1877 :
1878 : PRFileDesc *fd;
1879 : {
1880 12 : MutexAutoLock lock(mLock);
1881 6 : if (NS_FAILED(mCondition))
1882 0 : return NS_OK;
1883 6 : fd = GetFD_Locked();
1884 6 : if (!fd)
1885 0 : return NS_OK;
1886 : }
1887 :
1888 : // XXX do some idle-time based checks??
1889 :
1890 : char c;
1891 6 : PRInt32 rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
1892 :
1893 6 : if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
1894 6 : *result = true;
1895 :
1896 : {
1897 12 : MutexAutoLock lock(mLock);
1898 6 : ReleaseFD_Locked(fd);
1899 : }
1900 6 : return NS_OK;
1901 : }
1902 :
1903 : NS_IMETHODIMP
1904 2865 : nsSocketTransport::GetHost(nsACString &host)
1905 : {
1906 2865 : host = SocketHost();
1907 2865 : return NS_OK;
1908 : }
1909 :
1910 : NS_IMETHODIMP
1911 5730 : nsSocketTransport::GetPort(PRInt32 *port)
1912 : {
1913 5730 : *port = (PRInt32) SocketPort();
1914 5730 : return NS_OK;
1915 : }
1916 :
1917 : NS_IMETHODIMP
1918 2937 : nsSocketTransport::GetPeerAddr(PRNetAddr *addr)
1919 : {
1920 : // once we are in the connected state, mNetAddr will not change.
1921 : // so if we can verify that we are in the connected state, then
1922 : // we can freely access mNetAddr from any thread without being
1923 : // inside a critical section.
1924 :
1925 2937 : if (!mNetAddrIsSet) {
1926 0 : SOCKET_LOG(("nsSocketTransport::GetPeerAddr [this=%p state=%d] "
1927 : "NOT_AVAILABLE because not yet connected.", this, mState));
1928 0 : return NS_ERROR_NOT_AVAILABLE;
1929 : }
1930 :
1931 2937 : memcpy(addr, &mNetAddr, sizeof(mNetAddr));
1932 2937 : return NS_OK;
1933 : }
1934 :
1935 : NS_IMETHODIMP
1936 2933 : nsSocketTransport::GetSelfAddr(PRNetAddr *addr)
1937 : {
1938 : // we must not call any PR methods on our file descriptor
1939 : // while holding mLock since those methods might re-enter
1940 : // socket transport code.
1941 :
1942 : PRFileDesc *fd;
1943 : {
1944 5866 : MutexAutoLock lock(mLock);
1945 2933 : fd = GetFD_Locked();
1946 : }
1947 :
1948 2933 : if (!fd)
1949 3 : return NS_ERROR_NOT_CONNECTED;
1950 :
1951 : nsresult rv =
1952 2930 : (PR_GetSockName(fd, addr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
1953 :
1954 : {
1955 5860 : MutexAutoLock lock(mLock);
1956 2930 : ReleaseFD_Locked(fd);
1957 : }
1958 :
1959 2930 : return rv;
1960 : }
1961 :
1962 : /* nsINetAddr getScriptablePeerAddr (); */
1963 : NS_IMETHODIMP
1964 2 : nsSocketTransport::GetScriptablePeerAddr(nsINetAddr * *addr NS_OUTPARAM)
1965 : {
1966 : PRNetAddr rawAddr;
1967 :
1968 : nsresult rv;
1969 2 : rv = GetPeerAddr(&rawAddr);
1970 2 : if (NS_FAILED(rv))
1971 0 : return rv;
1972 :
1973 2 : NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
1974 :
1975 2 : return NS_OK;
1976 : }
1977 :
1978 : /* nsINetAddr getScriptableSelfAddr (); */
1979 : NS_IMETHODIMP
1980 2 : nsSocketTransport::GetScriptableSelfAddr(nsINetAddr * *addr NS_OUTPARAM)
1981 : {
1982 : PRNetAddr rawAddr;
1983 :
1984 : nsresult rv;
1985 2 : rv = GetSelfAddr(&rawAddr);
1986 2 : if (NS_FAILED(rv))
1987 0 : return rv;
1988 :
1989 2 : NS_ADDREF(*addr = new nsNetAddr(&rawAddr));
1990 :
1991 2 : return NS_OK;
1992 : }
1993 :
1994 : NS_IMETHODIMP
1995 0 : nsSocketTransport::GetTimeout(PRUint32 type, PRUint32 *value)
1996 : {
1997 0 : NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
1998 0 : *value = (PRUint32) mTimeouts[type];
1999 0 : return NS_OK;
2000 : }
2001 :
2002 : NS_IMETHODIMP
2003 0 : nsSocketTransport::SetTimeout(PRUint32 type, PRUint32 value)
2004 : {
2005 0 : NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
2006 : // truncate overly large timeout values.
2007 0 : mTimeouts[type] = (PRUint16) NS_MIN(value, PR_UINT16_MAX);
2008 0 : PostEvent(MSG_TIMEOUT_CHANGED);
2009 0 : return NS_OK;
2010 : }
2011 :
2012 : NS_IMETHODIMP
2013 2976 : nsSocketTransport::SetQoSBits(PRUint8 aQoSBits)
2014 : {
2015 : // Don't do any checking here of bits. Why? Because as of RFC-4594
2016 : // several different Class Selector and Assured Forwarding values
2017 : // have been defined, but that isn't to say more won't be added later.
2018 : // In that case, any checking would be an impediment to interoperating
2019 : // with newer QoS definitions.
2020 :
2021 2976 : mQoSBits = aQoSBits;
2022 2976 : return NS_OK;
2023 : }
2024 :
2025 : NS_IMETHODIMP
2026 0 : nsSocketTransport::GetQoSBits(PRUint8 *aQoSBits)
2027 : {
2028 0 : *aQoSBits = mQoSBits;
2029 0 : return NS_OK;
2030 : }
2031 :
2032 : NS_IMETHODIMP
2033 3013 : nsSocketTransport::OnLookupComplete(nsICancelable *request,
2034 : nsIDNSRecord *rec,
2035 : nsresult status)
2036 : {
2037 : // flag host lookup complete for the benefit of the ResolveHost method.
2038 3013 : mResolving = false;
2039 :
2040 3013 : nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec);
2041 :
2042 : // if posting a message fails, then we should assume that the socket
2043 : // transport has been shutdown. this should never happen! if it does
2044 : // it means that the socket transport service was shutdown before the
2045 : // DNS service.
2046 3013 : if (NS_FAILED(rv))
2047 0 : NS_WARNING("unable to post DNS lookup complete message");
2048 :
2049 3013 : return NS_OK;
2050 : }
2051 :
2052 : NS_IMETHODIMP
2053 2907 : nsSocketTransport::GetInterfaces(PRUint32 *count, nsIID * **array)
2054 : {
2055 2907 : return NS_CI_INTERFACE_GETTER_NAME(nsSocketTransport)(count, array);
2056 : }
2057 :
2058 : NS_IMETHODIMP
2059 2907 : nsSocketTransport::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
2060 : {
2061 2907 : *_retval = nsnull;
2062 2907 : return NS_OK;
2063 : }
2064 :
2065 : NS_IMETHODIMP
2066 0 : nsSocketTransport::GetContractID(char * *aContractID)
2067 : {
2068 0 : *aContractID = nsnull;
2069 0 : return NS_OK;
2070 : }
2071 :
2072 : NS_IMETHODIMP
2073 0 : nsSocketTransport::GetClassDescription(char * *aClassDescription)
2074 : {
2075 0 : *aClassDescription = nsnull;
2076 0 : return NS_OK;
2077 : }
2078 :
2079 : NS_IMETHODIMP
2080 0 : nsSocketTransport::GetClassID(nsCID * *aClassID)
2081 : {
2082 0 : *aClassID = nsnull;
2083 0 : return NS_OK;
2084 : }
2085 :
2086 : NS_IMETHODIMP
2087 0 : nsSocketTransport::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
2088 : {
2089 0 : *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
2090 0 : return NS_OK;
2091 : }
2092 :
2093 : NS_IMETHODIMP
2094 2908 : nsSocketTransport::GetFlags(PRUint32 *aFlags)
2095 : {
2096 2908 : *aFlags = nsIClassInfo::THREADSAFE;
2097 2908 : return NS_OK;
2098 : }
2099 :
2100 : NS_IMETHODIMP
2101 0 : nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
2102 : {
2103 0 : return NS_ERROR_NOT_AVAILABLE;
2104 : }
2105 :
2106 :
2107 : NS_IMETHODIMP
2108 0 : nsSocketTransport::GetConnectionFlags(PRUint32 *value)
2109 : {
2110 0 : *value = mConnectionFlags;
2111 0 : return NS_OK;
2112 : }
2113 :
2114 : NS_IMETHODIMP
2115 2976 : nsSocketTransport::SetConnectionFlags(PRUint32 value)
2116 : {
2117 2976 : mConnectionFlags = value;
2118 2976 : return NS_OK;
2119 : }
2120 :
2121 :
2122 : #ifdef ENABLE_SOCKET_TRACING
2123 :
2124 : #include <stdio.h>
2125 : #include <ctype.h>
2126 : #include "prenv.h"
2127 :
2128 : static void
2129 : DumpBytesToFile(const char *path, const char *header, const char *buf, PRInt32 n)
2130 : {
2131 : FILE *fp = fopen(path, "a");
2132 :
2133 : fprintf(fp, "\n%s [%d bytes]\n", header, n);
2134 :
2135 : const unsigned char *p;
2136 : while (n) {
2137 : p = (const unsigned char *) buf;
2138 :
2139 : PRInt32 i, row_max = NS_MIN(16, n);
2140 :
2141 : for (i = 0; i < row_max; ++i)
2142 : fprintf(fp, "%02x ", *p++);
2143 : for (i = row_max; i < 16; ++i)
2144 : fprintf(fp, " ");
2145 :
2146 : p = (const unsigned char *) buf;
2147 : for (i = 0; i < row_max; ++i, ++p) {
2148 : if (isprint(*p))
2149 : fprintf(fp, "%c", *p);
2150 : else
2151 : fprintf(fp, ".");
2152 : }
2153 :
2154 : fprintf(fp, "\n");
2155 : buf += row_max;
2156 : n -= row_max;
2157 : }
2158 :
2159 : fprintf(fp, "\n");
2160 : fclose(fp);
2161 : }
2162 :
2163 : void
2164 : nsSocketTransport::TraceInBuf(const char *buf, PRInt32 n)
2165 : {
2166 : char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2167 : if (!val || !*val)
2168 : return;
2169 :
2170 : nsCAutoString header;
2171 : header.Assign(NS_LITERAL_CSTRING("Reading from: ") + mHost);
2172 : header.Append(':');
2173 : header.AppendInt(mPort);
2174 :
2175 : DumpBytesToFile(val, header.get(), buf, n);
2176 : }
2177 :
2178 : void
2179 : nsSocketTransport::TraceOutBuf(const char *buf, PRInt32 n)
2180 : {
2181 : char *val = PR_GetEnv("NECKO_SOCKET_TRACE_LOG");
2182 : if (!val || !*val)
2183 : return;
2184 :
2185 : nsCAutoString header;
2186 : header.Assign(NS_LITERAL_CSTRING("Writing to: ") + mHost);
2187 : header.Append(':');
2188 : header.AppendInt(mPort);
2189 :
2190 : DumpBytesToFile(val, header.get(), buf, n);
2191 : }
2192 :
2193 : #endif
|