Back to index

lightning-sunbird  0.9+nobinonly
nsSOCKSIOLayer.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Justin Bradford <jab@atdot.org>
00025  *   Bradley Baetz <bbaetz@acm.org>
00026  *   Darin Fisher <darin@meer.net>
00027  *   Malcolm Smith <malsmith@cs.rmit.edu.au>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either the GNU General Public License Version 2 or later (the "GPL"), or
00031  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 #include "nspr.h"
00044 #include "nsString.h"
00045 #include "nsCRT.h"
00046 
00047 #include "nsIServiceManager.h"
00048 #include "nsIDNSService.h"
00049 #include "nsIDNSRecord.h"
00050 #include "nsISOCKSSocketInfo.h"
00051 #include "nsISocketProvider.h"
00052 #include "nsSOCKSIOLayer.h"
00053 #include "nsNetCID.h"
00054 
00055 static PRDescIdentity       nsSOCKSIOLayerIdentity;
00056 static PRIOMethods   nsSOCKSIOLayerMethods;
00057 static PRBool firstTime = PR_TRUE;
00058 
00059 #if defined(PR_LOGGING)
00060 static PRLogModuleInfo *gSOCKSLog;
00061 #define LOGDEBUG(args) PR_LOG(gSOCKSLog, PR_LOG_DEBUG, args)
00062 #define LOGERROR(args) PR_LOG(gSOCKSLog, PR_LOG_ERROR , args)
00063 
00064 #else
00065 #define LOGDEBUG(args)
00066 #define LOGERROR(args)
00067 #endif
00068 
00069 class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
00070 {
00071 public:
00072     nsSOCKSSocketInfo();
00073     virtual ~nsSOCKSSocketInfo() {}
00074 
00075     NS_DECL_ISUPPORTS
00076     NS_DECL_NSISOCKSSOCKETINFO
00077 
00078     void Init(PRInt32 version,
00079               const char *proxyHost,
00080               PRInt32 proxyPort,
00081               const char *destinationHost,
00082               PRUint32 flags);
00083 
00084     const nsCString &DestinationHost() { return mDestinationHost; }
00085     const nsCString &ProxyHost()       { return mProxyHost; }
00086     PRInt32          ProxyPort()       { return mProxyPort; }
00087     PRInt32          Version()         { return mVersion; }
00088     PRUint32         Flags()           { return mFlags; }
00089 
00090 private:
00091     nsCString mDestinationHost;
00092     nsCString mProxyHost;
00093     PRInt32   mProxyPort;
00094     PRInt32   mVersion;   // SOCKS version 4 or 5
00095     PRUint32  mFlags;
00096     PRNetAddr mInternalProxyAddr;
00097     PRNetAddr mExternalProxyAddr;
00098     PRNetAddr mDestinationAddr;
00099 };
00100 
00101 nsSOCKSSocketInfo::nsSOCKSSocketInfo()
00102     : mProxyPort(-1)
00103     , mVersion(-1)
00104     , mFlags(0)
00105 {
00106     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mInternalProxyAddr);
00107     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mExternalProxyAddr);
00108     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mDestinationAddr);
00109 }
00110 
00111 void
00112 nsSOCKSSocketInfo::Init(PRInt32 version, const char *proxyHost, PRInt32 proxyPort, const char *host, PRUint32 flags)
00113 {
00114     mVersion         = version;
00115     mProxyHost       = proxyHost;
00116     mProxyPort       = proxyPort;
00117     mDestinationHost = host;
00118     mFlags           = flags;
00119 }
00120 
00121 NS_IMPL_THREADSAFE_ISUPPORTS1(nsSOCKSSocketInfo, nsISOCKSSocketInfo)
00122 
00123 NS_IMETHODIMP 
00124 nsSOCKSSocketInfo::GetExternalProxyAddr(PRNetAddr * *aExternalProxyAddr)
00125 {
00126     memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(PRNetAddr));
00127     return NS_OK;
00128 }
00129 
00130 NS_IMETHODIMP 
00131 nsSOCKSSocketInfo::SetExternalProxyAddr(PRNetAddr *aExternalProxyAddr)
00132 {
00133     memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(PRNetAddr));
00134     return NS_OK;
00135 }
00136 
00137 NS_IMETHODIMP 
00138 nsSOCKSSocketInfo::GetDestinationAddr(PRNetAddr * *aDestinationAddr)
00139 {
00140     memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(PRNetAddr));
00141     return NS_OK;
00142 }
00143 
00144 NS_IMETHODIMP 
00145 nsSOCKSSocketInfo::SetDestinationAddr(PRNetAddr *aDestinationAddr)
00146 {
00147     memcpy(&mDestinationAddr, aDestinationAddr, sizeof(PRNetAddr));
00148     return NS_OK;
00149 }
00150 
00151 NS_IMETHODIMP 
00152 nsSOCKSSocketInfo::GetInternalProxyAddr(PRNetAddr * *aInternalProxyAddr)
00153 {
00154     memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(PRNetAddr));
00155     return NS_OK;
00156 }
00157 
00158 NS_IMETHODIMP 
00159 nsSOCKSSocketInfo::SetInternalProxyAddr(PRNetAddr *aInternalProxyAddr)
00160 {
00161     memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(PRNetAddr));
00162     return NS_OK;
00163 }
00164 
00165 
00166 // Negotiate a SOCKS 5 connection. Assumes the TCP connection to the socks 
00167 // server port has been established.
00168 static nsresult
00169 ConnectSOCKS5(PRFileDesc *fd, const PRNetAddr *addr, PRNetAddr *extAddr, PRIntervalTime timeout)
00170 {
00171     NS_ENSURE_TRUE(fd, NS_ERROR_NOT_INITIALIZED);
00172     NS_ENSURE_TRUE(addr, NS_ERROR_NOT_INITIALIZED);
00173     NS_ENSURE_TRUE(extAddr, NS_ERROR_NOT_INITIALIZED);
00174 
00175     unsigned char request[22];
00176     int request_len = 0;
00177     unsigned char response[22];
00178     int response_len = 0;
00179 
00180     request[0] = 0x05; // SOCKS version 5
00181     request[1] = 0x01; // number of auth procotols we recognize
00182     // auth protocols
00183     request[2] = 0x00; // no authentication required
00184     // compliant implementations MUST implement GSSAPI
00185     // and SHOULD implement username/password and MAY
00186     // implement CHAP
00187     // TODO: we don't implement these
00188     //request[3] = 0x01; // GSSAPI
00189     //request[4] = 0x02; // username/password
00190     //request[5] = 0x03; // CHAP
00191 
00192     request_len = 2 + request[1];
00193     int write_len = PR_Send(fd, request, request_len, 0, timeout);
00194     if (write_len != request_len) {
00195 
00196         LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
00197         return NS_ERROR_FAILURE;
00198     }
00199 
00200     // get the server's response. Use PR_Recv() instead of 
00201     response_len = 2;
00202     response_len = PR_Recv(fd, response, response_len, 0, timeout);
00203 
00204     if (response_len <= 0) {
00205 
00206         LOGERROR(("PR_Recv() failed. response_len = %d.", response_len));
00207         return NS_ERROR_FAILURE;
00208     }
00209 
00210     if (response[0] != 0x05) {
00211         // it's a either not SOCKS or not our version
00212         LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
00213         return NS_ERROR_FAILURE;
00214     }
00215     switch (response[1]) {
00216         case 0x00:
00217             // no auth
00218             break;
00219         case 0x01:
00220             // GSSAPI
00221             // TODO: implement
00222             LOGERROR(("Server want to use GSSAPI to authenticate, but we don't support it."));
00223             return NS_ERROR_FAILURE;
00224         case 0x02:
00225             // username/password
00226             // TODO: implement
00227             LOGERROR(("Server want to use username/password to authenticate, but we don't support it."));
00228             return NS_ERROR_FAILURE;
00229         case 0x03:
00230             // CHAP
00231             // TODO: implement?
00232             LOGERROR(("Server want to use CHAP to authenticate, but we don't support it."));
00233             return NS_ERROR_FAILURE;
00234         default:
00235             // unrecognized auth method
00236             LOGERROR(("Uncrecognized authentication method received: %x", response[1]));
00237             return NS_ERROR_FAILURE;
00238     }
00239 
00240     // we are now authenticated, so lets tell
00241     // the server where to connect to
00242 
00243     request_len = 0;
00244 
00245     request[0] = 0x05; // SOCKS version 5
00246     request[1] = 0x01; // CONNECT command
00247     request[2] = 0x00; // obligatory reserved field (perfect for MS tampering!)
00248 
00249     // get destination port
00250     PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
00251     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
00252 
00253     if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
00254 
00255         LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
00256 
00257         // if the PROXY_RESOLVES_HOST flag is set, we assume
00258         // that the transport wants us to pass the SOCKS server the 
00259         // hostname and port and let it do the name resolution.
00260 
00261         // the real destination hostname and port was stored
00262         // in our info object earlier when this layer was created.
00263 
00264         const nsCString& destHost = info->DestinationHost();
00265 
00266         LOGDEBUG(("host:port -> %s:%li", destHost.get(), destPort));
00267 
00268         request[3] = 0x03; // encoding of destination address (3 == hostname)
00269 
00270         int host_len = destHost.Length();
00271         if (host_len > 255) {
00272             // SOCKS5 transmits the length of the hostname in a single char.
00273             // This gives us an absolute limit of 255 chars in a hostname, and
00274             // there's nothing we can do to extend it.  I don't think many
00275             // hostnames will ever be bigger than this, so hopefully it's an
00276             // uneventful abort condition.
00277             LOGERROR (("Hostname too big for SOCKS5."));
00278             return NS_ERROR_INVALID_ARG;
00279         }
00280         request[4] = (char) host_len;
00281         request_len = 5;
00282 
00283         // Send the initial header first...
00284         write_len = PR_Send(fd, request, request_len, 0, timeout);
00285         if (write_len != request_len) {
00286             // bad write
00287             LOGERROR(("PR_Send() failed sending connect command. Wrote: %d bytes; Expected: %d.", write_len, request_len));
00288             return NS_ERROR_FAILURE;
00289         }
00290 
00291         // Now send the hostname...
00292         write_len = PR_Send(fd, destHost.get(), host_len, 0, timeout);
00293         if (write_len != host_len) {
00294             // bad write
00295             LOGERROR(("PR_Send() failed sending connect command. Wrote: %d bytes; Expected: %d.", write_len, host_len));
00296             return NS_ERROR_FAILURE;
00297         }
00298 
00299         // There's no data left because we just sent it.
00300         request_len = 0;
00301 
00302     } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
00303 
00304         request[3] = 0x01; // encoding of destination address (1 == IPv4)
00305         request_len = 8;   // 4 for address, 4 SOCKS headers
00306 
00307         char * ip = (char*)(&addr->inet.ip);
00308         request[4] = *ip++;
00309         request[5] = *ip++;
00310         request[6] = *ip++;
00311         request[7] = *ip++;
00312 
00313     } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
00314 
00315         request[3] = 0x04; // encoding of destination address (4 == IPv6)
00316         request_len = 20;  // 16 for address, 4 SOCKS headers
00317 
00318         char * ip = (char*)(&addr->ipv6.ip.pr_s6_addr);
00319         request[4] = *ip++; request[5] = *ip++; 
00320         request[6] = *ip++; request[7] = *ip++;
00321         request[8] = *ip++; request[9] = *ip++; 
00322         request[10] = *ip++; request[11] = *ip++;
00323         request[12] = *ip++; request[13] = *ip++; 
00324         request[14] = *ip++; request[15] = *ip++;
00325         request[16] = *ip++; request[17] = *ip++; 
00326         request[18] = *ip++; request[19] = *ip++;
00327 
00328         // we're going to test to see if this address can
00329         // be mapped back into IPv4 without loss. if so,
00330         // we'll use IPv4 instead, as reliable SOCKS server 
00331         // support for IPv6 is probably questionable.
00332 
00333         if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
00334             request[3] = 0x01; // ipv4 encoding
00335             request[4] = request[16];
00336             request[5] = request[17];
00337             request[6] = request[18];
00338             request[7] = request[19];
00339             request_len -= 12;
00340         }
00341     } else {
00342         // Unknown address type
00343         LOGERROR(("Don't know what kind of IP address this is."));
00344         return NS_ERROR_FAILURE;
00345     }
00346 
00347     // add the destination port to the request
00348     request[request_len] = (unsigned char)(destPort >> 8);
00349     request[request_len+1] = (unsigned char)destPort;
00350     request_len += 2;
00351 
00352     write_len = PR_Send(fd, request, request_len, 0, timeout);
00353     if (write_len != request_len) {
00354 
00355         // bad write
00356         LOGERROR(("PR_Send() failed sending connect command. Wrote: %d bytes; Expected: %d.", write_len, request_len));
00357         return NS_ERROR_FAILURE;
00358     }
00359 
00360     response_len = 22;
00361     response_len = PR_Recv(fd, response, response_len, 0, timeout);
00362     if (response_len <= 0) {
00363 
00364         // bad read
00365         LOGERROR(("PR_Recv() failed getting connect command reply. response_len = %d.", response_len));
00366         return NS_ERROR_FAILURE;
00367     }
00368 
00369     if (response[0] != 0x05) {
00370 
00371         // bad response
00372         LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
00373         return NS_ERROR_FAILURE;
00374     }
00375 
00376     switch(response[1]) {
00377         case 0x00:  break;      // success
00378         case 0x01:  LOGERROR(("SOCKS 5 server rejected connect request: 01, General SOCKS server failure."));
00379                     return NS_ERROR_FAILURE;
00380         case 0x02:  LOGERROR(("SOCKS 5 server rejected connect request: 02, Connection not allowed by ruleset."));
00381                     return NS_ERROR_FAILURE;
00382         case 0x03:  LOGERROR(("SOCKS 5 server rejected connect request: 03, Network unreachable."));
00383                     return NS_ERROR_FAILURE;
00384         case 0x04:  LOGERROR(("SOCKS 5 server rejected connect request: 04, Host unreachable."));
00385                     return NS_ERROR_FAILURE;
00386         case 0x05:  LOGERROR(("SOCKS 5 server rejected connect request: 05, Connection refused."));
00387                     return NS_ERROR_FAILURE;
00388         case 0x06:  LOGERROR(("SOCKS 5 server rejected connect request: 06, TTL expired."));
00389                     return NS_ERROR_FAILURE;
00390         case 0x07:  LOGERROR(("SOCKS 5 server rejected connect request: 07, Command not supported."));
00391                     return NS_ERROR_FAILURE;
00392         case 0x08:  LOGERROR(("SOCKS 5 server rejected connect request: 08, Address type not supported."));
00393                     return NS_ERROR_FAILURE;
00394         default:    LOGERROR(("SOCKS 5 server rejected connect request: %x.", response[1]));
00395                     return NS_ERROR_FAILURE;
00396 
00397 
00398     }
00399 
00400 
00401     // get external bound address (this is what 
00402     // the outside world sees as "us")
00403     char *ip = nsnull;
00404     PRUint16 extPort = 0;
00405 
00406     switch (response[3]) {
00407         case 0x01: // IPv4
00408 
00409             extPort = (response[8] << 8) | response[9];
00410 
00411             PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, extPort, extAddr);
00412 
00413             ip = (char*)(&extAddr->inet.ip);
00414             *ip++ = response[4];
00415             *ip++ = response[5];
00416             *ip++ = response[6];
00417             *ip++ = response[7];
00418 
00419             break;
00420         case 0x04: // IPv6
00421 
00422             extPort = (response[20] << 8) | response[21];
00423 
00424             PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, extPort, extAddr);
00425 
00426             ip = (char*)(&extAddr->ipv6.ip.pr_s6_addr);
00427             *ip++ = response[4]; *ip++ = response[5]; 
00428             *ip++ = response[6]; *ip++ = response[7];
00429             *ip++ = response[8]; *ip++ = response[9]; 
00430             *ip++ = response[10]; *ip++ = response[11];
00431             *ip++ = response[12]; *ip++ = response[13]; 
00432             *ip++ = response[14]; *ip++ = response[15];
00433             *ip++ = response[16]; *ip++ = response[17]; 
00434             *ip++ = response[18]; *ip++ = response[19];
00435 
00436             break;
00437         case 0x03: // FQDN (should not get this back)
00438         default: // unknown format
00439             // if we get here, we don't know our external address.
00440             // however, as that's possibly not critical to the user,
00441             // we let it slide.
00442             PR_InitializeNetAddr(PR_IpAddrNull, 0, extAddr);
00443             //return NS_ERROR_FAILURE;
00444             break;
00445     }
00446     return NS_OK;
00447 
00448 }
00449 
00450 // Negotiate a SOCKS 4 connection. Assumes the TCP connection to the socks 
00451 // server port has been established.
00452 static nsresult
00453 ConnectSOCKS4(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
00454 {
00455     NS_ENSURE_TRUE(fd, NS_ERROR_NOT_INITIALIZED);
00456     NS_ENSURE_TRUE(addr, NS_ERROR_NOT_INITIALIZED);
00457 
00458     unsigned char request[12];
00459     int request_len = 0;
00460     int write_len;
00461     unsigned char response[10];
00462     int response_len = 0;
00463     char *ip = nsnull;
00464 
00465     request[0] = 0x04; // SOCKS version 4
00466     request[1] = 0x01; // CD command code -- 1 for connect
00467 
00468     // destination port
00469     PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
00470 
00471     // store the port
00472     request[2] = (unsigned char)(destPort >> 8);
00473     request[3] = (unsigned char)destPort;
00474 
00475     // username
00476     request[8] = 'M';
00477     request[9] = 'O';
00478     request[10] = 'Z';
00479 
00480     request[11] = 0x00;
00481 
00482     request_len = 12;
00483 
00484     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
00485 
00486     if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
00487 
00488         LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
00489 
00490         // if the PROXY_RESOLVES_HOST flag is set, we assume that the
00491         // transport wants us to pass the SOCKS server the hostname
00492         // and port and let it do the name resolution.
00493 
00494         // an extension to SOCKS 4, called 4a, specifies a way
00495         // to do this, so we'll try that and hope the
00496         // server supports it.
00497 
00498         // the real destination hostname and port was stored
00499         // in our info object earlier when this layer was created.
00500 
00501         const nsCString& destHost = info->DestinationHost();
00502 
00503         LOGDEBUG(("host:port -> %s:%li\n", destHost.get(), destPort));
00504 
00505         // the IP portion of the query is set to this special address.
00506         request[4] = 0;
00507         request[5] = 0;
00508         request[6] = 0;
00509         request[7] = 1;
00510 
00511         write_len = PR_Send(fd, request, request_len, 0, timeout);
00512         if (write_len != request_len) {
00513             LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
00514             return NS_ERROR_FAILURE;
00515         }
00516 
00517         // Remember the NULL.
00518         int host_len = destHost.Length() + 1;
00519 
00520         write_len = PR_Send(fd, destHost.get(), host_len, 0, timeout);
00521         if (write_len != host_len) {
00522             LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, host_len));
00523             return NS_ERROR_FAILURE;
00524         }
00525 
00526         // No data to send, just sent it.
00527         request_len = 0;
00528 
00529     } else if (PR_NetAddrFamily(addr) == PR_AF_INET) { // IPv4
00530 
00531         // store the ip
00532         ip = (char*)(&addr->inet.ip);
00533         request[4] = *ip++;
00534         request[5] = *ip++;
00535         request[6] = *ip++;
00536         request[7] = *ip++;
00537 
00538     } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) { // IPv6
00539 
00540         // IPv4 address encoded in an IPv6 address
00541         if (PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
00542             // store the ip
00543             ip = (char*)(&addr->ipv6.ip.pr_s6_addr[12]);
00544             request[4] = *ip++;
00545             request[5] = *ip++;
00546             request[6] = *ip++;
00547             request[7] = *ip++;
00548         } else {
00549             LOGERROR(("IPv6 is not supported in SOCKS 4."));
00550             return NS_ERROR_FAILURE;      // SOCKS 4 can't do IPv6
00551         }
00552 
00553     } else {
00554         LOGERROR(("Don't know what kind of IP address this is."));
00555         return NS_ERROR_FAILURE;          // don't recognize this type
00556     }
00557 
00558     if (request_len > 0) {
00559         write_len = PR_Send(fd, request, request_len, 0, timeout);
00560         if (write_len != request_len) {
00561             LOGERROR(("PR_Send() failed. Wrote: %d bytes; Expected: %d.", write_len, request_len));
00562             return NS_ERROR_FAILURE;
00563         }
00564     }
00565 
00566     // get the server's response
00567     response_len = 8;       // size of the response
00568     response_len = PR_Recv(fd, response, response_len, 0, timeout);
00569 
00570     if (response_len <= 0) {
00571         LOGERROR(("PR_Recv() failed. response_len = %d.", response_len));
00572         return NS_ERROR_FAILURE;
00573     }
00574 
00575     if ((response[0] != 0x00) && (response[0] != 0x04)) {
00576         // Novell BorderManager sends a response of type 4, should be zero
00577         // According to the spec. Cope with this brokenness.        
00578         // it's not a SOCKS 4 reply or version 0 of the reply code
00579         LOGERROR(("Not a SOCKS 4 reply. Expected: 0; received: %x.", response[0]));
00580         return NS_ERROR_FAILURE;
00581     }
00582 
00583     if (response[1] != 0x5A) { // = 90: request granted
00584         // connect request not granted
00585         LOGERROR(("Connection request refused. Expected: 90; received: %d.", response[1]));
00586         return NS_ERROR_FAILURE;
00587     }
00588  
00589     return NS_OK;
00590 
00591 }
00592 
00593 
00594 static PRStatus PR_CALLBACK
00595 nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*timeout*/)
00596 {
00597 
00598     PRStatus status;
00599 
00600     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
00601     if (info == NULL) return PR_FAILURE;
00602 
00603     // First, we need to look up our proxy...
00604     const nsCString &proxyHost = info->ProxyHost();
00605 
00606     if (proxyHost.IsEmpty())
00607         return PR_FAILURE;
00608 
00609     PRInt32 socksVersion = info->Version();
00610 
00611     LOGDEBUG(("nsSOCKSIOLayerConnect SOCKS %u; proxyHost: %s.", socksVersion, proxyHost.get()));
00612 
00613     // Sync resolve the proxy hostname.
00614     PRNetAddr proxyAddr;
00615     {
00616         nsCOMPtr<nsIDNSService> dns;
00617         nsCOMPtr<nsIDNSRecord> rec; 
00618         nsresult rv;
00619 
00620         dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
00621         if (NS_FAILED(rv))
00622             return PR_FAILURE;
00623 
00624         rv = dns->Resolve(proxyHost, 0, getter_AddRefs(rec));
00625         if (NS_FAILED(rv))
00626             return PR_FAILURE;
00627 
00628         rv = rec->GetNextAddr(info->ProxyPort(), &proxyAddr);
00629         if (NS_FAILED(rv))
00630             return PR_FAILURE;
00631     }
00632 
00633     info->SetInternalProxyAddr(&proxyAddr);
00634 
00635     // For now, we'll do this as a blocking connect,
00636     // but with nspr 4.1, the necessary functions to
00637     // do a non-blocking connect will be available
00638 
00639     // Preserve the non-blocking state of the socket
00640     PRBool nonblocking;
00641     PRSocketOptionData sockopt;
00642     sockopt.option = PR_SockOpt_Nonblocking;
00643     status = PR_GetSocketOption(fd, &sockopt);
00644 
00645     if (PR_SUCCESS != status) {
00646         LOGERROR(("PR_GetSocketOption() failed. status = %x.", status));
00647         return status;
00648     }
00649 
00650     // Store blocking option
00651     nonblocking = sockopt.value.non_blocking;
00652 
00653     sockopt.option = PR_SockOpt_Nonblocking;
00654     sockopt.value.non_blocking = PR_FALSE;
00655     status = PR_SetSocketOption(fd, &sockopt);
00656 
00657     if (PR_SUCCESS != status) {
00658         LOGERROR(("PR_SetSocketOption() failed. status = %x.", status));
00659         return status;
00660     }
00661 
00662     // Now setup sockopts, so we can restore the value later.
00663     sockopt.option = PR_SockOpt_Nonblocking;
00664     sockopt.value.non_blocking = nonblocking;
00665 
00666 
00667     // This connectWait should be long enough to connect to local proxy
00668     // servers, but not much longer. Since this protocol negotiation
00669     // uses blocking network calls, the app can appear to hang for a maximum
00670     // of this time if the user presses the STOP button during the SOCKS
00671     // connection negotiation. Note that this value only applies to the 
00672     // connecting to the SOCKS server: once the SOCKS connection has been
00673     // established, the value is not used anywhere else.
00674     PRIntervalTime connectWait = PR_SecondsToInterval(10);
00675 
00676     // Connect to the proxy server.
00677     status = fd->lower->methods->connect(fd->lower, &proxyAddr, connectWait);
00678 
00679     if (PR_SUCCESS != status) {
00680         LOGERROR(("Failed to TCP connect to the proxy server (%s): timeout = %d, status = %x.",proxyHost.get(), connectWait, status));
00681         PR_SetSocketOption(fd, &sockopt);
00682         return status;
00683     }
00684 
00685 
00686     // We are now connected to the SOCKS proxy server.
00687     // Now we will negotiate a connection to the desired server.
00688 
00689     // External IP address returned from ConnectSOCKS5(). Not supported in SOCKS4.
00690     PRNetAddr extAddr;      
00691     PR_InitializeNetAddr(PR_IpAddrNull, 0, &extAddr);
00692 
00693  
00694 
00695     NS_ASSERTION((socksVersion == 4) || (socksVersion == 5), "SOCKS Version must be selected");
00696 
00697     nsresult rv;
00698 
00699     // Try to connect via SOCKS 5.
00700     if (socksVersion == 5) {
00701         rv = ConnectSOCKS5(fd, addr, &extAddr, connectWait);
00702 
00703         if (NS_FAILED(rv)) {
00704             PR_SetSocketOption(fd, &sockopt);
00705             return PR_FAILURE;     
00706         }
00707 
00708     }
00709 
00710     // Try to connect via SOCKS 4.
00711     else {
00712         rv = ConnectSOCKS4(fd, addr, connectWait);
00713 
00714         if (NS_FAILED(rv)) {
00715             PR_SetSocketOption(fd, &sockopt);
00716             return PR_FAILURE;     
00717         }
00718 
00719     }
00720 
00721 
00722     info->SetDestinationAddr((PRNetAddr*)addr);
00723     info->SetExternalProxyAddr(&extAddr);
00724 
00725     // restore non-blocking option
00726     PR_SetSocketOption(fd, &sockopt);
00727 
00728     // we're set-up and connected. 
00729     // this socket can be used as normal now.
00730 
00731     return PR_SUCCESS;
00732 }
00733 
00734 static PRStatus PR_CALLBACK
00735 nsSOCKSIOLayerClose(PRFileDesc *fd)
00736 {
00737     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
00738     PRDescIdentity id = PR_GetLayersIdentity(fd);
00739 
00740     if (info && id == nsSOCKSIOLayerIdentity)
00741     {
00742         NS_RELEASE(info);
00743         fd->identity = PR_INVALID_IO_LAYER;
00744     }
00745 
00746     return fd->lower->methods->close(fd->lower);
00747 }
00748 
00749 static PRFileDesc* PR_CALLBACK
00750 nsSOCKSIOLayerAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
00751 {
00752     // TODO: implement SOCKS support for accept
00753     return fd->lower->methods->accept(fd->lower, addr, timeout);
00754 }
00755 
00756 static PRInt32 PR_CALLBACK
00757 nsSOCKSIOLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout)
00758 {
00759     // TODO: implement SOCKS support for accept, then read from it
00760     return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, timeout);
00761 }
00762 
00763 static PRStatus PR_CALLBACK
00764 nsSOCKSIOLayerBind(PRFileDesc *fd, const PRNetAddr *addr)
00765 {
00766     // TODO: implement SOCKS support for bind (very similar to connect)
00767     return fd->lower->methods->bind(fd->lower, addr);
00768 }
00769 
00770 static PRStatus PR_CALLBACK
00771 nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr)
00772 {
00773     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
00774     
00775     if (info != NULL && addr != NULL) {
00776         if (info->GetExternalProxyAddr(&addr) == NS_OK)
00777             return PR_SUCCESS;
00778     }
00779 
00780     return PR_FAILURE;
00781 }
00782 
00783 static PRStatus PR_CALLBACK
00784 nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
00785 {
00786     nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
00787 
00788     if (info != NULL && addr != NULL) {
00789         if (info->GetDestinationAddr(&addr) == NS_OK)
00790             return PR_SUCCESS;
00791     }
00792 
00793     return PR_FAILURE;
00794 }
00795 
00796 static PRStatus PR_CALLBACK
00797 nsSOCKSIOLayerListen(PRFileDesc *fd, PRIntn backlog)
00798 {
00799     // TODO: implement SOCKS support for listen
00800     return fd->lower->methods->listen(fd->lower, backlog);
00801 }
00802 
00803 // add SOCKS IO layer to an existing socket
00804 nsresult
00805 nsSOCKSIOLayerAddToSocket(PRInt32 family,
00806                           const char *host, 
00807                           PRInt32 port,
00808                           const char *proxyHost,
00809                           PRInt32 proxyPort,
00810                           PRInt32 socksVersion,
00811                           PRUint32 flags,
00812                           PRFileDesc *fd, 
00813                           nsISupports** info)
00814 {
00815     NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED);
00816 
00817 
00818     if (firstTime)
00819     {
00820         nsSOCKSIOLayerIdentity            = PR_GetUniqueIdentity("SOCKS layer");
00821         nsSOCKSIOLayerMethods             = *PR_GetDefaultIOMethods();
00822 
00823         nsSOCKSIOLayerMethods.connect     = nsSOCKSIOLayerConnect;
00824         nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
00825         nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
00826         nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
00827         nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
00828         nsSOCKSIOLayerMethods.accept      = nsSOCKSIOLayerAccept;
00829         nsSOCKSIOLayerMethods.listen      = nsSOCKSIOLayerListen;
00830         nsSOCKSIOLayerMethods.close       = nsSOCKSIOLayerClose;
00831 
00832         firstTime                  = PR_FALSE;
00833 
00834 #if defined(PR_LOGGING)
00835         gSOCKSLog = PR_NewLogModule("SOCKS");
00836 #endif
00837 
00838     }
00839 
00840     LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
00841 
00842     PRFileDesc *     layer;
00843     PRStatus  rv;
00844 
00845     layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
00846     if (! layer)
00847     {
00848         LOGERROR(("PR_CreateIOLayerStub() failed."));
00849         return NS_ERROR_FAILURE;
00850     }
00851 
00852     nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo();
00853     if (!infoObject)
00854     {
00855         // clean up IOLayerStub
00856         LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
00857         PR_DELETE(layer);
00858         return NS_ERROR_FAILURE;
00859     }
00860 
00861     NS_ADDREF(infoObject);
00862     infoObject->Init(socksVersion, proxyHost, proxyPort, host, flags);
00863     layer->secret = (PRFilePrivate*) infoObject;
00864     rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
00865 
00866     if (NS_FAILED(rv))
00867     {
00868         LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
00869         NS_RELEASE(infoObject);
00870         PR_DELETE(layer);
00871         return NS_ERROR_FAILURE;
00872     }
00873 
00874     *info = infoObject;
00875     NS_ADDREF(*info);
00876     return NS_OK;
00877 }