Files
Mozilla/mozilla/netwerk/base/src/nsSocketTransport2.cpp
darin%netscape.com 9423cbd435 fixes bug 199173 "IPv6:Page fails to load. No message." r=dougt sr=alecf a=sspitzer
git-svn-id: svn://10.0.0.236/trunk@142513 18797224-902f-48f8-a5cc-f745e15eee43
2003-05-15 21:27:46 +00:00

1687 lines
50 KiB
C++

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifdef MOZ_LOGGING
#define FORCE_PR_LOG
#endif
#include "nsSocketTransport2.h"
#include "nsIOService.h"
#include "nsStreamUtils.h"
#include "nsNetSegmentUtils.h"
#include "nsNetCID.h"
#include "nsAutoLock.h"
#include "nsCOMPtr.h"
#include "netCore.h"
#include "prmem.h"
#include "pratom.h"
#include "plstr.h"
#include "prerror.h"
#include "prerr.h"
#include "nsIServiceManager.h"
#include "nsIProxyObjectManager.h"
#include "nsISocketProviderService.h"
#include "nsISocketProvider.h"
#include "nsISSLSocketControl.h"
#include "nsIDNSService.h"
#include "nsIProxyInfo.h"
#include "nsIPipe.h"
#if defined(XP_WIN)
#include "nsNativeConnectionHelper.h"
#endif
//-----------------------------------------------------------------------------
static NS_DEFINE_CID(kSocketProviderServiceCID, NS_SOCKETPROVIDERSERVICE_CID);
static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID);
//-----------------------------------------------------------------------------
//#define TEST_CONNECT_ERRORS
#ifdef TEST_CONNECT_ERRORS
#include <stdlib.h>
static PRErrorCode RandomizeConnectError(PRErrorCode code)
{
//
// To test out these errors, load http://www.yahoo.com/. It should load
// correctly despite the random occurance of there errors.
//
int n = rand();
if (n > RAND_MAX/2) {
struct {
PRErrorCode err_code;
const char *err_name;
}
errors[] = {
//
// These errors should be recoverable provided there is another
// IP address in mNetAddrList.
//
{ PR_CONNECT_REFUSED_ERROR, "PR_CONNECT_REFUSED_ERROR" },
{ PR_CONNECT_TIMEOUT_ERROR, "PR_CONNECT_TIMEOUT_ERROR" },
//
// This error will cause this socket transport to error out;
// however, if the consumer is HTTP, then the HTTP transaction
// should be restarted when this error occurs.
//
{ PR_CONNECT_RESET_ERROR, "PR_CONNECT_RESET_ERROR" },
};
n = n % (sizeof(errors)/sizeof(errors[0]));
code = errors[n].err_code;
LOG(("simulating NSPR error %d [%s]\n", code, errors[n].err_name));
}
return code;
}
#endif
//-----------------------------------------------------------------------------
static nsresult
ErrorAccordingToNSPR(PRErrorCode errorCode)
{
nsresult rv;
switch (errorCode) {
case PR_WOULD_BLOCK_ERROR:
rv = NS_BASE_STREAM_WOULD_BLOCK;
break;
case PR_CONNECT_ABORTED_ERROR:
case PR_CONNECT_RESET_ERROR:
rv = NS_ERROR_NET_RESET;
break;
case PR_END_OF_FILE_ERROR: // XXX document this correlation
rv = NS_ERROR_NET_INTERRUPT;
break;
case PR_CONNECT_REFUSED_ERROR:
case PR_NETWORK_UNREACHABLE_ERROR: // XXX need new nsresult for this!
case PR_HOST_UNREACHABLE_ERROR: // XXX and this!
rv = NS_ERROR_CONNECTION_REFUSED;
break;
case PR_IO_TIMEOUT_ERROR:
case PR_CONNECT_TIMEOUT_ERROR:
rv = NS_ERROR_NET_TIMEOUT;
break;
default:
rv = NS_ERROR_FAILURE;
}
LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
return rv;
}
//-----------------------------------------------------------------------------
// socket input stream impl
//-----------------------------------------------------------------------------
nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans)
: mTransport(trans)
, mReaderRefCnt(0)
, mCondition(NS_OK)
, mByteCount(0)
{
}
nsSocketInputStream::~nsSocketInputStream()
{
}
// called on the socket transport thread...
//
// condition : failure code if socket has been closed
//
void
nsSocketInputStream::OnSocketReady(nsresult condition)
{
LOG(("nsSocketInputStream::OnSocketReady [this=%x cond=%x]\n",
this, condition));
nsCOMPtr<nsIInputStreamNotify> notify;
{
nsAutoLock lock(mTransport->mLock);
// update condition, but be careful not to erase an already
// existing error condition.
if (NS_SUCCEEDED(mCondition))
mCondition = condition;
// see if anyone wants to be notified...
notify = mNotify;
mNotify = nsnull;
}
if (notify)
notify->OnInputStreamReady(this);
}
NS_IMPL_QUERY_INTERFACE2(nsSocketInputStream,
nsIInputStream,
nsIAsyncInputStream)
NS_IMETHODIMP_(nsrefcnt)
nsSocketInputStream::AddRef()
{
PR_AtomicIncrement((PRInt32*)&mReaderRefCnt);
return mTransport->AddRef();
}
NS_IMETHODIMP_(nsrefcnt)
nsSocketInputStream::Release()
{
if (PR_AtomicDecrement((PRInt32*)&mReaderRefCnt) == 0)
Close();
return mTransport->Release();
}
NS_IMETHODIMP
nsSocketInputStream::Close()
{
return CloseEx(NS_BASE_STREAM_CLOSED);
}
NS_IMETHODIMP
nsSocketInputStream::Available(PRUint32 *avail)
{
LOG(("nsSocketInputStream::Available [this=%x]\n", this));
*avail = 0;
PRFileDesc *fd;
{
nsAutoLock lock(mTransport->mLock);
if (NS_FAILED(mCondition))
return mCondition;
fd = mTransport->GetFD_Locked();
if (!fd)
return NS_BASE_STREAM_WOULD_BLOCK;
}
// cannot hold lock while calling NSPR. (worried about the fact that PSM
// synchronously proxies notifications over to the UI thread, which could
// mistakenly try to re-enter this code.)
PRInt32 n = PR_Available(fd);
nsresult rv;
{
nsAutoLock lock(mTransport->mLock);
mTransport->ReleaseFD_Locked(fd);
if (n >= 0)
*avail = n;
else {
PRErrorCode code = PR_GetError();
if (code == PR_WOULD_BLOCK_ERROR)
return NS_BASE_STREAM_WOULD_BLOCK;
mCondition = ErrorAccordingToNSPR(code);
}
rv = mCondition;
}
if (NS_FAILED(rv))
mTransport->OnInputClosed(rv);
return rv;
}
NS_IMETHODIMP
nsSocketInputStream::Read(char *buf, PRUint32 count, PRUint32 *countRead)
{
LOG(("nsSocketInputStream::Read [this=%x count=%u]\n", this, count));
*countRead = 0;
PRFileDesc *fd;
{
nsAutoLock lock(mTransport->mLock);
if (NS_FAILED(mCondition))
return (mCondition == NS_BASE_STREAM_CLOSED) ? NS_OK : mCondition;
fd = mTransport->GetFD_Locked();
if (!fd)
return NS_BASE_STREAM_WOULD_BLOCK;
}
LOG((" calling PR_Read [count=%u]\n", count));
// cannot hold lock while calling NSPR. (worried about the fact that PSM
// synchronously proxies notifications over to the UI thread, which could
// mistakenly try to re-enter this code.)
PRInt32 n = PR_Read(fd, buf, count);
LOG((" PR_Read returned [n=%d]\n", n));
nsresult rv;
{
nsAutoLock lock(mTransport->mLock);
mTransport->ReleaseFD_Locked(fd);
if (n > 0)
mByteCount += (*countRead = n);
else if (n < 0) {
PRErrorCode code = PR_GetError();
if (code == PR_WOULD_BLOCK_ERROR)
return NS_BASE_STREAM_WOULD_BLOCK;
mCondition = ErrorAccordingToNSPR(code);
}
rv = mCondition;
}
if (NS_FAILED(rv))
mTransport->OnInputClosed(rv);
// only send this notification if we have indeed read some data.
// see bug 196827 for an example of why this is important.
if (n > 0)
mTransport->SendStatus(nsISocketTransport::STATUS_RECEIVING_FROM);
return rv;
}
NS_IMETHODIMP
nsSocketInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure,
PRUint32 count, PRUint32 *countRead)
{
// socket stream is unbuffered
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsSocketInputStream::IsNonBlocking(PRBool *nonblocking)
{
*nonblocking = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsSocketInputStream::CloseEx(nsresult reason)
{
LOG(("nsSocketInputStream::CloseEx [this=%x reason=%x]\n", this, reason));
// may be called from any thread
nsresult rv;
{
nsAutoLock lock(mTransport->mLock);
if (NS_SUCCEEDED(mCondition))
rv = mCondition = reason;
else
rv = NS_OK;
}
if (NS_FAILED(rv))
mTransport->OnInputClosed(rv);
return NS_OK;
}
NS_IMETHODIMP
nsSocketInputStream::AsyncWait(nsIInputStreamNotify *notify, PRUint32 amount,
nsIEventQueue *eventQ)
{
LOG(("nsSocketInputStream::AsyncWait [this=%x]\n", this));
{
nsAutoLock lock(mTransport->mLock);
if (eventQ) {
//
// build event proxy
//
// failure to create an event proxy (most likely out of memory)
// shouldn't alter the state of the transport.
//
nsCOMPtr<nsIInputStreamNotify> temp;
nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(temp),
notify, eventQ);
if (NS_FAILED(rv)) return rv;
mNotify = temp;
}
else
mNotify = notify;
}
mTransport->OnInputPending();
return NS_OK;
}
//-----------------------------------------------------------------------------
// socket output stream impl
//-----------------------------------------------------------------------------
nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans)
: mTransport(trans)
, mWriterRefCnt(0)
, mCondition(NS_OK)
, mByteCount(0)
{
}
nsSocketOutputStream::~nsSocketOutputStream()
{
}
// called on the socket transport thread...
//
// condition : failure code if socket has been closed
//
void
nsSocketOutputStream::OnSocketReady(nsresult condition)
{
LOG(("nsSocketOutputStream::OnSocketReady [this=%x cond=%x]\n",
this, condition));
nsCOMPtr<nsIOutputStreamNotify> notify;
{
nsAutoLock lock(mTransport->mLock);
// update condition, but be careful not to erase an already
// existing error condition.
if (NS_SUCCEEDED(mCondition))
mCondition = condition;
// see if anyone wants to be notified...
notify = mNotify;
mNotify = nsnull;
}
if (notify)
notify->OnOutputStreamReady(this);
}
NS_IMPL_QUERY_INTERFACE2(nsSocketOutputStream,
nsIOutputStream,
nsIAsyncOutputStream)
NS_IMETHODIMP_(nsrefcnt)
nsSocketOutputStream::AddRef()
{
PR_AtomicIncrement((PRInt32*)&mWriterRefCnt);
return mTransport->AddRef();
}
NS_IMETHODIMP_(nsrefcnt)
nsSocketOutputStream::Release()
{
if (PR_AtomicDecrement((PRInt32*)&mWriterRefCnt) == 0)
Close();
return mTransport->Release();
}
NS_IMETHODIMP
nsSocketOutputStream::Close()
{
return CloseEx(NS_BASE_STREAM_CLOSED);
}
NS_IMETHODIMP
nsSocketOutputStream::Flush()
{
return NS_OK;
}
NS_IMETHODIMP
nsSocketOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *countWritten)
{
LOG(("nsSocketOutputStream::Write [this=%x count=%u]\n", this, count));
*countWritten = 0;
if (count == 0)
return NS_OK;
PRFileDesc *fd;
{
nsAutoLock lock(mTransport->mLock);
if (NS_FAILED(mCondition))
return mCondition;
fd = mTransport->GetFD_Locked();
if (!fd)
return NS_BASE_STREAM_WOULD_BLOCK;
}
LOG((" calling PR_Write [count=%u]\n", count));
// cannot hold lock while calling NSPR. (worried about the fact that PSM
// synchronously proxies notifications over to the UI thread, which could
// mistakenly try to re-enter this code.)
PRInt32 n = PR_Write(fd, buf, count);
LOG((" PR_Write returned [n=%d]\n", n));
NS_ASSERTION(n != 0, "unexpected return value");
nsresult rv;
{
nsAutoLock lock(mTransport->mLock);
mTransport->ReleaseFD_Locked(fd);
if (n > 0)
mByteCount += (*countWritten = n);
else if (n < 0) {
PRErrorCode code = PR_GetError();
if (code == PR_WOULD_BLOCK_ERROR)
return NS_BASE_STREAM_WOULD_BLOCK;
mCondition = ErrorAccordingToNSPR(code);
}
rv = mCondition;
}
if (NS_FAILED(rv))
mTransport->OnOutputClosed(rv);
// only send this notification if we have indeed written some data.
// see bug 196827 for an example of why this is important.
if (n > 0)
mTransport->SendStatus(nsISocketTransport::STATUS_SENDING_TO);
return rv;
}
NS_IMETHODIMP
nsSocketOutputStream::WriteSegments(nsReadSegmentFun reader, void *closure,
PRUint32 count, PRUint32 *countRead)
{
// socket stream is unbuffered
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_METHOD
nsSocketOutputStream::WriteFromSegments(nsIInputStream *input,
void *closure,
const char *fromSegment,
PRUint32 offset,
PRUint32 count,
PRUint32 *countRead)
{
nsSocketOutputStream *self = (nsSocketOutputStream *) closure;
return self->Write(fromSegment, count, countRead);
}
NS_IMETHODIMP
nsSocketOutputStream::WriteFrom(nsIInputStream *stream, PRUint32 count, PRUint32 *countRead)
{
return stream->ReadSegments(WriteFromSegments, this, count, countRead);
}
NS_IMETHODIMP
nsSocketOutputStream::IsNonBlocking(PRBool *nonblocking)
{
*nonblocking = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsSocketOutputStream::CloseEx(nsresult reason)
{
LOG(("nsSocketOutputStream::CloseEx [this=%x reason=%x]\n", this, reason));
// may be called from any thread
nsresult rv;
{
nsAutoLock lock(mTransport->mLock);
if (NS_SUCCEEDED(mCondition))
rv = mCondition = reason;
else
rv = NS_OK;
}
if (NS_FAILED(rv))
mTransport->OnOutputClosed(rv);
return NS_OK;
}
NS_IMETHODIMP
nsSocketOutputStream::AsyncWait(nsIOutputStreamNotify *notify, PRUint32 amount,
nsIEventQueue *eventQ)
{
LOG(("nsSocketOutputStream::AsyncWait [this=%x]\n", this));
{
nsAutoLock lock(mTransport->mLock);
if (eventQ) {
//
// build event proxy
//
// failure to create an event proxy (most likely out of memory)
// shouldn't alter the state of the transport.
//
nsCOMPtr<nsIOutputStreamNotify> temp;
nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(temp),
notify, eventQ);
if (NS_FAILED(rv)) return rv;
mNotify = temp;
}
else
mNotify = notify;
}
mTransport->OnOutputPending();
return NS_OK;
}
//-----------------------------------------------------------------------------
// socket transport impl
//-----------------------------------------------------------------------------
nsSocketTransport::nsSocketTransport()
: mTypes(nsnull)
, mTypeCount(0)
, mPort(0)
, mProxyPort(0)
, mProxyTransparent(PR_FALSE)
, mState(STATE_CLOSED)
, mAttached(PR_FALSE)
, mInputClosed(PR_TRUE)
, mOutputClosed(PR_TRUE)
, mLock(PR_NewLock())
, mFD(nsnull)
, mFDref(0)
, mFDconnected(PR_FALSE)
, mInput(this)
, mOutput(this)
, mNetAddr(nsnull)
{
LOG(("creating nsSocketTransport @%x\n", this));
NS_ADDREF(gSocketTransportService);
}
nsSocketTransport::~nsSocketTransport()
{
LOG(("destroying nsSocketTransport @%x\n", this));
// cleanup socket type info
if (mTypes) {
PRUint32 i;
for (i=0; i<mTypeCount; ++i)
PL_strfree(mTypes[i]);
PR_Free(mTypes);
}
if (mLock)
PR_DestroyLock(mLock);
nsSocketTransportService *serv = gSocketTransportService;
NS_RELEASE(serv); // nulls argument
}
nsresult
nsSocketTransport::Init(const char **types, PRUint32 typeCount,
const nsACString &host, PRUint16 port,
nsIProxyInfo *proxyInfo)
{
// init socket type info
mPort = port;
mHost = host;
const char *proxyType = nsnull;
if (proxyInfo) {
mProxyPort = proxyInfo->Port();
mProxyHost = proxyInfo->Host();
// grab proxy type (looking for "socks" for example)
proxyType = proxyInfo->Type();
if (proxyType && (strcmp(proxyType, "http") == 0 ||
strcmp(proxyType, "direct") == 0))
proxyType = nsnull;
}
LOG(("nsSocketTransport::Init [this=%x host=%s:%hu proxy=%s:%hu]\n",
this, mHost.get(), mPort, mProxyHost.get(), mProxyPort));
// include proxy type as a socket type if proxy type is not "http"
mTypeCount = typeCount + (proxyType != nsnull);
if (mTypeCount) {
mTypes = (char **) PR_Malloc(mTypeCount * sizeof(char *));
if (!mTypes)
return NS_ERROR_OUT_OF_MEMORY;
// store socket types
PRUint32 i, type = 0;
if (proxyType)
mTypes[type++] = PL_strdup(proxyType);
for (i=0; i<typeCount; ++i)
mTypes[type++] = PL_strdup(types[i]);
// if we have socket types, then the socket provider service had
// better exist!
nsresult rv;
nsCOMPtr<nsISocketProviderService> spserv =
do_GetService(kSocketProviderServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
// now verify that each socket type has a registered socket provider.
for (i=0; i<mTypeCount; ++i) {
nsCOMPtr<nsISocketProvider> provider;
rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
if (NS_FAILED(rv)) {
NS_WARNING("no registered socket provider");
return rv;
}
// note if socket type corresponds to a transparent proxy
if ((strcmp(mTypes[i], "socks") == 0) ||
(strcmp(mTypes[i], "socks4") == 0))
mProxyTransparent = PR_TRUE;
}
}
return NS_OK;
}
void
nsSocketTransport::SendStatus(nsresult status)
{
LOG(("nsSocketTransport::SendStatus [this=%x status=%x]\n", this, status));
nsCOMPtr<nsITransportEventSink> sink;
PRUint32 progress;
{
nsAutoLock lock(mLock);
sink = mEventSink;
switch (status) {
case STATUS_SENDING_TO:
progress = mOutput.ByteCount();
break;
case STATUS_RECEIVING_FROM:
progress = mInput.ByteCount();
break;
default:
progress = 0;
break;
}
}
if (sink)
sink->OnTransportStatus(this, status, progress, ~0U);
}
nsresult
nsSocketTransport::ResolveHost()
{
LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this));
nsresult rv;
PRIPv6Addr addr;
rv = gSocketTransportService->LookupHost(SocketHost(), SocketPort(), &addr);
if (NS_SUCCEEDED(rv)) {
// found address!
mNetAddrList.Init(1);
mNetAddr = mNetAddrList.GetNext(nsnull);
PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, SocketPort(), mNetAddr);
memcpy(&mNetAddr->ipv6.ip, &addr, sizeof(addr));
#ifdef PR_LOGGING
if (LOG_ENABLED()) {
char buf[128];
PR_NetAddrToString(mNetAddr, buf, sizeof(buf));
LOG((" -> using cached ip address [%s]\n", buf));
}
#endif
// suppress resolving status message since we are bypassing that step.
mState = STATE_RESOLVING;
rv = gSocketTransportService->PostEvent(this,
MSG_DNS_LOOKUP_COMPLETE,
NS_OK, nsnull);
}
else {
const char *host = SocketHost().get();
nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = dns->Lookup(host, this, nsnull, getter_AddRefs(mDNSRequest));
if (NS_FAILED(rv)) return rv;
LOG((" advancing to STATE_RESOLVING\n"));
mState = STATE_RESOLVING;
SendStatus(STATUS_RESOLVING);
}
return rv;
}
nsresult
nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool &usingSSL)
{
LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
nsresult rv;
proxyTransparent = PR_FALSE;
usingSSL = PR_FALSE;
if (mTypeCount == 0) {
fd = PR_OpenTCPSocket(PR_AF_INET6);
rv = fd ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
else {
fd = nsnull;
nsCOMPtr<nsISocketProviderService> spserv =
do_GetService(kSocketProviderServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
const char *host = mHost.get();
PRInt32 port = (PRInt32) mPort;
const char *proxyHost = mProxyHost.IsEmpty() ? nsnull : mProxyHost.get();
PRInt32 proxyPort = (PRInt32) mProxyPort;
PRUint32 i;
for (i=0; i<mTypeCount; ++i) {
nsCOMPtr<nsISocketProvider> provider;
LOG((" pushing io layer [%u:%s]\n", i, mTypes[i]));
rv = spserv->GetSocketProvider(mTypes[i], getter_AddRefs(provider));
if (NS_FAILED(rv))
break;
nsCOMPtr<nsISupports> secinfo;
if (i == 0) {
// if this is the first type, we'll want the
// service to allocate a new socket
rv = provider->NewSocket(host, port, proxyHost, proxyPort,
&fd, getter_AddRefs(secinfo));
if (NS_SUCCEEDED(rv) && !fd) {
NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
rv = NS_ERROR_UNEXPECTED;
}
}
else {
// the socket has already been allocated,
// so we just want the service to add itself
// to the stack (such as pushing an io layer)
rv = provider->AddToSocket(host, port, proxyHost, proxyPort,
fd, getter_AddRefs(secinfo));
}
if (NS_FAILED(rv))
break;
// if the service was ssl or starttls, we want to hold onto the socket info
PRBool isSSL = (strcmp(mTypes[i], "ssl") == 0);
if (isSSL || (strcmp(mTypes[i], "starttls") == 0)) {
// remember security info and give notification callbacks to PSM...
nsCOMPtr<nsIInterfaceRequestor> callbacks;
{
nsAutoLock lock(mLock);
mSecInfo = secinfo;
callbacks = mCallbacks;
LOG((" [secinfo=%x callbacks=%x]\n", mSecInfo.get(), mCallbacks.get()));
}
// don't call into PSM while holding mLock!!
nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
if (secCtrl)
secCtrl->SetNotificationCallbacks(callbacks);
// remember if socket type is SSL so we can ProxyStartSSL if need be.
usingSSL = isSSL;
}
else if ((strcmp(mTypes[i], "socks") == 0) ||
(strcmp(mTypes[i], "socks4") == 0)) {
// since socks is transparent, any layers above
// it do not have to worry about proxy stuff
proxyHost = nsnull;
proxyPort = -1;
proxyTransparent = PR_TRUE;
}
}
if (NS_FAILED(rv)) {
LOG((" error pushing io layer [%u:%s rv=%x]\n", i, mTypes[i], rv));
if (fd)
PR_Close(fd);
}
}
return rv;
}
nsresult
nsSocketTransport::InitiateSocket()
{
LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this));
//
// find out if it is going to be ok to attach another socket to the STS.
// if not then we have to wait for the STS to tell us that it is ok.
// the notification is asynchronous, which means that when we could be
// in a race to call AttachSocket once notified. for this reason, when
// we get notified, we just re-enter this function. as a result, we are
// sure to ask again before calling AttachSocket. in this way we deal
// with the race condition. though it isn't the most elegant solution,
// it is far simpler than trying to build a system that would guarantee
// FIFO ordering (which wouldn't even be that valuable IMO). see bug
// 194402 for more info.
//
if (!gSocketTransportService->CanAttachSocket())
return gSocketTransportService->NotifyWhenCanAttachSocket(this, MSG_RETRY_INIT_SOCKET);
//
// create new socket fd, push io layers, etc.
//
PRFileDesc *fd;
PRBool proxyTransparent;
PRBool usingSSL;
nsresult rv = BuildSocket(fd, proxyTransparent, usingSSL);
if (NS_FAILED(rv)) {
LOG((" BuildSocket failed [rv=%x]\n", rv));
return rv;
}
PRStatus status;
// Make the socket non-blocking...
PRSocketOptionData opt;
opt.option = PR_SockOpt_Nonblocking;
opt.value.non_blocking = PR_TRUE;
status = PR_SetSocketOption(fd, &opt);
NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
// inform socket transport about this newly created socket...
rv = gSocketTransportService->AttachSocket(fd, this);
if (NS_FAILED(rv)) {
PR_Close(fd);
return rv;
}
mAttached = PR_TRUE;
// assign mFD so that we can properly handle OnSocketDetached before we've
// established a connection.
{
nsAutoLock lock(mLock);
mFD = fd;
mFDref = 1;
mFDconnected = PR_FALSE;
}
LOG((" advancing to STATE_CONNECTING\n"));
mState = STATE_CONNECTING;
SendStatus(STATUS_CONNECTING_TO);
//
// Initiate the connect() to the host...
//
status = PR_Connect(fd, mNetAddr, NS_SOCKET_CONNECT_TIMEOUT);
if (status == PR_SUCCESS) {
//
// we are connected!
//
OnSocketConnected();
}
else {
PRErrorCode code = PR_GetError();
#if defined(TEST_CONNECT_ERRORS)
code = RandomizeConnectError(code);
#endif
//
// If the PR_Connect(...) would block, then poll for a connection.
//
if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code))
mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
//
// If the socket is already connected, then return success...
//
else if (PR_IS_CONNECTED_ERROR == code) {
//
// we are connected!
//
OnSocketConnected();
if (mSecInfo && !mProxyHost.IsEmpty() && proxyTransparent && usingSSL) {
// if the connection phase is finished, and the ssl layer has
// been pushed, and we were proxying (transparently; ie. nothing
// has to happen in the protocol layer above us), it's time for
// the ssl to start doing it's thing.
nsCOMPtr<nsISSLSocketControl> secCtrl =
do_QueryInterface(mSecInfo);
if (secCtrl) {
LOG((" calling ProxyStartSSL()\n"));
secCtrl->ProxyStartSSL();
}
// XXX what if we were forced to poll on the socket for a successful
// connection... wouldn't we need to call ProxyStartSSL after a call
// to PR_ConnectContinue indicates that we are connected?
//
// XXX this appears to be what the old socket transport did. why
// isn't this broken?
}
}
//
// The connection was refused...
//
else {
rv = ErrorAccordingToNSPR(code);
if ((rv == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
}
}
return rv;
}
PRBool
nsSocketTransport::RecoverFromError()
{
NS_ASSERTION(NS_FAILED(mCondition), "there should be something wrong");
LOG(("nsSocketTransport::RecoverFromError [this=%x state=%x cond=%x]\n",
this, mState, mCondition));
// can only recover from errors in these states
if (mState != STATE_RESOLVING && mState != STATE_CONNECTING)
return PR_FALSE;
// OK to check this outside mLock
NS_ASSERTION(!mFDconnected, "socket should not be connected");
// can only recover from these errors
if (mCondition != NS_ERROR_CONNECTION_REFUSED &&
mCondition != NS_ERROR_PROXY_CONNECTION_REFUSED &&
mCondition != NS_ERROR_NET_TIMEOUT &&
mCondition != NS_ERROR_UNKNOWN_HOST &&
mCondition != NS_ERROR_UNKNOWN_PROXY_HOST)
return PR_FALSE;
PRBool tryAgain = PR_FALSE;
// try next ip address only if past the resolver stage...
if (mState == STATE_CONNECTING) {
PRNetAddr *nextAddr = mNetAddrList.GetNext(mNetAddr);
if (nextAddr) {
mNetAddr = nextAddr;
#if defined(PR_LOGGING)
if (LOG_ENABLED()) {
char buf[64];
PR_NetAddrToString(mNetAddr, buf, sizeof(buf));
LOG((" ...trying next address: %s\n", buf));
}
#endif
tryAgain = PR_TRUE;
}
}
#if defined(XP_WIN)
// If not trying next address, try to make a connection using dialup.
// Retry if that connection is made.
if (!tryAgain) {
PRBool autodialEnabled;
gSocketTransportService->GetAutodialEnabled(&autodialEnabled);
if (autodialEnabled)
tryAgain = nsNativeConnectionHelper::OnConnectionFailed(SocketHost().get());
}
#endif
// prepare to try again.
if (tryAgain) {
nsresult rv;
PRUint32 msg;
if (mState == STATE_CONNECTING) {
mState = STATE_RESOLVING;
msg = MSG_DNS_LOOKUP_COMPLETE;
}
else {
mState = STATE_CLOSED;
msg = MSG_ENSURE_CONNECT;
}
rv = gSocketTransportService->PostEvent(this, msg, NS_OK, nsnull);
if (NS_FAILED(rv))
tryAgain = PR_FALSE;
}
return tryAgain;
}
// called on the socket thread only
void
nsSocketTransport::OnMsgInputClosed(nsresult reason)
{
LOG(("nsSocketTransport::OnMsgInputClosed [this=%x reason=%x]\n",
this, reason));
mInputClosed = PR_TRUE;
// check if event should effect entire transport
if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
else if (mOutputClosed)
mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
else {
if (mState == STATE_TRANSFERRING)
mPollFlags &= ~PR_POLL_READ;
mInput.OnSocketReady(reason);
}
}
// called on the socket thread only
void
nsSocketTransport::OnMsgOutputClosed(nsresult reason)
{
LOG(("nsSocketTransport::OnMsgOutputClosed [this=%x reason=%x]\n",
this, reason));
mOutputClosed = PR_TRUE;
// check if event should effect entire transport
if (NS_FAILED(reason) && (reason != NS_BASE_STREAM_CLOSED))
mCondition = reason; // XXX except if NS_FAILED(mCondition), right??
else if (mInputClosed)
mCondition = NS_BASE_STREAM_CLOSED; // XXX except if NS_FAILED(mCondition), right??
else {
if (mState == STATE_TRANSFERRING)
mPollFlags &= ~PR_POLL_WRITE;
mOutput.OnSocketReady(reason);
}
}
void
nsSocketTransport::OnSocketConnected()
{
LOG((" advancing to STATE_TRANSFERRING\n"));
mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT);
mState = STATE_TRANSFERRING;
SendStatus(STATUS_CONNECTED_TO);
// assign mFD (must do this within the transport lock), but take care not
// to trample over mFDref if mFD is already set.
{
nsAutoLock lock(mLock);
NS_ASSERTION(mFD, "no socket");
NS_ASSERTION(mFDref == 1, "wrong socket ref count");
mFDconnected = PR_TRUE;
}
gSocketTransportService->RememberHost(SocketHost(), SocketPort(),
&mNetAddr->ipv6.ip);
}
PRFileDesc *
nsSocketTransport::GetFD_Locked()
{
// mFD is not available to the streams while disconnected.
if (!mFDconnected)
return nsnull;
if (mFD)
mFDref++;
return mFD;
}
void
nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
{
NS_ASSERTION(mFD == fd, "wrong fd");
if (--mFDref == 0) {
LOG(("nsSocketTransport: calling PR_Close [this=%x]\n", this));
PR_Close(mFD);
mFD = nsnull;
}
}
//-----------------------------------------------------------------------------
// socket event handler impl
NS_IMETHODIMP
nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam)
{
LOG(("nsSocketTransport::OnSocketEvent [this=%x type=%u u=%x v=%x]\n",
this, type, uparam, vparam));
if (NS_FAILED(mCondition)) {
// block event since we're apparently already dead.
LOG((" blocking event [condition=%x]\n", mCondition));
//
// notify input/output streams in case either has a pending notify.
//
mInput.OnSocketReady(mCondition);
mOutput.OnSocketReady(mCondition);
return NS_OK;
}
switch (type) {
case MSG_ENSURE_CONNECT:
LOG((" MSG_ENSURE_CONNECT\n"));
//
// ensure that we have created a socket, attached it, and have a
// connection.
//
if (mState == STATE_CLOSED)
mCondition = ResolveHost();
else
LOG((" ignoring redundant event\n"));
break;
case MSG_DNS_LOOKUP_COMPLETE:
LOG((" MSG_DNS_LOOKUP_COMPLETE\n"));
mDNSRequest = 0;
// uparam contains DNS lookup status
if (NS_FAILED(uparam)) {
// fixup error code if proxy was not found
if ((uparam == NS_ERROR_UNKNOWN_HOST) && !mProxyHost.IsEmpty())
mCondition = NS_ERROR_UNKNOWN_PROXY_HOST;
else
mCondition = uparam;
}
else if (mState == STATE_RESOLVING)
mCondition = InitiateSocket();
break;
case MSG_RETRY_INIT_SOCKET:
mCondition = InitiateSocket();
break;
case MSG_INPUT_CLOSED:
LOG((" MSG_INPUT_CLOSED\n"));
OnMsgInputClosed(uparam);
break;
case MSG_INPUT_PENDING:
LOG((" MSG_INPUT_PENDING\n"));
OnMsgInputPending();
break;
case MSG_OUTPUT_CLOSED:
LOG((" MSG_OUTPUT_CLOSED\n"));
OnMsgOutputClosed(uparam);
break;
case MSG_OUTPUT_PENDING:
LOG((" MSG_OUTPUT_PENDING\n"));
OnMsgOutputPending();
break;
default:
LOG((" unhandled event!\n"));
}
if (NS_FAILED(mCondition)) {
LOG((" after event [this=%x cond=%x]\n", this, mCondition));
if (!mAttached) // need to process this error ourselves...
OnSocketDetached(nsnull);
}
else if (mPollFlags == PR_POLL_EXCEPT)
mPollFlags = 0; // make idle
return NS_OK;
}
//-----------------------------------------------------------------------------
// socket handler impl
void
nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
{
LOG(("nsSocketTransport::OnSocketReady [this=%x outFlags=%hd]\n",
this, outFlags));
if (mState == STATE_TRANSFERRING) {
// if waiting to write and socket is writable or hit an exception.
if ((mPollFlags & PR_POLL_WRITE) && (outFlags & ~PR_POLL_READ)) {
// assume that we won't need to poll any longer (the stream will
// request that we poll again if it is still pending).
mPollFlags &= ~PR_POLL_WRITE;
mOutput.OnSocketReady(NS_OK);
}
// if waiting to read and socket is readable or hit an exception.
if ((mPollFlags & PR_POLL_READ) && (outFlags & ~PR_POLL_WRITE)) {
// assume that we won't need to poll any longer (the stream will
// request that we poll again if it is still pending).
mPollFlags &= ~PR_POLL_READ;
mInput.OnSocketReady(NS_OK);
}
}
else if (mState == STATE_CONNECTING) {
PRStatus status = PR_ConnectContinue(fd, outFlags);
if (status == PR_SUCCESS) {
//
// we are connected!
//
OnSocketConnected();
}
else {
PRErrorCode code = PR_GetError();
#if defined(TEST_CONNECT_ERRORS)
code = RandomizeConnectError(code);
#endif
//
// If the connect is still not ready, then continue polling...
//
if ((PR_WOULD_BLOCK_ERROR == code) || (PR_IN_PROGRESS_ERROR == code)) {
// Set up the select flags for connect...
mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
}
else {
//
// else, the connection failed...
//
mCondition = ErrorAccordingToNSPR(code);
if ((mCondition == NS_ERROR_CONNECTION_REFUSED) && !mProxyHost.IsEmpty())
mCondition = NS_ERROR_PROXY_CONNECTION_REFUSED;
LOG((" connection failed! [reason=%x]\n", mCondition));
}
}
}
else {
NS_ERROR("unexpected socket state");
mCondition = NS_ERROR_UNEXPECTED;
}
if (mPollFlags == PR_POLL_EXCEPT)
mPollFlags = 0; // make idle
}
// called on the socket thread only
void
nsSocketTransport::OnSocketDetached(PRFileDesc *fd)
{
LOG(("nsSocketTransport::OnSocketDetached [this=%x cond=%x]\n",
this, mCondition));
// if we didn't initiate this detach, then be sure to pass an error
// condition up to our consumers. (e.g., STS is shutting down.)
if (NS_SUCCEEDED(mCondition))
mCondition = NS_ERROR_ABORT;
else if (RecoverFromError())
mCondition = NS_OK;
else {
mState = STATE_CLOSED;
// make sure there isn't any pending DNS request
if (mDNSRequest) {
mDNSRequest->Cancel(mCondition);
mDNSRequest = 0;
}
//
// notify input/output streams
//
mInput.OnSocketReady(mCondition);
mOutput.OnSocketReady(mCondition);
}
// finally, release our reference to the socket (must do this within
// the transport lock) possibly closing the socket.
{
nsAutoLock lock(mLock);
if (mFD) {
ReleaseFD_Locked(mFD);
// flag mFD as unusable; this prevents other consumers from
// acquiring a reference to mFD.
mFDconnected = PR_FALSE;
}
}
}
//-----------------------------------------------------------------------------
// xpcom api
NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport,
nsISocketEventHandler,
nsISocketTransport,
nsITransport,
nsIDNSListener)
NS_IMETHODIMP
nsSocketTransport::OpenInputStream(PRUint32 flags,
PRUint32 segsize,
PRUint32 segcount,
nsIInputStream **result)
{
LOG(("nsSocketTransport::OpenInputStream [this=%x flags=%x]\n",
this, flags));
NS_ENSURE_TRUE(!mInput.IsReferenced(), NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsIAsyncInputStream> pipeIn;
if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
// XXX if the caller wants blocking, then the caller also gets buffered!
//PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
PRBool openBlocking = (flags & OPEN_BLOCKING);
net_ResolveSegmentParams(segsize, segcount);
nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
// create a pipe
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
!openBlocking, PR_TRUE, segsize, segcount, segalloc);
if (NS_FAILED(rv)) return rv;
// async copy from socket to pipe
rv = NS_AsyncCopy(&mInput, pipeOut, PR_FALSE, PR_TRUE,
segsize, 1, segalloc);
if (NS_FAILED(rv)) return rv;
*result = pipeIn;
}
else
*result = &mInput;
// flag input stream as open
mInputClosed = PR_FALSE;
rv = gSocketTransportService->PostEvent(this, MSG_ENSURE_CONNECT,
0, nsnull);
if (NS_FAILED(rv)) return rv;
NS_ADDREF(*result);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::OpenOutputStream(PRUint32 flags,
PRUint32 segsize,
PRUint32 segcount,
nsIOutputStream **result)
{
LOG(("nsSocketTransport::OpenOutputStream [this=%x flags=%x]\n",
this, flags));
NS_ENSURE_TRUE(!mOutput.IsReferenced(), NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
if (!(flags & OPEN_UNBUFFERED) || (flags & OPEN_BLOCKING)) {
// XXX if the caller wants blocking, then the caller also gets buffered!
//PRBool openBuffered = !(flags & OPEN_UNBUFFERED);
PRBool openBlocking = (flags & OPEN_BLOCKING);
net_ResolveSegmentParams(segsize, segcount);
nsIMemory *segalloc = net_GetSegmentAlloc(segsize);
// create a pipe
nsCOMPtr<nsIAsyncInputStream> pipeIn;
rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
PR_TRUE, !openBlocking, segsize, segcount, segalloc);
if (NS_FAILED(rv)) return rv;
// async copy from socket to pipe
rv = NS_AsyncCopy(pipeIn, &mOutput, PR_TRUE, PR_FALSE,
segsize, 1, segalloc);
if (NS_FAILED(rv)) return rv;
*result = pipeOut;
}
else
*result = &mOutput;
// flag output stream as open
mOutputClosed = PR_FALSE;
rv = gSocketTransportService->PostEvent(this, MSG_ENSURE_CONNECT,
0, nsnull);
if (NS_FAILED(rv)) return rv;
NS_ADDREF(*result);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::Close(nsresult reason)
{
if (NS_SUCCEEDED(reason))
reason = NS_BASE_STREAM_CLOSED;
mInput.CloseEx(reason);
mOutput.CloseEx(reason);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetSecurityInfo(nsISupports **secinfo)
{
nsAutoLock lock(mLock);
NS_IF_ADDREF(*secinfo = mSecInfo);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
{
nsAutoLock lock(mLock);
NS_IF_ADDREF(*callbacks = mCallbacks);
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
{
nsAutoLock lock(mLock);
mCallbacks = callbacks;
// XXX should we tell PSM about this?
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::SetEventSink(nsITransportEventSink *sink,
nsIEventQueue *eventQ)
{
nsCOMPtr<nsITransportEventSink> temp;
if (eventQ) {
nsresult rv;
rv = NS_GetProxyForObject(eventQ,
NS_GET_IID(nsITransportEventSink),
sink,
PROXY_ASYNC | PROXY_ALWAYS,
getter_AddRefs(temp));
if (NS_FAILED(rv)) return rv;
sink = temp.get();
}
nsAutoLock lock(mLock);
mEventSink = sink;
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::IsAlive(PRBool *result)
{
*result = PR_FALSE;
PRFileDesc *fd;
{
nsAutoLock lock(mLock);
if (NS_FAILED(mCondition))
return NS_OK;
fd = GetFD_Locked();
if (!fd)
return NS_OK;
}
// XXX do some idle-time based checks??
char c;
PRInt32 rval = PR_Recv(fd, &c, 1, PR_MSG_PEEK, 0);
if ((rval > 0) || (rval < 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR))
*result = PR_TRUE;
{
nsAutoLock lock(mLock);
ReleaseFD_Locked(fd);
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetHost(nsACString &host)
{
host = SocketHost();
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetPort(PRInt32 *port)
{
*port = (PRInt32) SocketPort();
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::GetAddress(PRNetAddr *addr)
{
//
// NOTE: mNetAddr is assigned on either the socket thread or the DNS thread.
// assuming pointer assignment is atomic, this code does not need any
// locks to maintain thread safety.
//
if (!mNetAddr)
return NS_ERROR_NOT_AVAILABLE;
memcpy(addr, mNetAddr, sizeof(PRNetAddr));
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::OnStartLookup(nsISupports *ctx, const char *host)
{
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::OnFound(nsISupports *ctx,
const char *host,
nsHostEnt *hostEnt)
{
// no locking should be required... yes, we are running on the DNS
// thread, but our state variables we're going to touch will not be
// touched by anyone else until we post a MSG_DNS_LOOKUP_COMPLETE
// event, or until the socket transports destructor runs.
char **addrList = hostEnt->hostEnt.h_addr_list;
if (addrList && addrList[0]) {
PRUint32 len = 0;
PRUint16 port = SocketPort();
LOG(("nsSocketTransport::OnFound [%s:%hu this=%x] lookup succeeded [FQDN=%s]\n",
host, port, this, hostEnt->hostEnt.h_name));
// determine the number of address in the list
for (; *addrList; ++addrList)
++len;
addrList -= len;
// allocate space for the addresses
mNetAddrList.Init(len);
// populate the address list
PRNetAddr *addr = nsnull;
while ((addr = mNetAddrList.GetNext(addr)) != nsnull) {
PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, port, addr);
if (hostEnt->hostEnt.h_addrtype == PR_AF_INET6)
memcpy(&addr->ipv6.ip, *addrList, sizeof(addr->ipv6.ip));
else
PR_ConvertIPv4AddrToIPv6(*(PRUint32 *)(*addrList), &addr->ipv6.ip);
++addrList;
#if defined(PR_LOGGING)
if (LOG_ENABLED()) {
char buf[50];
PR_NetAddrToString(addr, buf, sizeof(buf));
LOG((" => %s\n", buf));
}
#endif
}
// start with first address in list
mNetAddr = mNetAddrList.GetNext(nsnull);
}
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransport::OnStopLookup(nsISupports *ctx, const char *host,
nsresult status)
{
LOG(("nsSocketTransport::OnStopLookup [this=%x status=%x]\n",
this, status));
// keep the DNS service honest...
if (NS_SUCCEEDED(status) && (mNetAddr == nsnull)) {
NS_ERROR("success without a result");
status = NS_ERROR_UNEXPECTED;
}
nsresult rv = gSocketTransportService->PostEvent(this,
MSG_DNS_LOOKUP_COMPLETE,
status, nsnull);
// if posting a message fails, then we should assume that the socket
// transport has been shutdown. this should never happen! if it does
// it means that the socket transport service was shutdown before the
// DNS service.
if (NS_FAILED(rv))
NS_WARNING("unable to post DNS lookup complete message");
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsSocketTransport::NetAddrList
//-----------------------------------------------------------------------------
nsresult
nsSocketTransport::NetAddrList::Init(PRUint32 len)
{
NS_ASSERTION(!mList, "already initialized");
mList = new PRNetAddr[len];
if (!mList)
return NS_ERROR_OUT_OF_MEMORY;
mLen = len;
return NS_OK;
}
PRNetAddr *
nsSocketTransport::NetAddrList::GetNext(PRNetAddr *addr)
{
if (!addr)
return mList;
PRUint32 offset = addr - mList;
NS_ASSERTION(offset < mLen, "invalid address");
if (offset + 1 < mLen)
return addr + 1;
return nsnull;
}