mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 435028 - IPv6 sites not reachable when using IPv4 SOCKS proxy. r=biesi
This commit is contained in:
parent
30fbf181d9
commit
5fd3e2bc43
@ -5,6 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nspr.h"
|
||||
#include "private/pprio.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCRT.h"
|
||||
|
||||
@ -22,6 +23,7 @@
|
||||
static PRDescIdentity nsSOCKSIOLayerIdentity;
|
||||
static PRIOMethods nsSOCKSIOLayerMethods;
|
||||
static bool firstTime = true;
|
||||
static bool ipv6Supported = true;
|
||||
|
||||
#if defined(PR_LOGGING)
|
||||
static PRLogModuleInfo *gSOCKSLog;
|
||||
@ -66,6 +68,7 @@ public:
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
|
||||
void Init(PRInt32 version,
|
||||
PRInt32 family,
|
||||
const char *proxyHost,
|
||||
PRInt32 proxyPort,
|
||||
const char *destinationHost,
|
||||
@ -80,6 +83,7 @@ private:
|
||||
void HandshakeFinished(PRErrorCode err = 0);
|
||||
PRStatus StartDNS(PRFileDesc *fd);
|
||||
PRStatus ConnectToProxy(PRFileDesc *fd);
|
||||
void FixupAddressFamily(PRFileDesc *fd, PRNetAddr *proxy);
|
||||
PRStatus ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags);
|
||||
PRStatus WriteV4ConnectRequest();
|
||||
PRStatus ReadV4ConnectResponse();
|
||||
@ -123,6 +127,7 @@ private:
|
||||
nsCString mProxyHost;
|
||||
PRInt32 mProxyPort;
|
||||
PRInt32 mVersion; // SOCKS version 4 or 5
|
||||
PRInt32 mDestinationFamily;
|
||||
PRUint32 mFlags;
|
||||
PRNetAddr mInternalProxyAddr;
|
||||
PRNetAddr mExternalProxyAddr;
|
||||
@ -138,6 +143,7 @@ nsSOCKSSocketInfo::nsSOCKSSocketInfo()
|
||||
, mAmountToRead(0)
|
||||
, mProxyPort(-1)
|
||||
, mVersion(-1)
|
||||
, mDestinationFamily(PR_AF_INET)
|
||||
, mFlags(0)
|
||||
, mTimeout(PR_INTERVAL_NO_TIMEOUT)
|
||||
{
|
||||
@ -148,9 +154,10 @@ nsSOCKSSocketInfo::nsSOCKSSocketInfo()
|
||||
}
|
||||
|
||||
void
|
||||
nsSOCKSSocketInfo::Init(PRInt32 version, const char *proxyHost, PRInt32 proxyPort, const char *host, PRUint32 flags)
|
||||
nsSOCKSSocketInfo::Init(PRInt32 version, PRInt32 family, const char *proxyHost, PRInt32 proxyPort, const char *host, PRUint32 flags)
|
||||
{
|
||||
mVersion = version;
|
||||
mDestinationFamily = family;
|
||||
mProxyHost = proxyHost;
|
||||
mProxyPort = proxyPort;
|
||||
mDestinationHost = host;
|
||||
@ -285,6 +292,12 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
|
||||
return PR_FAILURE;
|
||||
}
|
||||
|
||||
// Try socks5 if the destination addrress is IPv6
|
||||
if (mVersion == 4 &&
|
||||
PR_NetAddrFamily(&mDestinationAddr) == PR_AF_INET6) {
|
||||
mVersion = 5;
|
||||
}
|
||||
|
||||
PRInt32 addresses = 0;
|
||||
do {
|
||||
if (addresses++)
|
||||
@ -304,8 +317,9 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
|
||||
LOGDEBUG(("socks: trying proxy server, %s:%hu",
|
||||
buf, PR_ntohs(PR_NetAddrInetPort(&mInternalProxyAddr))));
|
||||
#endif
|
||||
status = fd->lower->methods->connect(fd->lower,
|
||||
&mInternalProxyAddr, mTimeout);
|
||||
PRNetAddr proxy = mInternalProxyAddr;
|
||||
FixupAddressFamily(fd, &proxy);
|
||||
status = fd->lower->methods->connect(fd->lower, &proxy, mTimeout);
|
||||
if (status != PR_SUCCESS) {
|
||||
PRErrorCode c = PR_GetError();
|
||||
// If EINPROGRESS, return now and check back later after polling
|
||||
@ -322,6 +336,59 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
|
||||
return WriteV5AuthRequest();
|
||||
}
|
||||
|
||||
void
|
||||
nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, PRNetAddr *proxy)
|
||||
{
|
||||
PRInt32 proxyFamily = PR_NetAddrFamily(&mInternalProxyAddr);
|
||||
// Do nothing if the address family is already matched
|
||||
if (proxyFamily == mDestinationFamily) {
|
||||
return;
|
||||
}
|
||||
// If the system does not support IPv6 and the proxy address is IPv6,
|
||||
// We can do nothing here.
|
||||
if (proxyFamily == PR_AF_INET6 && !ipv6Supported) {
|
||||
return;
|
||||
}
|
||||
// If the system does not support IPv6 and the destination address is
|
||||
// IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy
|
||||
// the emulation layer
|
||||
if (mDestinationFamily == PR_AF_INET6 && !ipv6Supported) {
|
||||
proxy->ipv6.family = PR_AF_INET6;
|
||||
proxy->ipv6.port = mInternalProxyAddr.inet.port;
|
||||
PRUint8 *proxyp = proxy->ipv6.ip.pr_s6_addr;
|
||||
memset(proxyp, 0, 10);
|
||||
memset(proxyp + 10, 0xff, 2);
|
||||
memcpy(proxyp + 12,(char *) &mInternalProxyAddr.inet.ip, 4);
|
||||
// mDestinationFamily should not be updated
|
||||
return;
|
||||
}
|
||||
// Get an OS native handle from a specified FileDesc
|
||||
PROsfd osfd = PR_FileDesc2NativeHandle(fd);
|
||||
if (osfd == -1) {
|
||||
return;
|
||||
}
|
||||
// Create a new FileDesc with a specified family
|
||||
PRFileDesc *tmpfd = PR_OpenTCPSocket(proxyFamily);
|
||||
if (!tmpfd) {
|
||||
return;
|
||||
}
|
||||
PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd);
|
||||
if (newsd == -1) {
|
||||
PR_Close(tmpfd);
|
||||
return;
|
||||
}
|
||||
// Must succeed because PR_FileDesc2NativeHandle succeeded
|
||||
fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
|
||||
MOZ_ASSERT(fd);
|
||||
// Swap OS native handles
|
||||
PR_ChangeFileDescNativeHandle(fd, newsd);
|
||||
PR_ChangeFileDescNativeHandle(tmpfd, osfd);
|
||||
// Close temporary FileDesc which is now associated with
|
||||
// old OS native handle
|
||||
PR_Close(tmpfd);
|
||||
mDestinationFamily = proxyFamily;
|
||||
}
|
||||
|
||||
PRStatus
|
||||
nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags)
|
||||
{
|
||||
@ -1136,6 +1203,18 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
|
||||
|
||||
if (firstTime)
|
||||
{
|
||||
//XXX hack until NSPR provides an official way to detect system IPv6
|
||||
// support (bug 388519)
|
||||
PRFileDesc *tmpfd = PR_OpenTCPSocket(PR_AF_INET6);
|
||||
if (!tmpfd) {
|
||||
ipv6Supported = false;
|
||||
} else {
|
||||
// If the system does not support IPv6, NSPR will push
|
||||
// IPv6-to-IPv4 emulation layer onto the native layer
|
||||
ipv6Supported = PR_GetIdentitiesLayer(tmpfd, PR_NSPR_IO_LAYER) == tmpfd;
|
||||
PR_Close(tmpfd);
|
||||
}
|
||||
|
||||
nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
|
||||
nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
|
||||
|
||||
@ -1180,7 +1259,7 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
|
||||
}
|
||||
|
||||
NS_ADDREF(infoObject);
|
||||
infoObject->Init(socksVersion, proxyHost, proxyPort, host, flags);
|
||||
infoObject->Init(socksVersion, family, proxyHost, proxyPort, host, flags);
|
||||
layer->secret = (PRFilePrivate*) infoObject;
|
||||
rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
|
||||
|
||||
|
@ -38,7 +38,7 @@ function launchConnection(socks_vers, socks_port, dest_host, dest_port, dns)
|
||||
|
||||
for each (var arg in arguments) {
|
||||
print('client: running test', arg);
|
||||
test = arg.split(':');
|
||||
test = arg.split('|');
|
||||
launchConnection(test[0], parseInt(test[1]), test[2],
|
||||
parseInt(test[3]), test[4]);
|
||||
}
|
||||
|
@ -57,8 +57,26 @@ function runScriptSubprocess(script, args)
|
||||
|
||||
function buf2ip(buf)
|
||||
{
|
||||
// XXX this doesn't work with IPv6
|
||||
return buf.join('.');
|
||||
if (buf.length == 16) {
|
||||
var ip = (buf[0] << 4 | buf[1]).toString(16) + ':' +
|
||||
(buf[2] << 4 | buf[3]).toString(16) + ':' +
|
||||
(buf[4] << 4 | buf[5]).toString(16) + ':' +
|
||||
(buf[6] << 4 | buf[7]).toString(16) + ':' +
|
||||
(buf[8] << 4 | buf[9]).toString(16) + ':' +
|
||||
(buf[10] << 4 | buf[11]).toString(16) + ':' +
|
||||
(buf[12] << 4 | buf[13]).toString(16) + ':' +
|
||||
(buf[14] << 4 | buf[15]).toString(16);
|
||||
for (var i = 8; i >= 2; i--) {
|
||||
var re = new RegExp("(^|:)(0(:|$)){" + i + "}");
|
||||
var shortip = ip.replace(re, '::');
|
||||
if (shortip != ip) {
|
||||
return shortip;
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
} else {
|
||||
return buf.join('.');
|
||||
}
|
||||
}
|
||||
|
||||
function buf2int(buf)
|
||||
@ -325,8 +343,13 @@ SocksClient.prototype = {
|
||||
|
||||
sendSocks5Response: function()
|
||||
{
|
||||
// send a successful response with the address, 127.0.0.1:80
|
||||
this.outbuf += '\x05\x00\x00\x01\x7f\x00\x00\x01\x00\x80';
|
||||
if (this.dest_addr.length == 16) {
|
||||
// send a successful response with the address, [::1]:80
|
||||
this.outbuf += '\x05\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x80';
|
||||
} else {
|
||||
// send a successful response with the address, 127.0.0.1:80
|
||||
this.outbuf += '\x05\x00\x00\x01\x7f\x00\x00\x01\x00\x80';
|
||||
}
|
||||
this.sendPing();
|
||||
},
|
||||
|
||||
@ -395,7 +418,7 @@ SocksTestServer.prototype = {
|
||||
|
||||
print('server: test finished', test.port);
|
||||
do_check_true(test != null);
|
||||
do_check_eq(test.type, client.type);
|
||||
do_check_eq(test.expectedType || test.type, client.type);
|
||||
do_check_eq(test.port, port_id);
|
||||
|
||||
if (test.remote_dns)
|
||||
@ -414,11 +437,11 @@ SocksTestServer.prototype = {
|
||||
{
|
||||
var argv = [];
|
||||
|
||||
// marshaled: socks_ver:server_port:dest_host:dest_port:remote|local
|
||||
// marshaled: socks_ver|server_port|dest_host|dest_port|<remote|local>
|
||||
for each (var test in this.test_cases) {
|
||||
var arg = test.type + ':' +
|
||||
String(socks_listen_port) + ':' +
|
||||
test.host + ':' + test.port + ':';
|
||||
var arg = test.type + '|' +
|
||||
String(socks_listen_port) + '|' +
|
||||
test.host + '|' + test.port + '|';
|
||||
if (test.remote_dns)
|
||||
arg += 'remote';
|
||||
else
|
||||
@ -484,6 +507,12 @@ function run_test()
|
||||
host: '12345.xxx',
|
||||
remote_dns: true,
|
||||
});
|
||||
socks_test_server.addTestCase({
|
||||
type: "socks4",
|
||||
expectedType: "socks",
|
||||
host: '::1',
|
||||
remote_dns: false,
|
||||
});
|
||||
socks_test_server.addTestCase({
|
||||
type: "socks",
|
||||
host: '127.0.0.1',
|
||||
@ -494,6 +523,11 @@ function run_test()
|
||||
host: 'abcdefg.xxx',
|
||||
remote_dns: true,
|
||||
});
|
||||
socks_test_server.addTestCase({
|
||||
type: "socks",
|
||||
host: '::1',
|
||||
remote_dns: false,
|
||||
});
|
||||
socks_test_server.runClientSubprocess();
|
||||
|
||||
do_timeout(120 * 1000, test_timeout);
|
||||
|
Loading…
Reference in New Issue
Block a user