Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff -r 59f917fd649f netwerk/socket/base/nsSOCKSIOLayer.cpp
- --- a/netwerk/socket/base/nsSOCKSIOLayer.cpp Fri Oct 23 09:50:43 2009 -0700
- +++ b/netwerk/socket/base/nsSOCKSIOLayer.cpp Sun Oct 25 20:37:15 2009 -0700
- @@ -63,48 +63,101 @@ static PRLogModuleInfo *gSOCKSLog;
- #else
- #define LOGDEBUG(args)
- #define LOGERROR(args)
- #endif
- class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
- {
- + enum State {
- + SOCKS_INITIAL,
- + SOCKS_CONNECTING_TO_PROXY,
- + SOCKS4_WANT_CONNECT_OK,
- + SOCKS5_WANT_AUTH_NONE,
- + SOCKS5_WANT_CONNECT_OK,
- + SOCKS_CONNECTED
- + };
- +
- public:
- nsSOCKSSocketInfo();
- virtual ~nsSOCKSSocketInfo() {}
- NS_DECL_ISUPPORTS
- NS_DECL_NSISOCKSSOCKETINFO
- void Init(PRInt32 version,
- const char *proxyHost,
- PRInt32 proxyPort,
- const char *destinationHost,
- PRUint32 flags);
- +
- + const char *StateToStr() const;
- + PRStatus DoHandshake(PRFileDesc *fd, PRInt16 oflags = -1,
- + PRIntervalTime to = PR_INTERVAL_NO_TIMEOUT);
- + PRInt16 HowToPoll() const { return mPollFlags; }
- + PRInt32 IsConnected() const { return mState == SOCKS_CONNECTED; }
- const nsCString &DestinationHost() { return mDestinationHost; }
- const nsCString &ProxyHost() { return mProxyHost; }
- PRInt32 ProxyPort() { return mProxyPort; }
- PRInt32 Version() { return mVersion; }
- PRUint32 Flags() { return mFlags; }
- private:
- + PRStatus SendGreeting(PRFileDesc *fd);
- + PRStatus ConnectToProxy(PRFileDesc *fd, PRIntervalTime to);
- + PRStatus ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags);
- + PRStatus HandleSocks4Connect(PRFileDesc *fd);
- + PRStatus HandleSocks5Auth(PRFileDesc *fd);
- + PRStatus HandleSocks5Connect(PRFileDesc *fd);
- +
- + void PushUint8(PRUint8 d);
- + void PushUint16(PRUint16 d);
- + void PushUint32(PRUint32 d);
- + void PushNetAddr(const PRNetAddr *addr);
- + void PushNetPort(const PRNetAddr *addr);
- +
- + PRUint8 PopUint8();
- + PRUint16 PopUint16();
- + PRUint32 PopUint32();
- + void PopNetAddr(PRNetAddr *addr, PRUint16 fam);
- + void PopNetPort(PRNetAddr *addr);
- +
- + void WantRead(PRInt32 sz);
- + PRStatus Fill(PRFileDesc *fd);
- + PRStatus Flush(PRFileDesc *fd);
- +
- +private:
- + State mState;
- + nsCString mInBuf;
- + nsCString mOutBuf;
- + PRInt32 mAmountToRead;
- + PRUint32 mPopOffset;
- + char * mReadPtr;
- + PRInt16 mPollFlags;
- + nsCOMPtr<nsIDNSRecord> mDnsRec;
- +
- nsCString mDestinationHost;
- nsCString mProxyHost;
- PRInt32 mProxyPort;
- PRInt32 mVersion; // SOCKS version 4 or 5
- PRUint32 mFlags;
- PRNetAddr mInternalProxyAddr;
- PRNetAddr mExternalProxyAddr;
- PRNetAddr mDestinationAddr;
- };
- nsSOCKSSocketInfo::nsSOCKSSocketInfo()
- - : mProxyPort(-1)
- + : mState(SOCKS_INITIAL)
- + , mAmountToRead(0)
- + , mPopOffset(0)
- + , mReadPtr(nsnull)
- + , mPollFlags(0)
- + , mProxyPort(-1)
- , mVersion(-1)
- , mFlags(0)
- {
- PR_InitializeNetAddr(PR_IpAddrAny, 0, &mInternalProxyAddr);
- PR_InitializeNetAddr(PR_IpAddrAny, 0, &mExternalProxyAddr);
- PR_InitializeNetAddr(PR_IpAddrAny, 0, &mDestinationAddr);
- }
- @@ -157,647 +210,667 @@ nsSOCKSSocketInfo::GetInternalProxyAddr(
- NS_IMETHODIMP
- nsSOCKSSocketInfo::SetInternalProxyAddr(PRNetAddr *aInternalProxyAddr)
- {
- memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(PRNetAddr));
- return NS_OK;
- }
- -static PRInt32
- -pr_RecvAll(PRFileDesc *fd, unsigned char *buf, PRInt32 amount, PRIntn flags,
- - PRIntervalTime *timeout)
- +PRStatus
- +nsSOCKSSocketInfo::SendGreeting(PRFileDesc *fd)
- {
- - PRInt32 bytesRead = 0;
- - PRInt32 offset = 0;
- + NS_ABORT_IF_FALSE(mVersion == 4 || mVersion == 5,
- + "SOCKS version must be 4 or 5!");
- + NS_ABORT_IF_FALSE(mState <= SOCKS_CONNECTING_TO_PROXY,
- + "Can only send SOCKS greeting in initial state");
- +
- + if (mVersion == 4) {
- + PRNetAddr *addr = &mDestinationAddr;
- + PRInt32 proxy_resolve;
- + proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
- - while (offset < amount) {
- - PRIntervalTime start_time = PR_IntervalNow();
- - bytesRead = PR_Recv(fd, buf + offset, amount - offset, flags, *timeout);
- - PRIntervalTime elapsed = PR_IntervalNow() - start_time;
- + LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
- + proxy_resolve? "yes" : "no"));
- - if (elapsed > *timeout) {
- - *timeout = 0;
- - } else {
- - *timeout -= elapsed;
- + // Send a SOCKS 4 connect request.
- + PushUint8(0x04); // version -- 4
- + PushUint8(0x01); // command -- connect
- + PushNetPort(addr);
- + if (proxy_resolve) {
- + // Add the full name, null-terminated, to the request
- + // according to SOCKS 4a
- + PushUint32(PR_htonl(0x00000001));
- + PushUint8(0x00); // Send an emtpy username
- + mOutBuf.Append(mDestinationHost);
- + PushUint8(0x00);
- + } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
- + // Add the IPv4 address
- + PushNetAddr(addr);
- + PushUint8(0x00); // Send an emtpy username
- + } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
- + LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!"));
- + PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
- + return PR_FAILURE;
- }
- +
- + WantRead(8);
- + mState = SOCKS4_WANT_CONNECT_OK;
- + } else {
- + // Send an initial SOCKS 5 greeting
- + LOGDEBUG(("socks5: sending auth methods"));
- + PushUint8(0x05); // version -- 5
- + PushUint8(0x01); // # auth methods -- 1
- + PushUint8(0x00); // we don't support authentication
- - if (bytesRead > 0) {
- - offset += bytesRead;
- - } else if (bytesRead == 0 || offset != 0) {
- - return offset;
- - } else {
- - return bytesRead;
- - }
- + WantRead(2);
- + mState = SOCKS5_WANT_AUTH_NONE;
- + }
- - if (*timeout == 0) {
- - LOGERROR(("PR_Recv() timed out. amount = %d. offset = %d.",
- - amount, offset));
- - return offset;
- - }
- - }
- - return offset;
- + return Flush(fd);
- }
- -static PRInt32
- -pr_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
- - PRIntervalTime *timeout)
- +PRStatus
- +nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd, PRIntervalTime to)
- {
- - PRIntervalTime start_time = PR_IntervalNow();
- - PRInt32 retval = PR_Send(fd, buf, amount, flags, *timeout);
- - PRIntervalTime elapsed = PR_IntervalNow() - start_time;
- + PRStatus status;
- + nsresult rv;
- - if (elapsed > *timeout) {
- - *timeout = 0;
- - LOGERROR(("PR_Send() timed out. amount = %d. retval = %d.",
- - amount, retval));
- - return retval;
- - } else {
- - *timeout -= elapsed;
- - }
- + NS_ABORT_IF_FALSE(mState == SOCKS_INITIAL,
- + "Must be in initial state to make connection!");
- - if (retval <= 0) {
- - LOGERROR(("PR_Send() failed. amount = %d. retval = %d.",
- - amount, retval));
- - }
- + // If we haven't performed the DNS lookup, do that now.
- + if (!mDnsRec) {
- + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
- + if (!dns)
- + return PR_FAILURE;
- - return retval;
- -}
- -
- -// Negotiate a SOCKS 5 connection. Assumes the TCP connection to the socks
- -// server port has been established.
- -static nsresult
- -ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRIntervalTime timeout)
- -{
- - int request_len = 0;
- - int response_len = 0;
- - int desired_len = 0;
- - unsigned char request[22];
- - unsigned char response[262];
- -
- - NS_ENSURE_TRUE(fd, NS_ERROR_NOT_INITIALIZED);
- - NS_ENSURE_TRUE(addr, NS_ERROR_NOT_INITIALIZED);
- - NS_ENSURE_TRUE(extAddr, NS_ERROR_NOT_INITIALIZED);
- -
- - request[0] = 0x05; // SOCKS version 5
- - request[1] = 0x01; // number of auth procotols we recognize
- - // auth protocols
- - request[2] = 0x00; // no authentication required
- - // compliant implementations MUST implement GSSAPI
- - // and SHOULD implement username/password and MAY
- - // implement CHAP
- - // TODO: we don't implement these
- - //request[3] = 0x01; // GSSAPI
- - //request[4] = 0x02; // username/password
- - //request[5] = 0x03; // CHAP
- -
- - request_len = 2 + request[1];
- - int write_len = pr_Send(fd, request, request_len, 0, &timeout);
- - if (write_len != request_len) {
- - return NS_ERROR_FAILURE;
- - }
- -
- - // get the server's response.
- - desired_len = 2;
- - response_len = pr_RecvAll(fd, response, desired_len, 0, &timeout);
- -
- - if (response_len < desired_len) {
- - LOGERROR(("pr_RecvAll() failed. response_len = %d.", response_len));
- - return NS_ERROR_FAILURE;
- - }
- -
- - if (response[0] != 0x05) {
- - // it's a either not SOCKS or not our version
- - LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
- - return NS_ERROR_FAILURE;
- - }
- - switch (response[1]) {
- - case 0x00:
- - // no auth
- - break;
- - case 0x01:
- - // GSSAPI
- - // TODO: implement
- - LOGERROR(("Server want to use GSSAPI to authenticate, but we don't support it."));
- - return NS_ERROR_FAILURE;
- - case 0x02:
- - // username/password
- - // TODO: implement
- - LOGERROR(("Server want to use username/password to authenticate, but we don't support it."));
- - return NS_ERROR_FAILURE;
- - case 0x03:
- - // CHAP
- - // TODO: implement?
- - LOGERROR(("Server want to use CHAP to authenticate, but we don't support it."));
- - return NS_ERROR_FAILURE;
- - default:
- - // unrecognized auth method
- - LOGERROR(("Uncrecognized authentication method received: %x", response[1]));
- - return NS_ERROR_FAILURE;
- - }
- -
- - // we are now authenticated, so lets tell
- - // the server where to connect to
- -
- - request_len = 0;
- -
- - request[0] = 0x05; // SOCKS version 5
- - request[1] = 0x01; // CONNECT command
- - request[2] = 0x00; // obligatory reserved field (perfect for MS tampering!)
- -
- - // get destination port
- - PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
- - nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
- -
- - if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
- -
- - LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
- -
- - // if the PROXY_RESOLVES_HOST flag is set, we assume
- - // that the transport wants us to pass the SOCKS server the
- - // hostname and port and let it do the name resolution.
- -
- - // the real destination hostname and port was stored
- - // in our info object earlier when this layer was created.
- -
- - const nsCString& destHost = info->DestinationHost();
- -
- - LOGDEBUG(("host:port -> %s:%li", destHost.get(), destPort));
- -
- - request[3] = 0x03; // encoding of destination address (3 == hostname)
- -
- - int host_len = destHost.Length();
- - if (host_len > 255) {
- - // SOCKS5 transmits the length of the hostname in a single char.
- - // This gives us an absolute limit of 255 chars in a hostname, and
- - // there's nothing we can do to extend it. I don't think many
- - // hostnames will ever be bigger than this, so hopefully it's an
- - // uneventful abort condition.
- - LOGERROR (("Hostname too big for SOCKS5."));
- - return NS_ERROR_INVALID_ARG;
- - }
- - request[4] = (char) host_len;
- - request_len = 5;
- -
- - // Send the initial header first...
- - write_len = pr_Send(fd, request, request_len, 0, &timeout);
- - if (write_len != request_len) {
- - // bad write
- - return NS_ERROR_FAILURE;
- - }
- -
- - // Now send the hostname...
- - write_len = pr_Send(fd, destHost.get(), host_len, 0, &timeout);
- - if (write_len != host_len) {
- - // bad write
- - return NS_ERROR_FAILURE;
- - }
- -
- - // There's no data left because we just sent it.
- - request_len = 0;
- -
- - } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
- -
- - request[3] = 0x01; // encoding of destination address (1 == IPv4)
- - request_len = 8; // 4 for address, 4 SOCKS headers
- -
- - char * ip = (char*)(&addr->inet.ip);
- - request[4] = *ip++;
- - request[5] = *ip++;
- - request[6] = *ip++;
- - request[7] = *ip++;
- -
- - } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
- -
- - request[3] = 0x04; // encoding of destination address (4 == IPv6)
- - request_len = 20; // 16 for address, 4 SOCKS headers
- -
- - char * ip = (char*)(&addr->ipv6.ip.pr_s6_addr);
- - request[4] = *ip++; request[5] = *ip++;
- - request[6] = *ip++; request[7] = *ip++;
- - request[8] = *ip++; request[9] = *ip++;
- - request[10] = *ip++; request[11] = *ip++;
- - request[12] = *ip++; request[13] = *ip++;
- - request[14] = *ip++; request[15] = *ip++;
- - request[16] = *ip++; request[17] = *ip++;
- - request[18] = *ip++; request[19] = *ip++;
- -
- - // we're going to test to see if this address can
- - // be mapped back into IPv4 without loss. if so,
- - // we'll use IPv4 instead, as reliable SOCKS server
- - // support for IPv6 is probably questionable.
- -
- - if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
- - request[3] = 0x01; // ipv4 encoding
- - request[4] = request[16];
- - request[5] = request[17];
- - request[6] = request[18];
- - request[7] = request[19];
- - request_len -= 12;
- - }
- - } else {
- - // Unknown address type
- - LOGERROR(("Don't know what kind of IP address this is."));
- - return NS_ERROR_FAILURE;
- - }
- -
- - // add the destination port to the request
- - request[request_len] = (unsigned char)(destPort >> 8);
- - request[request_len+1] = (unsigned char)destPort;
- - request_len += 2;
- -
- - write_len = pr_Send(fd, request, request_len, 0, &timeout);
- - if (write_len != request_len) {
- - // bad write
- - return NS_ERROR_FAILURE;
- - }
- -
- - desired_len = 5;
- - response_len = pr_RecvAll(fd, response, desired_len, 0, &timeout);
- - if (response_len < desired_len) { // bad read
- - LOGERROR(("pr_RecvAll() failed getting connect command reply. response_len = %d.", response_len));
- - return NS_ERROR_FAILURE;
- - }
- -
- - if (response[0] != 0x05) {
- - // bad response
- - LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
- - return NS_ERROR_FAILURE;
- - }
- -
- - switch(response[1]) {
- - case 0x00: break; // success
- - case 0x01: LOGERROR(("SOCKS 5 server rejected connect request: 01, General SOCKS server failure."));
- - return NS_ERROR_FAILURE;
- - case 0x02: LOGERROR(("SOCKS 5 server rejected connect request: 02, Connection not allowed by ruleset."));
- - return NS_ERROR_FAILURE;
- - case 0x03: LOGERROR(("SOCKS 5 server rejected connect request: 03, Network unreachable."));
- - return NS_ERROR_FAILURE;
- - case 0x04: LOGERROR(("SOCKS 5 server rejected connect request: 04, Host unreachable."));
- - return NS_ERROR_FAILURE;
- - case 0x05: LOGERROR(("SOCKS 5 server rejected connect request: 05, Connection refused."));
- - return NS_ERROR_FAILURE;
- - case 0x06: LOGERROR(("SOCKS 5 server rejected connect request: 06, TTL expired."));
- - return NS_ERROR_FAILURE;
- - case 0x07: LOGERROR(("SOCKS 5 server rejected connect request: 07, Command not supported."));
- - return NS_ERROR_FAILURE;
- - case 0x08: LOGERROR(("SOCKS 5 server rejected connect request: 08, Address type not supported."));
- - return NS_ERROR_FAILURE;
- - default: LOGERROR(("SOCKS 5 server rejected connect request: %x.", response[1]));
- - return NS_ERROR_FAILURE;
- -
- -
- - }
- -
- - switch (response[3]) {
- - case 0x01: // IPv4
- - desired_len = 4 + 2 - 1;
- - break;
- - case 0x03: // FQDN
- - desired_len = response[4] + 2;
- - break;
- - case 0x04: // IPv6
- - desired_len = 16 + 2 - 1;
- - break;
- - default: // unknown format
- - return NS_ERROR_FAILURE;
- - break;
- - }
- - response_len = pr_RecvAll(fd, response + 5, desired_len, 0, &timeout);
- - if (response_len < desired_len) { // bad read
- - LOGERROR(("pr_RecvAll() failed getting connect command reply. response_len = %d.", response_len));
- - return NS_ERROR_FAILURE;
- - }
- - response_len += 5;
- -
- - // get external bound address (this is what
- - // the outside world sees as "us")
- - char *ip = nsnull;
- - PRUint16 extPort = 0;
- -
- - switch (response[3]) {
- - case 0x01: // IPv4
- -
- - extPort = (response[8] << 8) | response[9];
- -
- - PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, extPort, extAddr);
- -
- - ip = (char*)(&extAddr->inet.ip);
- - *ip++ = response[4];
- - *ip++ = response[5];
- - *ip++ = response[6];
- - *ip++ = response[7];
- -
- - break;
- - case 0x04: // IPv6
- -
- - extPort = (response[20] << 8) | response[21];
- -
- - PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, extPort, extAddr);
- -
- - ip = (char*)(&extAddr->ipv6.ip.pr_s6_addr);
- - *ip++ = response[4]; *ip++ = response[5];
- - *ip++ = response[6]; *ip++ = response[7];
- - *ip++ = response[8]; *ip++ = response[9];
- - *ip++ = response[10]; *ip++ = response[11];
- - *ip++ = response[12]; *ip++ = response[13];
- - *ip++ = response[14]; *ip++ = response[15];
- - *ip++ = response[16]; *ip++ = response[17];
- - *ip++ = response[18]; *ip++ = response[19];
- -
- - break;
- - case 0x03: // FQDN
- - // if we get here, we don't know our external address.
- - // however, as that's possibly not critical to the user,
- - // we let it slide.
- - extPort = (response[response_len - 2] << 8) |
- - response[response_len - 1];
- - PR_InitializeNetAddr(PR_IpAddrNull, extPort, extAddr);
- - break;
- - }
- - return NS_OK;
- -}
- -
- -// Negotiate a SOCKS 4 connection. Assumes the TCP connection to the socks
- -// server port has been established.
- -static nsresult
- -ConnectSOCKS4(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
- -{
- - int request_len = 0;
- - int write_len;
- - int response_len = 0;
- - int desired_len = 0;
- - char *ip = nsnull;
- - unsigned char request[12];
- - unsigned char response[10];
- -
- - NS_ENSURE_TRUE(fd, NS_ERROR_NOT_INITIALIZED);
- - NS_ENSURE_TRUE(addr, NS_ERROR_NOT_INITIALIZED);
- -
- - request[0] = 0x04; // SOCKS version 4
- - request[1] = 0x01; // CD command code -- 1 for connect
- -
- - // destination port
- - PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
- -
- - // store the port
- - request[2] = (unsigned char)(destPort >> 8);
- - request[3] = (unsigned char)destPort;
- -
- - // username
- - request[8] = 'M';
- - request[9] = 'O';
- - request[10] = 'Z';
- -
- - request[11] = 0x00;
- -
- - request_len = 12;
- -
- - nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
- -
- - if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
- -
- - LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
- -
- - // if the PROXY_RESOLVES_HOST flag is set, we assume that the
- - // transport wants us to pass the SOCKS server the hostname
- - // and port and let it do the name resolution.
- -
- - // an extension to SOCKS 4, called 4a, specifies a way
- - // to do this, so we'll try that and hope the
- - // server supports it.
- -
- - // the real destination hostname and port was stored
- - // in our info object earlier when this layer was created.
- -
- - const nsCString& destHost = info->DestinationHost();
- -
- - LOGDEBUG(("host:port -> %s:%li\n", destHost.get(), destPort));
- -
- - // the IP portion of the query is set to this special address.
- - request[4] = 0;
- - request[5] = 0;
- - request[6] = 0;
- - request[7] = 1;
- -
- - write_len = pr_Send(fd, request, request_len, 0, &timeout);
- - if (write_len != request_len) {
- - return NS_ERROR_FAILURE;
- - }
- -
- - // Remember the NULL.
- - int host_len = destHost.Length() + 1;
- -
- - write_len = pr_Send(fd, destHost.get(), host_len, 0, &timeout);
- - if (write_len != host_len) {
- - return NS_ERROR_FAILURE;
- - }
- -
- - // No data to send, just sent it.
- - request_len = 0;
- -
- - } else if (PR_NetAddrFamily(addr) == PR_AF_INET) { // IPv4
- -
- - // store the ip
- - ip = (char*)(&addr->inet.ip);
- - request[4] = *ip++;
- - request[5] = *ip++;
- - request[6] = *ip++;
- - request[7] = *ip++;
- -
- - } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) { // IPv6
- -
- - // IPv4 address encoded in an IPv6 address
- - if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
- - // store the ip
- - ip = (char*)(&addr->ipv6.ip.pr_s6_addr[12]);
- - request[4] = *ip++;
- - request[5] = *ip++;
- - request[6] = *ip++;
- - request[7] = *ip++;
- - } else {
- - LOGERROR(("IPv6 is not supported in SOCKS 4."));
- - return NS_ERROR_FAILURE; // SOCKS 4 can't do IPv6
- - }
- -
- - } else {
- - LOGERROR(("Don't know what kind of IP address this is."));
- - return NS_ERROR_FAILURE; // don't recognize this type
- - }
- -
- - if (request_len > 0) {
- - write_len = pr_Send(fd, request, request_len, 0, &timeout);
- - if (write_len != request_len) {
- - return NS_ERROR_FAILURE;
- + rv = dns->Resolve(mProxyHost, 0, getter_AddRefs(mDnsRec));
- + if (NS_FAILED(rv)) {
- + LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed",
- + mProxyHost.get()));
- + return PR_FAILURE;
- }
- }
- - // get the server's response
- - desired_len = 8; // size of the response
- - response_len = pr_RecvAll(fd, response, desired_len, 0, &timeout);
- - if (response_len < desired_len) {
- - LOGERROR(("pr_RecvAll() failed. response_len = %d.", response_len));
- - return NS_ERROR_FAILURE;
- + do {
- + rv = mDnsRec->GetNextAddr(mProxyPort, &mInternalProxyAddr);
- + // No more addresses?
- + if (NS_FAILED(rv)) {
- + LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
- + mProxyHost.get()));
- + return PR_FAILURE;
- + }
- +
- +#if defined(PR_LOGGING)
- + char buf[64];
- + PR_NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf));
- + LOGDEBUG(("socks: trying proxy server, %s:%hu",
- + buf, PR_ntohs(PR_NetAddrInetPort(&mInternalProxyAddr))));
- +#endif
- + status = fd->lower->methods->connect(fd->lower,
- + &mInternalProxyAddr, to);
- + 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;
- + mPollFlags = PR_POLL_EXCEPT | PR_POLL_WRITE;
- + return status;
- + }
- + }
- + } while (status != PR_SUCCESS);
- +
- + // Connected now, send greeting
- + return SendGreeting(fd);
- +}
- +
- +PRStatus
- +nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags)
- +{
- + PRStatus status;
- +
- + NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
- + "Continuing connection in wrong state!");
- +
- + LOGDEBUG(("socks: continuing connection to proxy"));
- +
- + 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
- + mState = SOCKS_INITIAL;
- + return ConnectToProxy(fd, PR_INTERVAL_NO_TIMEOUT);
- + }
- +
- + // We're still connecting
- + mPollFlags = PR_POLL_EXCEPT | PR_POLL_WRITE;
- + return PR_FAILURE;
- }
- - if ((response[0] != 0x00) && (response[0] != 0x04)) {
- - // Novell BorderManager sends a response of type 4, should be zero
- - // According to the spec. Cope with this brokenness.
- - // it's not a SOCKS 4 reply or version 0 of the reply code
- - LOGERROR(("Not a SOCKS 4 reply. Expected: 0; received: %x.", response[0]));
- - return NS_ERROR_FAILURE;
- + // Connected now, send greeting
- + return SendGreeting(fd);
- +}
- +
- +PRStatus
- +nsSOCKSSocketInfo::HandleSocks4Connect(PRFileDesc *fd)
- +{
- + NS_ABORT_IF_FALSE(mState == SOCKS4_WANT_CONNECT_OK,
- + "Handling SOCKS 4 connection reply in wrong state!");
- + NS_ABORT_IF_FALSE(mInBuf.Length() == 8,
- + "SOCKS 4 connection reply must be 8 bytes!");
- +
- + LOGDEBUG(("socks4: checking connection reply"));
- +
- + // ignore version number
- + mPopOffset = 1;
- +
- + // see if our request was granted
- + if (PopUint8() == 90) {
- + LOGDEBUG(("socks4: connection successful!"));
- + mInBuf.Truncate();
- + mState = SOCKS_CONNECTED;
- + return PR_SUCCESS;
- }
- - if (response[1] != 0x5A) { // = 90: request granted
- - // connect request not granted
- - LOGERROR(("Connection request refused. Expected: 90; received: %d.", response[1]));
- - return NS_ERROR_FAILURE;
- - }
- -
- - return NS_OK;
- -
- + LOGERROR(("socks4: unable to connect"));
- + PR_SetError(PR_CONNECT_REFUSED_ERROR, 0);
- + return PR_FAILURE;
- }
- +PRStatus
- +nsSOCKSSocketInfo::HandleSocks5Auth(PRFileDesc *fd)
- +{
- + NS_ABORT_IF_FALSE(mState == SOCKS5_WANT_AUTH_NONE,
- + "Handling SOCKS 5 auth method reply in wrong state!");
- + NS_ABORT_IF_FALSE(mInBuf.Length() == 2,
- + "SOCKS 5 auth method reply must be 2 bytes!");
- +
- + LOGDEBUG(("socks5: checking auth method reply"));
- +
- + // ignore version number
- + mPopOffset = 1;
- +
- + // make sure our authentication choice was accepted
- + if (PopUint8() != 0x00) {
- + LOGERROR(("socks5: server did not accept our authentication method"));
- + PR_SetError(PR_CONNECT_REFUSED_ERROR, 0);
- + return PR_FAILURE;
- + }
- +
- + mInBuf.Truncate();
- +
- + // send socks 5 connect request
- + PRNetAddr *addr = &mDestinationAddr;
- + PRInt32 proxy_resolve;
- + proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
- +
- + LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
- + proxy_resolve? "yes" : "no"));
- +
- + PushUint8(0x05); // version -- 5
- + PushUint8(0x01); // command -- connect
- + PushUint8(0x00); // reserved
- +
- + if (proxy_resolve) {
- + // add the host name
- + if (mDestinationHost.Length() > 0xff) {
- + LOGERROR(("socks5: destination host name is too long!"));
- + PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
- + return PR_FAILURE;
- + }
- + PushUint8(0x03); // addr type -- domainname
- + PushUint8(mDestinationHost.Length()); // name length
- + mOutBuf.Append(mDestinationHost); //
- + } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
- + PushUint8(0x01); // addr type -- IPv4
- + PushNetAddr(addr);
- + } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
- + PushUint8(0x04); // addr type -- IPv6
- + PushNetAddr(addr);
- + }
- +
- + PushNetPort(addr); // port
- +
- + mState = SOCKS5_WANT_CONNECT_OK;
- + WantRead(5);
- +
- + return Flush(fd);
- +}
- +
- +PRStatus
- +nsSOCKSSocketInfo::HandleSocks5Connect(PRFileDesc *fd)
- +{
- + PRUint8 res;
- + PRUint32 len;
- +
- + NS_ABORT_IF_FALSE(mState == SOCKS5_WANT_CONNECT_OK,
- + "Handling SOCKS 5 auth method reply in wrong state!");
- + NS_ABORT_IF_FALSE(mInBuf.Length() >= 5,
- + "SOCKS 5 connection reply must be at least 5 bytes!");
- +
- + LOGDEBUG(("socks5: checking connection reply"));
- +
- + // ignore version number
- + mPopOffset = 1;
- +
- + // check response
- + res = PopUint8();
- + 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;
- + }
- +
- + PR_SetError(c, 0);
- + return PR_FAILURE;
- + }
- +
- + // Ignore reserved field
- + mPopOffset++;
- +
- + // Determine address length
- + res = PopUint8();
- + switch (res) {
- + case 0x01: // ipv4
- + len = 4;
- + break;
- + case 0x04: // ipv6
- + len = 16;
- + break;
- + case 0x03: // fqdn
- + len = PopUint8() + 1;
- + break;
- + }
- +
- + // XXX check arithmatic
- + if (mInBuf.Length() < len + 6) {
- + // we've read in 5 bytes already
- + WantRead(len + 1);
- + // Attempt to read the rest immediately
- + if (Fill(fd) != PR_SUCCESS) {
- + LOGDEBUG(("socks5: need to wait to read addr and port"));
- + return PR_FAILURE;
- + }
- + }
- +
- + LOGDEBUG(("socks5: loading addr and port"));
- + // now read the address and port
- + switch (res) {
- + case 0x01: // ipv4
- + PopNetAddr(&mExternalProxyAddr, PR_AF_INET);
- + break;
- + case 0x04: // ipv6
- + PopNetAddr(&mExternalProxyAddr, PR_AF_INET6);
- + break;
- + case 0x03: // fqdn (skip)
- + mPopOffset += len - 1;
- + mExternalProxyAddr.raw.family = PR_AF_INET6;
- + break;
- + }
- +
- + PopNetPort(&mExternalProxyAddr);
- +
- + mInBuf.Truncate();
- + mState = SOCKS_CONNECTED;
- + LOGDEBUG(("socks5: connected!"));
- +
- + return PR_SUCCESS;
- +}
- +
- +const char *
- +nsSOCKSSocketInfo::StateToStr() const
- +{
- + switch (mState) {
- + case SOCKS_INITIAL:
- + return "SOCKS_INITIAL";
- + case SOCKS_CONNECTING_TO_PROXY:
- + return "SOCKS_CONNECTING_TO_PROXY";
- + case SOCKS4_WANT_CONNECT_OK:
- + return "SOCKS4_WANT_CONNECT_OK";
- + case SOCKS5_WANT_AUTH_NONE:
- + return "SOCKS5_WANT_AUTH_NONE";
- + case SOCKS5_WANT_CONNECT_OK:
- + return "SOCKS5_WANT_CONNECT_OK";
- + case SOCKS_CONNECTED:
- + return "SOCKS_CONNECTED";
- + }
- +
- + return "???";
- +}
- +
- +PRStatus
- +nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, PRInt16 oflags,
- + PRIntervalTime to)
- +{
- + PRStatus ret;
- +
- + LOGDEBUG(("socks: DoHandshake(), state %s", StateToStr()));
- +
- + // Flush all pending data for a SOCKS request, then read the
- + // response. If we're still connecting to the SOCKS proxy and
- + // there's no I/O to do yet, Flush() and Fill() will simply return
- + // PR_SUCCESS
- + //
- + ret = Flush(fd);
- + if (ret != PR_SUCCESS)
- + return ret;
- +
- + ret = Fill(fd);
- + if (ret != PR_SUCCESS)
- + return ret;
- +
- + mPollFlags = 0;
- +
- + switch (mState) {
- + case SOCKS_INITIAL:
- + return ConnectToProxy(fd, to);
- + case SOCKS_CONNECTING_TO_PROXY:
- + return ContinueConnectingToProxy(fd, oflags);
- + case SOCKS4_WANT_CONNECT_OK:
- + return HandleSocks4Connect(fd);
- + case SOCKS5_WANT_AUTH_NONE:
- + return HandleSocks5Auth(fd);
- + case SOCKS5_WANT_CONNECT_OK:
- + return HandleSocks5Connect(fd);
- + case SOCKS_CONNECTED:
- + LOGERROR(("socks: already connected"));
- + PR_SetError(PR_IS_CONNECTED_ERROR, 0);
- + return PR_FAILURE;
- + }
- +
- + LOGERROR(("socks: executing handshake in invalid state, %d", mState));
- + PR_SetError(PR_INVALID_STATE_ERROR, 0);
- + return PR_FAILURE;
- +}
- +
- +void
- +nsSOCKSSocketInfo::PushUint8(PRUint8 v)
- +{
- + mOutBuf.Append((char)v);
- +}
- +
- +void
- +nsSOCKSSocketInfo::PushUint16(PRUint16 v)
- +{
- + mOutBuf.Append((char*)&v, sizeof(v));
- +}
- +
- +void
- +nsSOCKSSocketInfo::PushUint32(PRUint32 v)
- +{
- + mOutBuf.Append((char*)&v, sizeof(v));
- +}
- +
- +void
- +nsSOCKSSocketInfo::PushNetAddr(const PRNetAddr *addr)
- +{
- + if (PR_NetAddrFamily(addr) == PR_AF_INET) {
- + const char *ip = (const char*)&addr->inet.ip;
- + mOutBuf.Append(ip, sizeof(addr->inet.ip));
- + } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
- + const char *ip = (const char*)addr->ipv6.ip.pr_s6_addr;
- + mOutBuf.Append(ip, sizeof(addr->ipv6.ip.pr_s6_addr));
- + }
- +}
- +
- +void
- +nsSOCKSSocketInfo::PushNetPort(const PRNetAddr *addr)
- +{
- + PushUint16(PR_NetAddrInetPort(addr));
- +}
- +
- +PRUint8
- +nsSOCKSSocketInfo::PopUint8()
- +{
- + PRUint8 rv;
- + NS_ABORT_IF_FALSE(mPopOffset + sizeof(rv) <= mInBuf.Length(),
- + "Not enough space to pop a uint8!");
- + rv = (PRUint8)mInBuf[mPopOffset];
- + mPopOffset += sizeof(rv);
- + return rv;
- +}
- +
- +PRUint16
- +nsSOCKSSocketInfo::PopUint16()
- +{
- + PRUint16 rv;
- + NS_ABORT_IF_FALSE(mPopOffset + sizeof(rv) <= mInBuf.Length(),
- + "Not enough space to pop a uint16!");
- + memcpy(&rv, mInBuf.Data() + mPopOffset, sizeof(rv));
- + mPopOffset += sizeof(rv);
- + return rv;
- +}
- +
- +PRUint32
- +nsSOCKSSocketInfo::PopUint32()
- +{
- + PRUint32 rv;
- + NS_ABORT_IF_FALSE(mPopOffset + sizeof(rv) <= mInBuf.Length(),
- + "Not enough space to pop a uint32!");
- + memcpy(&rv, mInBuf.Data() + mPopOffset, sizeof(rv));
- + mPopOffset += sizeof(rv);
- + return rv;
- +}
- +
- +void
- +nsSOCKSSocketInfo::PopNetAddr(PRNetAddr *addr, PRUint16 fam)
- +{
- + PRUint32 amt;
- + const char *ip = mInBuf.Data() + mPopOffset;
- +
- + addr->raw.family = fam;
- + if (fam == PR_AF_INET) {
- + amt = sizeof(addr->inet.ip);
- + NS_ABORT_IF_FALSE(mPopOffset + amt <= mInBuf.Length(),
- + "Not enough space to pop an ipv4 addr!");
- + memcpy(&addr->inet.ip, ip, amt);
- + } else if (fam == PR_AF_INET6) {
- + amt = sizeof(addr->ipv6.ip.pr_s6_addr);
- + NS_ABORT_IF_FALSE(mPopOffset + amt <= mInBuf.Length(),
- + "Not enough space to pop an ipv6 addr!");
- + memcpy(addr->ipv6.ip.pr_s6_addr, ip, amt);
- + }
- +
- + mPopOffset += amt;
- +}
- +
- +void
- +nsSOCKSSocketInfo::PopNetPort(PRNetAddr *addr)
- +{
- + addr->inet.port = PopUint16();
- +}
- +
- +void
- +nsSOCKSSocketInfo::WantRead(PRInt32 sz)
- +{
- + NS_ABORT_IF_FALSE(mReadPtr == nsnull,
- + "WantRead() called while read already in progress!");
- +
- + PRUint32 oldsz = mInBuf.Length();
- + mInBuf.GetMutableData(&mReadPtr, oldsz + sz);
- + mReadPtr += oldsz;
- + mAmountToRead = sz;
- +}
- +
- +PRStatus
- +nsSOCKSSocketInfo::Fill(PRFileDesc *fd)
- +{
- + PRInt32 rc;
- + const char *end;
- +
- + if (!mReadPtr) {
- + LOGDEBUG(("socks: Fill(), nothing to do"));
- + return PR_SUCCESS;
- + }
- +
- + end = mInBuf.EndReading();
- +
- + while (mReadPtr < end) {
- + rc = PR_Read(fd, mReadPtr, end - mReadPtr);
- + if (rc <= 0) {
- + if (rc == 0) {
- + LOGERROR(("socks: proxy server closed connection"));
- + PR_SetError(PR_CONNECT_REFUSED_ERROR, 0);
- + } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
- + LOGDEBUG(("socks: Fill(), want read"));
- + mPollFlags = PR_POLL_READ;
- + }
- + break;
- + }
- +
- + mReadPtr += rc;
- + }
- +
- + LOGDEBUG(("socks: Fill(), have %d bytes total", mReadPtr - mInBuf.Data()));
- + if (mReadPtr == end) {
- + mReadPtr = nsnull;
- + mAmountToRead = 0;
- + return PR_SUCCESS;
- + }
- +
- + return PR_FAILURE;
- +}
- +
- +PRStatus
- +nsSOCKSSocketInfo::Flush(PRFileDesc *fd)
- +{
- + PRInt32 len;
- + PRInt32 offset;
- + PRInt32 rc;
- +
- + offset = 0;
- + len = mOutBuf.Length();
- + if (len == 0) {
- + LOGDEBUG(("socks: Flush(), nothing to do"));
- + return PR_SUCCESS;
- + }
- +
- + while (offset < len) {
- + rc = PR_Write(fd, mOutBuf.Data() + offset, len - offset);
- + if (rc < 0) {
- + if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
- + LOGDEBUG(("socks: Flush(), want write"));
- + mPollFlags = PR_POLL_WRITE;
- + }
- + break;
- + }
- +
- + offset += rc;
- + }
- +
- + LOGDEBUG(("socks: Flush(), wrote %d of %d bytes", (int)offset, (int)len));
- + if (offset == len) {
- + mOutBuf.Truncate();
- + return PR_SUCCESS;
- + }
- +
- + if (offset)
- + mOutBuf.Cut(0, offset);
- +
- + return PR_FAILURE;
- +}
- static PRStatus
- -nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*timeout*/)
- +nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
- {
- + PRStatus status;
- + PRNetAddr dst;
- + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
- + if (info == NULL) return PR_FAILURE;
- +
- + if (PR_NetAddrFamily(addr) == PR_AF_INET6 &&
- + PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
- + const PRUint8 *srcp;
- +
- + LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
- +
- + // copied from _PR_ConvertToIpv4NetAddr()
- + PR_InitializeNetAddr(PR_IpAddrAny, 0, &dst);
- + srcp = addr->ipv6.ip.pr_s6_addr;
- + memcpy(&dst.inet.ip, srcp + 12, 4);
- + dst.inet.family = PR_AF_INET;
- + dst.inet.port = addr->ipv6.port;
- + } else {
- + memcpy(&dst, addr, sizeof(dst));
- + }
- +
- + info->SetDestinationAddr(&dst);
- + do {
- + status = info->DoHandshake(fd, -1, to);
- + } while (status == PR_SUCCESS && !info->IsConnected());
- +
- + return status;
- +}
- +
- +static PRStatus
- +nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, PRInt16 oflags)
- +{
- PRStatus status;
- nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
- if (info == NULL) return PR_FAILURE;
- - // First, we need to look up our proxy...
- - const nsCString &proxyHost = info->ProxyHost();
- + do {
- + status = info->DoHandshake(fd, oflags);
- + } while (status == PR_SUCCESS && !info->IsConnected());
- - if (proxyHost.IsEmpty())
- - return PR_FAILURE;
- + return status;
- +}
- - PRInt32 socksVersion = info->Version();
- +static PRInt16
- +nsSOCKSIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
- +{
- + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
- + if (info == NULL) return PR_FAILURE;
- - LOGDEBUG(("nsSOCKSIOLayerConnect SOCKS %u; proxyHost: %s.", socksVersion, proxyHost.get()));
- -
- - // Sync resolve the proxy hostname.
- - PRNetAddr proxyAddr;
- - nsCOMPtr<nsIDNSRecord> rec;
- - nsresult rv;
- - {
- - nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
- - if (!dns)
- - return PR_FAILURE;
- -
- - rv = dns->Resolve(proxyHost, 0, getter_AddRefs(rec));
- - if (NS_FAILED(rv))
- - return PR_FAILURE;
- + if (!info->IsConnected()) {
- + *out_flags = 0;
- + //LOGDEBUG(("socks: state %s, polling %hd",
- + // info->StateToStr(), info->HowToPoll()));
- + return info->HowToPoll();
- }
- - info->SetInternalProxyAddr(&proxyAddr);
- -
- - // For now, we'll do this as a blocking connect,
- - // but with nspr 4.1, the necessary functions to
- - // do a non-blocking connect will be available
- -
- - // Preserve the non-blocking state of the socket
- - PRBool nonblocking;
- - PRSocketOptionData sockopt;
- - sockopt.option = PR_SockOpt_Nonblocking;
- - status = PR_GetSocketOption(fd, &sockopt);
- -
- - if (PR_SUCCESS != status) {
- - LOGERROR(("PR_GetSocketOption() failed. status = %x.", status));
- - return status;
- - }
- -
- - // Store blocking option
- - nonblocking = sockopt.value.non_blocking;
- -
- - sockopt.option = PR_SockOpt_Nonblocking;
- - sockopt.value.non_blocking = PR_FALSE;
- - status = PR_SetSocketOption(fd, &sockopt);
- -
- - if (PR_SUCCESS != status) {
- - LOGERROR(("PR_SetSocketOption() failed. status = %x.", status));
- - return status;
- - }
- -
- - // Now setup sockopts, so we can restore the value later.
- - sockopt.option = PR_SockOpt_Nonblocking;
- - sockopt.value.non_blocking = nonblocking;
- -
- - // This connectWait should be long enough to connect to local proxy
- - // servers, but not much longer. Since this protocol negotiation
- - // uses blocking network calls, the app can appear to hang for a maximum
- - // of this time if the user presses the STOP button during the SOCKS
- - // connection negotiation. Note that this value only applies to the
- - // connecting to the SOCKS server: once the SOCKS connection has been
- - // established, the value is not used anywhere else.
- - PRIntervalTime connectWait = PR_SecondsToInterval(10);
- -
- - // Connect to the proxy server.
- - PRInt32 addresses = 0;
- - do {
- - rv = rec->GetNextAddr(info->ProxyPort(), &proxyAddr);
- - if (NS_FAILED(rv)) {
- - status = PR_FAILURE;
- - break;
- - }
- - ++addresses;
- - status = fd->lower->methods->connect(fd->lower, &proxyAddr, connectWait);
- - } while (PR_SUCCESS != status);
- -
- - if (PR_SUCCESS != status) {
- - LOGERROR(("Failed to TCP connect to the proxy server (%s): timeout = %d, status = %x, tried %d addresses.", proxyHost.get(), connectWait, status, addresses));
- - PR_SetSocketOption(fd, &sockopt);
- - return status;
- - }
- -
- -
- - // We are now connected to the SOCKS proxy server.
- - // Now we will negotiate a connection to the desired server.
- -
- - // External IP address returned from ConnectSOCKS5(). Not supported in SOCKS4.
- - PRNetAddr extAddr;
- - PR_InitializeNetAddr(PR_IpAddrNull, 0, &extAddr);
- -
- - NS_ASSERTION((socksVersion == 4) || (socksVersion == 5), "SOCKS Version must be selected");
- -
- - // Try to connect via SOCKS 5.
- - if (socksVersion == 5) {
- - rv = ConnectSOCKS5(fd, addr, &extAddr, connectWait);
- -
- - if (NS_FAILED(rv)) {
- - PR_SetSocketOption(fd, &sockopt);
- - return PR_FAILURE;
- - }
- -
- - }
- -
- - // Try to connect via SOCKS 4.
- - else {
- - rv = ConnectSOCKS4(fd, addr, connectWait);
- -
- - if (NS_FAILED(rv)) {
- - PR_SetSocketOption(fd, &sockopt);
- - return PR_FAILURE;
- - }
- -
- - }
- -
- -
- - info->SetDestinationAddr((PRNetAddr*)addr);
- - info->SetExternalProxyAddr(&extAddr);
- -
- - // restore non-blocking option
- - PR_SetSocketOption(fd, &sockopt);
- -
- - // we're set-up and connected.
- - // this socket can be used as normal now.
- -
- - return PR_SUCCESS;
- + return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
- }
- static PRStatus
- nsSOCKSIOLayerClose(PRFileDesc *fd)
- {
- nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
- PRDescIdentity id = PR_GetLayersIdentity(fd);
- @@ -880,16 +953,18 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family
- if (firstTime)
- {
- nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
- nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
- nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
- + nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue;
- + nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll;
- nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
- nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
- nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
- nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
- nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
- nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
- nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
Add Comment
Please, Sign In to add comment