2012-08-25 18:19:00 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2012-05-21 11:12:37 +00:00
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2000-06-16 17:53:22 +00:00
|
|
|
|
|
|
|
#include "nspr.h"
|
2012-07-21 00:19:37 +00:00
|
|
|
#include "private/pprio.h"
|
2000-06-16 17:53:22 +00:00
|
|
|
#include "nsString.h"
|
2002-05-15 18:55:21 +00:00
|
|
|
#include "nsCRT.h"
|
2000-06-16 17:53:22 +00:00
|
|
|
|
|
|
|
#include "nsIServiceManager.h"
|
2003-09-11 20:32:33 +00:00
|
|
|
#include "nsIDNSService.h"
|
2005-04-06 01:33:28 +00:00
|
|
|
#include "nsIDNSRecord.h"
|
2003-09-11 20:32:33 +00:00
|
|
|
#include "nsISOCKSSocketInfo.h"
|
2004-12-16 02:46:12 +00:00
|
|
|
#include "nsISocketProvider.h"
|
2000-06-16 17:53:22 +00:00
|
|
|
#include "nsSOCKSIOLayer.h"
|
2003-09-11 20:32:33 +00:00
|
|
|
#include "nsNetCID.h"
|
2012-06-28 22:24:02 +00:00
|
|
|
#include "nsIDNSListener.h"
|
|
|
|
#include "nsICancelable.h"
|
|
|
|
#include "nsThreadUtils.h"
|
2012-12-23 21:08:43 +00:00
|
|
|
#include "mozilla/net/DNS.h"
|
2000-06-16 17:53:22 +00:00
|
|
|
|
2012-12-23 21:08:43 +00:00
|
|
|
using namespace mozilla::net;
|
|
|
|
|
|
|
|
static PRDescIdentity nsSOCKSIOLayerIdentity;
|
|
|
|
static PRIOMethods nsSOCKSIOLayerMethods;
|
2011-09-29 06:19:26 +00:00
|
|
|
static bool firstTime = true;
|
2012-07-21 00:19:37 +00:00
|
|
|
static bool ipv6Supported = true;
|
2000-06-16 17:53:22 +00:00
|
|
|
|
2001-07-25 00:28:28 +00:00
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
static PRLogModuleInfo *gSOCKSLog;
|
|
|
|
#define LOGDEBUG(args) PR_LOG(gSOCKSLog, PR_LOG_DEBUG, args)
|
|
|
|
#define LOGERROR(args) PR_LOG(gSOCKSLog, PR_LOG_ERROR , args)
|
|
|
|
|
|
|
|
#else
|
|
|
|
#define LOGDEBUG(args)
|
|
|
|
#define LOGERROR(args)
|
|
|
|
#endif
|
|
|
|
|
2000-06-16 17:53:22 +00:00
|
|
|
class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
|
2012-06-28 22:24:02 +00:00
|
|
|
, public nsIDNSListener
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2011-02-24 13:10:08 +00:00
|
|
|
enum State {
|
|
|
|
SOCKS_INITIAL,
|
2012-06-28 22:24:02 +00:00
|
|
|
SOCKS_DNS_IN_PROGRESS,
|
|
|
|
SOCKS_DNS_COMPLETE,
|
2011-02-24 13:10:08 +00:00
|
|
|
SOCKS_CONNECTING_TO_PROXY,
|
|
|
|
SOCKS4_WRITE_CONNECT_REQUEST,
|
|
|
|
SOCKS4_READ_CONNECT_RESPONSE,
|
|
|
|
SOCKS5_WRITE_AUTH_REQUEST,
|
|
|
|
SOCKS5_READ_AUTH_RESPONSE,
|
|
|
|
SOCKS5_WRITE_CONNECT_REQUEST,
|
|
|
|
SOCKS5_READ_CONNECT_RESPONSE_TOP,
|
|
|
|
SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
|
|
|
|
SOCKS_CONNECTED,
|
|
|
|
SOCKS_FAILED
|
|
|
|
};
|
|
|
|
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
// A buffer of 265 bytes should be enough for any request and response
|
2011-02-24 13:10:08 +00:00
|
|
|
// in case of SOCKS4 as well as SOCKS5
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
static const uint32_t BUFFER_SIZE = 265;
|
2012-08-22 15:56:38 +00:00
|
|
|
static const uint32_t MAX_HOSTNAME_LEN = 255;
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
public:
|
|
|
|
nsSOCKSSocketInfo();
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2013-07-19 02:24:13 +00:00
|
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
2000-07-01 10:25:25 +00:00
|
|
|
NS_DECL_NSISOCKSSOCKETINFO
|
2012-06-28 22:24:02 +00:00
|
|
|
NS_DECL_NSIDNSLISTENER
|
2003-09-11 20:32:33 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
void Init(int32_t version,
|
|
|
|
int32_t family,
|
2004-12-16 02:46:12 +00:00
|
|
|
const char *proxyHost,
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t proxyPort,
|
2004-12-16 02:46:12 +00:00
|
|
|
const char *destinationHost,
|
2012-08-22 15:56:38 +00:00
|
|
|
uint32_t flags);
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
void SetConnectTimeout(PRIntervalTime to);
|
2012-08-22 15:56:38 +00:00
|
|
|
PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1);
|
|
|
|
int16_t GetPollFlags() const;
|
2011-02-24 13:10:08 +00:00
|
|
|
bool IsConnected() const { return mState == SOCKS_CONNECTED; }
|
2012-10-31 21:17:29 +00:00
|
|
|
void ForgetFD() { mFD = nullptr; }
|
2011-02-24 13:10:08 +00:00
|
|
|
|
|
|
|
private:
|
2014-06-24 16:36:44 +00:00
|
|
|
virtual ~nsSOCKSSocketInfo() { HandshakeFinished(); }
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
void HandshakeFinished(PRErrorCode err = 0);
|
2012-06-28 22:24:02 +00:00
|
|
|
PRStatus StartDNS(PRFileDesc *fd);
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus ConnectToProxy(PRFileDesc *fd);
|
2012-12-23 21:08:43 +00:00
|
|
|
void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy);
|
2012-08-22 15:56:38 +00:00
|
|
|
PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags);
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus WriteV4ConnectRequest();
|
|
|
|
PRStatus ReadV4ConnectResponse();
|
|
|
|
PRStatus WriteV5AuthRequest();
|
|
|
|
PRStatus ReadV5AuthResponse();
|
|
|
|
PRStatus WriteV5ConnectRequest();
|
2012-08-22 15:56:38 +00:00
|
|
|
PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len);
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus ReadV5ConnectResponseTop();
|
|
|
|
PRStatus ReadV5ConnectResponseBottom();
|
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
uint8_t ReadUint8();
|
|
|
|
uint16_t ReadUint16();
|
|
|
|
uint32_t ReadUint32();
|
2012-12-23 21:08:43 +00:00
|
|
|
void ReadNetAddr(NetAddr *addr, uint16_t fam);
|
|
|
|
void ReadNetPort(NetAddr *addr);
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
void WantRead(uint32_t sz);
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus ReadFromSocket(PRFileDesc *fd);
|
|
|
|
PRStatus WriteToSocket(PRFileDesc *fd);
|
2003-09-11 20:32:33 +00:00
|
|
|
|
|
|
|
private:
|
2011-02-24 13:10:08 +00:00
|
|
|
State mState;
|
2012-08-22 15:56:38 +00:00
|
|
|
uint8_t * mData;
|
|
|
|
uint8_t * mDataIoPtr;
|
|
|
|
uint32_t mDataLength;
|
|
|
|
uint32_t mReadOffset;
|
|
|
|
uint32_t mAmountToRead;
|
2012-06-28 22:24:02 +00:00
|
|
|
nsCOMPtr<nsIDNSRecord> mDnsRec;
|
|
|
|
nsCOMPtr<nsICancelable> mLookup;
|
|
|
|
nsresult mLookupStatus;
|
|
|
|
PRFileDesc *mFD;
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2004-12-16 02:46:12 +00:00
|
|
|
nsCString mDestinationHost;
|
2003-09-11 20:32:33 +00:00
|
|
|
nsCString mProxyHost;
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t mProxyPort;
|
|
|
|
int32_t mVersion; // SOCKS version 4 or 5
|
|
|
|
int32_t mDestinationFamily;
|
|
|
|
uint32_t mFlags;
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr mInternalProxyAddr;
|
|
|
|
NetAddr mExternalProxyAddr;
|
|
|
|
NetAddr mDestinationAddr;
|
2011-02-24 13:10:08 +00:00
|
|
|
PRIntervalTime mTimeout;
|
2000-06-16 17:53:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
nsSOCKSSocketInfo::nsSOCKSSocketInfo()
|
2011-02-24 13:10:08 +00:00
|
|
|
: mState(SOCKS_INITIAL)
|
2012-07-30 14:20:58 +00:00
|
|
|
, mDataIoPtr(nullptr)
|
2011-02-24 13:10:08 +00:00
|
|
|
, mDataLength(0)
|
|
|
|
, mReadOffset(0)
|
|
|
|
, mAmountToRead(0)
|
|
|
|
, mProxyPort(-1)
|
2003-09-11 20:32:33 +00:00
|
|
|
, mVersion(-1)
|
2012-12-23 21:08:43 +00:00
|
|
|
, mDestinationFamily(AF_INET)
|
2004-12-16 02:46:12 +00:00
|
|
|
, mFlags(0)
|
2011-02-24 13:10:08 +00:00
|
|
|
, mTimeout(PR_INTERVAL_NO_TIMEOUT)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
mData = new uint8_t[BUFFER_SIZE];
|
2012-12-23 21:08:43 +00:00
|
|
|
|
|
|
|
mInternalProxyAddr.raw.family = AF_INET;
|
|
|
|
mInternalProxyAddr.inet.ip = htonl(INADDR_ANY);
|
|
|
|
mInternalProxyAddr.inet.port = htons(0);
|
|
|
|
|
|
|
|
mExternalProxyAddr.raw.family = AF_INET;
|
|
|
|
mExternalProxyAddr.inet.ip = htonl(INADDR_ANY);
|
|
|
|
mExternalProxyAddr.inet.port = htons(0);
|
|
|
|
|
|
|
|
mDestinationAddr.raw.family = AF_INET;
|
|
|
|
mDestinationAddr.inet.ip = htonl(INADDR_ANY);
|
|
|
|
mDestinationAddr.inet.port = htons(0);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
/* Helper template class to statically check that writes to a fixed-size
|
|
|
|
* buffer are not going to overflow.
|
|
|
|
*
|
|
|
|
* Example usage:
|
|
|
|
* uint8_t real_buf[TOTAL_SIZE];
|
|
|
|
* Buffer<TOTAL_SIZE> buf(&real_buf);
|
|
|
|
* auto buf2 = buf.WriteUint16(1);
|
|
|
|
* auto buf3 = buf2.WriteUint8(2);
|
|
|
|
*
|
|
|
|
* It is possible to chain them, to limit the number of (error-prone)
|
|
|
|
* intermediate variables:
|
|
|
|
* auto buf = Buffer<TOTAL_SIZE>(&real_buf)
|
|
|
|
* .WriteUint16(1)
|
|
|
|
* .WriteUint8(2);
|
|
|
|
*
|
|
|
|
* Debug builds assert when intermediate variables are reused:
|
|
|
|
* Buffer<TOTAL_SIZE> buf(&real_buf);
|
|
|
|
* auto buf2 = buf.WriteUint16(1);
|
|
|
|
* auto buf3 = buf.WriteUint8(2); // Asserts
|
|
|
|
*
|
|
|
|
* Strings can be written, given an explicit maximum length.
|
|
|
|
* buf.WriteString<MAX_STRING_LENGTH>(str);
|
|
|
|
*
|
|
|
|
* The Written() method returns how many bytes have been written so far:
|
|
|
|
* Buffer<TOTAL_SIZE> buf(&real_buf);
|
|
|
|
* auto buf2 = buf.WriteUint16(1);
|
|
|
|
* auto buf3 = buf2.WriteUint8(2);
|
|
|
|
* buf3.Written(); // returns 3.
|
|
|
|
*/
|
|
|
|
template <size_t Size>
|
|
|
|
class Buffer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Buffer() : mBuf(nullptr), mLength(0) {}
|
|
|
|
|
|
|
|
explicit Buffer(uint8_t* aBuf, size_t aLength=0)
|
|
|
|
: mBuf(aBuf), mLength(aLength) {}
|
|
|
|
|
|
|
|
template <size_t Size2>
|
|
|
|
Buffer(const Buffer<Size2>& aBuf) : mBuf(aBuf.mBuf), mLength(aBuf.mLength) {
|
|
|
|
static_assert(Size2 > Size, "Cannot cast buffer");
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer<Size - sizeof(uint8_t)> WriteUint8(uint8_t aValue) {
|
|
|
|
return Write(aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer<Size - sizeof(uint16_t)> WriteUint16(uint16_t aValue) {
|
|
|
|
return Write(aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer<Size - sizeof(uint32_t)> WriteUint32(uint32_t aValue) {
|
|
|
|
return Write(aValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer<Size - sizeof(uint16_t)> WriteNetPort(const NetAddr* aAddr) {
|
|
|
|
return WriteUint16(aAddr->inet.port);
|
|
|
|
}
|
|
|
|
|
|
|
|
Buffer<Size - sizeof(IPv6Addr)> WriteNetAddr(const NetAddr* aAddr) {
|
|
|
|
if (aAddr->raw.family == AF_INET) {
|
|
|
|
return Write(aAddr->inet.ip);
|
|
|
|
} else if (aAddr->raw.family == AF_INET6) {
|
|
|
|
return Write(aAddr->inet6.ip.u8);
|
|
|
|
}
|
|
|
|
NS_NOTREACHED("Unknown address family");
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <size_t MaxLength>
|
|
|
|
Buffer<Size - MaxLength> WriteString(const nsACString& aStr) {
|
|
|
|
if (aStr.Length() > MaxLength) {
|
|
|
|
return Buffer<Size - MaxLength>(nullptr);
|
|
|
|
}
|
|
|
|
return WritePtr<char, MaxLength>(aStr.Data(), aStr.Length());
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Written() {
|
|
|
|
MOZ_ASSERT(mBuf);
|
|
|
|
return mLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool() { return !!mBuf; }
|
|
|
|
private:
|
|
|
|
template <size_t Size2>
|
|
|
|
friend class Buffer;
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
Buffer<Size - sizeof(T)> Write(T& aValue) {
|
|
|
|
return WritePtr<T, sizeof(T)>(&aValue, sizeof(T));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, size_t Length>
|
|
|
|
Buffer<Size - Length> WritePtr(const T* aValue, size_t aCopyLength) {
|
|
|
|
static_assert(Size >= Length, "Cannot write that much");
|
|
|
|
MOZ_ASSERT(aCopyLength <= Length);
|
|
|
|
MOZ_ASSERT(mBuf);
|
|
|
|
memcpy(mBuf, aValue, aCopyLength);
|
|
|
|
Buffer<Size - Length> result(mBuf + aCopyLength, mLength + aCopyLength);
|
|
|
|
mBuf = nullptr;
|
|
|
|
mLength = 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* mBuf;
|
|
|
|
size_t mLength;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2003-09-11 20:32:33 +00:00
|
|
|
void
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSSocketInfo::Init(int32_t version, int32_t family, const char *proxyHost, int32_t proxyPort, const char *host, uint32_t flags)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2004-12-16 02:46:12 +00:00
|
|
|
mVersion = version;
|
2012-07-21 00:19:37 +00:00
|
|
|
mDestinationFamily = family;
|
2004-12-16 02:46:12 +00:00
|
|
|
mProxyHost = proxyHost;
|
|
|
|
mProxyPort = proxyPort;
|
|
|
|
mDestinationHost = host;
|
|
|
|
mFlags = flags;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2014-04-27 07:06:00 +00:00
|
|
|
NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
|
2000-06-16 17:53:22 +00:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr *aExternalProxyAddr)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::GetDestinationAddr(NetAddr * *aDestinationAddr)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::SetDestinationAddr(NetAddr *aDestinationAddr)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr * *aInternalProxyAddr)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr *aInternalProxyAddr)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// There needs to be a means of distinguishing between connection errors
|
|
|
|
// that the SOCKS server reports when it rejects a connection request, and
|
|
|
|
// connection errors that happen while attempting to connect to the SOCKS
|
|
|
|
// server. Otherwise, Firefox will report incorrectly that the proxy server
|
|
|
|
// is refusing connections when a SOCKS request is rejected by the proxy.
|
|
|
|
// When a SOCKS handshake failure occurs, the PR error is set to
|
|
|
|
// PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
|
|
|
|
void
|
|
|
|
nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
2011-02-24 13:10:08 +00:00
|
|
|
if (err == 0) {
|
|
|
|
mState = SOCKS_CONNECTED;
|
2011-03-24 08:37:53 +00:00
|
|
|
} else {
|
2011-02-24 13:10:08 +00:00
|
|
|
mState = SOCKS_FAILED;
|
|
|
|
PR_SetError(PR_UNKNOWN_ERROR, err);
|
2000-07-01 10:25:25 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// We don't need the buffer any longer, so free it.
|
|
|
|
delete [] mData;
|
2012-07-30 14:20:58 +00:00
|
|
|
mData = nullptr;
|
|
|
|
mDataIoPtr = nullptr;
|
2011-02-24 13:10:08 +00:00
|
|
|
mDataLength = 0;
|
|
|
|
mReadOffset = 0;
|
|
|
|
mAmountToRead = 0;
|
2012-06-28 22:24:02 +00:00
|
|
|
if (mLookup) {
|
|
|
|
mLookup->Cancel(NS_ERROR_FAILURE);
|
2012-07-30 14:20:58 +00:00
|
|
|
mLookup = nullptr;
|
2012-06-28 22:24:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::StartDNS(PRFileDesc *fd)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(!mDnsRec && mState == SOCKS_INITIAL,
|
|
|
|
"Must be in initial state to make DNS Lookup");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
|
|
|
if (!dns)
|
|
|
|
return PR_FAILURE;
|
|
|
|
|
|
|
|
mFD = fd;
|
|
|
|
nsresult rv = dns->AsyncResolve(mProxyHost, 0, this,
|
|
|
|
NS_GetCurrentThread(),
|
|
|
|
getter_AddRefs(mLookup));
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed",
|
|
|
|
mProxyHost.get()));
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
mState = SOCKS_DNS_IN_PROGRESS;
|
|
|
|
PR_SetError(PR_IN_PROGRESS_ERROR, 0);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsSOCKSSocketInfo::OnLookupComplete(nsICancelable *aRequest,
|
|
|
|
nsIDNSRecord *aRecord,
|
|
|
|
nsresult aStatus)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(aRequest == mLookup, "wrong DNS query");
|
2012-07-30 14:20:58 +00:00
|
|
|
mLookup = nullptr;
|
2012-06-28 22:24:02 +00:00
|
|
|
mLookupStatus = aStatus;
|
|
|
|
mDnsRec = aRecord;
|
|
|
|
mState = SOCKS_DNS_COMPLETE;
|
2012-10-31 21:17:29 +00:00
|
|
|
if (mFD) {
|
|
|
|
ConnectToProxy(mFD);
|
|
|
|
ForgetFD();
|
|
|
|
}
|
2012-06-28 22:24:02 +00:00
|
|
|
return NS_OK;
|
2011-02-24 13:10:08 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
|
|
|
|
{
|
|
|
|
PRStatus status;
|
|
|
|
nsresult rv;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-06-28 22:24:02 +00:00
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS_DNS_COMPLETE,
|
|
|
|
"Must have DNS to make connection!");
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-06-28 22:24:02 +00:00
|
|
|
if (NS_FAILED(mLookupStatus)) {
|
|
|
|
PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
|
|
|
|
return PR_FAILURE;
|
2000-07-01 10:25:25 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-07-21 00:19:37 +00:00
|
|
|
// Try socks5 if the destination addrress is IPv6
|
|
|
|
if (mVersion == 4 &&
|
2012-12-23 21:08:43 +00:00
|
|
|
mDestinationAddr.raw.family == AF_INET6) {
|
2012-07-21 00:19:37 +00:00
|
|
|
mVersion = 5;
|
|
|
|
}
|
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t addresses = 0;
|
2011-02-24 13:10:08 +00:00
|
|
|
do {
|
2011-07-21 13:18:01 +00:00
|
|
|
if (addresses++)
|
|
|
|
mDnsRec->ReportUnusable(mProxyPort);
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
rv = mDnsRec->GetNextAddr(mProxyPort, &mInternalProxyAddr);
|
|
|
|
// No more addresses to try? If so, we'll need to bail
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
|
|
|
|
mProxyHost.get()));
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
#if defined(PR_LOGGING)
|
2012-12-23 21:08:43 +00:00
|
|
|
char buf[kIPv6CStrBufSize];
|
|
|
|
NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf));
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks: trying proxy server, %s:%hu",
|
2012-12-23 21:08:43 +00:00
|
|
|
buf, ntohs(mInternalProxyAddr.inet.port)));
|
2011-02-24 13:10:08 +00:00
|
|
|
#endif
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr proxy = mInternalProxyAddr;
|
2012-07-21 00:19:37 +00:00
|
|
|
FixupAddressFamily(fd, &proxy);
|
2012-12-23 21:08:43 +00:00
|
|
|
PRNetAddr prProxy;
|
|
|
|
NetAddrToPRNetAddr(&proxy, &prProxy);
|
|
|
|
status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout);
|
2011-02-24 13:10:08 +00:00
|
|
|
if (status != PR_SUCCESS) {
|
|
|
|
PRErrorCode c = PR_GetError();
|
|
|
|
// If EINPROGRESS, return now and check back later after polling
|
|
|
|
if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
|
|
|
|
mState = SOCKS_CONNECTING_TO_PROXY;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (status != PR_SUCCESS);
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Connected now, start SOCKS
|
|
|
|
if (mVersion == 4)
|
|
|
|
return WriteV4ConnectRequest();
|
|
|
|
return WriteV5AuthRequest();
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2012-07-21 00:19:37 +00:00
|
|
|
void
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy)
|
2012-07-21 00:19:37 +00:00
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
int32_t proxyFamily = mInternalProxyAddr.raw.family;
|
2012-07-21 00:19:37 +00:00
|
|
|
// 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.
|
2012-12-23 21:08:43 +00:00
|
|
|
if (proxyFamily == AF_INET6 && !ipv6Supported) {
|
2012-07-21 00:19:37 +00:00
|
|
|
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
|
2012-12-23 21:08:43 +00:00
|
|
|
if (mDestinationFamily == AF_INET6 && !ipv6Supported) {
|
|
|
|
proxy->inet6.family = AF_INET6;
|
|
|
|
proxy->inet6.port = mInternalProxyAddr.inet.port;
|
|
|
|
uint8_t *proxyp = proxy->inet6.ip.u8;
|
2012-07-21 00:19:37 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
|
|
|
PRStatus status;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
|
|
|
|
"Continuing connection in wrong state!");
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks: continuing connection to proxy"));
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
status = fd->lower->methods->connectcontinue(fd->lower, oflags);
|
|
|
|
if (status != PR_SUCCESS) {
|
|
|
|
PRErrorCode c = PR_GetError();
|
|
|
|
if (c != PR_WOULD_BLOCK_ERROR && c != PR_IN_PROGRESS_ERROR) {
|
|
|
|
// A connection failure occured, try another address
|
2012-06-28 22:24:02 +00:00
|
|
|
mState = SOCKS_DNS_COMPLETE;
|
2011-02-24 13:10:08 +00:00
|
|
|
return ConnectToProxy(fd);
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// We're still connecting
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Connected now, start SOCKS
|
|
|
|
if (mVersion == 4)
|
|
|
|
return WriteV4ConnectRequest();
|
|
|
|
return WriteV5AuthRequest();
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::WriteV4ConnectRequest()
|
|
|
|
{
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr *addr = &mDestinationAddr;
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t proxy_resolve;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
|
|
|
|
"Invalid state!");
|
|
|
|
|
|
|
|
proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
|
|
|
|
|
|
|
|
mDataLength = 0;
|
|
|
|
mState = SOCKS4_WRITE_CONNECT_REQUEST;
|
|
|
|
|
|
|
|
LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
|
|
|
|
proxy_resolve? "yes" : "no"));
|
|
|
|
|
|
|
|
// Send a SOCKS 4 connect request.
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
auto buf = Buffer<BUFFER_SIZE>(mData)
|
|
|
|
.WriteUint8(0x04) // version -- 4
|
|
|
|
.WriteUint8(0x01) // command -- connect
|
|
|
|
.WriteNetPort(addr);
|
|
|
|
|
|
|
|
// We don't have anything more to write after the if, so we can
|
|
|
|
// use a buffer with no further writes allowed.
|
|
|
|
Buffer<0> buf3;
|
2011-02-24 13:10:08 +00:00
|
|
|
if (proxy_resolve) {
|
|
|
|
// Add the full name, null-terminated, to the request
|
|
|
|
// according to SOCKS 4a. A fake IP address, with the first
|
|
|
|
// four bytes set to 0 and the last byte set to something other
|
|
|
|
// than 0, is used to notify the proxy that this is a SOCKS 4a
|
|
|
|
// request. This request type works for Tor and perhaps others.
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
auto buf2 = buf.WriteUint32(htonl(0x00000001)) // Fake IP
|
|
|
|
.WriteUint8(0x00) // Send an emtpy username
|
|
|
|
.WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
|
|
|
|
if (!buf2) {
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGERROR(("socks4: destination host name is too long!"));
|
|
|
|
HandshakeFinished(PR_BAD_ADDRESS_ERROR);
|
|
|
|
return PR_FAILURE;
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
buf3 = buf2.WriteUint8(0x00);
|
2012-12-23 21:08:43 +00:00
|
|
|
} else if (addr->raw.family == AF_INET) {
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
buf3 = buf.WriteNetAddr(addr) // Add the IPv4 address
|
|
|
|
.WriteUint8(0x00); // Send an emtpy username
|
|
|
|
} else {
|
|
|
|
LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
|
2011-02-24 13:10:08 +00:00
|
|
|
HandshakeFinished(PR_BAD_ADDRESS_ERROR);
|
|
|
|
return PR_FAILURE;
|
2011-02-24 13:10:08 +00:00
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
mDataLength = buf3.Written();
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::ReadV4ConnectResponse()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS4_READ_CONNECT_RESPONSE,
|
|
|
|
"Handling SOCKS 4 connection reply in wrong state!");
|
|
|
|
NS_ABORT_IF_FALSE(mDataLength == 8,
|
|
|
|
"SOCKS 4 connection reply must be 8 bytes!");
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks4: checking connection reply"));
|
|
|
|
|
|
|
|
if (ReadUint8() != 0x00) {
|
|
|
|
LOGERROR(("socks4: wrong connection reply"));
|
|
|
|
HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
|
|
|
|
return PR_FAILURE;
|
2011-02-24 13:10:08 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// See if our connection request was granted
|
|
|
|
if (ReadUint8() == 90) {
|
|
|
|
LOGDEBUG(("socks4: connection successful!"));
|
|
|
|
HandshakeFinished();
|
|
|
|
return PR_SUCCESS;
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGERROR(("socks4: unable to connect"));
|
|
|
|
HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::WriteV5AuthRequest()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
mState = SOCKS5_WRITE_AUTH_REQUEST;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Send an initial SOCKS 5 greeting
|
|
|
|
LOGDEBUG(("socks5: sending auth methods"));
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
mDataLength = Buffer<BUFFER_SIZE>(mData)
|
|
|
|
.WriteUint8(0x05) // version -- 5
|
|
|
|
.WriteUint8(0x01) // # auth methods -- 1
|
|
|
|
.WriteUint8(0x00) // we don't support authentication
|
|
|
|
.Written();
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::ReadV5AuthResponse()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE,
|
|
|
|
"Handling SOCKS 5 auth method reply in wrong state!");
|
|
|
|
NS_ABORT_IF_FALSE(mDataLength == 2,
|
|
|
|
"SOCKS 5 auth method reply must be 2 bytes!");
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks5: checking auth method reply"));
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Check version number
|
|
|
|
if (ReadUint8() != 0x05) {
|
|
|
|
LOGERROR(("socks5: unexpected version in the reply"));
|
|
|
|
HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Make sure our authentication choice was accepted
|
|
|
|
if (ReadUint8() != 0x00) {
|
|
|
|
LOGERROR(("socks5: server did not accept our authentication method"));
|
|
|
|
HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return WriteV5ConnectRequest();
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::WriteV5ConnectRequest()
|
|
|
|
{
|
|
|
|
// Send SOCKS 5 connect request
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr *addr = &mDestinationAddr;
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t proxy_resolve;
|
2011-02-24 13:10:08 +00:00
|
|
|
proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
|
|
|
|
|
|
|
|
LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
|
|
|
|
proxy_resolve? "yes" : "no"));
|
|
|
|
|
|
|
|
mDataLength = 0;
|
|
|
|
mState = SOCKS5_WRITE_CONNECT_REQUEST;
|
|
|
|
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
auto buf = Buffer<BUFFER_SIZE>(mData)
|
|
|
|
.WriteUint8(0x05) // version -- 5
|
|
|
|
.WriteUint8(0x01) // command -- connect
|
|
|
|
.WriteUint8(0x00); // reserved
|
2011-02-24 13:10:08 +00:00
|
|
|
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
// We're writing a net port after the if, so we need a buffer allowing
|
|
|
|
// to write that much.
|
|
|
|
Buffer<sizeof(uint16_t)> buf2;
|
2011-02-24 13:10:08 +00:00
|
|
|
// Add the address to the SOCKS 5 request. SOCKS 5 supports several
|
|
|
|
// address types, so we pick the one that works best for us.
|
|
|
|
if (proxy_resolve) {
|
|
|
|
// Add the host name. Only a single byte is used to store the length,
|
|
|
|
// so we must prevent long names from being used.
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
buf2 = buf.WriteUint8(0x03) // addr type -- domainname
|
|
|
|
.WriteUint8(mDestinationHost.Length()) // name length
|
|
|
|
.WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
|
|
|
|
if (!buf2) {
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGERROR(("socks5: destination host name is too long!"));
|
|
|
|
HandshakeFinished(PR_BAD_ADDRESS_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2012-12-23 21:08:43 +00:00
|
|
|
} else if (addr->raw.family == AF_INET) {
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
buf2 = buf.WriteUint8(0x01) // addr type -- IPv4
|
|
|
|
.WriteNetAddr(addr);
|
2012-12-23 21:08:43 +00:00
|
|
|
} else if (addr->raw.family == AF_INET6) {
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
buf2 = buf.WriteUint8(0x04) // addr type -- IPv6
|
|
|
|
.WriteNetAddr(addr);
|
2011-02-24 13:10:08 +00:00
|
|
|
} else {
|
|
|
|
LOGERROR(("socks5: destination address of unknown type!"));
|
|
|
|
HandshakeFinished(PR_BAD_ADDRESS_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
Bug 1102022 - Increase the SOCKS I/O buffer size to avoid buffer overflows. r=mcmanus
This also adds static checks that buffer overflows do not sneak in again in
the future.
Interestingly, this also makes (at least) GCC generate more efficient code.
For example, before, writing to the buffer in WriteV5AuthRequest would look
like this:
mov 0x38(%rbx),%eax
mov 0x28(%rbx),%rcx
movb $0x5,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x1,(%rcx,%rax,1)
mov 0x38(%rbx),%eax
inc %eax
mov %eax,0x38(%rbx)
mov 0x28(%rbx),%rcx
movb $0x0,(%rcx,%rax,1)
incl 0x38(%rbx)
Now it looks like this:
mov 0x28(%rbx),%rax
movb $0x5,(%rax)
movb $0x1,0x1(%rax)
movb $0x0,0x2(%rax)
movl $0x3,0x38(%rbx)
2014-11-24 23:46:59 +00:00
|
|
|
auto buf3 = buf2.WriteNetPort(addr); // port
|
|
|
|
mDataLength = buf3.Written();
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
|
|
|
|
mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
|
|
|
|
"Invalid state!");
|
|
|
|
NS_ABORT_IF_FALSE(mDataLength >= 5,
|
|
|
|
"SOCKS 5 connection reply must be at least 5 bytes!");
|
|
|
|
|
|
|
|
// Seek to the address location
|
|
|
|
mReadOffset = 3;
|
|
|
|
|
|
|
|
*type = ReadUint8();
|
|
|
|
|
|
|
|
switch (*type) {
|
|
|
|
case 0x01: // ipv4
|
|
|
|
*len = 4 - 1;
|
|
|
|
break;
|
|
|
|
case 0x04: // ipv6
|
|
|
|
*len = 16 - 1;
|
2011-03-24 08:37:53 +00:00
|
|
|
break;
|
2011-02-24 13:10:08 +00:00
|
|
|
case 0x03: // fqdn
|
|
|
|
*len = ReadUint8();
|
2011-03-24 08:37:53 +00:00
|
|
|
break;
|
2011-02-24 13:10:08 +00:00
|
|
|
default: // wrong address type
|
|
|
|
LOGERROR(("socks5: wrong address type in connection reply!"));
|
|
|
|
return PR_FAILURE;
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
2011-02-24 13:10:08 +00:00
|
|
|
|
|
|
|
return PR_SUCCESS;
|
2011-02-24 13:10:08 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::ReadV5ConnectResponseTop()
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
uint8_t res;
|
|
|
|
uint32_t len;
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP,
|
|
|
|
"Invalid state!");
|
|
|
|
NS_ABORT_IF_FALSE(mDataLength == 5,
|
|
|
|
"SOCKS 5 connection reply must be exactly 5 bytes!");
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks5: checking connection reply"));
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Check version number
|
|
|
|
if (ReadUint8() != 0x05) {
|
|
|
|
LOGERROR(("socks5: unexpected version in the reply"));
|
|
|
|
HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// Check response
|
|
|
|
res = ReadUint8();
|
|
|
|
if (res != 0x00) {
|
|
|
|
PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
|
|
|
|
|
|
|
|
switch (res) {
|
|
|
|
case 0x01:
|
|
|
|
LOGERROR(("socks5: connect failed: "
|
|
|
|
"01, General SOCKS server failure."));
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
LOGERROR(("socks5: connect failed: "
|
|
|
|
"02, Connection not allowed by ruleset."));
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
LOGERROR(("socks5: connect failed: 03, Network unreachable."));
|
|
|
|
c = PR_NETWORK_UNREACHABLE_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
LOGERROR(("socks5: connect failed: 04, Host unreachable."));
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
LOGERROR(("socks5: connect failed: 05, Connection refused."));
|
|
|
|
break;
|
|
|
|
case 0x06:
|
|
|
|
LOGERROR(("socks5: connect failed: 06, TTL expired."));
|
|
|
|
c = PR_CONNECT_TIMEOUT_ERROR;
|
|
|
|
break;
|
|
|
|
case 0x07:
|
|
|
|
LOGERROR(("socks5: connect failed: "
|
|
|
|
"07, Command not supported."));
|
|
|
|
break;
|
|
|
|
case 0x08:
|
|
|
|
LOGERROR(("socks5: connect failed: "
|
|
|
|
"08, Address type not supported."));
|
|
|
|
c = PR_BAD_ADDRESS_ERROR;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOGERROR(("socks5: connect failed."));
|
|
|
|
break;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
HandshakeFinished(c);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
|
|
|
|
HandshakeFinished(PR_BAD_ADDRESS_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
|
|
|
|
WantRead(len + 2);
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::ReadV5ConnectResponseBottom()
|
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
uint8_t type;
|
|
|
|
uint32_t len;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
|
|
|
|
"Invalid state!");
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
|
|
|
|
HandshakeFinished(PR_BAD_ADDRESS_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mDataLength == 7+len,
|
|
|
|
"SOCKS 5 unexpected length of connection reply!");
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks5: loading source addr and port"));
|
|
|
|
// Read what the proxy says is our source address
|
|
|
|
switch (type) {
|
|
|
|
case 0x01: // ipv4
|
2012-12-23 21:08:43 +00:00
|
|
|
ReadNetAddr(&mExternalProxyAddr, AF_INET);
|
2011-02-24 13:10:08 +00:00
|
|
|
break;
|
|
|
|
case 0x04: // ipv6
|
2012-12-23 21:08:43 +00:00
|
|
|
ReadNetAddr(&mExternalProxyAddr, AF_INET6);
|
2011-02-24 13:10:08 +00:00
|
|
|
break;
|
|
|
|
case 0x03: // fqdn (skip)
|
|
|
|
mReadOffset += len;
|
2012-12-23 21:08:43 +00:00
|
|
|
mExternalProxyAddr.raw.family = AF_INET;
|
2011-02-24 13:10:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
ReadNetPort(&mExternalProxyAddr);
|
2003-09-11 20:32:33 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks5: connected!"));
|
|
|
|
HandshakeFinished();
|
2003-09-11 20:32:33 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
void
|
|
|
|
nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to)
|
|
|
|
{
|
|
|
|
mTimeout = to;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
|
|
|
LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
|
|
|
|
|
|
|
|
switch (mState) {
|
|
|
|
case SOCKS_INITIAL:
|
2012-06-28 22:24:02 +00:00
|
|
|
return StartDNS(fd);
|
|
|
|
case SOCKS_DNS_IN_PROGRESS:
|
|
|
|
PR_SetError(PR_IN_PROGRESS_ERROR, 0);
|
|
|
|
return PR_FAILURE;
|
|
|
|
case SOCKS_DNS_COMPLETE:
|
2011-02-24 13:10:08 +00:00
|
|
|
return ConnectToProxy(fd);
|
|
|
|
case SOCKS_CONNECTING_TO_PROXY:
|
|
|
|
return ContinueConnectingToProxy(fd, oflags);
|
|
|
|
case SOCKS4_WRITE_CONNECT_REQUEST:
|
|
|
|
if (WriteToSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
WantRead(8);
|
|
|
|
mState = SOCKS4_READ_CONNECT_RESPONSE;
|
|
|
|
return PR_SUCCESS;
|
|
|
|
case SOCKS4_READ_CONNECT_RESPONSE:
|
|
|
|
if (ReadFromSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
return ReadV4ConnectResponse();
|
|
|
|
|
|
|
|
case SOCKS5_WRITE_AUTH_REQUEST:
|
|
|
|
if (WriteToSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
WantRead(2);
|
|
|
|
mState = SOCKS5_READ_AUTH_RESPONSE;
|
|
|
|
return PR_SUCCESS;
|
|
|
|
case SOCKS5_READ_AUTH_RESPONSE:
|
|
|
|
if (ReadFromSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
return ReadV5AuthResponse();
|
|
|
|
case SOCKS5_WRITE_CONNECT_REQUEST:
|
|
|
|
if (WriteToSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
|
|
|
|
// The SOCKS 5 response to the connection request is variable
|
|
|
|
// length. First, we'll read enough to tell how long the response
|
|
|
|
// is, and will read the rest later.
|
|
|
|
WantRead(5);
|
|
|
|
mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
|
|
|
|
return PR_SUCCESS;
|
|
|
|
case SOCKS5_READ_CONNECT_RESPONSE_TOP:
|
|
|
|
if (ReadFromSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
return ReadV5ConnectResponseTop();
|
|
|
|
case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
|
|
|
|
if (ReadFromSocket(fd) != PR_SUCCESS)
|
|
|
|
return PR_FAILURE;
|
|
|
|
return ReadV5ConnectResponseBottom();
|
|
|
|
|
|
|
|
case SOCKS_CONNECTED:
|
|
|
|
LOGERROR(("socks: already connected"));
|
|
|
|
HandshakeFinished(PR_IS_CONNECTED_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
case SOCKS_FAILED:
|
|
|
|
LOGERROR(("socks: already failed"));
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGERROR(("socks: executing handshake in invalid state, %d", mState));
|
|
|
|
HandshakeFinished(PR_INVALID_STATE_ERROR);
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
int16_t
|
2011-02-24 13:10:08 +00:00
|
|
|
nsSOCKSSocketInfo::GetPollFlags() const
|
|
|
|
{
|
|
|
|
switch (mState) {
|
2012-06-28 22:24:02 +00:00
|
|
|
case SOCKS_DNS_IN_PROGRESS:
|
|
|
|
case SOCKS_DNS_COMPLETE:
|
2011-02-24 13:10:08 +00:00
|
|
|
case SOCKS_CONNECTING_TO_PROXY:
|
|
|
|
return PR_POLL_EXCEPT | PR_POLL_WRITE;
|
|
|
|
case SOCKS4_WRITE_CONNECT_REQUEST:
|
|
|
|
case SOCKS5_WRITE_AUTH_REQUEST:
|
|
|
|
case SOCKS5_WRITE_CONNECT_REQUEST:
|
|
|
|
return PR_POLL_WRITE;
|
|
|
|
case SOCKS4_READ_CONNECT_RESPONSE:
|
|
|
|
case SOCKS5_READ_AUTH_RESPONSE:
|
|
|
|
case SOCKS5_READ_CONNECT_RESPONSE_TOP:
|
|
|
|
case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
|
|
|
|
return PR_POLL_READ;
|
|
|
|
default:
|
|
|
|
break;
|
2001-07-25 00:28:28 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
inline uint8_t
|
2011-02-24 13:10:08 +00:00
|
|
|
nsSOCKSSocketInfo::ReadUint8()
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
uint8_t rv;
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
|
2012-10-11 23:38:04 +00:00
|
|
|
"Not enough space to pop a uint8_t!");
|
2011-02-24 13:10:08 +00:00
|
|
|
rv = mData[mReadOffset];
|
|
|
|
mReadOffset += sizeof(rv);
|
|
|
|
return rv;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
inline uint16_t
|
2011-02-24 13:10:08 +00:00
|
|
|
nsSOCKSSocketInfo::ReadUint16()
|
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
uint16_t rv;
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
|
2012-10-11 23:38:04 +00:00
|
|
|
"Not enough space to pop a uint16_t!");
|
2011-02-24 13:10:08 +00:00
|
|
|
memcpy(&rv, mData + mReadOffset, sizeof(rv));
|
|
|
|
mReadOffset += sizeof(rv);
|
|
|
|
return rv;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
inline uint32_t
|
2011-02-24 13:10:08 +00:00
|
|
|
nsSOCKSSocketInfo::ReadUint32()
|
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
uint32_t rv;
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
|
2012-10-11 23:38:04 +00:00
|
|
|
"Not enough space to pop a uint32_t!");
|
2011-02-24 13:10:08 +00:00
|
|
|
memcpy(&rv, mData + mReadOffset, sizeof(rv));
|
|
|
|
mReadOffset += sizeof(rv);
|
|
|
|
return rv;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
void
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
2012-08-25 18:19:00 +00:00
|
|
|
uint32_t amt = 0;
|
2012-08-22 15:56:38 +00:00
|
|
|
const uint8_t *ip = mData + mReadOffset;
|
2011-02-24 13:10:08 +00:00
|
|
|
|
|
|
|
addr->raw.family = fam;
|
2012-12-23 21:08:43 +00:00
|
|
|
if (fam == AF_INET) {
|
2011-02-24 13:10:08 +00:00
|
|
|
amt = sizeof(addr->inet.ip);
|
|
|
|
NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength,
|
|
|
|
"Not enough space to pop an ipv4 addr!");
|
|
|
|
memcpy(&addr->inet.ip, ip, amt);
|
2012-12-23 21:08:43 +00:00
|
|
|
} else if (fam == AF_INET6) {
|
|
|
|
amt = sizeof(addr->inet6.ip.u8);
|
2011-02-24 13:10:08 +00:00
|
|
|
NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength,
|
|
|
|
"Not enough space to pop an ipv6 addr!");
|
2012-12-23 21:08:43 +00:00
|
|
|
memcpy(addr->inet6.ip.u8, ip, amt);
|
2011-02-24 13:10:08 +00:00
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
mReadOffset += amt;
|
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
void
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSSocketInfo::ReadNetPort(NetAddr *addr)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
|
|
|
addr->inet.port = ReadUint16();
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
void
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSSocketInfo::WantRead(uint32_t sz)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
2013-09-19 19:29:27 +00:00
|
|
|
NS_ABORT_IF_FALSE(mDataIoPtr == nullptr,
|
2011-02-24 13:10:08 +00:00
|
|
|
"WantRead() called while I/O already in progress!");
|
|
|
|
NS_ABORT_IF_FALSE(mDataLength + sz <= BUFFER_SIZE,
|
|
|
|
"Can't read that much data!");
|
|
|
|
mAmountToRead = sz;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd)
|
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t rc;
|
|
|
|
const uint8_t *end;
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (!mAmountToRead) {
|
|
|
|
LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
|
|
|
|
return PR_SUCCESS;
|
2001-07-25 00:28:28 +00:00
|
|
|
}
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (!mDataIoPtr) {
|
|
|
|
mDataIoPtr = mData + mDataLength;
|
|
|
|
mDataLength += mAmountToRead;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
end = mData + mDataLength;
|
|
|
|
|
|
|
|
while (mDataIoPtr < end) {
|
|
|
|
rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
|
|
|
|
if (rc <= 0) {
|
|
|
|
if (rc == 0) {
|
|
|
|
LOGERROR(("socks: proxy server closed connection"));
|
|
|
|
HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
|
|
|
|
return PR_FAILURE;
|
|
|
|
} else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
|
|
|
|
LOGDEBUG(("socks: ReadFromSocket(), want read"));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
mDataIoPtr += rc;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
|
|
|
|
unsigned(mDataIoPtr - mData)));
|
|
|
|
if (mDataIoPtr == end) {
|
2012-07-30 14:20:58 +00:00
|
|
|
mDataIoPtr = nullptr;
|
2011-02-24 13:10:08 +00:00
|
|
|
mAmountToRead = 0;
|
|
|
|
mReadOffset = 0;
|
|
|
|
return PR_SUCCESS;
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
PRStatus
|
|
|
|
nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd)
|
|
|
|
{
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t rc;
|
|
|
|
const uint8_t *end;
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (!mDataLength) {
|
|
|
|
LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
|
|
|
|
return PR_SUCCESS;
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (!mDataIoPtr)
|
|
|
|
mDataIoPtr = mData;
|
|
|
|
|
|
|
|
end = mData + mDataLength;
|
|
|
|
|
|
|
|
while (mDataIoPtr < end) {
|
|
|
|
rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
|
|
|
|
if (rc < 0) {
|
|
|
|
if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
|
|
|
|
LOGDEBUG(("socks: WriteToSocket(), want write"));
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-02-24 13:10:08 +00:00
|
|
|
|
|
|
|
mDataIoPtr += rc;
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (mDataIoPtr == end) {
|
2012-07-30 14:20:58 +00:00
|
|
|
mDataIoPtr = nullptr;
|
2011-02-24 13:10:08 +00:00
|
|
|
mDataLength = 0;
|
|
|
|
mReadOffset = 0;
|
|
|
|
return PR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FAILURE;
|
|
|
|
}
|
2011-02-24 13:10:08 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
static PRStatus
|
|
|
|
nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
|
|
|
|
{
|
|
|
|
PRStatus status;
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr dst;
|
2001-07-25 00:28:28 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
|
2013-09-19 19:29:27 +00:00
|
|
|
if (info == nullptr) return PR_FAILURE;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-12-23 21:08:43 +00:00
|
|
|
if (addr->raw.family == PR_AF_INET6 &&
|
2011-02-24 13:10:08 +00:00
|
|
|
PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
|
2012-08-22 15:56:38 +00:00
|
|
|
const uint8_t *srcp;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
// copied from _PR_ConvertToIpv4NetAddr()
|
2012-12-23 21:08:43 +00:00
|
|
|
dst.raw.family = AF_INET;
|
|
|
|
dst.inet.ip = htonl(INADDR_ANY);
|
|
|
|
dst.inet.port = htons(0);
|
2011-02-24 13:10:08 +00:00
|
|
|
srcp = addr->ipv6.ip.pr_s6_addr;
|
|
|
|
memcpy(&dst.inet.ip, srcp + 12, 4);
|
2012-12-23 21:08:43 +00:00
|
|
|
dst.inet.family = AF_INET;
|
2011-02-24 13:10:08 +00:00
|
|
|
dst.inet.port = addr->ipv6.port;
|
|
|
|
} else {
|
|
|
|
memcpy(&dst, addr, sizeof(dst));
|
2011-03-24 08:37:53 +00:00
|
|
|
}
|
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
info->SetDestinationAddr(&dst);
|
|
|
|
info->SetConnectTimeout(to);
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
do {
|
|
|
|
status = info->DoHandshake(fd, -1);
|
|
|
|
} while (status == PR_SUCCESS && !info->IsConnected());
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRStatus
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, int16_t oflags)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
|
|
|
PRStatus status;
|
|
|
|
|
|
|
|
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
|
2013-09-19 19:29:27 +00:00
|
|
|
if (info == nullptr) return PR_FAILURE;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
do {
|
|
|
|
status = info->DoHandshake(fd, oflags);
|
|
|
|
} while (status == PR_SUCCESS && !info->IsConnected());
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return status;
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
static int16_t
|
|
|
|
nsSOCKSIOLayerPoll(PRFileDesc *fd, int16_t in_flags, int16_t *out_flags)
|
2011-02-24 13:10:08 +00:00
|
|
|
{
|
|
|
|
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
|
2013-09-19 19:29:27 +00:00
|
|
|
if (info == nullptr) return PR_FAILURE;
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
if (!info->IsConnected()) {
|
|
|
|
*out_flags = 0;
|
|
|
|
return info->GetPollFlags();
|
|
|
|
}
|
2011-03-24 08:37:53 +00:00
|
|
|
|
2011-02-24 13:10:08 +00:00
|
|
|
return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:04:34 +00:00
|
|
|
static PRStatus
|
2000-06-16 17:53:22 +00:00
|
|
|
nsSOCKSIOLayerClose(PRFileDesc *fd)
|
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
|
|
|
|
PRDescIdentity id = PR_GetLayersIdentity(fd);
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
if (info && id == nsSOCKSIOLayerIdentity)
|
|
|
|
{
|
2012-10-31 21:17:29 +00:00
|
|
|
info->ForgetFD();
|
2000-07-01 10:25:25 +00:00
|
|
|
NS_RELEASE(info);
|
|
|
|
fd->identity = PR_INVALID_IO_LAYER;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
return fd->lower->methods->close(fd->lower);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:04:34 +00:00
|
|
|
static PRFileDesc*
|
2000-06-16 17:53:22 +00:00
|
|
|
nsSOCKSIOLayerAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
|
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
// TODO: implement SOCKS support for accept
|
|
|
|
return fd->lower->methods->accept(fd->lower, addr, timeout);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2012-08-22 15:56:38 +00:00
|
|
|
static int32_t
|
|
|
|
nsSOCKSIOLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, int32_t amount, PRIntervalTime timeout)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
// TODO: implement SOCKS support for accept, then read from it
|
|
|
|
return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, timeout);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:04:34 +00:00
|
|
|
static PRStatus
|
2000-06-16 17:53:22 +00:00
|
|
|
nsSOCKSIOLayerBind(PRFileDesc *fd, const PRNetAddr *addr)
|
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
// TODO: implement SOCKS support for bind (very similar to connect)
|
|
|
|
return fd->lower->methods->bind(fd->lower, addr);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:04:34 +00:00
|
|
|
static PRStatus
|
2000-06-16 17:53:22 +00:00
|
|
|
nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr)
|
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
|
|
|
|
|
2013-09-19 19:29:27 +00:00
|
|
|
if (info != nullptr && addr != nullptr) {
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr temp;
|
|
|
|
NetAddr *tempPtr = &temp;
|
|
|
|
if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) {
|
|
|
|
NetAddrToPRNetAddr(tempPtr, addr);
|
2000-07-01 10:25:25 +00:00
|
|
|
return PR_SUCCESS;
|
2012-12-23 21:08:43 +00:00
|
|
|
}
|
2000-07-01 10:25:25 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
return PR_FAILURE;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:04:34 +00:00
|
|
|
static PRStatus
|
2000-06-16 17:53:22 +00:00
|
|
|
nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
|
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2013-09-19 19:29:27 +00:00
|
|
|
if (info != nullptr && addr != nullptr) {
|
2012-12-23 21:08:43 +00:00
|
|
|
NetAddr temp;
|
|
|
|
NetAddr *tempPtr = &temp;
|
|
|
|
if (info->GetDestinationAddr(&tempPtr) == NS_OK) {
|
|
|
|
NetAddrToPRNetAddr(tempPtr, addr);
|
2000-07-01 10:25:25 +00:00
|
|
|
return PR_SUCCESS;
|
2012-12-23 21:08:43 +00:00
|
|
|
}
|
2000-07-01 10:25:25 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
return PR_FAILURE;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
2008-10-10 15:04:34 +00:00
|
|
|
static PRStatus
|
2012-08-09 07:09:40 +00:00
|
|
|
nsSOCKSIOLayerListen(PRFileDesc *fd, int backlog)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2000-07-01 10:25:25 +00:00
|
|
|
// TODO: implement SOCKS support for listen
|
|
|
|
return fd->lower->methods->listen(fd->lower, backlog);
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// add SOCKS IO layer to an existing socket
|
|
|
|
nsresult
|
2012-08-22 15:56:38 +00:00
|
|
|
nsSOCKSIOLayerAddToSocket(int32_t family,
|
2003-09-11 20:32:33 +00:00
|
|
|
const char *host,
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t port,
|
2000-07-01 10:25:25 +00:00
|
|
|
const char *proxyHost,
|
2012-08-22 15:56:38 +00:00
|
|
|
int32_t proxyPort,
|
|
|
|
int32_t socksVersion,
|
|
|
|
uint32_t flags,
|
2000-07-01 10:25:25 +00:00
|
|
|
PRFileDesc *fd,
|
|
|
|
nsISupports** info)
|
2000-06-16 17:53:22 +00:00
|
|
|
{
|
2001-07-25 00:28:28 +00:00
|
|
|
NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED);
|
|
|
|
|
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
if (firstTime)
|
|
|
|
{
|
2012-07-21 00:19:37 +00:00
|
|
|
//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);
|
|
|
|
}
|
|
|
|
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
|
|
|
|
nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
|
|
|
|
nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue;
|
|
|
|
nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll;
|
|
|
|
nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
|
2000-07-01 10:25:25 +00:00
|
|
|
nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
|
|
|
|
nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
|
|
|
|
nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
|
2012-12-23 21:08:43 +00:00
|
|
|
nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
|
|
|
|
nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
|
|
|
|
nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-12-23 21:08:43 +00:00
|
|
|
firstTime = false;
|
2001-07-25 00:28:28 +00:00
|
|
|
|
|
|
|
#if defined(PR_LOGGING)
|
|
|
|
gSOCKSLog = PR_NewLogModule("SOCKS");
|
|
|
|
#endif
|
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2001-07-25 00:28:28 +00:00
|
|
|
LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-12-23 21:08:43 +00:00
|
|
|
PRFileDesc *layer;
|
|
|
|
PRStatus rv;
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
|
|
|
|
if (! layer)
|
|
|
|
{
|
2001-07-25 00:28:28 +00:00
|
|
|
LOGERROR(("PR_CreateIOLayerStub() failed."));
|
2000-07-01 10:25:25 +00:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo();
|
|
|
|
if (!infoObject)
|
|
|
|
{
|
|
|
|
// clean up IOLayerStub
|
2001-07-25 00:28:28 +00:00
|
|
|
LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
|
2000-07-01 10:25:25 +00:00
|
|
|
PR_DELETE(layer);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2000-07-01 10:25:25 +00:00
|
|
|
NS_ADDREF(infoObject);
|
2012-07-21 00:19:37 +00:00
|
|
|
infoObject->Init(socksVersion, family, proxyHost, proxyPort, host, flags);
|
2000-07-01 10:25:25 +00:00
|
|
|
layer->secret = (PRFilePrivate*) infoObject;
|
|
|
|
rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-08-13 13:33:21 +00:00
|
|
|
if (rv == PR_FAILURE) {
|
2001-07-25 00:28:28 +00:00
|
|
|
LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
|
2000-07-01 10:25:25 +00:00
|
|
|
NS_RELEASE(infoObject);
|
|
|
|
PR_DELETE(layer);
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2004-12-16 02:46:12 +00:00
|
|
|
|
2012-06-28 22:24:02 +00:00
|
|
|
*info = static_cast<nsISOCKSSocketInfo*>(infoObject);
|
2000-07-01 10:25:25 +00:00
|
|
|
NS_ADDREF(*info);
|
|
|
|
return NS_OK;
|
2000-06-16 17:53:22 +00:00
|
|
|
}
|