landing backend changes for bug 134105 "SOCKS5: DNS lookups should occur on proxy, not client side." (remote dns pref disabled by default) patch by malsmith@cs.rmit.edu.au, r=biesi, sr=darin

This commit is contained in:
darin%meer.net 2004-12-16 02:46:12 +00:00
parent 0bb526779c
commit d19eff04b7
15 changed files with 403 additions and 169 deletions

View File

@ -210,8 +210,8 @@ nsLDAPSSLConnect(const char *hostlist, int defport, int timeout,
// the certificate. Need to investigate.
//
rv = tlsSocketProvider->AddToSocket(PR_AF_INET,
sessionClosure->hostname, defport,
nsnull, 0, socketInfo.soinfo_prfd,
sessionClosure->hostname, defport,
nsnull, 0, 0, socketInfo.soinfo_prfd,
getter_AddRefs(securityInfo));
if (NS_FAILED(rv)) {
NS_ERROR("nsLDAPSSLConnect(): unable to add SSL layer to socket");

View File

@ -622,6 +622,7 @@ pref("network.proxy.ssl_port", 0);
pref("network.proxy.socks", "");
pref("network.proxy.socks_port", 0);
pref("network.proxy.socks_version", 5);
pref("network.proxy.socks_remote_dns", false);
pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1");
pref("network.proxy.failover_timeout", 1800); // 30 minutes
pref("network.online", true); //online/offline

View File

@ -47,7 +47,7 @@ interface nsIURI;
* nsIProtocolProxyService provides methods to access information about
* various network proxies.
*/
[scriptable, uuid(6b40d918-c01a-42a2-ab4d-4ea0b707f91f)]
[scriptable, uuid(ec2da5ae-eb2e-11d8-9782-0004e22243f8)]
interface nsIProtocolProxyService : nsISupports
{
/**
@ -97,8 +97,12 @@ interface nsIProtocolProxyService : nsISupports
* The proxy hostname or IP address.
* @param aPort
* The proxy port.
* @param aFlags
* Flags associated with this connection. See nsIProxyInfo.idl
* for currently defined flags.
*/
nsIProxyInfo newProxyInfo(in ACString aType, in AUTF8String aHost, in long aPort);
nsIProxyInfo newProxyInfo(in ACString aType, in AUTF8String aHost,
in long aPort, in unsigned long aFlags);
/**
* This method may be called to re-configure proxy settings given a URI

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Bradley Baetz <bbaetz@netscape.com> (Original Developer)
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -52,10 +53,17 @@
*/
native constCharPtr(const char*);
[scriptable, uuid(d49f429c-7c8e-4f7a-90a3-ea6122bd3887)]
[scriptable, uuid(cefb3b30-e82f-11d8-329c-0004e22243f8)]
interface nsIProxyInfo : nsISupports
{
[noscript, notxpcom] constCharPtr Host();
[noscript, notxpcom] PRInt32 Port();
[noscript, notxpcom] constCharPtr Type();
[noscript, notxpcom] PRUint32 Flags();
/* This flag is set if the proxy is to perform name resolution
* itself. If this is the case, the hostname is used in some
* fashion, and we shouldn't do any form of DNS lookup ourselves.
*/
const unsigned short TRANSPARENT_PROXY_RESOLVES_HOST = 1 << 0;
};

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Bradley Baetz <bbaetz@student.usyd.edu.au>
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -588,13 +589,14 @@ inline nsresult
NS_NewProxyInfo(const nsACString &type,
const nsACString &host,
PRInt32 port,
PRUint32 flags,
nsIProxyInfo **result)
{
nsresult rv;
static NS_DEFINE_CID(kPPSServiceCID, NS_PROTOCOLPROXYSERVICE_CID);
nsCOMPtr<nsIProtocolProxyService> pps = do_GetService(kPPSServiceCID, &rv);
if (NS_SUCCEEDED(rv))
rv = pps->NewProxyInfo(type, host, port, result);
rv = pps->NewProxyInfo(type, host, port, flags, result);
return rv;
}

View File

@ -21,6 +21,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -131,6 +132,19 @@ proxy_GetIntPref(nsIPrefBranch *aPrefBranch,
aResult = temp;
}
static void
proxy_GetBoolPref(nsIPrefBranch *aPrefBranch,
const char *aPref,
PRBool &aResult)
{
PRBool temp;
nsresult rv = aPrefBranch->GetBoolPref(aPref, &temp);
if (NS_FAILED(rv))
aResult = PR_FALSE;
else
aResult = temp;
}
//----------------------------------------------------------------------------
class nsProxyInfo : public nsIProxyInfo
@ -153,6 +167,10 @@ public:
return mType;
}
NS_IMETHOD_(PRUint32) Flags() {
return mFlags;
}
~nsProxyInfo()
{
NS_IF_RELEASE(mNext);
@ -161,12 +179,14 @@ public:
nsProxyInfo(const char *type = nsnull)
: mType(type)
, mPort(-1)
, mFlags(0)
, mNext(nsnull)
{}
const char *mType; // pointer to static kProxyType_XYZ value
nsCString mHost;
PRInt32 mPort;
PRUint32 mFlags;
nsProxyInfo *mNext;
};
@ -207,6 +227,7 @@ nsProtocolProxyService::nsProtocolProxyService()
, mHTTPSProxyPort(-1)
, mSOCKSProxyPort(-1)
, mSOCKSProxyVersion(4)
, mSOCKSProxyRemoteDNS(PR_FALSE)
, mSessionStart(PR_Now())
, mFailedProxyTimeout(30 * 60) // 30 minute default
{
@ -320,6 +341,9 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
mSOCKSProxyVersion = 4;
}
if (!pref || !strcmp(pref, "network.proxy.socks_remote_dns"))
proxy_GetBoolPref(prefBranch, "network.proxy.socks_remote_dns", mSOCKSProxyRemoteDNS);
if (!pref || !strcmp(pref, "network.proxy.failover_timeout"))
proxy_GetIntPref(prefBranch, "network.proxy.failover_timeout", mFailedProxyTimeout);
@ -484,6 +508,7 @@ const char *
nsProtocolProxyService::ExtractProxyInfo(const char *start, PRBool permitHttp, nsProxyInfo **result)
{
*result = nsnull;
PRUint32 flags = 0;
// see BNF in nsIProxyAutoConfig.idl
@ -518,6 +543,13 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, PRBool permitHttp, n
if (type) {
const char *host = nsnull, *hostEnd = nsnull;
PRInt32 port = -1;
// If it's a SOCKS5 proxy, do name resolution on the server side.
// We could use this with SOCKS4a servers too, but they might not
// support it.
if (type == kProxyType_SOCKS)
flags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
// extract host:port
start = sp;
while ((*start == ' ' || *start == '\t') && start < end)
@ -539,6 +571,7 @@ nsProtocolProxyService::ExtractProxyInfo(const char *start, PRBool permitHttp, n
nsProxyInfo *pi = new nsProxyInfo;
if (pi) {
pi->mType = type;
pi->mFlags = flags;
// YES, it is ok to specify a null proxy host.
if (host) {
pi->mHost.Assign(host, hostEnd - host);
@ -762,6 +795,8 @@ nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxyInfo **aResult)
const nsACString *host = nsnull;
PRInt32 port = -1;
PRUint32 proxyFlags = 0;
if (!mHTTPProxyHost.IsEmpty() && mHTTPProxyPort > 0 &&
scheme.EqualsLiteral("http")) {
host = &mHTTPProxyHost;
@ -793,10 +828,12 @@ nsProtocolProxyService::ExamineForProxy(nsIURI *aURI, nsIProxyInfo **aResult)
else
type = kProxyType_SOCKS;
port = mSOCKSProxyPort;
if (mSOCKSProxyRemoteDNS)
proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
}
if (type)
return NewProxyInfo_Internal(type, *host, port, aResult);
return NewProxyInfo_Internal(type, *host, port, proxyFlags, aResult);
return NS_OK;
}
@ -805,6 +842,7 @@ NS_IMETHODIMP
nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
const nsACString &aHost,
PRInt32 aPort,
PRUint32 aFlags,
nsIProxyInfo **aResult)
{
static const char *types[] = {
@ -827,7 +865,7 @@ nsProtocolProxyService::NewProxyInfo(const nsACString &aType,
if (aPort <= 0)
aPort = -1;
return NewProxyInfo_Internal(type, aHost, aPort, aResult);
return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aResult);
}
NS_IMETHODIMP
@ -1075,6 +1113,7 @@ nsresult
nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
const nsACString &aHost,
PRInt32 aPort,
PRUint32 aFlags,
nsIProxyInfo **aResult)
{
nsProxyInfo *proxyInfo = new nsProxyInfo();
@ -1084,6 +1123,7 @@ nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
proxyInfo->mType = aType;
proxyInfo->mHost = aHost;
proxyInfo->mPort = aPort;
proxyInfo->mFlags = aFlags;
NS_ADDREF(*aResult = proxyInfo);
return NS_OK;

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -87,7 +88,7 @@ protected:
NS_HIDDEN_(PRBool) IsProxyDisabled(nsProxyInfo *);
NS_HIDDEN_(nsresult) ExaminePACForProxy(nsIURI *aURI, PRUint32 protoFlags, nsIProxyInfo **aResult);
NS_HIDDEN_(nsresult) GetProtocolInfo(const char *scheme, PRUint32 &flags, PRInt32 &defaultPort);
NS_HIDDEN_(nsresult) NewProxyInfo_Internal(const char *type, const nsACString &host, PRInt32 port, nsIProxyInfo **);
NS_HIDDEN_(nsresult) NewProxyInfo_Internal(const char *type, const nsACString &host, PRInt32 port, PRUint32 flags, nsIProxyInfo **);
NS_HIDDEN_(void) LoadFilters(const char *filters);
NS_HIDDEN_(PRBool) CanUseProxy(nsIURI *aURI, PRInt32 defaultPort);
NS_HIDDEN_(void) ConfigureFromWPAD();
@ -150,6 +151,7 @@ protected:
nsCString mSOCKSProxyHost;
PRInt32 mSOCKSProxyPort;
PRInt32 mSOCKSProxyVersion;
PRBool mSOCKSProxyRemoteDNS;
nsCOMPtr<nsIProxyAutoConfig> mPAC;
nsCString mPACURI;

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Darin Fisher <darin@netscape.com>
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -677,6 +678,7 @@ nsSocketTransport::nsSocketTransport()
, mPort(0)
, mProxyPort(0)
, mProxyTransparent(PR_FALSE)
, mProxyTransparentResolvesHost(PR_FALSE)
, mState(STATE_CLOSED)
, mAttached(PR_FALSE)
, mInputClosed(PR_TRUE)
@ -769,8 +771,15 @@ nsSocketTransport::Init(const char **types, PRUint32 typeCount,
// note if socket type corresponds to a transparent proxy
if ((strcmp(mTypes[i], "socks") == 0) ||
(strcmp(mTypes[i], "socks4") == 0))
(strcmp(mTypes[i], "socks4") == 0)) {
mProxyTransparent = PR_TRUE;
if (proxyInfo->Flags() & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
// we want the SOCKS layer to send the hostname
// and port to the proxy and let it do the DNS.
mProxyTransparentResolvesHost = PR_TRUE;
}
}
}
}
@ -865,6 +874,20 @@ nsSocketTransport::ResolveHost()
nsresult rv;
if (!mProxyHost.IsEmpty() && mProxyTransparentResolvesHost) {
// Name resolution is done on the server side. Just pretend
// client resolution is complete, this will get picked up later.
// since we don't need to do DNS now, we bypass the resolving
// step by initializing mNetAddr to an empty address, but we
// must keep the port. The SOCKS IO layer will use the hostname
// we send it when it's created, rather than the empty address
// we send with the connect call.
mState = STATE_RESOLVING;
PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, SocketPort(), &mNetAddr);
return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nsnull);
}
nsCOMPtr<nsIDNSService> dns = do_GetService(kDNSServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
@ -903,11 +926,12 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool
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;
const char *host = mHost.get();
PRInt32 port = (PRInt32) mPort;
const char *proxyHost = mProxyHost.IsEmpty() ? nsnull : mProxyHost.get();
PRInt32 proxyPort = (PRInt32) mProxyPort;
PRUint32 proxyFlags = 0;
PRUint32 i;
for (i=0; i<mTypeCount; ++i) {
nsCOMPtr<nsISocketProvider> provider;
@ -918,13 +942,17 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool
if (NS_FAILED(rv))
break;
if (mProxyTransparentResolvesHost)
proxyFlags |= nsISocketProvider::PROXY_RESOLVES_HOST;
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(mNetAddr.raw.family,
host, port, proxyHost, proxyPort,
&fd, getter_AddRefs(secinfo));
proxyFlags, &fd,
getter_AddRefs(secinfo));
if (NS_SUCCEEDED(rv) && !fd) {
NS_NOTREACHED("NewSocket succeeded but failed to create a PRFileDesc");
@ -937,8 +965,10 @@ nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool
// to the stack (such as pushing an io layer)
rv = provider->AddToSocket(mNetAddr.raw.family,
host, port, proxyHost, proxyPort,
fd, getter_AddRefs(secinfo));
proxyFlags, fd,
getter_AddRefs(secinfo));
}
proxyFlags = 0;
if (NS_FAILED(rv))
break;

View File

@ -187,13 +187,14 @@ private:
//-------------------------------------------------------------------------
// socket type info:
char **mTypes;
PRUint32 mTypeCount;
nsCString mHost;
nsCString mProxyHost;
PRUint16 mPort;
PRUint16 mProxyPort;
PRBool mProxyTransparent;
char **mTypes;
PRUint32 mTypeCount;
nsCString mHost;
nsCString mProxyHost;
PRUint16 mPort;
PRUint16 mProxyPort;
PRPackedBool mProxyTransparent;
PRPackedBool mProxyTransparentResolvesHost;
PRUint16 SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }
const nsCString &SocketHost() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; }

