netwerk/socket/nsSOCKSIOLayer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/socket/nsSOCKSIOLayer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1420 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim:set expandtab ts=4 sw=4 sts=4 cin: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nspr.h"
    1.11 +#include "private/pprio.h"
    1.12 +#include "nsString.h"
    1.13 +#include "nsCRT.h"
    1.14 +
    1.15 +#include "nsIServiceManager.h"
    1.16 +#include "nsIDNSService.h"
    1.17 +#include "nsIDNSRecord.h"
    1.18 +#include "nsISOCKSSocketInfo.h"
    1.19 +#include "nsISocketProvider.h"
    1.20 +#include "nsSOCKSIOLayer.h"
    1.21 +#include "nsNetCID.h"
    1.22 +#include "nsIDNSListener.h"
    1.23 +#include "nsICancelable.h"
    1.24 +#include "nsThreadUtils.h"
    1.25 +#include "mozilla/net/DNS.h"
    1.26 +
    1.27 +using namespace mozilla::net;
    1.28 +
    1.29 +static PRDescIdentity nsSOCKSIOLayerIdentity;
    1.30 +static PRIOMethods nsSOCKSIOLayerMethods;
    1.31 +static bool firstTime = true;
    1.32 +static bool ipv6Supported = true;
    1.33 +
    1.34 +#if defined(PR_LOGGING)
    1.35 +static PRLogModuleInfo *gSOCKSLog;
    1.36 +#define LOGDEBUG(args) PR_LOG(gSOCKSLog, PR_LOG_DEBUG, args)
    1.37 +#define LOGERROR(args) PR_LOG(gSOCKSLog, PR_LOG_ERROR , args)
    1.38 +
    1.39 +#else
    1.40 +#define LOGDEBUG(args)
    1.41 +#define LOGERROR(args)
    1.42 +#endif
    1.43 +
    1.44 +class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
    1.45 +                        , public nsIDNSListener
    1.46 +{
    1.47 +    enum State {
    1.48 +        SOCKS_INITIAL,
    1.49 +        SOCKS_DNS_IN_PROGRESS,
    1.50 +        SOCKS_DNS_COMPLETE,
    1.51 +        SOCKS_CONNECTING_TO_PROXY,
    1.52 +        SOCKS4_WRITE_CONNECT_REQUEST,
    1.53 +        SOCKS4_READ_CONNECT_RESPONSE,
    1.54 +        SOCKS5_WRITE_AUTH_REQUEST,
    1.55 +        SOCKS5_READ_AUTH_RESPONSE,
    1.56 +        SOCKS5_WRITE_USERNAME_REQUEST,
    1.57 +        SOCKS5_READ_USERNAME_RESPONSE,
    1.58 +        SOCKS5_WRITE_CONNECT_REQUEST,
    1.59 +        SOCKS5_READ_CONNECT_RESPONSE_TOP,
    1.60 +        SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
    1.61 +        SOCKS_CONNECTED,
    1.62 +        SOCKS_FAILED
    1.63 +    };
    1.64 +
    1.65 +    // A buffer of 520 bytes should be enough for any request and response
    1.66 +    // in case of SOCKS4 as well as SOCKS5
    1.67 +    static const uint32_t BUFFER_SIZE = 520;
    1.68 +    static const uint32_t MAX_HOSTNAME_LEN = 255;
    1.69 +    static const uint32_t MAX_USERNAME_LEN = 255;
    1.70 +    static const uint32_t MAX_PASSWORD_LEN = 255;
    1.71 +
    1.72 +public:
    1.73 +    nsSOCKSSocketInfo();
    1.74 +    virtual ~nsSOCKSSocketInfo() { HandshakeFinished(); }
    1.75 +
    1.76 +    NS_DECL_THREADSAFE_ISUPPORTS
    1.77 +    NS_DECL_NSISOCKSSOCKETINFO
    1.78 +    NS_DECL_NSIDNSLISTENER
    1.79 +
    1.80 +    void Init(int32_t version,
    1.81 +              int32_t family,
    1.82 +              nsIProxyInfo *proxy,
    1.83 +              const char *destinationHost,
    1.84 +              uint32_t flags);
    1.85 +
    1.86 +    void SetConnectTimeout(PRIntervalTime to);
    1.87 +    PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1);
    1.88 +    int16_t GetPollFlags() const;
    1.89 +    bool IsConnected() const { return (mState == SOCKS_CONNECTED ||
    1.90 +                                       mState == SOCKS5_READ_CONNECT_RESPONSE_TOP); }
    1.91 + 
    1.92 +    void ForgetFD() { mFD = nullptr; }
    1.93 +
    1.94 +private:
    1.95 +    void HandshakeFinished(PRErrorCode err = 0);
    1.96 +    PRStatus StartDNS(PRFileDesc *fd);
    1.97 +    PRStatus ConnectToProxy(PRFileDesc *fd);
    1.98 +    void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy);
    1.99 +    PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags);
   1.100 +    PRStatus WriteV4ConnectRequest();
   1.101 +    PRStatus ReadV4ConnectResponse();
   1.102 +    PRStatus WriteV5AuthRequest();
   1.103 +    PRStatus ReadV5AuthResponse();
   1.104 +    PRStatus WriteV5UsernameRequest();
   1.105 +    PRStatus ReadV5UsernameResponse();
   1.106 +    PRStatus WriteV5ConnectRequest();
   1.107 +    PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len);
   1.108 +    PRStatus ReadV5ConnectResponseTop();
   1.109 +    PRStatus ReadV5ConnectResponseBottom();
   1.110 +
   1.111 +    void WriteUint8(uint8_t d);
   1.112 +    void WriteUint16(uint16_t d);
   1.113 +    void WriteUint32(uint32_t d);
   1.114 +    void WriteNetAddr(const NetAddr *addr);
   1.115 +    void WriteNetPort(const NetAddr *addr);
   1.116 +    void WriteString(const nsACString &str);
   1.117 +
   1.118 +    uint8_t ReadUint8();
   1.119 +    uint16_t ReadUint16();
   1.120 +    uint32_t ReadUint32();
   1.121 +    void ReadNetAddr(NetAddr *addr, uint16_t fam);
   1.122 +    void ReadNetPort(NetAddr *addr);
   1.123 +
   1.124 +    void WantRead(uint32_t sz);
   1.125 +    PRStatus ReadFromSocket(PRFileDesc *fd);
   1.126 +    PRStatus WriteToSocket(PRFileDesc *fd);
   1.127 +
   1.128 +private:
   1.129 +    State     mState;
   1.130 +    uint8_t * mData;
   1.131 +    uint8_t * mDataIoPtr;
   1.132 +    uint32_t  mDataLength;
   1.133 +    uint32_t  mReadOffset;
   1.134 +    uint32_t  mAmountToRead;
   1.135 +    nsCOMPtr<nsIDNSRecord>  mDnsRec;
   1.136 +    nsCOMPtr<nsICancelable> mLookup;
   1.137 +    nsresult                mLookupStatus;
   1.138 +    PRFileDesc             *mFD;
   1.139 +
   1.140 +    nsCString mDestinationHost;
   1.141 +    nsCOMPtr<nsIProxyInfo> mProxy;
   1.142 +    int32_t   mVersion;   // SOCKS version 4 or 5
   1.143 +    int32_t   mDestinationFamily;
   1.144 +    uint32_t  mFlags;
   1.145 +    NetAddr   mInternalProxyAddr;
   1.146 +    NetAddr   mExternalProxyAddr;
   1.147 +    NetAddr   mDestinationAddr;
   1.148 +    PRIntervalTime mTimeout;
   1.149 +    nsCString mProxyUsername; // Cache, from mProxy
   1.150 +};
   1.151 +
   1.152 +nsSOCKSSocketInfo::nsSOCKSSocketInfo()
   1.153 +    : mState(SOCKS_INITIAL)
   1.154 +    , mDataIoPtr(nullptr)
   1.155 +    , mDataLength(0)
   1.156 +    , mReadOffset(0)
   1.157 +    , mAmountToRead(0)
   1.158 +    , mVersion(-1)
   1.159 +    , mDestinationFamily(AF_INET)
   1.160 +    , mFlags(0)
   1.161 +    , mTimeout(PR_INTERVAL_NO_TIMEOUT)
   1.162 +{
   1.163 +    mData = new uint8_t[BUFFER_SIZE];
   1.164 +
   1.165 +    mInternalProxyAddr.raw.family = AF_INET;
   1.166 +    mInternalProxyAddr.inet.ip = htonl(INADDR_ANY);
   1.167 +    mInternalProxyAddr.inet.port = htons(0);
   1.168 +
   1.169 +    mExternalProxyAddr.raw.family = AF_INET;
   1.170 +    mExternalProxyAddr.inet.ip = htonl(INADDR_ANY);
   1.171 +    mExternalProxyAddr.inet.port = htons(0);
   1.172 +
   1.173 +    mDestinationAddr.raw.family = AF_INET;
   1.174 +    mDestinationAddr.inet.ip = htonl(INADDR_ANY);
   1.175 +    mDestinationAddr.inet.port = htons(0);
   1.176 +}
   1.177 +
   1.178 +void
   1.179 +nsSOCKSSocketInfo::Init(int32_t version, int32_t family, nsIProxyInfo *proxy, const char *host, uint32_t flags)
   1.180 +{
   1.181 +    mVersion         = version;
   1.182 +    mDestinationFamily = family;
   1.183 +    mProxy           = proxy;
   1.184 +    mDestinationHost = host;
   1.185 +    mFlags           = flags;
   1.186 +    mProxy->GetUsername(mProxyUsername); // cache
   1.187 +}
   1.188 +
   1.189 +NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
   1.190 +
   1.191 +NS_IMETHODIMP 
   1.192 +nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr)
   1.193 +{
   1.194 +    memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr));
   1.195 +    return NS_OK;
   1.196 +}
   1.197 +
   1.198 +NS_IMETHODIMP 
   1.199 +nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr *aExternalProxyAddr)
   1.200 +{
   1.201 +    memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr));
   1.202 +    return NS_OK;
   1.203 +}
   1.204 +
   1.205 +NS_IMETHODIMP 
   1.206 +nsSOCKSSocketInfo::GetDestinationAddr(NetAddr * *aDestinationAddr)
   1.207 +{
   1.208 +    memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr));
   1.209 +    return NS_OK;
   1.210 +}
   1.211 +
   1.212 +NS_IMETHODIMP 
   1.213 +nsSOCKSSocketInfo::SetDestinationAddr(NetAddr *aDestinationAddr)
   1.214 +{
   1.215 +    memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr));
   1.216 +    return NS_OK;
   1.217 +}
   1.218 +
   1.219 +NS_IMETHODIMP 
   1.220 +nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr * *aInternalProxyAddr)
   1.221 +{
   1.222 +    memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr));
   1.223 +    return NS_OK;
   1.224 +}
   1.225 +
   1.226 +NS_IMETHODIMP 
   1.227 +nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr *aInternalProxyAddr)
   1.228 +{
   1.229 +    memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr));
   1.230 +    return NS_OK;
   1.231 +}
   1.232 +
   1.233 +// There needs to be a means of distinguishing between connection errors
   1.234 +// that the SOCKS server reports when it rejects a connection request, and
   1.235 +// connection errors that happen while attempting to connect to the SOCKS
   1.236 +// server. Otherwise, Firefox will report incorrectly that the proxy server
   1.237 +// is refusing connections when a SOCKS request is rejected by the proxy.
   1.238 +// When a SOCKS handshake failure occurs, the PR error is set to
   1.239 +// PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
   1.240 +void
   1.241 +nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err)
   1.242 +{
   1.243 +    if (err == 0) {
   1.244 +        mState = SOCKS_CONNECTED;
   1.245 +    } else {
   1.246 +        mState = SOCKS_FAILED;
   1.247 +        PR_SetError(PR_UNKNOWN_ERROR, err);
   1.248 +    }
   1.249 +
   1.250 +    // We don't need the buffer any longer, so free it.
   1.251 +    delete [] mData;
   1.252 +    mData = nullptr;
   1.253 +    mDataIoPtr = nullptr;
   1.254 +    mDataLength = 0;
   1.255 +    mReadOffset = 0;
   1.256 +    mAmountToRead = 0;
   1.257 +    if (mLookup) {
   1.258 +        mLookup->Cancel(NS_ERROR_FAILURE);
   1.259 +        mLookup = nullptr;
   1.260 +    }
   1.261 +}
   1.262 +
   1.263 +PRStatus
   1.264 +nsSOCKSSocketInfo::StartDNS(PRFileDesc *fd)
   1.265 +{
   1.266 +    NS_ABORT_IF_FALSE(!mDnsRec && mState == SOCKS_INITIAL,
   1.267 +                      "Must be in initial state to make DNS Lookup");
   1.268 +
   1.269 +    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
   1.270 +    if (!dns)
   1.271 +        return PR_FAILURE;
   1.272 +
   1.273 +    nsCString proxyHost;
   1.274 +    mProxy->GetHost(proxyHost);
   1.275 +
   1.276 +    mFD  = fd;
   1.277 +    nsresult rv = dns->AsyncResolve(proxyHost, 0, this,
   1.278 +                                    NS_GetCurrentThread(),
   1.279 +                                    getter_AddRefs(mLookup));
   1.280 +
   1.281 +    if (NS_FAILED(rv)) {
   1.282 +        LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed",
   1.283 +                  proxyHost.get()));
   1.284 +        return PR_FAILURE;
   1.285 +    }
   1.286 +    mState = SOCKS_DNS_IN_PROGRESS;
   1.287 +    PR_SetError(PR_IN_PROGRESS_ERROR, 0);
   1.288 +    return PR_FAILURE;
   1.289 +}
   1.290 +
   1.291 +NS_IMETHODIMP
   1.292 +nsSOCKSSocketInfo::OnLookupComplete(nsICancelable *aRequest,
   1.293 +                                    nsIDNSRecord *aRecord,
   1.294 +                                    nsresult aStatus)
   1.295 +{
   1.296 +    NS_ABORT_IF_FALSE(aRequest == mLookup, "wrong DNS query");
   1.297 +    mLookup = nullptr;
   1.298 +    mLookupStatus = aStatus;
   1.299 +    mDnsRec = aRecord;
   1.300 +    mState = SOCKS_DNS_COMPLETE;
   1.301 +    if (mFD) {
   1.302 +      ConnectToProxy(mFD);
   1.303 +      ForgetFD();
   1.304 +    }
   1.305 +    return NS_OK;
   1.306 +}
   1.307 +
   1.308 +PRStatus
   1.309 +nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
   1.310 +{
   1.311 +    PRStatus status;
   1.312 +    nsresult rv;
   1.313 +
   1.314 +    NS_ABORT_IF_FALSE(mState == SOCKS_DNS_COMPLETE,
   1.315 +                      "Must have DNS to make connection!");
   1.316 +
   1.317 +    if (NS_FAILED(mLookupStatus)) {
   1.318 +        PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
   1.319 +        return PR_FAILURE;
   1.320 +    }
   1.321 +
   1.322 +    // Try socks5 if the destination addrress is IPv6
   1.323 +    if (mVersion == 4 &&
   1.324 +        mDestinationAddr.raw.family == AF_INET6) {
   1.325 +        mVersion = 5;
   1.326 +    }
   1.327 +
   1.328 +    int32_t proxyPort;
   1.329 +    mProxy->GetPort(&proxyPort);
   1.330 +
   1.331 +    int32_t addresses = 0;
   1.332 +    do {
   1.333 +        if (addresses++)
   1.334 +            mDnsRec->ReportUnusable(proxyPort);
   1.335 +        
   1.336 +        rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr);
   1.337 +        // No more addresses to try? If so, we'll need to bail
   1.338 +        if (NS_FAILED(rv)) {
   1.339 +            nsCString proxyHost;
   1.340 +            mProxy->GetHost(proxyHost);
   1.341 +            LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
   1.342 +                     proxyHost.get()));
   1.343 +            return PR_FAILURE;
   1.344 +        }
   1.345 +
   1.346 +#if defined(PR_LOGGING)
   1.347 +        char buf[kIPv6CStrBufSize];
   1.348 +        NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf));
   1.349 +        LOGDEBUG(("socks: trying proxy server, %s:%hu",
   1.350 +                 buf, ntohs(mInternalProxyAddr.inet.port)));
   1.351 +#endif
   1.352 +        NetAddr proxy = mInternalProxyAddr;
   1.353 +        FixupAddressFamily(fd, &proxy);
   1.354 +        PRNetAddr prProxy;
   1.355 +        NetAddrToPRNetAddr(&proxy, &prProxy);
   1.356 +        status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout);
   1.357 +        if (status != PR_SUCCESS) {
   1.358 +            PRErrorCode c = PR_GetError();
   1.359 +            // If EINPROGRESS, return now and check back later after polling
   1.360 +            if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
   1.361 +                mState = SOCKS_CONNECTING_TO_PROXY;
   1.362 +                return status;
   1.363 +            }
   1.364 +        }
   1.365 +    } while (status != PR_SUCCESS);
   1.366 +
   1.367 +    // Connected now, start SOCKS
   1.368 +    if (mVersion == 4)
   1.369 +        return WriteV4ConnectRequest();
   1.370 +    return WriteV5AuthRequest();
   1.371 +}
   1.372 +
   1.373 +void
   1.374 +nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy)
   1.375 +{
   1.376 +    int32_t proxyFamily = mInternalProxyAddr.raw.family;
   1.377 +    // Do nothing if the address family is already matched
   1.378 +    if (proxyFamily == mDestinationFamily) {
   1.379 +        return;
   1.380 +    }
   1.381 +    // If the system does not support IPv6 and the proxy address is IPv6,
   1.382 +    // We can do nothing here.
   1.383 +    if (proxyFamily == AF_INET6 && !ipv6Supported) {
   1.384 +        return;
   1.385 +    }
   1.386 +    // If the system does not support IPv6 and the destination address is
   1.387 +    // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy
   1.388 +    // the emulation layer
   1.389 +    if (mDestinationFamily == AF_INET6 && !ipv6Supported) {
   1.390 +        proxy->inet6.family = AF_INET6;
   1.391 +        proxy->inet6.port = mInternalProxyAddr.inet.port;
   1.392 +        uint8_t *proxyp = proxy->inet6.ip.u8;
   1.393 +        memset(proxyp, 0, 10);
   1.394 +        memset(proxyp + 10, 0xff, 2);
   1.395 +        memcpy(proxyp + 12,(char *) &mInternalProxyAddr.inet.ip, 4);
   1.396 +        // mDestinationFamily should not be updated
   1.397 +        return;
   1.398 +    }
   1.399 +    // Get an OS native handle from a specified FileDesc
   1.400 +    PROsfd osfd = PR_FileDesc2NativeHandle(fd);
   1.401 +    if (osfd == -1) {
   1.402 +        return;
   1.403 +    }
   1.404 +    // Create a new FileDesc with a specified family
   1.405 +    PRFileDesc *tmpfd = PR_OpenTCPSocket(proxyFamily);
   1.406 +    if (!tmpfd) {
   1.407 +        return;
   1.408 +    }
   1.409 +    PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd);
   1.410 +    if (newsd == -1) {
   1.411 +        PR_Close(tmpfd);
   1.412 +        return;
   1.413 +    }
   1.414 +    // Must succeed because PR_FileDesc2NativeHandle succeeded
   1.415 +    fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
   1.416 +    MOZ_ASSERT(fd);
   1.417 +    // Swap OS native handles
   1.418 +    PR_ChangeFileDescNativeHandle(fd, newsd);
   1.419 +    PR_ChangeFileDescNativeHandle(tmpfd, osfd);
   1.420 +    // Close temporary FileDesc which is now associated with
   1.421 +    // old OS native handle
   1.422 +    PR_Close(tmpfd);
   1.423 +    mDestinationFamily = proxyFamily;
   1.424 +}
   1.425 +
   1.426 +PRStatus
   1.427 +nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags)
   1.428 +{
   1.429 +    PRStatus status;
   1.430 +
   1.431 +    NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
   1.432 +                      "Continuing connection in wrong state!");
   1.433 +
   1.434 +    LOGDEBUG(("socks: continuing connection to proxy"));
   1.435 +
   1.436 +    status = fd->lower->methods->connectcontinue(fd->lower, oflags);
   1.437 +    if (status != PR_SUCCESS) {
   1.438 +        PRErrorCode c = PR_GetError();
   1.439 +        if (c != PR_WOULD_BLOCK_ERROR && c != PR_IN_PROGRESS_ERROR) {
   1.440 +            // A connection failure occured, try another address
   1.441 +            mState = SOCKS_DNS_COMPLETE;
   1.442 +            return ConnectToProxy(fd);
   1.443 +        }
   1.444 +
   1.445 +        // We're still connecting
   1.446 +        return PR_FAILURE;
   1.447 +    }
   1.448 +
   1.449 +    // Connected now, start SOCKS
   1.450 +    if (mVersion == 4)
   1.451 +        return WriteV4ConnectRequest();
   1.452 +    return WriteV5AuthRequest();
   1.453 +}
   1.454 +
   1.455 +PRStatus
   1.456 +nsSOCKSSocketInfo::WriteV4ConnectRequest()
   1.457 +{
   1.458 +    if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
   1.459 +        LOGERROR(("socks username is too long"));
   1.460 +        HandshakeFinished(PR_UNKNOWN_ERROR);
   1.461 +        return PR_FAILURE;
   1.462 +    }
   1.463 +
   1.464 +    NetAddr *addr = &mDestinationAddr;
   1.465 +    int32_t proxy_resolve;
   1.466 +
   1.467 +    NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
   1.468 +                      "Invalid state!");
   1.469 +    
   1.470 +    proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
   1.471 +
   1.472 +    mDataLength = 0;
   1.473 +    mState = SOCKS4_WRITE_CONNECT_REQUEST;
   1.474 +
   1.475 +    LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
   1.476 +             proxy_resolve? "yes" : "no"));
   1.477 +
   1.478 +    // Send a SOCKS 4 connect request.
   1.479 +    WriteUint8(0x04); // version -- 4
   1.480 +    WriteUint8(0x01); // command -- connect
   1.481 +    WriteNetPort(addr);
   1.482 +    if (proxy_resolve) {
   1.483 +        // Add the full name, null-terminated, to the request
   1.484 +        // according to SOCKS 4a. A fake IP address, with the first
   1.485 +        // four bytes set to 0 and the last byte set to something other
   1.486 +        // than 0, is used to notify the proxy that this is a SOCKS 4a
   1.487 +        // request. This request type works for Tor and perhaps others.
   1.488 +        WriteUint32(htonl(0x00000001)); // Fake IP
   1.489 +        WriteString(mProxyUsername); // Send username. May be empty.
   1.490 +        WriteUint8(0x00); // Null-terminate username
   1.491 +        // Password not supported by V4.
   1.492 +        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
   1.493 +            LOGERROR(("socks4: destination host name is too long!"));
   1.494 +            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
   1.495 +            return PR_FAILURE;
   1.496 +        }
   1.497 +        WriteString(mDestinationHost); // Hostname
   1.498 +        WriteUint8(0x00);
   1.499 +    } else if (addr->raw.family == AF_INET) {
   1.500 +        WriteNetAddr(addr); // Add the IPv4 address
   1.501 +        WriteString(mProxyUsername); // Send username. May be empty.
   1.502 +        WriteUint8(0x00); // Null-terminate username
   1.503 +        // Password not supported by V4.
   1.504 +    } else if (addr->raw.family == AF_INET6) {
   1.505 +        LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!"));
   1.506 +        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
   1.507 +        return PR_FAILURE;
   1.508 +    }
   1.509 +
   1.510 +    return PR_SUCCESS;
   1.511 +}
   1.512 +
   1.513 +PRStatus
   1.514 +nsSOCKSSocketInfo::ReadV4ConnectResponse()
   1.515 +{
   1.516 +    NS_ABORT_IF_FALSE(mState == SOCKS4_READ_CONNECT_RESPONSE,
   1.517 +                      "Handling SOCKS 4 connection reply in wrong state!");
   1.518 +
   1.519 +    LOGDEBUG(("socks4: checking connection reply"));
   1.520 +
   1.521 +    if (mDataLength != 8) {
   1.522 +        LOGERROR(("SOCKS 4 connection reply must be 8 bytes!"));
   1.523 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.524 +        return PR_FAILURE;
   1.525 +    }
   1.526 +
   1.527 +    if (ReadUint8() != 0x00) {
   1.528 +        LOGERROR(("socks4: wrong connection reply"));
   1.529 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.530 +        return PR_FAILURE;
   1.531 +    }
   1.532 +
   1.533 +    // See if our connection request was granted
   1.534 +    if (ReadUint8() == 90) {
   1.535 +        LOGDEBUG(("socks4: connection successful!"));
   1.536 +        HandshakeFinished();
   1.537 +        return PR_SUCCESS;
   1.538 +    }
   1.539 +
   1.540 +    LOGERROR(("socks4: unable to connect"));
   1.541 +    HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.542 +    return PR_FAILURE;
   1.543 +}
   1.544 +
   1.545 +PRStatus
   1.546 +nsSOCKSSocketInfo::WriteV5AuthRequest()
   1.547 +{
   1.548 +    NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
   1.549 +
   1.550 +    mDataLength = 0;
   1.551 +    mState = SOCKS5_WRITE_AUTH_REQUEST;
   1.552 +
   1.553 +    // Send an initial SOCKS 5 greeting
   1.554 +    LOGDEBUG(("socks5: sending auth methods"));
   1.555 +    WriteUint8(0x05); // version -- 5
   1.556 +    WriteUint8(0x01); // # of auth methods -- 1
   1.557 +    if (mProxyUsername.IsEmpty()) {
   1.558 +      WriteUint8(0x00); // no authentication
   1.559 +    } else {
   1.560 +      WriteUint8(0x02); // username/password
   1.561 +    }
   1.562 +
   1.563 +    return PR_SUCCESS;
   1.564 +}
   1.565 +
   1.566 +PRStatus
   1.567 +nsSOCKSSocketInfo::ReadV5AuthResponse()
   1.568 +{
   1.569 +    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE,
   1.570 +                      "Handling SOCKS 5 auth method reply in wrong state!");
   1.571 +
   1.572 +    if (mDataLength != 2) {
   1.573 +        LOGERROR(("SOCKS 5 auth method reply must be 2 bytes"));
   1.574 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.575 +        return PR_FAILURE;
   1.576 +    }
   1.577 +
   1.578 +    // Check version number
   1.579 +    if (ReadUint8() != 0x05) {
   1.580 +        LOGERROR(("socks5: unexpected version in the reply"));
   1.581 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.582 +        return PR_FAILURE;
   1.583 +    }
   1.584 +
   1.585 +    // Make sure our authentication choice was accepted,
   1.586 +    // and continue accordingly
   1.587 +    uint8_t authMethod = ReadUint8();
   1.588 +    if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth
   1.589 +        LOGDEBUG(("socks5: server allows connection without authentication"));
   1.590 +        return WriteV5ConnectRequest();
   1.591 +    } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw
   1.592 +        LOGDEBUG(("socks5: auth method accepted by server"));
   1.593 +        return WriteV5UsernameRequest();
   1.594 +    } else { // 0xFF signals error
   1.595 +        LOGERROR(("socks5: server did not accept our authentication method"));
   1.596 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.597 +        return PR_FAILURE;
   1.598 +    }
   1.599 +}
   1.600 +
   1.601 +PRStatus
   1.602 +nsSOCKSSocketInfo::WriteV5UsernameRequest()
   1.603 +{
   1.604 +    NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
   1.605 +
   1.606 +    if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
   1.607 +        LOGERROR(("socks username is too long"));
   1.608 +        HandshakeFinished(PR_UNKNOWN_ERROR);
   1.609 +        return PR_FAILURE;
   1.610 +    }
   1.611 +
   1.612 +    nsCString password;
   1.613 +    mProxy->GetPassword(password);
   1.614 +    if (password.Length() > MAX_PASSWORD_LEN) {
   1.615 +        LOGERROR(("socks password is too long"));
   1.616 +        HandshakeFinished(PR_UNKNOWN_ERROR);
   1.617 +        return PR_FAILURE;
   1.618 +    }
   1.619 +
   1.620 +    mDataLength = 0;
   1.621 +    mState = SOCKS5_WRITE_USERNAME_REQUEST;
   1.622 +
   1.623 +    LOGDEBUG(("socks5: sending username and password"));
   1.624 +    // RFC 1929 Username/password auth for SOCKS 5
   1.625 +    WriteUint8(0x01); // version 1 (not 5)
   1.626 +    WriteUint8(mProxyUsername.Length()); // username length
   1.627 +    WriteString(mProxyUsername); // username
   1.628 +    WriteUint8(password.Length()); // password length
   1.629 +    WriteString(password); // password. WARNING: Sent unencrypted!
   1.630 +
   1.631 +    return PR_SUCCESS;
   1.632 +}
   1.633 +
   1.634 +PRStatus
   1.635 +nsSOCKSSocketInfo::ReadV5UsernameResponse()
   1.636 +{
   1.637 +    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_USERNAME_RESPONSE,
   1.638 +                      "Handling SOCKS 5 username/password reply in wrong state!");
   1.639 +
   1.640 +    if (mDataLength != 2) {
   1.641 +        LOGERROR(("SOCKS 5 username reply must be 2 bytes"));
   1.642 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.643 +        return PR_FAILURE;
   1.644 +    }
   1.645 +
   1.646 +    // Check version number, must be 1 (not 5)
   1.647 +    if (ReadUint8() != 0x01) {
   1.648 +        LOGERROR(("socks5: unexpected version in the reply"));
   1.649 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.650 +        return PR_FAILURE;
   1.651 +    }
   1.652 +
   1.653 +    // Check whether username/password were accepted
   1.654 +    if (ReadUint8() != 0x00) { // 0 = success
   1.655 +        LOGERROR(("socks5: username/password not accepted"));
   1.656 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.657 +        return PR_FAILURE;
   1.658 +    }
   1.659 +
   1.660 +    LOGDEBUG(("socks5: username/password accepted by server"));
   1.661 +
   1.662 +    return WriteV5ConnectRequest();
   1.663 +}
   1.664 +
   1.665 +PRStatus
   1.666 +nsSOCKSSocketInfo::WriteV5ConnectRequest()
   1.667 +{
   1.668 +    // Send SOCKS 5 connect request
   1.669 +    NetAddr *addr = &mDestinationAddr;
   1.670 +    int32_t proxy_resolve;
   1.671 +    proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
   1.672 +
   1.673 +    LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
   1.674 +             proxy_resolve? "yes" : "no"));
   1.675 +
   1.676 +    mDataLength = 0;
   1.677 +    mState = SOCKS5_WRITE_CONNECT_REQUEST;
   1.678 +
   1.679 +    WriteUint8(0x05); // version -- 5
   1.680 +    WriteUint8(0x01); // command -- connect
   1.681 +    WriteUint8(0x00); // reserved
   1.682 +   
   1.683 +    // Add the address to the SOCKS 5 request. SOCKS 5 supports several
   1.684 +    // address types, so we pick the one that works best for us.
   1.685 +    if (proxy_resolve) {
   1.686 +        // Add the host name. Only a single byte is used to store the length,
   1.687 +        // so we must prevent long names from being used.
   1.688 +        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
   1.689 +            LOGERROR(("socks5: destination host name is too long!"));
   1.690 +            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
   1.691 +            return PR_FAILURE;
   1.692 +        }
   1.693 +        WriteUint8(0x03); // addr type -- domainname
   1.694 +        WriteUint8(mDestinationHost.Length()); // name length
   1.695 +        WriteString(mDestinationHost);
   1.696 +    } else if (addr->raw.family == AF_INET) {
   1.697 +        WriteUint8(0x01); // addr type -- IPv4
   1.698 +        WriteNetAddr(addr);
   1.699 +    } else if (addr->raw.family == AF_INET6) {
   1.700 +        WriteUint8(0x04); // addr type -- IPv6
   1.701 +        WriteNetAddr(addr);
   1.702 +    } else {
   1.703 +        LOGERROR(("socks5: destination address of unknown type!"));
   1.704 +        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
   1.705 +        return PR_FAILURE;
   1.706 +    }
   1.707 +
   1.708 +    WriteNetPort(addr); // port
   1.709 +
   1.710 +    return PR_SUCCESS;
   1.711 +}
   1.712 +
   1.713 +PRStatus
   1.714 +nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len)
   1.715 +{
   1.716 +    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
   1.717 +                      mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
   1.718 +                      "Invalid state!");
   1.719 +    NS_ABORT_IF_FALSE(mDataLength >= 5,
   1.720 +                      "SOCKS 5 connection reply must be at least 5 bytes!");
   1.721 + 
   1.722 +    // Seek to the address location 
   1.723 +    mReadOffset = 3;
   1.724 +   
   1.725 +    *type = ReadUint8();
   1.726 +
   1.727 +    switch (*type) {
   1.728 +        case 0x01: // ipv4
   1.729 +            *len = 4 - 1;
   1.730 +            break;
   1.731 +        case 0x04: // ipv6
   1.732 +            *len = 16 - 1;
   1.733 +            break;
   1.734 +        case 0x03: // fqdn
   1.735 +            *len = ReadUint8();
   1.736 +            break;
   1.737 +        default:   // wrong address type
   1.738 +            LOGERROR(("socks5: wrong address type in connection reply!"));
   1.739 +            return PR_FAILURE;
   1.740 +    }
   1.741 +
   1.742 +    return PR_SUCCESS;
   1.743 +}
   1.744 +
   1.745 +PRStatus
   1.746 +nsSOCKSSocketInfo::ReadV5ConnectResponseTop()
   1.747 +{
   1.748 +    uint8_t res;
   1.749 +    uint32_t len;
   1.750 +
   1.751 +    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP,
   1.752 +                      "Invalid state!");
   1.753 +    NS_ABORT_IF_FALSE(mDataLength == 5,
   1.754 +                      "SOCKS 5 connection reply must be exactly 5 bytes!");
   1.755 +
   1.756 +    LOGDEBUG(("socks5: checking connection reply"));
   1.757 +
   1.758 +    // Check version number
   1.759 +    if (ReadUint8() != 0x05) {
   1.760 +        LOGERROR(("socks5: unexpected version in the reply"));
   1.761 +        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
   1.762 +        return PR_FAILURE;
   1.763 +    }
   1.764 +
   1.765 +    // Check response
   1.766 +    res = ReadUint8();
   1.767 +    if (res != 0x00) {
   1.768 +        PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
   1.769 +
   1.770 +        switch (res) {
   1.771 +            case 0x01:
   1.772 +                LOGERROR(("socks5: connect failed: "
   1.773 +                          "01, General SOCKS server failure."));
   1.774 +                break;
   1.775 +            case 0x02:
   1.776 +                LOGERROR(("socks5: connect failed: "
   1.777 +                          "02, Connection not allowed by ruleset."));
   1.778 +                break;
   1.779 +            case 0x03:
   1.780 +                LOGERROR(("socks5: connect failed: 03, Network unreachable."));
   1.781 +                c = PR_NETWORK_UNREACHABLE_ERROR;
   1.782 +                break;
   1.783 +            case 0x04:
   1.784 +                LOGERROR(("socks5: connect failed: 04, Host unreachable."));
   1.785 +                break;
   1.786 +            case 0x05:
   1.787 +                LOGERROR(("socks5: connect failed: 05, Connection refused."));
   1.788 +                break;
   1.789 +            case 0x06:  
   1.790 +                LOGERROR(("socks5: connect failed: 06, TTL expired."));
   1.791 +                c = PR_CONNECT_TIMEOUT_ERROR;
   1.792 +                break;
   1.793 +            case 0x07:
   1.794 +                LOGERROR(("socks5: connect failed: "
   1.795 +                          "07, Command not supported."));
   1.796 +                break;
   1.797 +            case 0x08:
   1.798 +                LOGERROR(("socks5: connect failed: "
   1.799 +                          "08, Address type not supported."));
   1.800 +                c = PR_BAD_ADDRESS_ERROR;
   1.801 +                break;
   1.802 +            default:
   1.803 +                LOGERROR(("socks5: connect failed."));
   1.804 +                break;
   1.805 +        }
   1.806 +
   1.807 +        HandshakeFinished(c);
   1.808 +        return PR_FAILURE;
   1.809 +    }
   1.810 +
   1.811 +    if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
   1.812 +        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
   1.813 +        return PR_FAILURE;
   1.814 +    }
   1.815 +
   1.816 +    mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
   1.817 +    WantRead(len + 2);
   1.818 +
   1.819 +    return PR_SUCCESS;
   1.820 +}
   1.821 +
   1.822 +PRStatus
   1.823 +nsSOCKSSocketInfo::ReadV5ConnectResponseBottom()
   1.824 +{
   1.825 +    uint8_t type;
   1.826 +    uint32_t len;
   1.827 +
   1.828 +    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
   1.829 +                      "Invalid state!");
   1.830 +
   1.831 +    if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
   1.832 +        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
   1.833 +        return PR_FAILURE;
   1.834 +    }
   1.835 +
   1.836 +    NS_ABORT_IF_FALSE(mDataLength == 7+len,
   1.837 +                      "SOCKS 5 unexpected length of connection reply!");
   1.838 +
   1.839 +    LOGDEBUG(("socks5: loading source addr and port"));
   1.840 +    // Read what the proxy says is our source address
   1.841 +    switch (type) {
   1.842 +        case 0x01: // ipv4
   1.843 +            ReadNetAddr(&mExternalProxyAddr, AF_INET);
   1.844 +            break;
   1.845 +        case 0x04: // ipv6
   1.846 +            ReadNetAddr(&mExternalProxyAddr, AF_INET6);
   1.847 +            break;
   1.848 +        case 0x03: // fqdn (skip)
   1.849 +            mReadOffset += len;
   1.850 +            mExternalProxyAddr.raw.family = AF_INET;
   1.851 +            break;
   1.852 +    }
   1.853 +
   1.854 +    ReadNetPort(&mExternalProxyAddr);
   1.855 +
   1.856 +    LOGDEBUG(("socks5: connected!"));
   1.857 +    HandshakeFinished();
   1.858 +
   1.859 +    return PR_SUCCESS;
   1.860 +}
   1.861 +
   1.862 +void
   1.863 +nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to)
   1.864 +{
   1.865 +    mTimeout = to;
   1.866 +}
   1.867 +
   1.868 +PRStatus
   1.869 +nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags)
   1.870 +{
   1.871 +    LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
   1.872 +
   1.873 +    switch (mState) {
   1.874 +        case SOCKS_INITIAL:
   1.875 +            return StartDNS(fd);
   1.876 +        case SOCKS_DNS_IN_PROGRESS:
   1.877 +            PR_SetError(PR_IN_PROGRESS_ERROR, 0);
   1.878 +            return PR_FAILURE;
   1.879 +        case SOCKS_DNS_COMPLETE:
   1.880 +            return ConnectToProxy(fd);
   1.881 +        case SOCKS_CONNECTING_TO_PROXY:
   1.882 +            return ContinueConnectingToProxy(fd, oflags);
   1.883 +        case SOCKS4_WRITE_CONNECT_REQUEST:
   1.884 +            if (WriteToSocket(fd) != PR_SUCCESS)
   1.885 +                return PR_FAILURE;
   1.886 +            WantRead(8);
   1.887 +            mState = SOCKS4_READ_CONNECT_RESPONSE;
   1.888 +            return PR_SUCCESS;
   1.889 +        case SOCKS4_READ_CONNECT_RESPONSE:
   1.890 +            if (ReadFromSocket(fd) != PR_SUCCESS)
   1.891 +                return PR_FAILURE;
   1.892 +            return ReadV4ConnectResponse();
   1.893 +
   1.894 +        case SOCKS5_WRITE_AUTH_REQUEST:
   1.895 +            if (WriteToSocket(fd) != PR_SUCCESS)
   1.896 +                return PR_FAILURE;
   1.897 +            WantRead(2);
   1.898 +            mState = SOCKS5_READ_AUTH_RESPONSE;
   1.899 +            return PR_SUCCESS;
   1.900 +        case SOCKS5_READ_AUTH_RESPONSE:
   1.901 +            if (ReadFromSocket(fd) != PR_SUCCESS)
   1.902 +                return PR_FAILURE;
   1.903 +            return ReadV5AuthResponse();
   1.904 +        case SOCKS5_WRITE_USERNAME_REQUEST:
   1.905 +            if (WriteToSocket(fd) != PR_SUCCESS)
   1.906 +                return PR_FAILURE;
   1.907 +            WantRead(2);
   1.908 +            mState = SOCKS5_READ_USERNAME_RESPONSE;
   1.909 +            return PR_SUCCESS;
   1.910 +        case SOCKS5_READ_USERNAME_RESPONSE:
   1.911 +            if (ReadFromSocket(fd) != PR_SUCCESS)
   1.912 +                return PR_FAILURE;
   1.913 +            return ReadV5UsernameResponse();
   1.914 +        case SOCKS5_WRITE_CONNECT_REQUEST:
   1.915 +            if (WriteToSocket(fd) != PR_SUCCESS)
   1.916 +                return PR_FAILURE;
   1.917 +
   1.918 +            // The SOCKS 5 response to the connection request is variable
   1.919 +            // length. First, we'll read enough to tell how long the response
   1.920 +            // is, and will read the rest later.
   1.921 +            WantRead(5);
   1.922 +            mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
   1.923 +            return PR_SUCCESS;
   1.924 +        case SOCKS5_READ_CONNECT_RESPONSE_TOP:
   1.925 +            if (ReadFromSocket(fd) != PR_SUCCESS)
   1.926 +                return PR_FAILURE;
   1.927 +            return ReadV5ConnectResponseTop();
   1.928 +        case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
   1.929 +            if (ReadFromSocket(fd) != PR_SUCCESS)
   1.930 +                return PR_FAILURE;
   1.931 +            return ReadV5ConnectResponseBottom();
   1.932 +
   1.933 +        case SOCKS_CONNECTED:
   1.934 +            LOGERROR(("socks: already connected"));
   1.935 +            HandshakeFinished(PR_IS_CONNECTED_ERROR);
   1.936 +            return PR_FAILURE;
   1.937 +        case SOCKS_FAILED:
   1.938 +            LOGERROR(("socks: already failed"));
   1.939 +            return PR_FAILURE;
   1.940 +    }
   1.941 +
   1.942 +    LOGERROR(("socks: executing handshake in invalid state, %d", mState));
   1.943 +    HandshakeFinished(PR_INVALID_STATE_ERROR);
   1.944 +
   1.945 +    return PR_FAILURE;
   1.946 +}
   1.947 +
   1.948 +int16_t
   1.949 +nsSOCKSSocketInfo::GetPollFlags() const
   1.950 +{
   1.951 +    switch (mState) {
   1.952 +        case SOCKS_DNS_IN_PROGRESS:
   1.953 +        case SOCKS_DNS_COMPLETE:
   1.954 +        case SOCKS_CONNECTING_TO_PROXY:
   1.955 +            return PR_POLL_EXCEPT | PR_POLL_WRITE;
   1.956 +        case SOCKS4_WRITE_CONNECT_REQUEST:
   1.957 +        case SOCKS5_WRITE_AUTH_REQUEST:
   1.958 +        case SOCKS5_WRITE_USERNAME_REQUEST:
   1.959 +        case SOCKS5_WRITE_CONNECT_REQUEST:
   1.960 +            return PR_POLL_WRITE;
   1.961 +        case SOCKS4_READ_CONNECT_RESPONSE:
   1.962 +        case SOCKS5_READ_AUTH_RESPONSE:
   1.963 +        case SOCKS5_READ_USERNAME_RESPONSE:
   1.964 +        case SOCKS5_READ_CONNECT_RESPONSE_TOP:
   1.965 +        case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
   1.966 +            return PR_POLL_READ;
   1.967 +        default:
   1.968 +            break;
   1.969 +    }
   1.970 +
   1.971 +    return 0;
   1.972 +}
   1.973 +
   1.974 +inline void
   1.975 +nsSOCKSSocketInfo::WriteUint8(uint8_t v)
   1.976 +{
   1.977 +    MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE,
   1.978 +                      "Can't write that much data!");
   1.979 +    mData[mDataLength] = v;
   1.980 +    mDataLength += sizeof(v);
   1.981 +}
   1.982 +
   1.983 +inline void
   1.984 +nsSOCKSSocketInfo::WriteUint16(uint16_t v)
   1.985 +{
   1.986 +    MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE,
   1.987 +                      "Can't write that much data!");
   1.988 +    memcpy(mData + mDataLength, &v, sizeof(v));
   1.989 +    mDataLength += sizeof(v);
   1.990 +}
   1.991 +
   1.992 +inline void
   1.993 +nsSOCKSSocketInfo::WriteUint32(uint32_t v)
   1.994 +{
   1.995 +    MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE,
   1.996 +                      "Can't write that much data!");
   1.997 +    memcpy(mData + mDataLength, &v, sizeof(v));
   1.998 +    mDataLength += sizeof(v);
   1.999 +}
  1.1000 +
  1.1001 +void
  1.1002 +nsSOCKSSocketInfo::WriteNetAddr(const NetAddr *addr)
  1.1003 +{
  1.1004 +    const char *ip = nullptr;
  1.1005 +    uint32_t len = 0;
  1.1006 +
  1.1007 +    if (addr->raw.family == AF_INET) {
  1.1008 +        ip = (const char*)&addr->inet.ip;
  1.1009 +        len = sizeof(addr->inet.ip);
  1.1010 +    } else if (addr->raw.family == AF_INET6) {
  1.1011 +        ip = (const char*)addr->inet6.ip.u8;
  1.1012 +        len = sizeof(addr->inet6.ip.u8);
  1.1013 +    }
  1.1014 +
  1.1015 +    MOZ_RELEASE_ASSERT(ip != nullptr, "Unknown address");
  1.1016 +    MOZ_RELEASE_ASSERT(mDataLength + len <= BUFFER_SIZE,
  1.1017 +                      "Can't write that much data!");
  1.1018 + 
  1.1019 +    memcpy(mData + mDataLength, ip, len);
  1.1020 +    mDataLength += len;
  1.1021 +}
  1.1022 +
  1.1023 +void
  1.1024 +nsSOCKSSocketInfo::WriteNetPort(const NetAddr *addr)
  1.1025 +{
  1.1026 +    WriteUint16(addr->inet.port);
  1.1027 +}
  1.1028 +
  1.1029 +void
  1.1030 +nsSOCKSSocketInfo::WriteString(const nsACString &str)
  1.1031 +{
  1.1032 +    MOZ_RELEASE_ASSERT(mDataLength + str.Length() <= BUFFER_SIZE,
  1.1033 +                      "Can't write that much data!");
  1.1034 +    memcpy(mData + mDataLength, str.Data(), str.Length());
  1.1035 +    mDataLength += str.Length();
  1.1036 +}
  1.1037 +
  1.1038 +inline uint8_t
  1.1039 +nsSOCKSSocketInfo::ReadUint8()
  1.1040 +{
  1.1041 +    uint8_t rv;
  1.1042 +    MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
  1.1043 +                      "Not enough space to pop a uint8_t!");
  1.1044 +    rv = mData[mReadOffset];
  1.1045 +    mReadOffset += sizeof(rv);
  1.1046 +    return rv;
  1.1047 +}
  1.1048 +
  1.1049 +inline uint16_t
  1.1050 +nsSOCKSSocketInfo::ReadUint16()
  1.1051 +{
  1.1052 +    uint16_t rv;
  1.1053 +    MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
  1.1054 +                      "Not enough space to pop a uint16_t!");
  1.1055 +    memcpy(&rv, mData + mReadOffset, sizeof(rv));
  1.1056 +    mReadOffset += sizeof(rv);
  1.1057 +    return rv;
  1.1058 +}
  1.1059 +
  1.1060 +inline uint32_t
  1.1061 +nsSOCKSSocketInfo::ReadUint32()
  1.1062 +{
  1.1063 +    uint32_t rv;
  1.1064 +    MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
  1.1065 +                      "Not enough space to pop a uint32_t!");
  1.1066 +    memcpy(&rv, mData + mReadOffset, sizeof(rv));
  1.1067 +    mReadOffset += sizeof(rv);
  1.1068 +    return rv;
  1.1069 +}
  1.1070 +
  1.1071 +void
  1.1072 +nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam)
  1.1073 +{
  1.1074 +    uint32_t amt = 0;
  1.1075 +    const uint8_t *ip = mData + mReadOffset;
  1.1076 +
  1.1077 +    addr->raw.family = fam;
  1.1078 +    if (fam == AF_INET) {
  1.1079 +        amt = sizeof(addr->inet.ip);
  1.1080 +        MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength,
  1.1081 +                          "Not enough space to pop an ipv4 addr!");
  1.1082 +        memcpy(&addr->inet.ip, ip, amt);
  1.1083 +    } else if (fam == AF_INET6) {
  1.1084 +        amt = sizeof(addr->inet6.ip.u8);
  1.1085 +        MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength,
  1.1086 +                          "Not enough space to pop an ipv6 addr!");
  1.1087 +        memcpy(addr->inet6.ip.u8, ip, amt);
  1.1088 +    }
  1.1089 +
  1.1090 +    mReadOffset += amt;
  1.1091 +}
  1.1092 +
  1.1093 +void
  1.1094 +nsSOCKSSocketInfo::ReadNetPort(NetAddr *addr)
  1.1095 +{
  1.1096 +    addr->inet.port = ReadUint16();
  1.1097 +}
  1.1098 +
  1.1099 +void
  1.1100 +nsSOCKSSocketInfo::WantRead(uint32_t sz)
  1.1101 +{
  1.1102 +    NS_ABORT_IF_FALSE(mDataIoPtr == nullptr,
  1.1103 +                      "WantRead() called while I/O already in progress!");
  1.1104 +    MOZ_RELEASE_ASSERT(mDataLength + sz <= BUFFER_SIZE,
  1.1105 +                      "Can't read that much data!");
  1.1106 +    mAmountToRead = sz;
  1.1107 +}
  1.1108 +
  1.1109 +PRStatus
  1.1110 +nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd)
  1.1111 +{
  1.1112 +    int32_t rc;
  1.1113 +    const uint8_t *end;
  1.1114 +
  1.1115 +    if (!mAmountToRead) {
  1.1116 +        LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
  1.1117 +        return PR_SUCCESS;
  1.1118 +    }
  1.1119 +
  1.1120 +    if (!mDataIoPtr) {
  1.1121 +        mDataIoPtr = mData + mDataLength;
  1.1122 +        mDataLength += mAmountToRead;
  1.1123 +    }
  1.1124 +
  1.1125 +    end = mData + mDataLength;
  1.1126 +
  1.1127 +    while (mDataIoPtr < end) {
  1.1128 +        rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
  1.1129 +        if (rc <= 0) {
  1.1130 +            if (rc == 0) {
  1.1131 +                LOGERROR(("socks: proxy server closed connection"));
  1.1132 +                HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
  1.1133 +                return PR_FAILURE;
  1.1134 +            } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
  1.1135 +                LOGDEBUG(("socks: ReadFromSocket(), want read"));
  1.1136 +            }
  1.1137 +            break;
  1.1138 +        }
  1.1139 +
  1.1140 +        mDataIoPtr += rc;
  1.1141 +    }
  1.1142 +
  1.1143 +    LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
  1.1144 +             unsigned(mDataIoPtr - mData)));
  1.1145 +    if (mDataIoPtr == end) {
  1.1146 +        mDataIoPtr = nullptr;
  1.1147 +        mAmountToRead = 0;
  1.1148 +        mReadOffset = 0;
  1.1149 +        return PR_SUCCESS;
  1.1150 +    }
  1.1151 +
  1.1152 +    return PR_FAILURE;
  1.1153 +}
  1.1154 +
  1.1155 +PRStatus
  1.1156 +nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd)
  1.1157 +{
  1.1158 +    int32_t rc;
  1.1159 +    const uint8_t *end;
  1.1160 +
  1.1161 +    if (!mDataLength) {
  1.1162 +        LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
  1.1163 +        return PR_SUCCESS;
  1.1164 +    }
  1.1165 +
  1.1166 +    if (!mDataIoPtr)
  1.1167 +        mDataIoPtr = mData;
  1.1168 +
  1.1169 +    end = mData + mDataLength;
  1.1170 +
  1.1171 +    while (mDataIoPtr < end) {
  1.1172 +        rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
  1.1173 +        if (rc < 0) {
  1.1174 +            if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
  1.1175 +                LOGDEBUG(("socks: WriteToSocket(), want write"));
  1.1176 +            }
  1.1177 +            break;
  1.1178 +        }
  1.1179 +        
  1.1180 +        mDataIoPtr += rc;
  1.1181 +    }
  1.1182 +
  1.1183 +    if (mDataIoPtr == end) {
  1.1184 +        mDataIoPtr = nullptr;
  1.1185 +        mDataLength = 0;
  1.1186 +        mReadOffset = 0;
  1.1187 +        return PR_SUCCESS;
  1.1188 +    }
  1.1189 +    
  1.1190 +    return PR_FAILURE;
  1.1191 +}
  1.1192 +
  1.1193 +static PRStatus
  1.1194 +nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
  1.1195 +{
  1.1196 +    PRStatus status;
  1.1197 +    NetAddr dst;
  1.1198 +
  1.1199 +    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
  1.1200 +    if (info == nullptr) return PR_FAILURE;
  1.1201 +
  1.1202 +    if (addr->raw.family == PR_AF_INET6 &&
  1.1203 +        PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
  1.1204 +        const uint8_t *srcp;
  1.1205 +
  1.1206 +        LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
  1.1207 +
  1.1208 +        // copied from _PR_ConvertToIpv4NetAddr()
  1.1209 +        dst.raw.family = AF_INET;
  1.1210 +        dst.inet.ip = htonl(INADDR_ANY);
  1.1211 +        dst.inet.port = htons(0);
  1.1212 +        srcp = addr->ipv6.ip.pr_s6_addr;
  1.1213 +        memcpy(&dst.inet.ip, srcp + 12, 4);
  1.1214 +        dst.inet.family = AF_INET;
  1.1215 +        dst.inet.port = addr->ipv6.port;
  1.1216 +    } else {
  1.1217 +        memcpy(&dst, addr, sizeof(dst));
  1.1218 +    }
  1.1219 +
  1.1220 +    info->SetDestinationAddr(&dst);
  1.1221 +    info->SetConnectTimeout(to);
  1.1222 +
  1.1223 +    do {
  1.1224 +        status = info->DoHandshake(fd, -1);
  1.1225 +    } while (status == PR_SUCCESS && !info->IsConnected());
  1.1226 +
  1.1227 +    return status;
  1.1228 +}
  1.1229 +
  1.1230 +static PRStatus
  1.1231 +nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, int16_t oflags)
  1.1232 +{
  1.1233 +    PRStatus status;
  1.1234 +
  1.1235 +    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
  1.1236 +    if (info == nullptr) return PR_FAILURE;
  1.1237 +
  1.1238 +    do { 
  1.1239 +        status = info->DoHandshake(fd, oflags);
  1.1240 +    } while (status == PR_SUCCESS && !info->IsConnected());
  1.1241 +
  1.1242 +    return status;
  1.1243 +}
  1.1244 +
  1.1245 +static int16_t
  1.1246 +nsSOCKSIOLayerPoll(PRFileDesc *fd, int16_t in_flags, int16_t *out_flags)
  1.1247 +{
  1.1248 +    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
  1.1249 +    if (info == nullptr) return PR_FAILURE;
  1.1250 +
  1.1251 +    if (!info->IsConnected()) {
  1.1252 +        *out_flags = 0;
  1.1253 +        return info->GetPollFlags();
  1.1254 +    }
  1.1255 +
  1.1256 +    return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
  1.1257 +}
  1.1258 +
  1.1259 +static PRStatus
  1.1260 +nsSOCKSIOLayerClose(PRFileDesc *fd)
  1.1261 +{
  1.1262 +    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
  1.1263 +    PRDescIdentity id = PR_GetLayersIdentity(fd);
  1.1264 +
  1.1265 +    if (info && id == nsSOCKSIOLayerIdentity)
  1.1266 +    {
  1.1267 +        info->ForgetFD();
  1.1268 +        NS_RELEASE(info);
  1.1269 +        fd->identity = PR_INVALID_IO_LAYER;
  1.1270 +    }
  1.1271 +
  1.1272 +    return fd->lower->methods->close(fd->lower);
  1.1273 +}
  1.1274 +
  1.1275 +static PRFileDesc*
  1.1276 +nsSOCKSIOLayerAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
  1.1277 +{
  1.1278 +    // TODO: implement SOCKS support for accept
  1.1279 +    return fd->lower->methods->accept(fd->lower, addr, timeout);
  1.1280 +}
  1.1281 +
  1.1282 +static int32_t
  1.1283 +nsSOCKSIOLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, int32_t amount, PRIntervalTime timeout)
  1.1284 +{
  1.1285 +    // TODO: implement SOCKS support for accept, then read from it
  1.1286 +    return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, timeout);
  1.1287 +}
  1.1288 +
  1.1289 +static PRStatus
  1.1290 +nsSOCKSIOLayerBind(PRFileDesc *fd, const PRNetAddr *addr)
  1.1291 +{
  1.1292 +    // TODO: implement SOCKS support for bind (very similar to connect)
  1.1293 +    return fd->lower->methods->bind(fd->lower, addr);
  1.1294 +}
  1.1295 +
  1.1296 +static PRStatus
  1.1297 +nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr)
  1.1298 +{
  1.1299 +    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
  1.1300 +    
  1.1301 +    if (info != nullptr && addr != nullptr) {
  1.1302 +        NetAddr temp;
  1.1303 +        NetAddr *tempPtr = &temp;
  1.1304 +        if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) {
  1.1305 +            NetAddrToPRNetAddr(tempPtr, addr);
  1.1306 +            return PR_SUCCESS;
  1.1307 +        }
  1.1308 +    }
  1.1309 +
  1.1310 +    return PR_FAILURE;
  1.1311 +}
  1.1312 +
  1.1313 +static PRStatus
  1.1314 +nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
  1.1315 +{
  1.1316 +    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
  1.1317 +
  1.1318 +    if (info != nullptr && addr != nullptr) {
  1.1319 +        NetAddr temp;
  1.1320 +        NetAddr *tempPtr = &temp;
  1.1321 +        if (info->GetDestinationAddr(&tempPtr) == NS_OK) {
  1.1322 +            NetAddrToPRNetAddr(tempPtr, addr);
  1.1323 +            return PR_SUCCESS;
  1.1324 +        }
  1.1325 +    }
  1.1326 +
  1.1327 +    return PR_FAILURE;
  1.1328 +}
  1.1329 +
  1.1330 +static PRStatus
  1.1331 +nsSOCKSIOLayerListen(PRFileDesc *fd, int backlog)
  1.1332 +{
  1.1333 +    // TODO: implement SOCKS support for listen
  1.1334 +    return fd->lower->methods->listen(fd->lower, backlog);
  1.1335 +}
  1.1336 +
  1.1337 +// add SOCKS IO layer to an existing socket
  1.1338 +nsresult
  1.1339 +nsSOCKSIOLayerAddToSocket(int32_t family,
  1.1340 +                          const char *host, 
  1.1341 +                          int32_t port,
  1.1342 +                          nsIProxyInfo *proxy,
  1.1343 +                          int32_t socksVersion,
  1.1344 +                          uint32_t flags,
  1.1345 +                          PRFileDesc *fd, 
  1.1346 +                          nsISupports** info)
  1.1347 +{
  1.1348 +    NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED);
  1.1349 +
  1.1350 +
  1.1351 +    if (firstTime)
  1.1352 +    {
  1.1353 +        //XXX hack until NSPR provides an official way to detect system IPv6
  1.1354 +        // support (bug 388519)
  1.1355 +        PRFileDesc *tmpfd = PR_OpenTCPSocket(PR_AF_INET6);
  1.1356 +        if (!tmpfd) {
  1.1357 +            ipv6Supported = false;
  1.1358 +        } else {
  1.1359 +            // If the system does not support IPv6, NSPR will push
  1.1360 +            // IPv6-to-IPv4 emulation layer onto the native layer
  1.1361 +            ipv6Supported = PR_GetIdentitiesLayer(tmpfd, PR_NSPR_IO_LAYER) == tmpfd;
  1.1362 +            PR_Close(tmpfd);
  1.1363 +        }
  1.1364 +
  1.1365 +        nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
  1.1366 +        nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
  1.1367 +
  1.1368 +        nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
  1.1369 +        nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue;
  1.1370 +        nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll;
  1.1371 +        nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
  1.1372 +        nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
  1.1373 +        nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
  1.1374 +        nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
  1.1375 +        nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
  1.1376 +        nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
  1.1377 +        nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
  1.1378 +
  1.1379 +        firstTime = false;
  1.1380 +
  1.1381 +#if defined(PR_LOGGING)
  1.1382 +        gSOCKSLog = PR_NewLogModule("SOCKS");
  1.1383 +#endif
  1.1384 +
  1.1385 +    }
  1.1386 +
  1.1387 +    LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
  1.1388 +
  1.1389 +    PRFileDesc *layer;
  1.1390 +    PRStatus rv;
  1.1391 +
  1.1392 +    layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
  1.1393 +    if (! layer)
  1.1394 +    {
  1.1395 +        LOGERROR(("PR_CreateIOLayerStub() failed."));
  1.1396 +        return NS_ERROR_FAILURE;
  1.1397 +    }
  1.1398 +
  1.1399 +    nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo();
  1.1400 +    if (!infoObject)
  1.1401 +    {
  1.1402 +        // clean up IOLayerStub
  1.1403 +        LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
  1.1404 +        PR_DELETE(layer);
  1.1405 +        return NS_ERROR_FAILURE;
  1.1406 +    }
  1.1407 +
  1.1408 +    NS_ADDREF(infoObject);
  1.1409 +    infoObject->Init(socksVersion, family, proxy, host, flags);
  1.1410 +    layer->secret = (PRFilePrivate*) infoObject;
  1.1411 +    rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
  1.1412 +
  1.1413 +    if (rv == PR_FAILURE) {
  1.1414 +        LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
  1.1415 +        NS_RELEASE(infoObject);
  1.1416 +        PR_DELETE(layer);
  1.1417 +        return NS_ERROR_FAILURE;
  1.1418 +    }
  1.1419 +
  1.1420 +    *info = static_cast<nsISOCKSSocketInfo*>(infoObject);
  1.1421 +    NS_ADDREF(*info);
  1.1422 +    return NS_OK;
  1.1423 +}

mercurial