View File

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -42,7 +43,7 @@
/**
* nsISocketProvider
*/
[noscript, uuid(4c29772e-cf73-414a-98d9-661761a4511a)]
[noscript, uuid(00b3df92-e830-11d8-d48e-0004e22243f8)]
interface nsISocketProvider : nsISupports
{
/**
@ -58,6 +59,8 @@ interface nsISocketProvider : nsISupports
* If non-null, the proxy hostname for this connection.
* @param aProxyPort
* The proxy port for this connection.
* @param aFlags
* Control flags that govern this connection (see below.)
* @param aFileDesc
* The resulting PRFileDesc.
* @param aSecurityInfo
@ -69,25 +72,38 @@ interface nsISocketProvider : nsISupports
in long aPort,
in string aProxyHost,
in long aProxyPort,
in unsigned long aFlags,
out nsFileDescStar aFileDesc,
out nsISupports aSecurityInfo);
/**
* addToSocket
*
* This function is called to allow the socket provider to layer a PRFileDesc
* on top of another PRFileDesc. For example, SSL via a SOCKS proxy.
* This function is called to allow the socket provider to layer a
* PRFileDesc on top of another PRFileDesc. For example, SSL via a SOCKS
* proxy.
*
* Parameters are the same as newSocket with the exception of aFileDesc, which
* is an in-param instead.
* Parameters are the same as newSocket with the exception of aFileDesc,
* which is an in-param instead.
*/
void addToSocket(in long aFamily,
in string aHost,
in long aPort,
in string aProxyHost,
in long aProxyPort,
in unsigned long aFlags,
in nsFileDescStar aFileDesc,
out nsISupports aSecurityInfo);
/**
* PROXY_RESOLVES_HOST
*
* This flag is set if the proxy is to perform hostname resolution instead
* of the client. When set, the hostname parameter passed when in this
* interface will be used instead of the address structure passed for a
* later connect et al. request.
*/
const long PROXY_RESOLVES_HOST = 1 << 0;
};
%{C++

View File

@ -24,6 +24,7 @@
* Justin Bradford <jab@atdot.org>
* Bradley Baetz <bbaetz@acm.org>
* Darin Fisher <darin@meer.net>
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -46,6 +47,7 @@
#include "nsIServiceManager.h"
#include "nsIDNSService.h"
#include "nsISOCKSSocketInfo.h"
#include "nsISocketProvider.h"
#include "nsSOCKSIOLayer.h"
#include "nsNetCID.h"
@ -68,20 +70,28 @@ class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
public:
nsSOCKSSocketInfo();
virtual ~nsSOCKSSocketInfo() {}
NS_DECL_ISUPPORTS
NS_DECL_NSISOCKSSOCKETINFO
void Init(PRInt32 version, const char *proxyHost, PRInt32 proxyPort);
const nsCString &ProxyHost() { return mProxyHost; }
PRInt32 ProxyPort() { return mProxyPort; }
PRInt32 Version() { return mVersion; }
void Init(PRInt32 version,
const char *proxyHost,
PRInt32 proxyPort,
const char *destinationHost,
PRUint32 flags);
const nsCString &DestinationHost() { return mDestinationHost; }
const nsCString &ProxyHost() { return mProxyHost; }
PRInt32 ProxyPort() { return mProxyPort; }
PRInt32 Version() { return mVersion; }
PRUint32 Flags() { return mFlags; }
private:
nsCString mDestinationHost;
nsCString mProxyHost;
PRInt32 mProxyPort;
PRInt32 mProxyPort;
PRInt32 mVersion; // SOCKS version 4 or 5
PRUint32 mFlags;
PRNetAddr mInternalProxyAddr;
PRNetAddr mExternalProxyAddr;
PRNetAddr mDestinationAddr;
@ -90,6 +100,7 @@ private:
nsSOCKSSocketInfo::nsSOCKSSocketInfo()
: mProxyPort(-1)
, mVersion(-1)
, mFlags(0)
{
PR_InitializeNetAddr(PR_IpAddrAny, 0, &mInternalProxyAddr);
PR_InitializeNetAddr(PR_IpAddrAny, 0, &mExternalProxyAddr);
@ -97,11 +108,13 @@ nsSOCKSSocketInfo::nsSOCKSSocketInfo()
}
void
nsSOCKSSocketInfo::Init(PRInt32 version, const char *proxyHost, PRInt32 proxyPort)
nsSOCKSSocketInfo::Init(PRInt32 version, const char *proxyHost, PRInt32 proxyPort, const char *host, PRUint32 flags)
{
mVersion = version;
mProxyHost = proxyHost;
mProxyPort = proxyPort;
mVersion = version;
mProxyHost = proxyHost;
mProxyPort = proxyPort;
mDestinationHost = host;
mFlags = flags;
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsSOCKSSocketInfo, nsISOCKSSocketInfo)
@ -182,17 +195,17 @@ ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRInter
LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
return NS_ERROR_FAILURE;
}
// get the server's response. Use PR_Recv() instead of
response_len = 2;
response_len = PR_Recv(fd, response, response_len, 0, timeout);
if (response_len <= 0) {
LOGERROR(("PR_Recv() failed. response_len = %d.", response_len));
return NS_ERROR_FAILURE;
}
if (response[0] != 0x05) {
// it's a either not SOCKS or not our version
LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
@ -222,67 +235,119 @@ ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRInter
LOGERROR(("Uncrecognized authentication method received: %x", response[1]));
return NS_ERROR_FAILURE;
}
// we are now authenticated, so lets tell
// the server where to connect to
request_len = 6;
request_len = 0;
request[0] = 0x05; // SOCKS version 5
request[1] = 0x01; // CONNECT command
request[2] = 0x00; // obligatory reserved field (perfect for MS tampering!)
if (PR_NetAddrFamily(addr) == PR_AF_INET) {
// get destination port
PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
// if the PROXY_RESOLVES_HOST flag is set, we assume
// that the transport wants us to pass the SOCKS server the
// hostname and port and let it do the name resolution.
// the real destination hostname and port was stored
// in our info object earlier when this layer was created.
const nsCString& destHost = info->DestinationHost();
LOGDEBUG(("host:port -> %s:%li", destHost.get(), destPort));
request[3] = 0x03; // encoding of destination address (3 == hostname)
int host_len = destHost.Length();
if (host_len > 255) {
// SOCKS5 transmits the length of the hostname in a single char.
// This gives us an absolute limit of 255 chars in a hostname, and
// there's nothing we can do to extend it. I don't think many
// hostnames will ever be bigger than this, so hopefully it's an
// uneventful abort condition.
LOGERROR (("Hostname too big for SOCKS5."));
return NS_ERROR_INVALID_ARG;
}
request[4] = (char) host_len;
request_len = 5;
// Send the initial header first...
write_len = PR_Send(fd, request, request_len, 0, timeout);
if (write_len != request_len) {
// bad write
LOGERROR(("PR_Send() failed sending connect command. Wrote: %d bytes; Expected: %d.", write_len, request_len));
return NS_ERROR_FAILURE;
}
// Now send the hostname...
write_len = PR_Send(fd, destHost.get(), host_len, 0, timeout);
if (write_len != host_len) {
// bad write
LOGERROR(("PR_Send() failed sending connect command. Wrote: %d bytes; Expected: %d.", write_len, host_len));
return NS_ERROR_FAILURE;
}
// There's no data left because we just sent it.
request_len = 0;
} else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
request[3] = 0x01; // encoding of destination address (1 == IPv4)
request_len += 4;
request_len = 8; // 4 for address, 4 SOCKS headers
char * ip = (char*)(&addr->inet.ip);
request[4] = *ip++;
request[5] = *ip++;
request[6] = *ip++;
request[7] = *ip++;
} else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
request[3] = 0x04; // encoding of destination address (4 == IPv6)
request_len += 16;
request_len = 20; // 16 for address, 4 SOCKS headers
char * ip = (char*)(&addr->ipv6.ip.pr_s6_addr);
request[4] = *ip++; request[5] = *ip++; request[6] = *ip++; request[7] = *ip++;
request[8] = *ip++; request[9] = *ip++; request[10] = *ip++; request[11] = *ip++;
request[12] = *ip++; request[13] = *ip++; request[14] = *ip++; request[15] = *ip++;
request[16] = *ip++; request[17] = *ip++; request[18] = *ip++; request[19] = *ip++;
request[4] = *ip++; request[5] = *ip++;
request[6] = *ip++; request[7] = *ip++;
request[8] = *ip++; request[9] = *ip++;
request[10] = *ip++; request[11] = *ip++;
request[12] = *ip++; request[13] = *ip++;
request[14] = *ip++; request[15] = *ip++;
request[16] = *ip++; request[17] = *ip++;
request[18] = *ip++; request[19] = *ip++;
// we're going to test to see if this address can
// be mapped back into IPv4 without loss. if so,
// we'll use IPv4 instead, as reliable SOCKS server
// support for IPv6 is probably questionable.
if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
request[3] = 0x01; // ipv4 encoding
request[4] = request[16];
request[5] = request[17];
request[6] = request[18];
request[7] = request[19];
request_len -= 12;
}
} else {
// Unknown address type
LOGERROR(("Don't know what kind of IP address this is."));
return NS_ERROR_FAILURE;
}
// destination port
PRUint16 destPort = PR_htons(PR_NetAddrInetPort(addr));
request[request_len-2] = (unsigned char)(destPort >> 8);
request[request_len-1] = (unsigned char)destPort;
// add the destination port to the request
request[request_len] = (unsigned char)(destPort >> 8);
request[request_len+1] = (unsigned char)destPort;
request_len += 2;
write_len = PR_Send(fd, request, request_len, 0, timeout);
if (write_len != request_len) {
@ -290,7 +355,7 @@ ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRInter
LOGERROR(("PR_Send() failed sending connect command. Wrote: %d bytes; Expected: %d.", write_len, request_len));
return NS_ERROR_FAILURE;
}
response_len = 22;
response_len = PR_Recv(fd, response, response_len, 0, timeout);
if (response_len <= 0) {
@ -299,14 +364,14 @@ ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRInter
LOGERROR(("PR_Recv() failed getting connect command reply. response_len = %d.", response_len));
return NS_ERROR_FAILURE;
}
if (response[0] != 0x05) {
// bad response
LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
return NS_ERROR_FAILURE;
}
switch(response[1]) {
case 0x00: break; // success
case 0x01: LOGERROR(("SOCKS 5 server rejected connect request: 01, General SOCKS server failure."));
@ -328,10 +393,10 @@ ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRInter
default: LOGERROR(("SOCKS 5 server rejected connect request: %x.", response[1]));
return NS_ERROR_FAILURE;
}
// get external bound address (this is what
// the outside world sees as "us")
char *ip = nsnull;
@ -339,30 +404,34 @@ ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRInter
switch (response[3]) {
case 0x01: // IPv4
extPort = (response[8] << 8) | response[9];
PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, extPort, extAddr);
ip = (char*)(&extAddr->inet.ip);
*ip++ = response[4];
*ip++ = response[5];
*ip++ = response[6];
*ip++ = response[7];
break;
case 0x04: // IPv6
extPort = (response[20] << 8) | response[21];
PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, extPort, extAddr);
ip = (char*)(&extAddr->ipv6.ip.pr_s6_addr);
*ip++ = response[4]; *ip++ = response[5]; *ip++ = response[6]; *ip++ = response[7];
*ip++ = response[8]; *ip++ = response[9]; *ip++ = response[10]; *ip++ = response[11];
*ip++ = response[12]; *ip++ = response[13]; *ip++ = response[14]; *ip++ = response[15];
*ip++ = response[16]; *ip++ = response[17]; *ip++ = response[18]; *ip++ = response[19];
*ip++ = response[4]; *ip++ = response[5];
*ip++ = response[6]; *ip++ = response[7];
*ip++ = response[8]; *ip++ = response[9];
*ip++ = response[10]; *ip++ = response[11];
*ip++ = response[12]; *ip++ = response[13];
*ip++ = response[14]; *ip++ = response[15];
*ip++ = response[16]; *ip++ = response[17];
*ip++ = response[18]; *ip++ = response[19];
break;
case 0x03: // FQDN (should not get this back)
default: // unknown format
@ -387,48 +456,21 @@ ConnectSOCKS4(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
unsigned char request[12];
int request_len = 0;
int write_len;
unsigned char response[10];
int response_len = 0;
char *ip = nsnull;
request[0] = 0x04; // SOCKS version 4
request[1] = 0x01; // CD command code -- 1 for connect
// destination port
PRUint16 destPort = PR_htons(PR_NetAddrInetPort(addr));
PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
// store the port
request[2] = (unsigned char)(destPort >> 8);
request[3] = (unsigned char)destPort;
// destination IP
char * ip = nsnull;
// IPv4
if (PR_NetAddrFamily(addr) == PR_AF_INET)
ip = (char*)(&addr->inet.ip);
// IPv6
else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
// IPv4 address encoded in an IPv6 address
if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped))
ip = (char*)(&addr->ipv6.ip.pr_s6_addr[12]);
else {
LOGERROR(("IPv6 not supported in SOCK 4."));
return NS_ERROR_FAILURE; // SOCKS 4 can't do IPv6
}
}
else {
LOGERROR(("Don't know what kind of IP address this is."));
return NS_ERROR_FAILURE; // don't recognize this type
}
request[4] = *ip++;
request[5] = *ip++;
request[6] = *ip++;
request[7] = *ip++;
// username
request[8] = 'M';
request[9] = 'O';
@ -437,13 +479,89 @@ ConnectSOCKS4(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
request[11] = 0x00;
request_len = 12;
int write_len = PR_Send(fd, request, request_len, 0, timeout);
if (write_len != request_len) {
LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
return NS_ERROR_FAILURE;
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
// if the PROXY_RESOLVES_HOST flag is set, we assume that the
// transport wants us to pass the SOCKS server the hostname
// and port and let it do the name resolution.
// an extension to SOCKS 4, called 4a, specifies a way
// to do this, so we'll try that and hope the
// server supports it.
// the real destination hostname and port was stored
// in our info object earlier when this layer was created.
const nsCString& destHost = info->DestinationHost();
LOGDEBUG(("host:port -> %s:%li\n", destHost.get(), destPort));
// the IP portion of the query is set to this special address.
request[4] = 0;
request[5] = 0;
request[6] = 0;
request[7] = 1;
write_len = PR_Send(fd, request, request_len, 0, timeout);
if (write_len != request_len) {
LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
return NS_ERROR_FAILURE;
}
// Remember the NULL.
int host_len = destHost.Length() + 1;
write_len = PR_Send(fd, destHost.get(), host_len, 0, timeout);
if (write_len != host_len) {
LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, host_len));
return NS_ERROR_FAILURE;
}
// No data to send, just sent it.
request_len = 0;
} else if (PR_NetAddrFamily(addr) == PR_AF_INET) { // IPv4
// store the ip
ip = (char*)(&addr->inet.ip);
request[4] = *ip++;
request[5] = *ip++;
request[6] = *ip++;
request[7] = *ip++;
} else if (PR_NetAddrFamily(addr) == PR_AF_INET6) { // IPv6
// IPv4 address encoded in an IPv6 address
if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
// store the ip
ip = (char*)(&addr->ipv6.ip.pr_s6_addr[12]);
request[4] = *ip++;
request[5] = *ip++;
request[6] = *ip++;
request[7] = *ip++;
} else {
LOGERROR(("IPv6 is not supported in SOCKS 4."));
return NS_ERROR_FAILURE; // SOCKS 4 can't do IPv6
}
} else {
LOGERROR(("Don't know what kind of IP address this is."));
return NS_ERROR_FAILURE; // don't recognize this type
}
if (request_len > 0) {
write_len = PR_Send(fd, request, request_len, 0, timeout);
if (write_len != request_len) {
LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
return NS_ERROR_FAILURE;
}
}
// get the server's response
response_len = 8; // size of the response
response_len = PR_Recv(fd, response, response_len, 0, timeout);
@ -452,7 +570,7 @@ ConnectSOCKS4(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
LOGERROR(("PR_Recv() failed. response_len = %d.", response_len));
return NS_ERROR_FAILURE;
}
if ((response[0] != 0x00) && (response[0] != 0x04)) {
// Novell BorderManager sends a response of type 4, should be zero
// According to the spec. Cope with this brokenness.
@ -477,10 +595,10 @@ nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*ti
{
PRStatus status;
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
if (info == NULL) return PR_FAILURE;
// First, we need to look up our proxy...
const nsCString &proxyHost = info->ProxyHost();
@ -497,7 +615,7 @@ nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*ti
nsCOMPtr<nsIDNSService> dns;
nsCOMPtr<nsIDNSRecord> rec;
nsresult rv;
dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return PR_FAILURE;
@ -512,38 +630,38 @@ nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*ti
}
info->SetInternalProxyAddr(&proxyAddr);
// For now, we'll do this as a blocking connect,
// but with nspr 4.1, the necessary functions to
// do a non-blocking connect will be available
// Preserve the non-blocking state of the socket
PRBool nonblocking;
PRSocketOptionData sockopt;
sockopt.option = PR_SockOpt_Nonblocking;
status = PR_GetSocketOption(fd, &sockopt);
if (PR_SUCCESS != status) {
LOGERROR(("PR_GetSocketOption() failed. status = %x.", status));
return status;
}
// Store blocking option
nonblocking = sockopt.value.non_blocking;
sockopt.option = PR_SockOpt_Nonblocking;
sockopt.value.non_blocking = PR_FALSE;
status = PR_SetSocketOption(fd, &sockopt);
if (PR_SUCCESS != status) {
LOGERROR(("PR_SetSocketOption() failed. status = %x.", status));
return status;
}
// Now setup sockopts, so we can restore the value later.
sockopt.option = PR_SockOpt_Nonblocking;
sockopt.value.non_blocking = nonblocking;
// This connectWait should be long enough to connect to local proxy
// servers, but not much longer. Since this protocol negotiation
@ -556,17 +674,17 @@ nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*ti
// Connect to the proxy server.
status = fd->lower->methods->connect(fd->lower, &proxyAddr, connectWait);
if (PR_SUCCESS != status) {
LOGERROR(("Failed to TCP connect to the proxy server (%s): timeout = %d, status = %x.",proxyHost.get(), connectWait, status));
PR_SetSocketOption(fd, &sockopt);
return status;
}
// We are now connected to the SOCKS proxy server.
// Now we will negotiate a connection to the desired server.
// External IP address returned from ConnectSOCKS5(). Not supported in SOCKS4.
PRNetAddr extAddr;
PR_InitializeNetAddr(PR_IpAddrNull, 0, &extAddr);
@ -599,16 +717,16 @@ nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*ti
}
info->SetDestinationAddr((PRNetAddr*)addr);
info->SetExternalProxyAddr(&extAddr);
// restore non-blocking option
PR_SetSocketOption(fd, &sockopt);
// we're set-up and connected.
// this socket can be used as normal now.
return PR_SUCCESS;
}
@ -617,13 +735,13 @@ nsSOCKSIOLayerClose(PRFileDesc *fd)
{
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
PRDescIdentity id = PR_GetLayersIdentity(fd);
if (info && id == nsSOCKSIOLayerIdentity)
{
NS_RELEASE(info);
fd->identity = PR_INVALID_IO_LAYER;
}
return fd->lower->methods->close(fd->lower);
}
@ -657,7 +775,7 @@ nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr)
if (info->GetExternalProxyAddr(&addr) == NS_OK)
return PR_SUCCESS;
}
return PR_FAILURE;
}
@ -665,12 +783,12 @@ static PRStatus PR_CALLBACK
nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
{
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
if (info != NULL && addr != NULL) {
if (info->GetDestinationAddr(&addr) == NS_OK)
return PR_SUCCESS;
}
return PR_FAILURE;
}
@ -689,6 +807,7 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
const char *proxyHost,
PRInt32 proxyPort,
PRInt32 socksVersion,
PRUint32 flags,
PRFileDesc *fd,
nsISupports** info)
{
@ -699,7 +818,7 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
{
nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
@ -708,7 +827,7 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
firstTime = PR_FALSE;
#if defined(PR_LOGGING)
@ -716,19 +835,19 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
#endif
}
LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
PRFileDesc * layer;
PRStatus rv;
layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
if (! layer)
{
LOGERROR(("PR_CreateIOLayerStub() failed."));
return NS_ERROR_FAILURE;
}
nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo();
if (!infoObject)
{
@ -737,12 +856,12 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
PR_DELETE(layer);
return NS_ERROR_FAILURE;
}
NS_ADDREF(infoObject);
infoObject->Init(socksVersion, proxyHost, proxyPort);
infoObject->Init(socksVersion, proxyHost, proxyPort, host, flags);
layer->secret = (PRFilePrivate*) infoObject;
rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
if (NS_FAILED(rv))
{
LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
@ -750,7 +869,7 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
PR_DELETE(layer);
return NS_ERROR_FAILURE;
}
*info = infoObject;
NS_ADDREF(*info);
return NS_OK;

View File

@ -22,6 +22,7 @@
*
* Contributor(s):
* Justin Bradford <jab@atdot.org>
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -44,13 +45,14 @@
#include "prio.h"
#include "nscore.h"
nsresult nsSOCKSIOLayerAddToSocket(PRInt32 family,
const char *host,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRInt32 socksVersion,
PRFileDesc *fd,
nsresult nsSOCKSIOLayerAddToSocket(PRInt32 family,
const char *host,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRInt32 socksVersion,
PRUint32 flags,
PRFileDesc *fd,
nsISupports **info);
#endif /* nsSOCKSIOLayer_h__ */

View File

@ -23,6 +23,7 @@
* Contributor(s):
* Justin Bradford <jab@atdot.org>
* Darin Fisher <darin@meer.net>
* Malcolm Smith <malsmith@cs.rmit.edu.au>
*
* 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
@ -80,6 +81,7 @@ nsSOCKSSocketProvider::NewSocket(PRInt32 family,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRUint32 flags,
PRFileDesc **result,
nsISupports **socksInfo)
{
@ -95,6 +97,7 @@ nsSOCKSSocketProvider::NewSocket(PRInt32 family,
proxyHost,
proxyPort,
mVersion,
flags,
sock,
socksInfo);
if (NS_SUCCEEDED(rv)) {
@ -111,6 +114,7 @@ nsSOCKSSocketProvider::AddToSocket(PRInt32 family,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRUint32 flags,
PRFileDesc *sock,
nsISupports **socksInfo)
{
@ -120,6 +124,7 @@ nsSOCKSSocketProvider::AddToSocket(PRInt32 family,
proxyHost,
proxyPort,
mVersion,
flags,
sock,
socksInfo);

View File

@ -58,6 +58,7 @@ nsSSLSocketProvider::NewSocket(PRInt32 family,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRUint32 flags,
PRFileDesc **_result,
nsISupports **securityInfo)
{
@ -79,6 +80,7 @@ nsSSLSocketProvider::AddToSocket(PRInt32 family,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRUint32 flags,
PRFileDesc *aSocket,
nsISupports **securityInfo)
{

View File

@ -58,6 +58,7 @@ nsTLSSocketProvider::NewSocket(PRInt32 family,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRUint32 flags,
PRFileDesc **_result,
nsISupports **securityInfo)
{
@ -80,6 +81,7 @@ nsTLSSocketProvider::AddToSocket(PRInt32 family,
PRInt32 port,
const char *proxyHost,
PRInt32 proxyPort,
PRUint32 flags,
PRFileDesc *aSocket,
nsISupports **securityInfo)
{