[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [torbrowser/master] Update patches for Firefox 5.



commit e98532da73eb3cbb2a9b708f68c23b0748f39305
Author: Mike Perry <mikeperry-git@xxxxxxxxxx>
Date:   Tue Jun 21 17:14:52 2011 -0700

    Update patches for Firefox 5.
    
    Keep Firefox 4 patches around just in case.
---
 .../0001-Firefox-Fix-SOCKS-timeout.patch           | 1500 --------------------
 .../0001-Firefox4-Fix-SOCKS-timeout.patch          | 1500 ++++++++++++++++++++
 ...ock-Components.interfaces-lookupMethod-fr.patch |   50 +
 ...ck-Components.interfaces-lookupMethod-fro.patch |   50 -
 ...ock-Components.interfaces-lookupMethod-fr.patch |   50 +
 ...-Make-Intermediate-Cert-Store-memory-only.patch |  283 ++++
 ...-Make-Intermediate-Cert-Store-memory-only.patch |  283 ----
 ...-Make-Intermediate-Cert-Store-memory-only.patch |  283 ++++
 ...fox5-Make-Permissions-Manager-memory-only.patch |   94 ++
 ...efox-Make-Permissions-Manager-memory-only.patch |   94 --
 ...fox4-Make-Permissions-Manager-memory-only.patch |   94 ++
 11 files changed, 2354 insertions(+), 1927 deletions(-)

diff --git a/src/current-patches/0001-Firefox-Fix-SOCKS-timeout.patch b/src/current-patches/0001-Firefox-Fix-SOCKS-timeout.patch
deleted file mode 100644
index 33591a1..0000000
--- a/src/current-patches/0001-Firefox-Fix-SOCKS-timeout.patch
+++ /dev/null
@@ -1,1500 +0,0 @@
-From 51be2e0f33325dcd6275229d3d99a2ae7b1bda40 Mon Sep 17 00:00:00 2001
-From: Mike Perry <mikeperry-git@xxxxxxxxxx>
-Date: Mon, 20 Jun 2011 17:07:33 -0700
-Subject: [PATCH 1/4] Firefox: Fix SOCKS timeout
-
-This patch by chrisd removes the hardcoded SOCKS timeout by rewriting the
-Firefox SOCKS code to use non-blocking IO.
-
-See also: https://bugzilla.mozilla.org/show_bug.cgi?id=280661
-https://trac.torproject.org/projects/tor/ticket/3247
----
- netwerk/base/src/nsSocketTransport2.cpp |   21 +-
- netwerk/socket/nsSOCKSIOLayer.cpp       | 1273 ++++++++++++++++++-------------
- 2 files changed, 775 insertions(+), 519 deletions(-)
-
-diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp
-index 3f95dfd..fb363db 100644
---- a/netwerk/base/src/nsSocketTransport2.cpp
-+++ b/netwerk/base/src/nsSocketTransport2.cpp
-@@ -1227,6 +1227,16 @@ nsSocketTransport::InitiateSocket()
-             }
-         }
-         //
-+        // A SOCKS request was rejected; get the actual error code from
-+        // the OS error
-+        //
-+        else if (PR_UNKNOWN_ERROR == code &&
-+                 mProxyTransparent &&
-+                 !mProxyHost.IsEmpty()) {
-+            code = PR_GetOSError();
-+            rv = ErrorAccordingToNSPR(code);
-+        }
-+        //
-         // The connection was refused...
-         //
-         else {
-@@ -1549,7 +1559,16 @@ nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
-                 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
-                 // Update poll timeout in case it was changed
-                 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
--            } 
-+            }
-+            //
-+            // The SOCKS proxy rejected our request. Find out why.
-+            //
-+            else if (PR_UNKNOWN_ERROR == code &&
-+                     mProxyTransparent &&
-+                     !mProxyHost.IsEmpty()) {
-+                code = PR_GetOSError();
-+                mCondition = ErrorAccordingToNSPR(code);
-+            }
-             else {
-                 //
-                 // else, the connection failed...
-diff --git a/netwerk/socket/nsSOCKSIOLayer.cpp b/netwerk/socket/nsSOCKSIOLayer.cpp
-index 9a15667..4d3a4e8 100644
---- a/netwerk/socket/nsSOCKSIOLayer.cpp
-+++ b/netwerk/socket/nsSOCKSIOLayer.cpp
-@@ -25,6 +25,7 @@
-  *   Bradley Baetz <bbaetz@xxxxxxx>
-  *   Darin Fisher <darin@xxxxxxxx>
-  *   Malcolm Smith <malsmith@xxxxxxxxxxxxxx>
-+ *   Christopher Davis <chrisd@xxxxxxxxxxxxxx>
-  *
-  * Alternatively, the contents of this file may be used under the terms of
-  * either the GNU General Public License Version 2 or later (the "GPL"), or
-@@ -68,9 +69,28 @@ static PRLogModuleInfo *gSOCKSLog;
- 
- class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
- {
-+    enum State {
-+        SOCKS_INITIAL,
-+        SOCKS_CONNECTING_TO_PROXY,
-+        SOCKS4_WRITE_CONNECT_REQUEST,
-+        SOCKS4_READ_CONNECT_RESPONSE,
-+        SOCKS5_WRITE_AUTH_REQUEST,
-+        SOCKS5_READ_AUTH_RESPONSE,
-+        SOCKS5_WRITE_CONNECT_REQUEST,
-+        SOCKS5_READ_CONNECT_RESPONSE_TOP,
-+        SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
-+        SOCKS_CONNECTED,
-+        SOCKS_FAILED
-+    };
-+
-+    // A buffer of 262 bytes should be enough for any request and response
-+    // in case of SOCKS4 as well as SOCKS5
-+    static const PRUint32 BUFFER_SIZE = 262;
-+    static const PRUint32 MAX_HOSTNAME_LEN = 255;
-+
- public:
-     nsSOCKSSocketInfo();
--    virtual ~nsSOCKSSocketInfo() {}
-+    virtual ~nsSOCKSSocketInfo() { HandshakeFinished(); }
- 
-     NS_DECL_ISUPPORTS
-     NS_DECL_NSISOCKSSOCKETINFO
-@@ -81,13 +101,50 @@ public:
-               const char *destinationHost,
-               PRUint32 flags);
- 
--    const nsCString &DestinationHost() { return mDestinationHost; }
--    const nsCString &ProxyHost()       { return mProxyHost; }
--    PRInt32          ProxyPort()       { return mProxyPort; }
--    PRInt32          Version()         { return mVersion; }
--    PRUint32         Flags()           { return mFlags; }
-+    void SetConnectTimeout(PRIntervalTime to);
-+    PRStatus DoHandshake(PRFileDesc *fd, PRInt16 oflags = -1);
-+    PRInt16 GetPollFlags() const;
-+    bool IsConnected() const { return mState == SOCKS_CONNECTED; }
-+
-+private:
-+    void HandshakeFinished(PRErrorCode err = 0);
-+    PRStatus ConnectToProxy(PRFileDesc *fd);
-+    PRStatus ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags);
-+    PRStatus WriteV4ConnectRequest();
-+    PRStatus ReadV4ConnectResponse();
-+    PRStatus WriteV5AuthRequest();
-+    PRStatus ReadV5AuthResponse();
-+    PRStatus WriteV5ConnectRequest();
-+    PRStatus ReadV5AddrTypeAndLength(PRUint8 *type, PRUint32 *len);
-+    PRStatus ReadV5ConnectResponseTop();
-+    PRStatus ReadV5ConnectResponseBottom();
-+
-+    void WriteUint8(PRUint8 d);
-+    void WriteUint16(PRUint16 d);
-+    void WriteUint32(PRUint32 d);
-+    void WriteNetAddr(const PRNetAddr *addr);
-+    void WriteNetPort(const PRNetAddr *addr);
-+    void WriteString(const nsACString &str);
-+
-+    PRUint8 ReadUint8();
-+    PRUint16 ReadUint16();
-+    PRUint32 ReadUint32();
-+    void ReadNetAddr(PRNetAddr *addr, PRUint16 fam);
-+    void ReadNetPort(PRNetAddr *addr);
-+
-+    void WantRead(PRUint32 sz);
-+    PRStatus ReadFromSocket(PRFileDesc *fd);
-+    PRStatus WriteToSocket(PRFileDesc *fd);
- 
- private:
-+    State     mState;
-+    PRUint8 * mData;
-+    PRUint8 * mDataIoPtr;
-+    PRUint32  mDataLength;
-+    PRUint32  mReadOffset;
-+    PRUint32  mAmountToRead;
-+    nsCOMPtr<nsIDNSRecord> mDnsRec;
-+
-     nsCString mDestinationHost;
-     nsCString mProxyHost;
-     PRInt32   mProxyPort;
-@@ -96,13 +153,21 @@ private:
-     PRNetAddr mInternalProxyAddr;
-     PRNetAddr mExternalProxyAddr;
-     PRNetAddr mDestinationAddr;
-+    PRIntervalTime mTimeout;
- };
- 
- nsSOCKSSocketInfo::nsSOCKSSocketInfo()
--    : mProxyPort(-1)
-+    : mState(SOCKS_INITIAL)
-+    , mDataIoPtr(nsnull)
-+    , mDataLength(0)
-+    , mReadOffset(0)
-+    , mAmountToRead(0)
-+    , mProxyPort(-1)
-     , mVersion(-1)
-     , mFlags(0)
-+    , mTimeout(PR_INTERVAL_NO_TIMEOUT)
- {
-+    mData = new PRUint8[BUFFER_SIZE];
-     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mInternalProxyAddr);
-     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mExternalProxyAddr);
-     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mDestinationAddr);
-@@ -162,637 +227,807 @@ nsSOCKSSocketInfo::SetInternalProxyAddr(PRNetAddr *aInternalProxyAddr)
-     return NS_OK;
- }
- 
--static PRInt32
--pr_RecvAll(PRFileDesc *fd, unsigned char *buf, PRInt32 amount, PRIntn flags, 
--           PRIntervalTime *timeout)
-+// There needs to be a means of distinguishing between connection errors
-+// that the SOCKS server reports when it rejects a connection request, and
-+// connection errors that happen while attempting to connect to the SOCKS
-+// server. Otherwise, Firefox will report incorrectly that the proxy server
-+// is refusing connections when a SOCKS request is rejected by the proxy.
-+// When a SOCKS handshake failure occurs, the PR error is set to
-+// PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
-+void
-+nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err)
- {
--    PRInt32 bytesRead = 0;
--    PRInt32 offset = 0;
-+    if (err == 0) {
-+        mState = SOCKS_CONNECTED;
-+    } else {
-+        mState = SOCKS_FAILED;
-+        PR_SetError(PR_UNKNOWN_ERROR, err);
-+    }
- 
--    while (offset < amount) {
--        PRIntervalTime start_time = PR_IntervalNow();
--        bytesRead = PR_Recv(fd, buf + offset, amount - offset, flags, *timeout);
--        PRIntervalTime elapsed = PR_IntervalNow() - start_time;
-+    // We don't need the buffer any longer, so free it.
-+    delete [] mData;
-+    mData = nsnull;
-+    mDataIoPtr = nsnull;
-+    mDataLength = 0;
-+    mReadOffset = 0;
-+    mAmountToRead = 0;
-+}
- 
--        if (elapsed > *timeout) {
--            *timeout = 0;
--        } else {
--            *timeout -= elapsed;
--        }
-+PRStatus
-+nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
-+{
-+    PRStatus status;
-+    nsresult rv;
- 
--        if (bytesRead > 0) {
--            offset += bytesRead;
--        } else if (bytesRead == 0 || offset != 0) {
--            return offset;
--        } else {
--            return bytesRead;
--        }
-+    NS_ABORT_IF_FALSE(mState == SOCKS_INITIAL,
-+                      "Must be in initial state to make connection!");
- 
--        if (*timeout == 0) {
--            LOGERROR(("PR_Recv() timed out. amount = %d. offset = %d.",
--                     amount, offset));
--            return offset;
-+    // 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;
-+
-+        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;
-         }
-     }
--    return offset;
--}
- 
--static PRInt32
--pr_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
--        PRIntervalTime *timeout)
--{
--    PRIntervalTime start_time = PR_IntervalNow();
--    PRInt32 retval = PR_Send(fd, buf, amount, flags, *timeout);
--    PRIntervalTime elapsed = PR_IntervalNow() - start_time;
--
--    if (elapsed > *timeout) {
--        *timeout = 0;
--        LOGERROR(("PR_Send() timed out. amount = %d. retval = %d.",
--                 amount, retval));
--        return retval;
--    } else {
--        *timeout -= elapsed;
--    }
-+    do {
-+        rv = mDnsRec->GetNextAddr(mProxyPort, &mInternalProxyAddr);
-+        // No more addresses to try? If so, we'll need to bail
-+        if (NS_FAILED(rv)) {
-+            LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
-+                     mProxyHost.get()));
-+            return PR_FAILURE;
-+        }
- 
--    if (retval <= 0) {
--        LOGERROR(("PR_Send() failed. amount = %d. retval = %d.",
--                 amount, retval));
--    }
-+#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, mTimeout);
-+        if (status != PR_SUCCESS) {
-+            PRErrorCode c = PR_GetError();
-+            // If EINPROGRESS, return now and check back later after polling
-+            if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
-+                mState = SOCKS_CONNECTING_TO_PROXY;
-+                return status;
-+            }
-+        }
-+    } while (status != PR_SUCCESS);
- 
--    return retval;
-+    // Connected now, start SOCKS
-+    if (mVersion == 4)
-+        return WriteV4ConnectRequest();
-+    return WriteV5AuthRequest();
- }
- 
--// 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)
-+PRStatus
-+nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags)
- {
--    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
-+    PRStatus status;
- 
--    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;
--    }
-+    NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
-+                      "Continuing connection in wrong state!");
- 
--    // get the server's response. 
--    desired_len = 2;
--    response_len = pr_RecvAll(fd, response, desired_len, 0, &timeout);
-+    LOGDEBUG(("socks: continuing connection to proxy"));
- 
--    if (response_len < desired_len) {
--        LOGERROR(("pr_RecvAll() failed. response_len = %d.", response_len));
--        return NS_ERROR_FAILURE;
--    }
-+    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);
-+        }
- 
--    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're still connecting
-+        return PR_FAILURE;
-     }
- 
--    // we are now authenticated, so lets tell
--    // the server where to connect to
-+    // Connected now, start SOCKS
-+    if (mVersion == 4)
-+        return WriteV4ConnectRequest();
-+    return WriteV5AuthRequest();
-+}
- 
--    request_len = 0;
-+PRStatus
-+nsSOCKSSocketInfo::WriteV4ConnectRequest()
-+{
-+    PRNetAddr *addr = &mDestinationAddr;
-+    PRInt32 proxy_resolve;
- 
--    request[0] = 0x05; // SOCKS version 5
--    request[1] = 0x01; // CONNECT command
--    request[2] = 0x00; // obligatory reserved field (perfect for MS tampering!)
-+    NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
-+                      "Invalid state!");
-+    
-+    proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
-+
-+    mDataLength = 0;
-+    mState = SOCKS4_WRITE_CONNECT_REQUEST;
-+
-+    LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
-+             proxy_resolve? "yes" : "no"));
-+
-+    // Send a SOCKS 4 connect request.
-+    WriteUint8(0x04); // version -- 4
-+    WriteUint8(0x01); // command -- connect
-+    WriteNetPort(addr);
-+    if (proxy_resolve) {
-+        // Add the full name, null-terminated, to the request
-+        // according to SOCKS 4a. A fake IP address, with the first
-+        // four bytes set to 0 and the last byte set to something other
-+        // than 0, is used to notify the proxy that this is a SOCKS 4a
-+        // request. This request type works for Tor and perhaps others.
-+        WriteUint32(PR_htonl(0x00000001)); // Fake IP
-+        WriteUint8(0x00); // Send an emtpy username
-+        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
-+            LOGERROR(("socks4: destination host name is too long!"));
-+            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
-+            return PR_FAILURE;
-+        }
-+        WriteString(mDestinationHost); // Hostname
-+        WriteUint8(0x00);
-+    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
-+        WriteNetAddr(addr); // Add the IPv4 address
-+        WriteUint8(0x00); // Send an emtpy username
-+    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
-+        LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!"));
-+        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--    // get destination port
--    PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
--    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
-+    return PR_SUCCESS;
-+}
- 
--    if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
-+PRStatus
-+nsSOCKSSocketInfo::ReadV4ConnectResponse()
-+{
-+    NS_ABORT_IF_FALSE(mState == SOCKS4_READ_CONNECT_RESPONSE,
-+                      "Handling SOCKS 4 connection reply in wrong state!");
-+    NS_ABORT_IF_FALSE(mDataLength == 8,
-+                      "SOCKS 4 connection reply must be 8 bytes!");
- 
--        LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
-+    LOGDEBUG(("socks4: checking connection reply"));
- 
--        // 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.
-+    if (ReadUint8() != 0x00) {
-+        LOGERROR(("socks4: wrong connection reply"));
-+        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--        // the real destination hostname and port was stored
--        // in our info object earlier when this layer was created.
-+    // See if our connection request was granted
-+    if (ReadUint8() == 90) {
-+        LOGDEBUG(("socks4: connection successful!"));
-+        HandshakeFinished();
-+        return PR_SUCCESS;
-+    }
- 
--        const nsCString& destHost = info->DestinationHost();
-+    LOGERROR(("socks4: unable to connect"));
-+    HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
-+    return PR_FAILURE;
-+}
- 
--        LOGDEBUG(("host:port -> %s:%li", destHost.get(), destPort));
-+PRStatus
-+nsSOCKSSocketInfo::WriteV5AuthRequest()
-+{
-+    NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
- 
--        request[3] = 0x03; // encoding of destination address (3 == hostname)
-+    mState = SOCKS5_WRITE_AUTH_REQUEST;
- 
--        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;
--        }
-+    // Send an initial SOCKS 5 greeting
-+    LOGDEBUG(("socks5: sending auth methods"));
-+    WriteUint8(0x05); // version -- 5
-+    WriteUint8(0x01); // # auth methods -- 1
-+    WriteUint8(0x00); // we don't support authentication
- 
--        // 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;
--        }
-+    return PR_SUCCESS;
-+}
- 
--        // There's no data left because we just sent it.
--        request_len = 0;
-+PRStatus
-+nsSOCKSSocketInfo::ReadV5AuthResponse()
-+{
-+    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE,
-+                      "Handling SOCKS 5 auth method reply in wrong state!");
-+    NS_ABORT_IF_FALSE(mDataLength == 2,
-+                      "SOCKS 5 auth method reply must be 2 bytes!");
- 
--    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
-+    LOGDEBUG(("socks5: checking auth method reply"));
- 
--        request[3] = 0x01; // encoding of destination address (1 == IPv4)
--        request_len = 8;   // 4 for address, 4 SOCKS headers
-+    // Check version number
-+    if (ReadUint8() != 0x05) {
-+        LOGERROR(("socks5: unexpected version in the reply"));
-+        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--        char * ip = (char*)(&addr->inet.ip);
--        request[4] = *ip++;
--        request[5] = *ip++;
--        request[6] = *ip++;
--        request[7] = *ip++;
-+    // Make sure our authentication choice was accepted
-+    if (ReadUint8() != 0x00) {
-+        LOGERROR(("socks5: server did not accept our authentication method"));
-+        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
-+    return WriteV5ConnectRequest();
-+}
- 
--        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;
-+PRStatus
-+nsSOCKSSocketInfo::WriteV5ConnectRequest()
-+{
-+    // 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"));
-+
-+    mDataLength = 0;
-+    mState = SOCKS5_WRITE_CONNECT_REQUEST;
-+
-+    WriteUint8(0x05); // version -- 5
-+    WriteUint8(0x01); // command -- connect
-+    WriteUint8(0x00); // reserved
-+   
-+    // Add the address to the SOCKS 5 request. SOCKS 5 supports several
-+    // address types, so we pick the one that works best for us.
-+    if (proxy_resolve) {
-+        // Add the host name. Only a single byte is used to store the length,
-+        // so we must prevent long names from being used.
-+        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
-+            LOGERROR(("socks5: destination host name is too long!"));
-+            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
-+            return PR_FAILURE;
-         }
-+        WriteUint8(0x03); // addr type -- domainname
-+        WriteUint8(mDestinationHost.Length()); // name length
-+        WriteString(mDestinationHost);
-+    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
-+        WriteUint8(0x01); // addr type -- IPv4
-+        WriteNetAddr(addr);
-+    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
-+        WriteUint8(0x04); // addr type -- IPv6
-+        WriteNetAddr(addr);
-     } 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;
-+        LOGERROR(("socks5: destination address of unknown type!"));
-+        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
-+        return PR_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;
--    }
-+    WriteNetPort(addr); // port
- 
--    if (response[0] != 0x05) {
--        // bad response
--        LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
--        return NS_ERROR_FAILURE;
--    }
-+    return PR_SUCCESS;
-+}
- 
--    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;
-+PRStatus
-+nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(PRUint8 *type, PRUint32 *len)
-+{
-+    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
-+                      mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
-+                      "Invalid state!");
-+    NS_ABORT_IF_FALSE(mDataLength >= 5,
-+                      "SOCKS 5 connection reply must be at least 5 bytes!");
-+ 
-+    // Seek to the address location 
-+    mReadOffset = 3;
-+   
-+    *type = ReadUint8();
-+
-+    switch (*type) {
-+        case 0x01: // ipv4
-+            *len = 4 - 1;
-             break;
--        case 0x04: // IPv6
--	    desired_len = 16 + 2 - 1;
-+        case 0x04: // ipv6
-+            *len = 16 - 1;
-             break;
--        default: // unknown format
--            return NS_ERROR_FAILURE;
-+        case 0x03: // fqdn
-+            *len = ReadUint8();
-             break;
-+        default:   // wrong address type
-+            LOGERROR(("socks5: wrong address type in connection reply!"));
-+            return PR_FAILURE;
-     }
--    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;
-+    return PR_SUCCESS;
-+}
- 
--    switch (response[3]) {
--        case 0x01: // IPv4
-+PRStatus
-+nsSOCKSSocketInfo::ReadV5ConnectResponseTop()
-+{
-+    PRUint8 res;
-+    PRUint32 len;
- 
--            extPort = (response[8] << 8) | response[9];
-+    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP,
-+                      "Invalid state!");
-+    NS_ABORT_IF_FALSE(mDataLength == 5,
-+                      "SOCKS 5 connection reply must be exactly 5 bytes!");
- 
--            PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, extPort, extAddr);
-+    LOGDEBUG(("socks5: checking connection reply"));
- 
--            ip = (char*)(&extAddr->inet.ip);
--            *ip++ = response[4];
--            *ip++ = response[5];
--            *ip++ = response[6];
--            *ip++ = response[7];
-+    // Check version number
-+    if (ReadUint8() != 0x05) {
-+        LOGERROR(("socks5: unexpected version in the reply"));
-+        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--            break;
--        case 0x04: // IPv6
-+    // Check response
-+    res = ReadUint8();
-+    if (res != 0x00) {
-+        PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
-+
-+        switch (res) {
-+            case 0x01:
-+                LOGERROR(("socks5: connect failed: "
-+                          "01, General SOCKS server failure."));
-+                break;
-+            case 0x02:
-+                LOGERROR(("socks5: connect failed: "
-+                          "02, Connection not allowed by ruleset."));
-+                break;
-+            case 0x03:
-+                LOGERROR(("socks5: connect failed: 03, Network unreachable."));
-+                c = PR_NETWORK_UNREACHABLE_ERROR;
-+                break;
-+            case 0x04:
-+                LOGERROR(("socks5: connect failed: 04, Host unreachable."));
-+                break;
-+            case 0x05:
-+                LOGERROR(("socks5: connect failed: 05, Connection refused."));
-+                break;
-+            case 0x06:  
-+                LOGERROR(("socks5: connect failed: 06, TTL expired."));
-+                c = PR_CONNECT_TIMEOUT_ERROR;
-+                break;
-+            case 0x07:
-+                LOGERROR(("socks5: connect failed: "
-+                          "07, Command not supported."));
-+                break;
-+            case 0x08:
-+                LOGERROR(("socks5: connect failed: "
-+                          "08, Address type not supported."));
-+                c = PR_BAD_ADDRESS_ERROR;
-+                break;
-+            default:
-+                LOGERROR(("socks5: connect failed."));
-+                break;
-+        }
- 
--            extPort = (response[20] << 8) | response[21];
-+        HandshakeFinished(c);
-+        return PR_FAILURE;
-+    }
- 
--            PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, extPort, extAddr);
-+    if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
-+        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--            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];
-+    mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
-+    WantRead(len + 2);
- 
--            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;
-+    return PR_SUCCESS;
- }
- 
--// 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)
-+PRStatus
-+nsSOCKSSocketInfo::ReadV5ConnectResponseBottom()
- {
--    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];
-+    PRUint8 type;
-+    PRUint32 len;
- 
--    NS_ENSURE_TRUE(fd, NS_ERROR_NOT_INITIALIZED);
--    NS_ENSURE_TRUE(addr, NS_ERROR_NOT_INITIALIZED);
-+    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
-+                      "Invalid state!");
- 
--    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 (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
-+        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
-+        return PR_FAILURE;
-+    }
- 
--    if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
-+    NS_ABORT_IF_FALSE(mDataLength == 7+len,
-+                      "SOCKS 5 unexpected length of connection reply!");
- 
--        LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
-+    LOGDEBUG(("socks5: loading source addr and port"));
-+    // Read what the proxy says is our source address
-+    switch (type) {
-+        case 0x01: // ipv4
-+            ReadNetAddr(&mExternalProxyAddr, PR_AF_INET);
-+            break;
-+        case 0x04: // ipv6
-+            ReadNetAddr(&mExternalProxyAddr, PR_AF_INET6);
-+            break;
-+        case 0x03: // fqdn (skip)
-+            mReadOffset += len;
-+            mExternalProxyAddr.raw.family = PR_AF_INET;
-+            break;
-+    }
- 
--        // 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.
-+    ReadNetPort(&mExternalProxyAddr);
- 
--        // an extension to SOCKS 4, called 4a, specifies a way
--        // to do this, so we'll try that and hope the
--        // server supports it.
-+    LOGDEBUG(("socks5: connected!"));
-+    HandshakeFinished();
- 
--        // the real destination hostname and port was stored
--        // in our info object earlier when this layer was created.
-+    return PR_SUCCESS;
-+}
- 
--        const nsCString& destHost = info->DestinationHost();
-+void
-+nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to)
-+{
-+    mTimeout = to;
-+}
- 
--        LOGDEBUG(("host:port -> %s:%li\n", destHost.get(), destPort));
-+PRStatus
-+nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, PRInt16 oflags)
-+{
-+    LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
-+
-+    switch (mState) {
-+        case SOCKS_INITIAL:
-+            return ConnectToProxy(fd);
-+        case SOCKS_CONNECTING_TO_PROXY:
-+            return ContinueConnectingToProxy(fd, oflags);
-+        case SOCKS4_WRITE_CONNECT_REQUEST:
-+            if (WriteToSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+            WantRead(8);
-+            mState = SOCKS4_READ_CONNECT_RESPONSE;
-+            return PR_SUCCESS;
-+        case SOCKS4_READ_CONNECT_RESPONSE:
-+            if (ReadFromSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+            return ReadV4ConnectResponse();
-+
-+        case SOCKS5_WRITE_AUTH_REQUEST:
-+            if (WriteToSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+            WantRead(2);
-+            mState = SOCKS5_READ_AUTH_RESPONSE;
-+            return PR_SUCCESS;
-+        case SOCKS5_READ_AUTH_RESPONSE:
-+            if (ReadFromSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+            return ReadV5AuthResponse();
-+        case SOCKS5_WRITE_CONNECT_REQUEST:
-+            if (WriteToSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+
-+            // The SOCKS 5 response to the connection request is variable
-+            // length. First, we'll read enough to tell how long the response
-+            // is, and will read the rest later.
-+            WantRead(5);
-+            mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
-+            return PR_SUCCESS;
-+        case SOCKS5_READ_CONNECT_RESPONSE_TOP:
-+            if (ReadFromSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+            return ReadV5ConnectResponseTop();
-+        case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
-+            if (ReadFromSocket(fd) != PR_SUCCESS)
-+                return PR_FAILURE;
-+            return ReadV5ConnectResponseBottom();
-+
-+        case SOCKS_CONNECTED:
-+            LOGERROR(("socks: already connected"));
-+            HandshakeFinished(PR_IS_CONNECTED_ERROR);
-+            return PR_FAILURE;
-+        case SOCKS_FAILED:
-+            LOGERROR(("socks: already failed"));
-+            return PR_FAILURE;
-+    }
- 
--        // the IP portion of the query is set to this special address.
--        request[4] = 0;
--        request[5] = 0;
--        request[6] = 0;
--        request[7] = 1;
-+    LOGERROR(("socks: executing handshake in invalid state, %d", mState));
-+    HandshakeFinished(PR_INVALID_STATE_ERROR);
- 
--        write_len = pr_Send(fd, request, request_len, 0, &timeout);
--        if (write_len != request_len) {
--            return NS_ERROR_FAILURE;
--        }
-+    return PR_FAILURE;
-+}
- 
--        // Remember the NULL.
--        int host_len = destHost.Length() + 1;
-+PRInt16
-+nsSOCKSSocketInfo::GetPollFlags() const
-+{
-+    switch (mState) {
-+        case SOCKS_CONNECTING_TO_PROXY:
-+            return PR_POLL_EXCEPT | PR_POLL_WRITE;
-+        case SOCKS4_WRITE_CONNECT_REQUEST:
-+        case SOCKS5_WRITE_AUTH_REQUEST:
-+        case SOCKS5_WRITE_CONNECT_REQUEST:
-+            return PR_POLL_WRITE;
-+        case SOCKS4_READ_CONNECT_RESPONSE:
-+        case SOCKS5_READ_AUTH_RESPONSE:
-+        case SOCKS5_READ_CONNECT_RESPONSE_TOP:
-+        case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
-+            return PR_POLL_READ;
-+        default:
-+            break;
-+    }
- 
--        write_len = pr_Send(fd, destHost.get(), host_len, 0, &timeout);
--        if (write_len != host_len) {
--            return NS_ERROR_FAILURE;
--        }
-+    return 0;
-+}
- 
--        // 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
--        }
-+inline void
-+nsSOCKSSocketInfo::WriteUint8(PRUint8 v)
-+{
-+    NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE,
-+                      "Can't write that much data!");
-+    mData[mDataLength] = v;
-+    mDataLength += sizeof(v);
-+}
- 
--    } else {
--        LOGERROR(("Don't know what kind of IP address this is."));
--        return NS_ERROR_FAILURE;		// don't recognize this type
--    }
-+inline void
-+nsSOCKSSocketInfo::WriteUint16(PRUint16 v)
-+{
-+    NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE,
-+                      "Can't write that much data!");
-+    memcpy(mData + mDataLength, &v, sizeof(v));
-+    mDataLength += sizeof(v);
-+}
- 
--    if (request_len > 0) {
--        write_len = pr_Send(fd, request, request_len, 0, &timeout);
--        if (write_len != request_len) {
--            return NS_ERROR_FAILURE;
--        }
--    }
-+inline void
-+nsSOCKSSocketInfo::WriteUint32(PRUint32 v)
-+{
-+    NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE,
-+                      "Can't write that much data!");
-+    memcpy(mData + mDataLength, &v, sizeof(v));
-+    mDataLength += sizeof(v);
-+}
- 
--    // 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;
--    }
-+void
-+nsSOCKSSocketInfo::WriteNetAddr(const PRNetAddr *addr)
-+{
-+    const char *ip = NULL;
-+    PRUint32 len = 0;
- 
--    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;
-+    if (PR_NetAddrFamily(addr) == PR_AF_INET) {
-+        ip = (const char*)&addr->inet.ip;
-+        len = sizeof(addr->inet.ip);
-+    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
-+        ip = (const char*)addr->ipv6.ip.pr_s6_addr;
-+        len = sizeof(addr->ipv6.ip.pr_s6_addr);
-     }
- 
--    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;
--    }
-+    NS_ABORT_IF_FALSE(ip != NULL, "Unknown address");
-+    NS_ABORT_IF_FALSE(mDataLength + len <= BUFFER_SIZE,
-+                      "Can't write that much data!");
-  
--    return NS_OK;
-+    memcpy(mData + mDataLength, ip, len);
-+    mDataLength += len;
-+}
- 
-+void
-+nsSOCKSSocketInfo::WriteNetPort(const PRNetAddr *addr)
-+{
-+    WriteUint16(PR_NetAddrInetPort(addr));
- }
- 
-+void
-+nsSOCKSSocketInfo::WriteString(const nsACString &str)
-+{
-+    NS_ABORT_IF_FALSE(mDataLength + str.Length() <= BUFFER_SIZE,
-+                      "Can't write that much data!");
-+    memcpy(mData + mDataLength, str.Data(), str.Length());
-+    mDataLength += str.Length();
-+}
- 
--static PRStatus
--nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*timeout*/)
-+inline PRUint8
-+nsSOCKSSocketInfo::ReadUint8()
- {
-+    PRUint8 rv;
-+    NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
-+                      "Not enough space to pop a uint8!");
-+    rv = mData[mReadOffset];
-+    mReadOffset += sizeof(rv);
-+    return rv;
-+}
- 
--    PRStatus status;
-+inline PRUint16
-+nsSOCKSSocketInfo::ReadUint16()
-+{
-+    PRUint16 rv;
-+    NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
-+                      "Not enough space to pop a uint16!");
-+    memcpy(&rv, mData + mReadOffset, sizeof(rv));
-+    mReadOffset += sizeof(rv);
-+    return rv;
-+}
- 
--    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
--    if (info == NULL) return PR_FAILURE;
-+inline PRUint32
-+nsSOCKSSocketInfo::ReadUint32()
-+{
-+    PRUint32 rv;
-+    NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
-+                      "Not enough space to pop a uint32!");
-+    memcpy(&rv, mData + mReadOffset, sizeof(rv));
-+    mReadOffset += sizeof(rv);
-+    return rv;
-+}
- 
--    // First, we need to look up our proxy...
--    const nsCString &proxyHost = info->ProxyHost();
-+void
-+nsSOCKSSocketInfo::ReadNetAddr(PRNetAddr *addr, PRUint16 fam)
-+{
-+    PRUint32 amt;
-+    const PRUint8 *ip = mData + mReadOffset;
-+
-+    addr->raw.family = fam;
-+    if (fam == PR_AF_INET) {
-+        amt = sizeof(addr->inet.ip);
-+        NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength,
-+                          "Not enough space to pop an ipv4 addr!");
-+        memcpy(&addr->inet.ip, ip, amt);
-+    } else if (fam == PR_AF_INET6) {
-+        amt = sizeof(addr->ipv6.ip.pr_s6_addr);
-+        NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength,
-+                          "Not enough space to pop an ipv6 addr!");
-+        memcpy(addr->ipv6.ip.pr_s6_addr, ip, amt);
-+    }
- 
--    if (proxyHost.IsEmpty())
--        return PR_FAILURE;
-+    mReadOffset += amt;
-+}
- 
--    PRInt32 socksVersion = info->Version();
-+void
-+nsSOCKSSocketInfo::ReadNetPort(PRNetAddr *addr)
-+{
-+    addr->inet.port = ReadUint16();
-+}
- 
--    LOGDEBUG(("nsSOCKSIOLayerConnect SOCKS %u; proxyHost: %s.", socksVersion, proxyHost.get()));
-+void
-+nsSOCKSSocketInfo::WantRead(PRUint32 sz)
-+{
-+    NS_ABORT_IF_FALSE(mDataIoPtr == NULL,
-+                      "WantRead() called while I/O already in progress!");
-+    NS_ABORT_IF_FALSE(mDataLength + sz <= BUFFER_SIZE,
-+                      "Can't read that much data!");
-+    mAmountToRead = sz;
-+}
- 
--    // 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;
-+PRStatus
-+nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd)
-+{
-+    PRInt32 rc;
-+    const PRUint8 *end;
- 
--        rv = dns->Resolve(proxyHost, 0, getter_AddRefs(rec));
--        if (NS_FAILED(rv))
--            return PR_FAILURE;
-+    if (!mAmountToRead) {
-+        LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
-+        return PR_SUCCESS;
-     }
- 
--    info->SetInternalProxyAddr(&proxyAddr);
-+    if (!mDataIoPtr) {
-+        mDataIoPtr = mData + mDataLength;
-+        mDataLength += mAmountToRead;
-+    }
- 
--    // 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
-+    end = mData + mDataLength;
-+
-+    while (mDataIoPtr < end) {
-+        rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
-+        if (rc <= 0) {
-+            if (rc == 0) {
-+                LOGERROR(("socks: proxy server closed connection"));
-+                HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
-+                return PR_FAILURE;
-+            } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
-+                LOGDEBUG(("socks: ReadFromSocket(), want read"));
-+            }
-+            break;
-+        }
- 
--    // Preserve the non-blocking state of the socket
--    PRBool nonblocking;
--    PRSocketOptionData sockopt;
--    sockopt.option = PR_SockOpt_Nonblocking;
--    status = PR_GetSocketOption(fd, &sockopt);
-+        mDataIoPtr += rc;
-+    }
- 
--    if (PR_SUCCESS != status) {
--        LOGERROR(("PR_GetSocketOption() failed. status = %x.", status));
--        return status;
-+    LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
-+             unsigned(mDataIoPtr - mData)));
-+    if (mDataIoPtr == end) {
-+        mDataIoPtr = nsnull;
-+        mAmountToRead = 0;
-+        mReadOffset = 0;
-+        return PR_SUCCESS;
-     }
- 
--    // Store blocking option
--    nonblocking = sockopt.value.non_blocking;
-+    return PR_FAILURE;
-+}
- 
--    sockopt.option = PR_SockOpt_Nonblocking;
--    sockopt.value.non_blocking = PR_FALSE;
--    status = PR_SetSocketOption(fd, &sockopt);
-+PRStatus
-+nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd)
-+{
-+    PRInt32 rc;
-+    const PRUint8 *end;
- 
--    if (PR_SUCCESS != status) {
--        LOGERROR(("PR_SetSocketOption() failed. status = %x.", status));
--        return status;
-+    if (!mDataLength) {
-+        LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
-+        return PR_SUCCESS;
-     }
- 
--    // Now setup sockopts, so we can restore the value later.
--    sockopt.option = PR_SockOpt_Nonblocking;
--    sockopt.value.non_blocking = nonblocking;
-+    if (!mDataIoPtr)
-+        mDataIoPtr = mData;
- 
--    // 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);
-+    end = mData + mDataLength;
- 
--    // Connect to the proxy server.
--    PRInt32 addresses = 0;
--    do {
--        rv = rec->GetNextAddr(info->ProxyPort(), &proxyAddr);
--        if (NS_FAILED(rv)) {
--            status = PR_FAILURE;
-+    while (mDataIoPtr < end) {
-+        rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
-+        if (rc < 0) {
-+            if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
-+                LOGDEBUG(("socks: WriteToSocket(), want write"));
-+            }
-             break;
-         }
--        ++addresses;
--        status = fd->lower->methods->connect(fd->lower, &proxyAddr, connectWait);
--    } while (PR_SUCCESS != status);
-+        
-+        mDataIoPtr += rc;
-+    }
- 
--    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;
-+    if (mDataIoPtr == end) {
-+        mDataIoPtr = nsnull;
-+        mDataLength = 0;
-+        mReadOffset = 0;
-+        return PR_SUCCESS;
-     }
-+    
-+    return PR_FAILURE;
-+}
- 
-+static PRStatus
-+nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
-+{
-+    PRStatus status;
-+    PRNetAddr dst;
- 
--    // We are now connected to the SOCKS proxy server.
--    // Now we will negotiate a connection to the desired server.
-+    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
-+    if (info == NULL) return PR_FAILURE;
- 
--    // External IP address returned from ConnectSOCKS5(). Not supported in SOCKS4.
--    PRNetAddr extAddr;
--    PR_InitializeNetAddr(PR_IpAddrNull, 0, &extAddr);
-+    if (PR_NetAddrFamily(addr) == PR_AF_INET6 &&
-+        PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
-+        const PRUint8 *srcp;
- 
--    NS_ASSERTION((socksVersion == 4) || (socksVersion == 5), "SOCKS Version must be selected");
-+        LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
- 
--    // Try to connect via SOCKS 5.
--    if (socksVersion == 5) {
--        rv = ConnectSOCKS5(fd, addr, &extAddr, connectWait);
-+        // 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));
-+    }
- 
--        if (NS_FAILED(rv)) {
--            PR_SetSocketOption(fd, &sockopt);
--            return PR_FAILURE;
--        }
-+    info->SetDestinationAddr(&dst);
-+    info->SetConnectTimeout(to);
- 
--    }
-+    do {
-+        status = info->DoHandshake(fd, -1);
-+    } while (status == PR_SUCCESS && !info->IsConnected());
- 
--    // Try to connect via SOCKS 4.
--    else {
--        rv = ConnectSOCKS4(fd, addr, connectWait);
-+    return status;
-+}
- 
--        if (NS_FAILED(rv)) {
--            PR_SetSocketOption(fd, &sockopt);
--            return PR_FAILURE;
--        }
-+static PRStatus
-+nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, PRInt16 oflags)
-+{
-+    PRStatus status;
- 
--    }
-+    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
-+    if (info == NULL) return PR_FAILURE;
- 
-+    do { 
-+        status = info->DoHandshake(fd, oflags);
-+    } while (status == PR_SUCCESS && !info->IsConnected());
- 
--    info->SetDestinationAddr((PRNetAddr*)addr);
--    info->SetExternalProxyAddr(&extAddr);
-+    return status;
-+}
- 
--    // restore non-blocking option
--    PR_SetSocketOption(fd, &sockopt);
-+static PRInt16
-+nsSOCKSIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
-+{
-+    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
-+    if (info == NULL) return PR_FAILURE;
- 
--    // we're set-up and connected.
--    // this socket can be used as normal now.
-+    if (!info->IsConnected()) {
-+        *out_flags = 0;
-+        return info->GetPollFlags();
-+    }
- 
--    return PR_SUCCESS;
-+    return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
- }
- 
- static PRStatus
-@@ -885,6 +1120,8 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
-         nsSOCKSIOLayerMethods		= *PR_GetDefaultIOMethods();
- 
-         nsSOCKSIOLayerMethods.connect	= nsSOCKSIOLayerConnect;
-+        nsSOCKSIOLayerMethods.connectcontinue	= nsSOCKSIOLayerConnectContinue;
-+        nsSOCKSIOLayerMethods.poll	= nsSOCKSIOLayerPoll;
-         nsSOCKSIOLayerMethods.bind	= nsSOCKSIOLayerBind;
-         nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
-         nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
--- 
-1.7.3.4
-
diff --git a/src/current-patches/0001-Firefox4-Fix-SOCKS-timeout.patch b/src/current-patches/0001-Firefox4-Fix-SOCKS-timeout.patch
new file mode 100644
index 0000000..3b9343e
--- /dev/null
+++ b/src/current-patches/0001-Firefox4-Fix-SOCKS-timeout.patch
@@ -0,0 +1,1500 @@
+From d37dce07b9eb9b40244d2fa867728e7a57a33f0f Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:33 -0700
+Subject: [PATCH 1/4] Firefox4: Fix SOCKS timeout
+
+This patch by chrisd removes the hardcoded SOCKS timeout by rewriting the
+Firefox SOCKS code to use non-blocking IO.
+
+See also: https://bugzilla.mozilla.org/show_bug.cgi?id=280661
+https://trac.torproject.org/projects/tor/ticket/3247
+---
+ netwerk/base/src/nsSocketTransport2.cpp |   21 +-
+ netwerk/socket/nsSOCKSIOLayer.cpp       | 1273 ++++++++++++++++++-------------
+ 2 files changed, 775 insertions(+), 519 deletions(-)
+
+diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp
+index 3f95dfd..fb363db 100644
+--- a/netwerk/base/src/nsSocketTransport2.cpp
++++ b/netwerk/base/src/nsSocketTransport2.cpp
+@@ -1227,6 +1227,16 @@ nsSocketTransport::InitiateSocket()
+             }
+         }
+         //
++        // A SOCKS request was rejected; get the actual error code from
++        // the OS error
++        //
++        else if (PR_UNKNOWN_ERROR == code &&
++                 mProxyTransparent &&
++                 !mProxyHost.IsEmpty()) {
++            code = PR_GetOSError();
++            rv = ErrorAccordingToNSPR(code);
++        }
++        //
+         // The connection was refused...
+         //
+         else {
+@@ -1549,7 +1559,16 @@ nsSocketTransport::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
+                 mPollFlags = (PR_POLL_EXCEPT | PR_POLL_WRITE);
+                 // Update poll timeout in case it was changed
+                 mPollTimeout = mTimeouts[TIMEOUT_CONNECT];
+-            } 
++            }
++            //
++            // The SOCKS proxy rejected our request. Find out why.
++            //
++            else if (PR_UNKNOWN_ERROR == code &&
++                     mProxyTransparent &&
++                     !mProxyHost.IsEmpty()) {
++                code = PR_GetOSError();
++                mCondition = ErrorAccordingToNSPR(code);
++            }
+             else {
+                 //
+                 // else, the connection failed...
+diff --git a/netwerk/socket/nsSOCKSIOLayer.cpp b/netwerk/socket/nsSOCKSIOLayer.cpp
+index 9a15667..4d3a4e8 100644
+--- a/netwerk/socket/nsSOCKSIOLayer.cpp
++++ b/netwerk/socket/nsSOCKSIOLayer.cpp
+@@ -25,6 +25,7 @@
+  *   Bradley Baetz <bbaetz@xxxxxxx>
+  *   Darin Fisher <darin@xxxxxxxx>
+  *   Malcolm Smith <malsmith@xxxxxxxxxxxxxx>
++ *   Christopher Davis <chrisd@xxxxxxxxxxxxxx>
+  *
+  * Alternatively, the contents of this file may be used under the terms of
+  * either the GNU General Public License Version 2 or later (the "GPL"), or
+@@ -68,9 +69,28 @@ static PRLogModuleInfo *gSOCKSLog;
+ 
+ class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
+ {
++    enum State {
++        SOCKS_INITIAL,
++        SOCKS_CONNECTING_TO_PROXY,
++        SOCKS4_WRITE_CONNECT_REQUEST,
++        SOCKS4_READ_CONNECT_RESPONSE,
++        SOCKS5_WRITE_AUTH_REQUEST,
++        SOCKS5_READ_AUTH_RESPONSE,
++        SOCKS5_WRITE_CONNECT_REQUEST,
++        SOCKS5_READ_CONNECT_RESPONSE_TOP,
++        SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
++        SOCKS_CONNECTED,
++        SOCKS_FAILED
++    };
++
++    // A buffer of 262 bytes should be enough for any request and response
++    // in case of SOCKS4 as well as SOCKS5
++    static const PRUint32 BUFFER_SIZE = 262;
++    static const PRUint32 MAX_HOSTNAME_LEN = 255;
++
+ public:
+     nsSOCKSSocketInfo();
+-    virtual ~nsSOCKSSocketInfo() {}
++    virtual ~nsSOCKSSocketInfo() { HandshakeFinished(); }
+ 
+     NS_DECL_ISUPPORTS
+     NS_DECL_NSISOCKSSOCKETINFO
+@@ -81,13 +101,50 @@ public:
+               const char *destinationHost,
+               PRUint32 flags);
+ 
+-    const nsCString &DestinationHost() { return mDestinationHost; }
+-    const nsCString &ProxyHost()       { return mProxyHost; }
+-    PRInt32          ProxyPort()       { return mProxyPort; }
+-    PRInt32          Version()         { return mVersion; }
+-    PRUint32         Flags()           { return mFlags; }
++    void SetConnectTimeout(PRIntervalTime to);
++    PRStatus DoHandshake(PRFileDesc *fd, PRInt16 oflags = -1);
++    PRInt16 GetPollFlags() const;
++    bool IsConnected() const { return mState == SOCKS_CONNECTED; }
++
++private:
++    void HandshakeFinished(PRErrorCode err = 0);
++    PRStatus ConnectToProxy(PRFileDesc *fd);
++    PRStatus ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags);
++    PRStatus WriteV4ConnectRequest();
++    PRStatus ReadV4ConnectResponse();
++    PRStatus WriteV5AuthRequest();
++    PRStatus ReadV5AuthResponse();
++    PRStatus WriteV5ConnectRequest();
++    PRStatus ReadV5AddrTypeAndLength(PRUint8 *type, PRUint32 *len);
++    PRStatus ReadV5ConnectResponseTop();
++    PRStatus ReadV5ConnectResponseBottom();
++
++    void WriteUint8(PRUint8 d);
++    void WriteUint16(PRUint16 d);
++    void WriteUint32(PRUint32 d);
++    void WriteNetAddr(const PRNetAddr *addr);
++    void WriteNetPort(const PRNetAddr *addr);
++    void WriteString(const nsACString &str);
++
++    PRUint8 ReadUint8();
++    PRUint16 ReadUint16();
++    PRUint32 ReadUint32();
++    void ReadNetAddr(PRNetAddr *addr, PRUint16 fam);
++    void ReadNetPort(PRNetAddr *addr);
++
++    void WantRead(PRUint32 sz);
++    PRStatus ReadFromSocket(PRFileDesc *fd);
++    PRStatus WriteToSocket(PRFileDesc *fd);
+ 
+ private:
++    State     mState;
++    PRUint8 * mData;
++    PRUint8 * mDataIoPtr;
++    PRUint32  mDataLength;
++    PRUint32  mReadOffset;
++    PRUint32  mAmountToRead;
++    nsCOMPtr<nsIDNSRecord> mDnsRec;
++
+     nsCString mDestinationHost;
+     nsCString mProxyHost;
+     PRInt32   mProxyPort;
+@@ -96,13 +153,21 @@ private:
+     PRNetAddr mInternalProxyAddr;
+     PRNetAddr mExternalProxyAddr;
+     PRNetAddr mDestinationAddr;
++    PRIntervalTime mTimeout;
+ };
+ 
+ nsSOCKSSocketInfo::nsSOCKSSocketInfo()
+-    : mProxyPort(-1)
++    : mState(SOCKS_INITIAL)
++    , mDataIoPtr(nsnull)
++    , mDataLength(0)
++    , mReadOffset(0)
++    , mAmountToRead(0)
++    , mProxyPort(-1)
+     , mVersion(-1)
+     , mFlags(0)
++    , mTimeout(PR_INTERVAL_NO_TIMEOUT)
+ {
++    mData = new PRUint8[BUFFER_SIZE];
+     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mInternalProxyAddr);
+     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mExternalProxyAddr);
+     PR_InitializeNetAddr(PR_IpAddrAny, 0, &mDestinationAddr);
+@@ -162,637 +227,807 @@ nsSOCKSSocketInfo::SetInternalProxyAddr(PRNetAddr *aInternalProxyAddr)
+     return NS_OK;
+ }
+ 
+-static PRInt32
+-pr_RecvAll(PRFileDesc *fd, unsigned char *buf, PRInt32 amount, PRIntn flags, 
+-           PRIntervalTime *timeout)
++// There needs to be a means of distinguishing between connection errors
++// that the SOCKS server reports when it rejects a connection request, and
++// connection errors that happen while attempting to connect to the SOCKS
++// server. Otherwise, Firefox will report incorrectly that the proxy server
++// is refusing connections when a SOCKS request is rejected by the proxy.
++// When a SOCKS handshake failure occurs, the PR error is set to
++// PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
++void
++nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err)
+ {
+-    PRInt32 bytesRead = 0;
+-    PRInt32 offset = 0;
++    if (err == 0) {
++        mState = SOCKS_CONNECTED;
++    } else {
++        mState = SOCKS_FAILED;
++        PR_SetError(PR_UNKNOWN_ERROR, err);
++    }
+ 
+-    while (offset < amount) {
+-        PRIntervalTime start_time = PR_IntervalNow();
+-        bytesRead = PR_Recv(fd, buf + offset, amount - offset, flags, *timeout);
+-        PRIntervalTime elapsed = PR_IntervalNow() - start_time;
++    // We don't need the buffer any longer, so free it.
++    delete [] mData;
++    mData = nsnull;
++    mDataIoPtr = nsnull;
++    mDataLength = 0;
++    mReadOffset = 0;
++    mAmountToRead = 0;
++}
+ 
+-        if (elapsed > *timeout) {
+-            *timeout = 0;
+-        } else {
+-            *timeout -= elapsed;
+-        }
++PRStatus
++nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
++{
++    PRStatus status;
++    nsresult rv;
+ 
+-        if (bytesRead > 0) {
+-            offset += bytesRead;
+-        } else if (bytesRead == 0 || offset != 0) {
+-            return offset;
+-        } else {
+-            return bytesRead;
+-        }
++    NS_ABORT_IF_FALSE(mState == SOCKS_INITIAL,
++                      "Must be in initial state to make connection!");
+ 
+-        if (*timeout == 0) {
+-            LOGERROR(("PR_Recv() timed out. amount = %d. offset = %d.",
+-                     amount, offset));
+-            return offset;
++    // 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;
++
++        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;
+         }
+     }
+-    return offset;
+-}
+ 
+-static PRInt32
+-pr_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+-        PRIntervalTime *timeout)
+-{
+-    PRIntervalTime start_time = PR_IntervalNow();
+-    PRInt32 retval = PR_Send(fd, buf, amount, flags, *timeout);
+-    PRIntervalTime elapsed = PR_IntervalNow() - start_time;
+-
+-    if (elapsed > *timeout) {
+-        *timeout = 0;
+-        LOGERROR(("PR_Send() timed out. amount = %d. retval = %d.",
+-                 amount, retval));
+-        return retval;
+-    } else {
+-        *timeout -= elapsed;
+-    }
++    do {
++        rv = mDnsRec->GetNextAddr(mProxyPort, &mInternalProxyAddr);
++        // No more addresses to try? If so, we'll need to bail
++        if (NS_FAILED(rv)) {
++            LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
++                     mProxyHost.get()));
++            return PR_FAILURE;
++        }
+ 
+-    if (retval <= 0) {
+-        LOGERROR(("PR_Send() failed. amount = %d. retval = %d.",
+-                 amount, retval));
+-    }
++#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, mTimeout);
++        if (status != PR_SUCCESS) {
++            PRErrorCode c = PR_GetError();
++            // If EINPROGRESS, return now and check back later after polling
++            if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
++                mState = SOCKS_CONNECTING_TO_PROXY;
++                return status;
++            }
++        }
++    } while (status != PR_SUCCESS);
+ 
+-    return retval;
++    // Connected now, start SOCKS
++    if (mVersion == 4)
++        return WriteV4ConnectRequest();
++    return WriteV5AuthRequest();
+ }
+ 
+-// 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)
++PRStatus
++nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, PRInt16 oflags)
+ {
+-    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
++    PRStatus status;
+ 
+-    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;
+-    }
++    NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
++                      "Continuing connection in wrong state!");
+ 
+-    // get the server's response. 
+-    desired_len = 2;
+-    response_len = pr_RecvAll(fd, response, desired_len, 0, &timeout);
++    LOGDEBUG(("socks: continuing connection to proxy"));
+ 
+-    if (response_len < desired_len) {
+-        LOGERROR(("pr_RecvAll() failed. response_len = %d.", response_len));
+-        return NS_ERROR_FAILURE;
+-    }
++    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);
++        }
+ 
+-    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're still connecting
++        return PR_FAILURE;
+     }
+ 
+-    // we are now authenticated, so lets tell
+-    // the server where to connect to
++    // Connected now, start SOCKS
++    if (mVersion == 4)
++        return WriteV4ConnectRequest();
++    return WriteV5AuthRequest();
++}
+ 
+-    request_len = 0;
++PRStatus
++nsSOCKSSocketInfo::WriteV4ConnectRequest()
++{
++    PRNetAddr *addr = &mDestinationAddr;
++    PRInt32 proxy_resolve;
+ 
+-    request[0] = 0x05; // SOCKS version 5
+-    request[1] = 0x01; // CONNECT command
+-    request[2] = 0x00; // obligatory reserved field (perfect for MS tampering!)
++    NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
++                      "Invalid state!");
++    
++    proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
++
++    mDataLength = 0;
++    mState = SOCKS4_WRITE_CONNECT_REQUEST;
++
++    LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
++             proxy_resolve? "yes" : "no"));
++
++    // Send a SOCKS 4 connect request.
++    WriteUint8(0x04); // version -- 4
++    WriteUint8(0x01); // command -- connect
++    WriteNetPort(addr);
++    if (proxy_resolve) {
++        // Add the full name, null-terminated, to the request
++        // according to SOCKS 4a. A fake IP address, with the first
++        // four bytes set to 0 and the last byte set to something other
++        // than 0, is used to notify the proxy that this is a SOCKS 4a
++        // request. This request type works for Tor and perhaps others.
++        WriteUint32(PR_htonl(0x00000001)); // Fake IP
++        WriteUint8(0x00); // Send an emtpy username
++        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
++            LOGERROR(("socks4: destination host name is too long!"));
++            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
++            return PR_FAILURE;
++        }
++        WriteString(mDestinationHost); // Hostname
++        WriteUint8(0x00);
++    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
++        WriteNetAddr(addr); // Add the IPv4 address
++        WriteUint8(0x00); // Send an emtpy username
++    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
++        LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!"));
++        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-    // get destination port
+-    PRInt32 destPort = PR_ntohs(PR_NetAddrInetPort(addr));
+-    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
++    return PR_SUCCESS;
++}
+ 
+-    if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
++PRStatus
++nsSOCKSSocketInfo::ReadV4ConnectResponse()
++{
++    NS_ABORT_IF_FALSE(mState == SOCKS4_READ_CONNECT_RESPONSE,
++                      "Handling SOCKS 4 connection reply in wrong state!");
++    NS_ABORT_IF_FALSE(mDataLength == 8,
++                      "SOCKS 4 connection reply must be 8 bytes!");
+ 
+-        LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
++    LOGDEBUG(("socks4: checking connection reply"));
+ 
+-        // 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.
++    if (ReadUint8() != 0x00) {
++        LOGERROR(("socks4: wrong connection reply"));
++        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-        // the real destination hostname and port was stored
+-        // in our info object earlier when this layer was created.
++    // See if our connection request was granted
++    if (ReadUint8() == 90) {
++        LOGDEBUG(("socks4: connection successful!"));
++        HandshakeFinished();
++        return PR_SUCCESS;
++    }
+ 
+-        const nsCString& destHost = info->DestinationHost();
++    LOGERROR(("socks4: unable to connect"));
++    HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
++    return PR_FAILURE;
++}
+ 
+-        LOGDEBUG(("host:port -> %s:%li", destHost.get(), destPort));
++PRStatus
++nsSOCKSSocketInfo::WriteV5AuthRequest()
++{
++    NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
+ 
+-        request[3] = 0x03; // encoding of destination address (3 == hostname)
++    mState = SOCKS5_WRITE_AUTH_REQUEST;
+ 
+-        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;
+-        }
++    // Send an initial SOCKS 5 greeting
++    LOGDEBUG(("socks5: sending auth methods"));
++    WriteUint8(0x05); // version -- 5
++    WriteUint8(0x01); // # auth methods -- 1
++    WriteUint8(0x00); // we don't support authentication
+ 
+-        // 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;
+-        }
++    return PR_SUCCESS;
++}
+ 
+-        // There's no data left because we just sent it.
+-        request_len = 0;
++PRStatus
++nsSOCKSSocketInfo::ReadV5AuthResponse()
++{
++    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE,
++                      "Handling SOCKS 5 auth method reply in wrong state!");
++    NS_ABORT_IF_FALSE(mDataLength == 2,
++                      "SOCKS 5 auth method reply must be 2 bytes!");
+ 
+-    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
++    LOGDEBUG(("socks5: checking auth method reply"));
+ 
+-        request[3] = 0x01; // encoding of destination address (1 == IPv4)
+-        request_len = 8;   // 4 for address, 4 SOCKS headers
++    // Check version number
++    if (ReadUint8() != 0x05) {
++        LOGERROR(("socks5: unexpected version in the reply"));
++        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-        char * ip = (char*)(&addr->inet.ip);
+-        request[4] = *ip++;
+-        request[5] = *ip++;
+-        request[6] = *ip++;
+-        request[7] = *ip++;
++    // Make sure our authentication choice was accepted
++    if (ReadUint8() != 0x00) {
++        LOGERROR(("socks5: server did not accept our authentication method"));
++        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
++    return WriteV5ConnectRequest();
++}
+ 
+-        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;
++PRStatus
++nsSOCKSSocketInfo::WriteV5ConnectRequest()
++{
++    // 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"));
++
++    mDataLength = 0;
++    mState = SOCKS5_WRITE_CONNECT_REQUEST;
++
++    WriteUint8(0x05); // version -- 5
++    WriteUint8(0x01); // command -- connect
++    WriteUint8(0x00); // reserved
++   
++    // Add the address to the SOCKS 5 request. SOCKS 5 supports several
++    // address types, so we pick the one that works best for us.
++    if (proxy_resolve) {
++        // Add the host name. Only a single byte is used to store the length,
++        // so we must prevent long names from being used.
++        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
++            LOGERROR(("socks5: destination host name is too long!"));
++            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
++            return PR_FAILURE;
+         }
++        WriteUint8(0x03); // addr type -- domainname
++        WriteUint8(mDestinationHost.Length()); // name length
++        WriteString(mDestinationHost);
++    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
++        WriteUint8(0x01); // addr type -- IPv4
++        WriteNetAddr(addr);
++    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
++        WriteUint8(0x04); // addr type -- IPv6
++        WriteNetAddr(addr);
+     } 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;
++        LOGERROR(("socks5: destination address of unknown type!"));
++        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
++        return PR_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;
+-    }
++    WriteNetPort(addr); // port
+ 
+-    if (response[0] != 0x05) {
+-        // bad response
+-        LOGERROR(("Not a SOCKS 5 reply. Expected: 5; received: %x", response[0]));
+-        return NS_ERROR_FAILURE;
+-    }
++    return PR_SUCCESS;
++}
+ 
+-    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;
++PRStatus
++nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(PRUint8 *type, PRUint32 *len)
++{
++    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
++                      mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
++                      "Invalid state!");
++    NS_ABORT_IF_FALSE(mDataLength >= 5,
++                      "SOCKS 5 connection reply must be at least 5 bytes!");
++ 
++    // Seek to the address location 
++    mReadOffset = 3;
++   
++    *type = ReadUint8();
++
++    switch (*type) {
++        case 0x01: // ipv4
++            *len = 4 - 1;
+             break;
+-        case 0x04: // IPv6
+-	    desired_len = 16 + 2 - 1;
++        case 0x04: // ipv6
++            *len = 16 - 1;
+             break;
+-        default: // unknown format
+-            return NS_ERROR_FAILURE;
++        case 0x03: // fqdn
++            *len = ReadUint8();
+             break;
++        default:   // wrong address type
++            LOGERROR(("socks5: wrong address type in connection reply!"));
++            return PR_FAILURE;
+     }
+-    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;
++    return PR_SUCCESS;
++}
+ 
+-    switch (response[3]) {
+-        case 0x01: // IPv4
++PRStatus
++nsSOCKSSocketInfo::ReadV5ConnectResponseTop()
++{
++    PRUint8 res;
++    PRUint32 len;
+ 
+-            extPort = (response[8] << 8) | response[9];
++    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP,
++                      "Invalid state!");
++    NS_ABORT_IF_FALSE(mDataLength == 5,
++                      "SOCKS 5 connection reply must be exactly 5 bytes!");
+ 
+-            PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, extPort, extAddr);
++    LOGDEBUG(("socks5: checking connection reply"));
+ 
+-            ip = (char*)(&extAddr->inet.ip);
+-            *ip++ = response[4];
+-            *ip++ = response[5];
+-            *ip++ = response[6];
+-            *ip++ = response[7];
++    // Check version number
++    if (ReadUint8() != 0x05) {
++        LOGERROR(("socks5: unexpected version in the reply"));
++        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-            break;
+-        case 0x04: // IPv6
++    // Check response
++    res = ReadUint8();
++    if (res != 0x00) {
++        PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
++
++        switch (res) {
++            case 0x01:
++                LOGERROR(("socks5: connect failed: "
++                          "01, General SOCKS server failure."));
++                break;
++            case 0x02:
++                LOGERROR(("socks5: connect failed: "
++                          "02, Connection not allowed by ruleset."));
++                break;
++            case 0x03:
++                LOGERROR(("socks5: connect failed: 03, Network unreachable."));
++                c = PR_NETWORK_UNREACHABLE_ERROR;
++                break;
++            case 0x04:
++                LOGERROR(("socks5: connect failed: 04, Host unreachable."));
++                break;
++            case 0x05:
++                LOGERROR(("socks5: connect failed: 05, Connection refused."));
++                break;
++            case 0x06:  
++                LOGERROR(("socks5: connect failed: 06, TTL expired."));
++                c = PR_CONNECT_TIMEOUT_ERROR;
++                break;
++            case 0x07:
++                LOGERROR(("socks5: connect failed: "
++                          "07, Command not supported."));
++                break;
++            case 0x08:
++                LOGERROR(("socks5: connect failed: "
++                          "08, Address type not supported."));
++                c = PR_BAD_ADDRESS_ERROR;
++                break;
++            default:
++                LOGERROR(("socks5: connect failed."));
++                break;
++        }
+ 
+-            extPort = (response[20] << 8) | response[21];
++        HandshakeFinished(c);
++        return PR_FAILURE;
++    }
+ 
+-            PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, extPort, extAddr);
++    if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
++        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-            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];
++    mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
++    WantRead(len + 2);
+ 
+-            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;
++    return PR_SUCCESS;
+ }
+ 
+-// 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)
++PRStatus
++nsSOCKSSocketInfo::ReadV5ConnectResponseBottom()
+ {
+-    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];
++    PRUint8 type;
++    PRUint32 len;
+ 
+-    NS_ENSURE_TRUE(fd, NS_ERROR_NOT_INITIALIZED);
+-    NS_ENSURE_TRUE(addr, NS_ERROR_NOT_INITIALIZED);
++    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
++                      "Invalid state!");
+ 
+-    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 (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
++        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
++        return PR_FAILURE;
++    }
+ 
+-    if (info->Flags() & nsISocketProvider::PROXY_RESOLVES_HOST) {
++    NS_ABORT_IF_FALSE(mDataLength == 7+len,
++                      "SOCKS 5 unexpected length of connection reply!");
+ 
+-        LOGDEBUG(("using server to resolve hostnames rather than resolving it first\n"));
++    LOGDEBUG(("socks5: loading source addr and port"));
++    // Read what the proxy says is our source address
++    switch (type) {
++        case 0x01: // ipv4
++            ReadNetAddr(&mExternalProxyAddr, PR_AF_INET);
++            break;
++        case 0x04: // ipv6
++            ReadNetAddr(&mExternalProxyAddr, PR_AF_INET6);
++            break;
++        case 0x03: // fqdn (skip)
++            mReadOffset += len;
++            mExternalProxyAddr.raw.family = PR_AF_INET;
++            break;
++    }
+ 
+-        // 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.
++    ReadNetPort(&mExternalProxyAddr);
+ 
+-        // an extension to SOCKS 4, called 4a, specifies a way
+-        // to do this, so we'll try that and hope the
+-        // server supports it.
++    LOGDEBUG(("socks5: connected!"));
++    HandshakeFinished();
+ 
+-        // the real destination hostname and port was stored
+-        // in our info object earlier when this layer was created.
++    return PR_SUCCESS;
++}
+ 
+-        const nsCString& destHost = info->DestinationHost();
++void
++nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to)
++{
++    mTimeout = to;
++}
+ 
+-        LOGDEBUG(("host:port -> %s:%li\n", destHost.get(), destPort));
++PRStatus
++nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, PRInt16 oflags)
++{
++    LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
++
++    switch (mState) {
++        case SOCKS_INITIAL:
++            return ConnectToProxy(fd);
++        case SOCKS_CONNECTING_TO_PROXY:
++            return ContinueConnectingToProxy(fd, oflags);
++        case SOCKS4_WRITE_CONNECT_REQUEST:
++            if (WriteToSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++            WantRead(8);
++            mState = SOCKS4_READ_CONNECT_RESPONSE;
++            return PR_SUCCESS;
++        case SOCKS4_READ_CONNECT_RESPONSE:
++            if (ReadFromSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++            return ReadV4ConnectResponse();
++
++        case SOCKS5_WRITE_AUTH_REQUEST:
++            if (WriteToSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++            WantRead(2);
++            mState = SOCKS5_READ_AUTH_RESPONSE;
++            return PR_SUCCESS;
++        case SOCKS5_READ_AUTH_RESPONSE:
++            if (ReadFromSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++            return ReadV5AuthResponse();
++        case SOCKS5_WRITE_CONNECT_REQUEST:
++            if (WriteToSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++
++            // The SOCKS 5 response to the connection request is variable
++            // length. First, we'll read enough to tell how long the response
++            // is, and will read the rest later.
++            WantRead(5);
++            mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
++            return PR_SUCCESS;
++        case SOCKS5_READ_CONNECT_RESPONSE_TOP:
++            if (ReadFromSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++            return ReadV5ConnectResponseTop();
++        case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
++            if (ReadFromSocket(fd) != PR_SUCCESS)
++                return PR_FAILURE;
++            return ReadV5ConnectResponseBottom();
++
++        case SOCKS_CONNECTED:
++            LOGERROR(("socks: already connected"));
++            HandshakeFinished(PR_IS_CONNECTED_ERROR);
++            return PR_FAILURE;
++        case SOCKS_FAILED:
++            LOGERROR(("socks: already failed"));
++            return PR_FAILURE;
++    }
+ 
+-        // the IP portion of the query is set to this special address.
+-        request[4] = 0;
+-        request[5] = 0;
+-        request[6] = 0;
+-        request[7] = 1;
++    LOGERROR(("socks: executing handshake in invalid state, %d", mState));
++    HandshakeFinished(PR_INVALID_STATE_ERROR);
+ 
+-        write_len = pr_Send(fd, request, request_len, 0, &timeout);
+-        if (write_len != request_len) {
+-            return NS_ERROR_FAILURE;
+-        }
++    return PR_FAILURE;
++}
+ 
+-        // Remember the NULL.
+-        int host_len = destHost.Length() + 1;
++PRInt16
++nsSOCKSSocketInfo::GetPollFlags() const
++{
++    switch (mState) {
++        case SOCKS_CONNECTING_TO_PROXY:
++            return PR_POLL_EXCEPT | PR_POLL_WRITE;
++        case SOCKS4_WRITE_CONNECT_REQUEST:
++        case SOCKS5_WRITE_AUTH_REQUEST:
++        case SOCKS5_WRITE_CONNECT_REQUEST:
++            return PR_POLL_WRITE;
++        case SOCKS4_READ_CONNECT_RESPONSE:
++        case SOCKS5_READ_AUTH_RESPONSE:
++        case SOCKS5_READ_CONNECT_RESPONSE_TOP:
++        case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
++            return PR_POLL_READ;
++        default:
++            break;
++    }
+ 
+-        write_len = pr_Send(fd, destHost.get(), host_len, 0, &timeout);
+-        if (write_len != host_len) {
+-            return NS_ERROR_FAILURE;
+-        }
++    return 0;
++}
+ 
+-        // 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
+-        }
++inline void
++nsSOCKSSocketInfo::WriteUint8(PRUint8 v)
++{
++    NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE,
++                      "Can't write that much data!");
++    mData[mDataLength] = v;
++    mDataLength += sizeof(v);
++}
+ 
+-    } else {
+-        LOGERROR(("Don't know what kind of IP address this is."));
+-        return NS_ERROR_FAILURE;		// don't recognize this type
+-    }
++inline void
++nsSOCKSSocketInfo::WriteUint16(PRUint16 v)
++{
++    NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE,
++                      "Can't write that much data!");
++    memcpy(mData + mDataLength, &v, sizeof(v));
++    mDataLength += sizeof(v);
++}
+ 
+-    if (request_len > 0) {
+-        write_len = pr_Send(fd, request, request_len, 0, &timeout);
+-        if (write_len != request_len) {
+-            return NS_ERROR_FAILURE;
+-        }
+-    }
++inline void
++nsSOCKSSocketInfo::WriteUint32(PRUint32 v)
++{
++    NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE,
++                      "Can't write that much data!");
++    memcpy(mData + mDataLength, &v, sizeof(v));
++    mDataLength += sizeof(v);
++}
+ 
+-    // 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;
+-    }
++void
++nsSOCKSSocketInfo::WriteNetAddr(const PRNetAddr *addr)
++{
++    const char *ip = NULL;
++    PRUint32 len = 0;
+ 
+-    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;
++    if (PR_NetAddrFamily(addr) == PR_AF_INET) {
++        ip = (const char*)&addr->inet.ip;
++        len = sizeof(addr->inet.ip);
++    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
++        ip = (const char*)addr->ipv6.ip.pr_s6_addr;
++        len = sizeof(addr->ipv6.ip.pr_s6_addr);
+     }
+ 
+-    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;
+-    }
++    NS_ABORT_IF_FALSE(ip != NULL, "Unknown address");
++    NS_ABORT_IF_FALSE(mDataLength + len <= BUFFER_SIZE,
++                      "Can't write that much data!");
+  
+-    return NS_OK;
++    memcpy(mData + mDataLength, ip, len);
++    mDataLength += len;
++}
+ 
++void
++nsSOCKSSocketInfo::WriteNetPort(const PRNetAddr *addr)
++{
++    WriteUint16(PR_NetAddrInetPort(addr));
+ }
+ 
++void
++nsSOCKSSocketInfo::WriteString(const nsACString &str)
++{
++    NS_ABORT_IF_FALSE(mDataLength + str.Length() <= BUFFER_SIZE,
++                      "Can't write that much data!");
++    memcpy(mData + mDataLength, str.Data(), str.Length());
++    mDataLength += str.Length();
++}
+ 
+-static PRStatus
+-nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime /*timeout*/)
++inline PRUint8
++nsSOCKSSocketInfo::ReadUint8()
+ {
++    PRUint8 rv;
++    NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
++                      "Not enough space to pop a uint8!");
++    rv = mData[mReadOffset];
++    mReadOffset += sizeof(rv);
++    return rv;
++}
+ 
+-    PRStatus status;
++inline PRUint16
++nsSOCKSSocketInfo::ReadUint16()
++{
++    PRUint16 rv;
++    NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
++                      "Not enough space to pop a uint16!");
++    memcpy(&rv, mData + mReadOffset, sizeof(rv));
++    mReadOffset += sizeof(rv);
++    return rv;
++}
+ 
+-    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
+-    if (info == NULL) return PR_FAILURE;
++inline PRUint32
++nsSOCKSSocketInfo::ReadUint32()
++{
++    PRUint32 rv;
++    NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength,
++                      "Not enough space to pop a uint32!");
++    memcpy(&rv, mData + mReadOffset, sizeof(rv));
++    mReadOffset += sizeof(rv);
++    return rv;
++}
+ 
+-    // First, we need to look up our proxy...
+-    const nsCString &proxyHost = info->ProxyHost();
++void
++nsSOCKSSocketInfo::ReadNetAddr(PRNetAddr *addr, PRUint16 fam)
++{
++    PRUint32 amt;
++    const PRUint8 *ip = mData + mReadOffset;
++
++    addr->raw.family = fam;
++    if (fam == PR_AF_INET) {
++        amt = sizeof(addr->inet.ip);
++        NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength,
++                          "Not enough space to pop an ipv4 addr!");
++        memcpy(&addr->inet.ip, ip, amt);
++    } else if (fam == PR_AF_INET6) {
++        amt = sizeof(addr->ipv6.ip.pr_s6_addr);
++        NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength,
++                          "Not enough space to pop an ipv6 addr!");
++        memcpy(addr->ipv6.ip.pr_s6_addr, ip, amt);
++    }
+ 
+-    if (proxyHost.IsEmpty())
+-        return PR_FAILURE;
++    mReadOffset += amt;
++}
+ 
+-    PRInt32 socksVersion = info->Version();
++void
++nsSOCKSSocketInfo::ReadNetPort(PRNetAddr *addr)
++{
++    addr->inet.port = ReadUint16();
++}
+ 
+-    LOGDEBUG(("nsSOCKSIOLayerConnect SOCKS %u; proxyHost: %s.", socksVersion, proxyHost.get()));
++void
++nsSOCKSSocketInfo::WantRead(PRUint32 sz)
++{
++    NS_ABORT_IF_FALSE(mDataIoPtr == NULL,
++                      "WantRead() called while I/O already in progress!");
++    NS_ABORT_IF_FALSE(mDataLength + sz <= BUFFER_SIZE,
++                      "Can't read that much data!");
++    mAmountToRead = sz;
++}
+ 
+-    // 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;
++PRStatus
++nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd)
++{
++    PRInt32 rc;
++    const PRUint8 *end;
+ 
+-        rv = dns->Resolve(proxyHost, 0, getter_AddRefs(rec));
+-        if (NS_FAILED(rv))
+-            return PR_FAILURE;
++    if (!mAmountToRead) {
++        LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
++        return PR_SUCCESS;
+     }
+ 
+-    info->SetInternalProxyAddr(&proxyAddr);
++    if (!mDataIoPtr) {
++        mDataIoPtr = mData + mDataLength;
++        mDataLength += mAmountToRead;
++    }
+ 
+-    // 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
++    end = mData + mDataLength;
++
++    while (mDataIoPtr < end) {
++        rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
++        if (rc <= 0) {
++            if (rc == 0) {
++                LOGERROR(("socks: proxy server closed connection"));
++                HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
++                return PR_FAILURE;
++            } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
++                LOGDEBUG(("socks: ReadFromSocket(), want read"));
++            }
++            break;
++        }
+ 
+-    // Preserve the non-blocking state of the socket
+-    PRBool nonblocking;
+-    PRSocketOptionData sockopt;
+-    sockopt.option = PR_SockOpt_Nonblocking;
+-    status = PR_GetSocketOption(fd, &sockopt);
++        mDataIoPtr += rc;
++    }
+ 
+-    if (PR_SUCCESS != status) {
+-        LOGERROR(("PR_GetSocketOption() failed. status = %x.", status));
+-        return status;
++    LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
++             unsigned(mDataIoPtr - mData)));
++    if (mDataIoPtr == end) {
++        mDataIoPtr = nsnull;
++        mAmountToRead = 0;
++        mReadOffset = 0;
++        return PR_SUCCESS;
+     }
+ 
+-    // Store blocking option
+-    nonblocking = sockopt.value.non_blocking;
++    return PR_FAILURE;
++}
+ 
+-    sockopt.option = PR_SockOpt_Nonblocking;
+-    sockopt.value.non_blocking = PR_FALSE;
+-    status = PR_SetSocketOption(fd, &sockopt);
++PRStatus
++nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd)
++{
++    PRInt32 rc;
++    const PRUint8 *end;
+ 
+-    if (PR_SUCCESS != status) {
+-        LOGERROR(("PR_SetSocketOption() failed. status = %x.", status));
+-        return status;
++    if (!mDataLength) {
++        LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
++        return PR_SUCCESS;
+     }
+ 
+-    // Now setup sockopts, so we can restore the value later.
+-    sockopt.option = PR_SockOpt_Nonblocking;
+-    sockopt.value.non_blocking = nonblocking;
++    if (!mDataIoPtr)
++        mDataIoPtr = mData;
+ 
+-    // 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);
++    end = mData + mDataLength;
+ 
+-    // Connect to the proxy server.
+-    PRInt32 addresses = 0;
+-    do {
+-        rv = rec->GetNextAddr(info->ProxyPort(), &proxyAddr);
+-        if (NS_FAILED(rv)) {
+-            status = PR_FAILURE;
++    while (mDataIoPtr < end) {
++        rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
++        if (rc < 0) {
++            if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
++                LOGDEBUG(("socks: WriteToSocket(), want write"));
++            }
+             break;
+         }
+-        ++addresses;
+-        status = fd->lower->methods->connect(fd->lower, &proxyAddr, connectWait);
+-    } while (PR_SUCCESS != status);
++        
++        mDataIoPtr += rc;
++    }
+ 
+-    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;
++    if (mDataIoPtr == end) {
++        mDataIoPtr = nsnull;
++        mDataLength = 0;
++        mReadOffset = 0;
++        return PR_SUCCESS;
+     }
++    
++    return PR_FAILURE;
++}
+ 
++static PRStatus
++nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
++{
++    PRStatus status;
++    PRNetAddr dst;
+ 
+-    // We are now connected to the SOCKS proxy server.
+-    // Now we will negotiate a connection to the desired server.
++    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
++    if (info == NULL) return PR_FAILURE;
+ 
+-    // External IP address returned from ConnectSOCKS5(). Not supported in SOCKS4.
+-    PRNetAddr extAddr;
+-    PR_InitializeNetAddr(PR_IpAddrNull, 0, &extAddr);
++    if (PR_NetAddrFamily(addr) == PR_AF_INET6 &&
++        PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
++        const PRUint8 *srcp;
+ 
+-    NS_ASSERTION((socksVersion == 4) || (socksVersion == 5), "SOCKS Version must be selected");
++        LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
+ 
+-    // Try to connect via SOCKS 5.
+-    if (socksVersion == 5) {
+-        rv = ConnectSOCKS5(fd, addr, &extAddr, connectWait);
++        // 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));
++    }
+ 
+-        if (NS_FAILED(rv)) {
+-            PR_SetSocketOption(fd, &sockopt);
+-            return PR_FAILURE;
+-        }
++    info->SetDestinationAddr(&dst);
++    info->SetConnectTimeout(to);
+ 
+-    }
++    do {
++        status = info->DoHandshake(fd, -1);
++    } while (status == PR_SUCCESS && !info->IsConnected());
+ 
+-    // Try to connect via SOCKS 4.
+-    else {
+-        rv = ConnectSOCKS4(fd, addr, connectWait);
++    return status;
++}
+ 
+-        if (NS_FAILED(rv)) {
+-            PR_SetSocketOption(fd, &sockopt);
+-            return PR_FAILURE;
+-        }
++static PRStatus
++nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, PRInt16 oflags)
++{
++    PRStatus status;
+ 
+-    }
++    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
++    if (info == NULL) return PR_FAILURE;
+ 
++    do { 
++        status = info->DoHandshake(fd, oflags);
++    } while (status == PR_SUCCESS && !info->IsConnected());
+ 
+-    info->SetDestinationAddr((PRNetAddr*)addr);
+-    info->SetExternalProxyAddr(&extAddr);
++    return status;
++}
+ 
+-    // restore non-blocking option
+-    PR_SetSocketOption(fd, &sockopt);
++static PRInt16
++nsSOCKSIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
++{
++    nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
++    if (info == NULL) return PR_FAILURE;
+ 
+-    // we're set-up and connected.
+-    // this socket can be used as normal now.
++    if (!info->IsConnected()) {
++        *out_flags = 0;
++        return info->GetPollFlags();
++    }
+ 
+-    return PR_SUCCESS;
++    return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
+ }
+ 
+ static PRStatus
+@@ -885,6 +1120,8 @@ nsSOCKSIOLayerAddToSocket(PRInt32 family,
+         nsSOCKSIOLayerMethods		= *PR_GetDefaultIOMethods();
+ 
+         nsSOCKSIOLayerMethods.connect	= nsSOCKSIOLayerConnect;
++        nsSOCKSIOLayerMethods.connectcontinue	= nsSOCKSIOLayerConnectContinue;
++        nsSOCKSIOLayerMethods.poll	= nsSOCKSIOLayerPoll;
+         nsSOCKSIOLayerMethods.bind	= nsSOCKSIOLayerBind;
+         nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
+         nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
+-- 
+1.7.3.4
+
diff --git a/src/current-patches/0001-Firefox5-Block-Components.interfaces-lookupMethod-fr.patch b/src/current-patches/0001-Firefox5-Block-Components.interfaces-lookupMethod-fr.patch
new file mode 100644
index 0000000..816e2d2
--- /dev/null
+++ b/src/current-patches/0001-Firefox5-Block-Components.interfaces-lookupMethod-fr.patch
@@ -0,0 +1,50 @@
+From cb6df58b95028693007936e423d43223609e17cc Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:41 -0700
+Subject: [PATCH 1/3] Firefox5: Block Components.interfaces,lookupMethod from content
+
+This patch removes the ability of content script to access
+Components.interfaces.* as well as call or access Components.lookupMethod.
+
+These two interfaces seem to be exposed to content script only to make our
+lives difficult. Components.lookupMethod can undo our JS hooks, and
+Components.interfaces is useful for fingerprinting the platform, OS, and
+Firebox version.
+
+They appear to have no other legitimate use. See also:
+https://bugzilla.mozilla.org/show_bug.cgi?id=429070
+https://trac.torproject.org/projects/tor/ticket/2873
+https://trac.torproject.org/projects/tor/ticket/2874
+---
+ js/src/xpconnect/src/xpccomponents.cpp |    8 ++++++--
+ 1 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp
+index 5e789e7..5c76981 100644
+--- a/js/src/xpconnect/src/xpccomponents.cpp
++++ b/js/src/xpconnect/src/xpccomponents.cpp
+@@ -4287,7 +4287,9 @@ nsXPCComponents::CanCreateWrapper(const nsIID * iid, char **_retval)
+ NS_IMETHODIMP
+ nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, char **_retval)
+ {
+-    static const char* allowed[] = { "isSuccessCode", "lookupMethod", nsnull };
++    // XXX: Pref observer? Also, is this what we want? Seems like a plan
++    //static const char* allowed[] = { "isSuccessCode", "lookupMethod", nsnull };
++    static const char* allowed[] = { "isSuccessCode", nsnull };
+     *_retval = xpc_CheckAccessList(methodName, allowed);
+     return NS_OK;
+ }
+@@ -4296,7 +4298,9 @@ nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, c
+ NS_IMETHODIMP
+ nsXPCComponents::CanGetProperty(const nsIID * iid, const PRUnichar *propertyName, char **_retval)
+ {
+-    static const char* allowed[] = { "interfaces", "interfacesByID", "results", nsnull};
++    // XXX: Pref observer? Also, is this what we want? Seems like a plan
++    //    static const char* allowed[] = { "interfaces", "interfacesByID", "results", nsnull};
++    static const char* allowed[] = { "results", nsnull};
+     *_retval = xpc_CheckAccessList(propertyName, allowed);
+     return NS_OK;
+ }
+-- 
+1.7.3.4
+
diff --git a/src/current-patches/0002-Firefox-Block-Components.interfaces-lookupMethod-fro.patch b/src/current-patches/0002-Firefox-Block-Components.interfaces-lookupMethod-fro.patch
deleted file mode 100644
index 21049fa..0000000
--- a/src/current-patches/0002-Firefox-Block-Components.interfaces-lookupMethod-fro.patch
+++ /dev/null
@@ -1,50 +0,0 @@
-From 8a056d35c126c6915c6e16997fd5db0865079e7d Mon Sep 17 00:00:00 2001
-From: Mike Perry <mikeperry-git@xxxxxxxxxx>
-Date: Mon, 20 Jun 2011 17:07:41 -0700
-Subject: [PATCH 2/4] Firefox: Block Components.interfaces,lookupMethod from content
-
-This patch removes the ability of content script to access
-Components.interfaces.* as well as call or access Components.lookupMethod.
-
-These two interfaces seem to be exposed to content script only to make our
-lives difficult. Components.lookupMethod can undo our JS hooks, and
-Components.interfaces is useful for fingerprinting the platform, OS, and
-Firebox version.
-
-They appear to have no other legitimate use. See also:
-https://bugzilla.mozilla.org/show_bug.cgi?id=429070
-https://trac.torproject.org/projects/tor/ticket/2873
-https://trac.torproject.org/projects/tor/ticket/2874
----
- js/src/xpconnect/src/xpccomponents.cpp |    8 ++++++--
- 1 files changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp
-index 1c141f9..85a2b4e 100644
---- a/js/src/xpconnect/src/xpccomponents.cpp
-+++ b/js/src/xpconnect/src/xpccomponents.cpp
-@@ -4294,7 +4294,9 @@ nsXPCComponents::CanCreateWrapper(const nsIID * iid, char **_retval)
- NS_IMETHODIMP
- nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, char **_retval)
- {
--    static const char* allowed[] = { "isSuccessCode", "lookupMethod", nsnull };
-+    // XXX: Pref observer? Also, is this what we want? Seems like a plan
-+    //static const char* allowed[] = { "isSuccessCode", "lookupMethod", nsnull };
-+    static const char* allowed[] = { "isSuccessCode", nsnull };
-     *_retval = xpc_CheckAccessList(methodName, allowed);
-     return NS_OK;
- }
-@@ -4303,7 +4305,9 @@ nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, c
- NS_IMETHODIMP
- nsXPCComponents::CanGetProperty(const nsIID * iid, const PRUnichar *propertyName, char **_retval)
- {
--    static const char* allowed[] = { "interfaces", "interfacesByID", "results", nsnull};
-+    // XXX: Pref observer? Also, is this what we want? Seems like a plan
-+    //    static const char* allowed[] = { "interfaces", "interfacesByID", "results", nsnull};
-+    static const char* allowed[] = { "results", nsnull};
-     *_retval = xpc_CheckAccessList(propertyName, allowed);
-     return NS_OK;
- }
--- 
-1.7.3.4
-
diff --git a/src/current-patches/0002-Firefox4-Block-Components.interfaces-lookupMethod-fr.patch b/src/current-patches/0002-Firefox4-Block-Components.interfaces-lookupMethod-fr.patch
new file mode 100644
index 0000000..8e34500
--- /dev/null
+++ b/src/current-patches/0002-Firefox4-Block-Components.interfaces-lookupMethod-fr.patch
@@ -0,0 +1,50 @@
+From ebaf58a014f98942886ae829da83fadd662df948 Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:41 -0700
+Subject: [PATCH 2/4] Firefox4: Block Components.interfaces,lookupMethod from content
+
+This patch removes the ability of content script to access
+Components.interfaces.* as well as call or access Components.lookupMethod.
+
+These two interfaces seem to be exposed to content script only to make our
+lives difficult. Components.lookupMethod can undo our JS hooks, and
+Components.interfaces is useful for fingerprinting the platform, OS, and
+Firebox version.
+
+They appear to have no other legitimate use. See also:
+https://bugzilla.mozilla.org/show_bug.cgi?id=429070
+https://trac.torproject.org/projects/tor/ticket/2873
+https://trac.torproject.org/projects/tor/ticket/2874
+---
+ js/src/xpconnect/src/xpccomponents.cpp |    8 ++++++--
+ 1 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp
+index 1c141f9..85a2b4e 100644
+--- a/js/src/xpconnect/src/xpccomponents.cpp
++++ b/js/src/xpconnect/src/xpccomponents.cpp
+@@ -4294,7 +4294,9 @@ nsXPCComponents::CanCreateWrapper(const nsIID * iid, char **_retval)
+ NS_IMETHODIMP
+ nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, char **_retval)
+ {
+-    static const char* allowed[] = { "isSuccessCode", "lookupMethod", nsnull };
++    // XXX: Pref observer? Also, is this what we want? Seems like a plan
++    //static const char* allowed[] = { "isSuccessCode", "lookupMethod", nsnull };
++    static const char* allowed[] = { "isSuccessCode", nsnull };
+     *_retval = xpc_CheckAccessList(methodName, allowed);
+     return NS_OK;
+ }
+@@ -4303,7 +4305,9 @@ nsXPCComponents::CanCallMethod(const nsIID * iid, const PRUnichar *methodName, c
+ NS_IMETHODIMP
+ nsXPCComponents::CanGetProperty(const nsIID * iid, const PRUnichar *propertyName, char **_retval)
+ {
+-    static const char* allowed[] = { "interfaces", "interfacesByID", "results", nsnull};
++    // XXX: Pref observer? Also, is this what we want? Seems like a plan
++    //    static const char* allowed[] = { "interfaces", "interfacesByID", "results", nsnull};
++    static const char* allowed[] = { "results", nsnull};
+     *_retval = xpc_CheckAccessList(propertyName, allowed);
+     return NS_OK;
+ }
+-- 
+1.7.3.4
+
diff --git a/src/current-patches/0002-Firefox5-Make-Intermediate-Cert-Store-memory-only.patch b/src/current-patches/0002-Firefox5-Make-Intermediate-Cert-Store-memory-only.patch
new file mode 100644
index 0000000..17ad3a2
--- /dev/null
+++ b/src/current-patches/0002-Firefox5-Make-Intermediate-Cert-Store-memory-only.patch
@@ -0,0 +1,283 @@
+From 16b89c54032d1ad0acf2d6fa005b292a6f434791 Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:49 -0700
+Subject: [PATCH 2/3] Firefox5: Make Intermediate Cert Store memory-only.
+
+This patch makes the intermediate SSL cert store exist in memory only. It
+exposes a pref ('security.nocertdb') to toggle to clear the store, but this
+seems buggy.
+
+The pref must be set before startup in prefs.js.
+https://trac.torproject.org/projects/tor/ticket/2949
+---
+ security/manager/ssl/src/nsNSSComponent.cpp |  180 ++++++++++++++++++---------
+ 1 files changed, 120 insertions(+), 60 deletions(-)
+
+diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp
+index d3ae772..fa37ace 100644
+--- a/security/manager/ssl/src/nsNSSComponent.cpp
++++ b/security/manager/ssl/src/nsNSSComponent.cpp
+@@ -1658,8 +1658,21 @@ nsNSSComponent::InitializeNSS(PRBool showWarningBox)
+     // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
+     // "/usr/lib/nss/libnssckbi.so".
+     PRUint32 init_flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
+-    SECStatus init_rv = ::NSS_Initialize(profileStr.get(), "", "",
++    PRBool nocertdb = false;
++    mPrefBranch->GetBoolPref("security.nocertdb", &nocertdb);
++
++    // XXX: We can also do the the following to only disable the certdb.
++    // Leaving this codepath in as a fallback in case InitNODB fails
++    if (nocertdb)
++      init_flags |= NSS_INIT_NOCERTDB;
++
++    SECStatus init_rv;
++    if (nocertdb) {
++        init_rv = ::NSS_NoDB_Init(NULL);
++    } else {
++        init_rv = ::NSS_Initialize(profileStr.get(), "", "",
+                                          SECMOD_DB, init_flags);
++    }
+ 
+     if (init_rv != SECSuccess) {
+       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("can not init NSS r/w in %s\n", profileStr.get()));
+@@ -2215,70 +2228,106 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic,
+       }
+     }
+   }
+-  else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { 
+-    nsNSSShutDownPreventionLock locker;
+-    PRBool clearSessionCache = PR_FALSE;
+-    PRBool enabled;
++  else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
+     NS_ConvertUTF16toUTF8  prefName(someData);
++    // XXX: This should be an observer notification, so we can properly cancel it
++    if (prefName.Equals("security.nocertdb")) {
++      // XXX: If these functions tell us to cancel, the browser seems to get left in an
++      // indeterminate state that prevents SSL from being used.
++      //
++      // We apparently need to wait for all SSL sockets to shut down on their
++      // own (this can take up to a minute!) and then attempt to alter the pref
++      // again before doing anything.
++      //
++      // So any implementation of New Identity based on this code will need to keep
++      // attempting to send the notification until it is not canceled. Ugh...
++      if (!DoProfileApproveChange(aSubject)) {
++        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled\n"));
++        return NS_OK;
++      }
+ 
+-    if (prefName.Equals("security.enable_ssl2")) {
+-      mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
+-      SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
+-      clearSessionCache = PR_TRUE;
+-    } else if (prefName.Equals("security.enable_ssl3")) {
+-      mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
+-      clearSessionCache = PR_TRUE;
+-    } else if (prefName.Equals("security.enable_tls")) {
+-      mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
+-      clearSessionCache = PR_TRUE;
+-    } else if (prefName.Equals("security.enable_tls_session_tickets")) {
+-      mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
+-    } else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
+-      mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
+-      SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
+-    } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
+-      mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, 
+-        enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
+-    } else if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
+-      char *unrestricted_hosts=nsnull;
+-      mPrefBranch->GetCharPref("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
+-      if (unrestricted_hosts) {
+-        nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(nsDependentCString(unrestricted_hosts));
+-        nsMemory::Free(unrestricted_hosts);
++      DoProfileChangeNetTeardown();
++      if (!DoProfileChangeTeardown(aSubject)) {
++        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled\n"));
++        return NS_OK;
++      }
++
++      if (!DoProfileBeforeChange(aSubject)) {
++        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled by NSS shutdown\n"));
++        // Need to re-add observer because ShutdownNSS removed it.
++        nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefBranch);
++        pbi->AddObserver("security.", this, PR_FALSE);
++        return NS_OK;
+       }
+-    } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
+-      mPrefBranch->GetBoolPref("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
+-      nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(enabled);
+-    } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
+-      PRInt32 warnLevel = 1;
+-      mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
+-      nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
++
++      DoProfileChangeNetRestore();
++      InitializeNSS(PR_FALSE);
++      InitializeCRLUpdateTimer();
++      return NS_OK;
++    } else {
++      nsNSSShutDownPreventionLock locker;
++      PRBool clearSessionCache = PR_FALSE;
++      PRBool enabled;
++
++      if (prefName.Equals("security.enable_ssl2")) {
++        mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
++        SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
++        clearSessionCache = PR_TRUE;
++      } else if (prefName.Equals("security.enable_ssl3")) {
++        mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
++        clearSessionCache = PR_TRUE;
++      } else if (prefName.Equals("security.enable_tls")) {
++        mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
++        clearSessionCache = PR_TRUE;
++      } else if (prefName.Equals("security.enable_tls_session_tickets")) {
++        mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
++      } else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
++        mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
++        SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
++      } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
++        mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, 
++          enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
++      } else if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
++        char *unrestricted_hosts=nsnull;
++        mPrefBranch->GetCharPref("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
++        if (unrestricted_hosts) {
++          nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(nsDependentCString(unrestricted_hosts));
++          nsMemory::Free(unrestricted_hosts);
++        }
++      } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
++        mPrefBranch->GetBoolPref("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
++        nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(enabled);
++      } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
++        PRInt32 warnLevel = 1;
++        mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
++        nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
+ #ifdef SSL_ENABLE_FALSE_START // Requires NSS 3.12.8
+-    } else if (prefName.Equals("security.ssl.enable_false_start")) {
+-      mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
++      } else if (prefName.Equals("security.ssl.enable_false_start")) {
++        mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
+ #endif
+-    } else if (prefName.Equals("security.OCSP.enabled")
+-               || prefName.Equals("security.OCSP.require")) {
+-      setOCSPOptions(mPrefBranch);
+-    } else {
+-      /* Look through the cipher table and set according to pref setting */
+-      for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
+-        if (prefName.Equals(cp->pref)) {
+-          mPrefBranch->GetBoolPref(cp->pref, &enabled);
+-          SSL_CipherPrefSetDefault(cp->id, enabled);
+-          clearSessionCache = PR_TRUE;
+-          break;
++      } else if (prefName.Equals("security.OCSP.enabled")
++                 || prefName.Equals("security.OCSP.require")) {
++        setOCSPOptions(mPrefBranch);
++      } else {
++        /* Look through the cipher table and set according to pref setting */
++        for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
++          if (prefName.Equals(cp->pref)) {
++            mPrefBranch->GetBoolPref(cp->pref, &enabled);
++            SSL_CipherPrefSetDefault(cp->id, enabled);
++            clearSessionCache = PR_TRUE;
++            break;
++          }
+         }
+       }
++      if (clearSessionCache)
++        SSL_ClearSessionCache();
+     }
+-    if (clearSessionCache)
+-      SSL_ClearSessionCache();
+   }
+   else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_TEARDOWN_TOPIC) == 0) {
+     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network teardown topic\n"));
+@@ -2447,16 +2496,19 @@ nsNSSComponent::RememberCert(CERTCertificate *cert)
+   return NS_OK;
+ }
+ 
+-void
++PRBool
+ nsNSSComponent::DoProfileApproveChange(nsISupports* aSubject)
+ {
+   if (mShutdownObjectList->isUIActive()) {
++    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS UI active in profile change!\n"));
+     ShowAlert(ai_crypto_ui_active);
+     nsCOMPtr<nsIProfileChangeStatus> status = do_QueryInterface(aSubject);
+     if (status) {
+       status->VetoChange();
+     }
++    return false;
+   }
++  return true;
+ }
+ 
+ void
+@@ -2469,16 +2521,18 @@ nsNSSComponent::DoProfileChangeNetTeardown()
+   mIsNetworkDown = PR_TRUE;
+ }
+ 
+-void
++PRBool
+ nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
+ {
+   PRBool callVeto = PR_FALSE;
+ 
+   if (!mShutdownObjectList->ifPossibleDisallowUI()) {
+     callVeto = PR_TRUE;
++    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: Not possible to disallow UI!\n"));
+     ShowAlert(ai_crypto_ui_active);
+   }
+   else if (mShutdownObjectList->areSSLSocketsActive()) {
++    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: SSL Sockets are active!\n"));
+     callVeto = PR_TRUE;
+     ShowAlert(ai_sockets_still_active);
+   }
+@@ -2489,9 +2543,11 @@ nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
+       status->VetoChange();
+     }
+   }
++
++  return !callVeto;
+ }
+ 
+-void
++PRBool
+ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
+ {
+   NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity");
+@@ -2510,16 +2566,20 @@ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
+   }
+     
+   StopCRLUpdateTimer();
++  PRBool allow_change = PR_TRUE;
+ 
+   if (needsCleanup) {
+     if (NS_FAILED(ShutdownNSS())) {
++      PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: Shutdown failed\n"));
+       nsCOMPtr<nsIProfileChangeStatus> status = do_QueryInterface(aSubject);
+       if (status) {
+         status->ChangeFailed();
+       }
++      allow_change = PR_FALSE;
+     }
+   }
+   mShutdownObjectList->allowUI();
++  return allow_change;
+ }
+ 
+ void
+-- 
+1.7.3.4
+
diff --git a/src/current-patches/0003-Firefox-Make-Intermediate-Cert-Store-memory-only.patch b/src/current-patches/0003-Firefox-Make-Intermediate-Cert-Store-memory-only.patch
deleted file mode 100644
index 7e5fc88..0000000
--- a/src/current-patches/0003-Firefox-Make-Intermediate-Cert-Store-memory-only.patch
+++ /dev/null
@@ -1,283 +0,0 @@
-From a401deb3598db247113a998fb287f663b6acfac0 Mon Sep 17 00:00:00 2001
-From: Mike Perry <mikeperry-git@xxxxxxxxxx>
-Date: Mon, 20 Jun 2011 17:07:49 -0700
-Subject: [PATCH 3/4] Firefox: Make Intermediate Cert Store memory-only.
-
-This patch makes the intermediate SSL cert store exist in memory only. It
-exposes a pref ('security.nocertdb') to toggle to clear the store, but this
-seems buggy.
-
-The pref must be set before startup in prefs.js.
-https://trac.torproject.org/projects/tor/ticket/2949
----
- security/manager/ssl/src/nsNSSComponent.cpp |  180 ++++++++++++++++++---------
- 1 files changed, 120 insertions(+), 60 deletions(-)
-
-diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp
-index 33377ac..716bcee 100644
---- a/security/manager/ssl/src/nsNSSComponent.cpp
-+++ b/security/manager/ssl/src/nsNSSComponent.cpp
-@@ -1674,8 +1674,21 @@ nsNSSComponent::InitializeNSS(PRBool showWarningBox)
-     // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
-     // "/usr/lib/nss/libnssckbi.so".
-     PRUint32 init_flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
--    SECStatus init_rv = ::NSS_Initialize(profileStr.get(), "", "",
-+    PRBool nocertdb = false;
-+    mPrefBranch->GetBoolPref("security.nocertdb", &nocertdb);
-+
-+    // XXX: We can also do the the following to only disable the certdb.
-+    // Leaving this codepath in as a fallback in case InitNODB fails
-+    if (nocertdb)
-+      init_flags |= NSS_INIT_NOCERTDB;
-+
-+    SECStatus init_rv;
-+    if (nocertdb) {
-+        init_rv = ::NSS_NoDB_Init(NULL);
-+    } else {
-+        init_rv = ::NSS_Initialize(profileStr.get(), "", "",
-                                          SECMOD_DB, init_flags);
-+    }
- 
-     if (init_rv != SECSuccess) {
-       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("can not init NSS r/w in %s\n", profileStr.get()));
-@@ -2231,70 +2244,106 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic,
-       }
-     }
-   }
--  else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { 
--    nsNSSShutDownPreventionLock locker;
--    PRBool clearSessionCache = PR_FALSE;
--    PRBool enabled;
-+  else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
-     NS_ConvertUTF16toUTF8  prefName(someData);
-+    // XXX: This should be an observer notification, so we can properly cancel it
-+    if (prefName.Equals("security.nocertdb")) {
-+      // XXX: If these functions tell us to cancel, the browser seems to get left in an
-+      // indeterminate state that prevents SSL from being used.
-+      //
-+      // We apparently need to wait for all SSL sockets to shut down on their
-+      // own (this can take up to a minute!) and then attempt to alter the pref
-+      // again before doing anything.
-+      //
-+      // So any implementation of New Identity based on this code will need to keep
-+      // attempting to send the notification until it is not canceled. Ugh...
-+      if (!DoProfileApproveChange(aSubject)) {
-+        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled\n"));
-+        return NS_OK;
-+      }
- 
--    if (prefName.Equals("security.enable_ssl2")) {
--      mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
--      SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
--      SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
--      clearSessionCache = PR_TRUE;
--    } else if (prefName.Equals("security.enable_ssl3")) {
--      mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
--      SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
--      clearSessionCache = PR_TRUE;
--    } else if (prefName.Equals("security.enable_tls")) {
--      mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
--      SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
--      clearSessionCache = PR_TRUE;
--    } else if (prefName.Equals("security.enable_tls_session_tickets")) {
--      mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
--      SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
--    } else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
--      mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
--      SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
--    } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
--      mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
--      SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, 
--        enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
--    } else if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
--      char *unrestricted_hosts=nsnull;
--      mPrefBranch->GetCharPref("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
--      if (unrestricted_hosts) {
--        nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(nsDependentCString(unrestricted_hosts));
--        nsMemory::Free(unrestricted_hosts);
-+      DoProfileChangeNetTeardown();
-+      if (!DoProfileChangeTeardown(aSubject)) {
-+        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled\n"));
-+        return NS_OK;
-+      }
-+
-+      if (!DoProfileBeforeChange(aSubject)) {
-+        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled by NSS shutdown\n"));
-+        // Need to re-add observer because ShutdownNSS removed it.
-+        nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefBranch);
-+        pbi->AddObserver("security.", this, PR_FALSE);
-+        return NS_OK;
-       }
--    } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
--      mPrefBranch->GetBoolPref("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
--      nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(enabled);
--    } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
--      PRInt32 warnLevel = 1;
--      mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
--      nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
-+
-+      DoProfileChangeNetRestore();
-+      InitializeNSS(PR_FALSE);
-+      InitializeCRLUpdateTimer();
-+      return NS_OK;
-+    } else {
-+      nsNSSShutDownPreventionLock locker;
-+      PRBool clearSessionCache = PR_FALSE;
-+      PRBool enabled;
-+
-+      if (prefName.Equals("security.enable_ssl2")) {
-+        mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
-+        SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
-+        SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
-+        clearSessionCache = PR_TRUE;
-+      } else if (prefName.Equals("security.enable_ssl3")) {
-+        mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
-+        SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
-+        clearSessionCache = PR_TRUE;
-+      } else if (prefName.Equals("security.enable_tls")) {
-+        mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
-+        SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
-+        clearSessionCache = PR_TRUE;
-+      } else if (prefName.Equals("security.enable_tls_session_tickets")) {
-+        mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
-+        SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
-+      } else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
-+        mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
-+        SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
-+      } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
-+        mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
-+        SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, 
-+          enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
-+      } else if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
-+        char *unrestricted_hosts=nsnull;
-+        mPrefBranch->GetCharPref("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
-+        if (unrestricted_hosts) {
-+          nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(nsDependentCString(unrestricted_hosts));
-+          nsMemory::Free(unrestricted_hosts);
-+        }
-+      } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
-+        mPrefBranch->GetBoolPref("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
-+        nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(enabled);
-+      } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
-+        PRInt32 warnLevel = 1;
-+        mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
-+        nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
- #ifdef SSL_ENABLE_FALSE_START // Requires NSS 3.12.8
--    } else if (prefName.Equals("security.ssl.enable_false_start")) {
--      mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
--      SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
-+      } else if (prefName.Equals("security.ssl.enable_false_start")) {
-+        mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
-+        SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
- #endif
--    } else if (prefName.Equals("security.OCSP.enabled")
--               || prefName.Equals("security.OCSP.require")) {
--      setOCSPOptions(mPrefBranch);
--    } else {
--      /* Look through the cipher table and set according to pref setting */
--      for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
--        if (prefName.Equals(cp->pref)) {
--          mPrefBranch->GetBoolPref(cp->pref, &enabled);
--          SSL_CipherPrefSetDefault(cp->id, enabled);
--          clearSessionCache = PR_TRUE;
--          break;
-+      } else if (prefName.Equals("security.OCSP.enabled")
-+                 || prefName.Equals("security.OCSP.require")) {
-+        setOCSPOptions(mPrefBranch);
-+      } else {
-+        /* Look through the cipher table and set according to pref setting */
-+        for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
-+          if (prefName.Equals(cp->pref)) {
-+            mPrefBranch->GetBoolPref(cp->pref, &enabled);
-+            SSL_CipherPrefSetDefault(cp->id, enabled);
-+            clearSessionCache = PR_TRUE;
-+            break;
-+          }
-         }
-       }
-+      if (clearSessionCache)
-+        SSL_ClearSessionCache();
-     }
--    if (clearSessionCache)
--      SSL_ClearSessionCache();
-   }
-   else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_TEARDOWN_TOPIC) == 0) {
-     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network teardown topic\n"));
-@@ -2463,16 +2512,19 @@ nsNSSComponent::RememberCert(CERTCertificate *cert)
-   return NS_OK;
- }
- 
--void
-+PRBool
- nsNSSComponent::DoProfileApproveChange(nsISupports* aSubject)
- {
-   if (mShutdownObjectList->isUIActive()) {
-+    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS UI active in profile change!\n"));
-     ShowAlert(ai_crypto_ui_active);
-     nsCOMPtr<nsIProfileChangeStatus> status = do_QueryInterface(aSubject);
-     if (status) {
-       status->VetoChange();
-     }
-+    return false;
-   }
-+  return true;
- }
- 
- void
-@@ -2485,16 +2537,18 @@ nsNSSComponent::DoProfileChangeNetTeardown()
-   mIsNetworkDown = PR_TRUE;
- }
- 
--void
-+PRBool
- nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
- {
-   PRBool callVeto = PR_FALSE;
- 
-   if (!mShutdownObjectList->ifPossibleDisallowUI()) {
-     callVeto = PR_TRUE;
-+    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: Not possible to disallow UI!\n"));
-     ShowAlert(ai_crypto_ui_active);
-   }
-   else if (mShutdownObjectList->areSSLSocketsActive()) {
-+    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: SSL Sockets are active!\n"));
-     callVeto = PR_TRUE;
-     ShowAlert(ai_sockets_still_active);
-   }
-@@ -2505,9 +2559,11 @@ nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
-       status->VetoChange();
-     }
-   }
-+
-+  return !callVeto;
- }
- 
--void
-+PRBool
- nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
- {
-   NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity");
-@@ -2526,16 +2582,20 @@ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
-   }
-     
-   StopCRLUpdateTimer();
-+  PRBool allow_change = PR_TRUE;
- 
-   if (needsCleanup) {
-     if (NS_FAILED(ShutdownNSS())) {
-+      PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: Shutdown failed\n"));
-       nsCOMPtr<nsIProfileChangeStatus> status = do_QueryInterface(aSubject);
-       if (status) {
-         status->ChangeFailed();
-       }
-+      allow_change = PR_FALSE;
-     }
-   }
-   mShutdownObjectList->allowUI();
-+  return allow_change;
- }
- 
- void
--- 
-1.7.3.4
-
diff --git a/src/current-patches/0003-Firefox4-Make-Intermediate-Cert-Store-memory-only.patch b/src/current-patches/0003-Firefox4-Make-Intermediate-Cert-Store-memory-only.patch
new file mode 100644
index 0000000..d4f7b73
--- /dev/null
+++ b/src/current-patches/0003-Firefox4-Make-Intermediate-Cert-Store-memory-only.patch
@@ -0,0 +1,283 @@
+From 4db14cbb5a0aff6102189d30b9202555dcc39ff4 Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:49 -0700
+Subject: [PATCH 3/4] Firefox4: Make Intermediate Cert Store memory-only.
+
+This patch makes the intermediate SSL cert store exist in memory only. It
+exposes a pref ('security.nocertdb') to toggle to clear the store, but this
+seems buggy.
+
+The pref must be set before startup in prefs.js.
+https://trac.torproject.org/projects/tor/ticket/2949
+---
+ security/manager/ssl/src/nsNSSComponent.cpp |  180 ++++++++++++++++++---------
+ 1 files changed, 120 insertions(+), 60 deletions(-)
+
+diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp
+index 33377ac..716bcee 100644
+--- a/security/manager/ssl/src/nsNSSComponent.cpp
++++ b/security/manager/ssl/src/nsNSSComponent.cpp
+@@ -1674,8 +1674,21 @@ nsNSSComponent::InitializeNSS(PRBool showWarningBox)
+     // Ubuntu 8.04, which loads any nonexistent "<configdir>/libnssckbi.so" as
+     // "/usr/lib/nss/libnssckbi.so".
+     PRUint32 init_flags = NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE;
+-    SECStatus init_rv = ::NSS_Initialize(profileStr.get(), "", "",
++    PRBool nocertdb = false;
++    mPrefBranch->GetBoolPref("security.nocertdb", &nocertdb);
++
++    // XXX: We can also do the the following to only disable the certdb.
++    // Leaving this codepath in as a fallback in case InitNODB fails
++    if (nocertdb)
++      init_flags |= NSS_INIT_NOCERTDB;
++
++    SECStatus init_rv;
++    if (nocertdb) {
++        init_rv = ::NSS_NoDB_Init(NULL);
++    } else {
++        init_rv = ::NSS_Initialize(profileStr.get(), "", "",
+                                          SECMOD_DB, init_flags);
++    }
+ 
+     if (init_rv != SECSuccess) {
+       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("can not init NSS r/w in %s\n", profileStr.get()));
+@@ -2231,70 +2244,106 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic,
+       }
+     }
+   }
+-  else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) { 
+-    nsNSSShutDownPreventionLock locker;
+-    PRBool clearSessionCache = PR_FALSE;
+-    PRBool enabled;
++  else if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
+     NS_ConvertUTF16toUTF8  prefName(someData);
++    // XXX: This should be an observer notification, so we can properly cancel it
++    if (prefName.Equals("security.nocertdb")) {
++      // XXX: If these functions tell us to cancel, the browser seems to get left in an
++      // indeterminate state that prevents SSL from being used.
++      //
++      // We apparently need to wait for all SSL sockets to shut down on their
++      // own (this can take up to a minute!) and then attempt to alter the pref
++      // again before doing anything.
++      //
++      // So any implementation of New Identity based on this code will need to keep
++      // attempting to send the notification until it is not canceled. Ugh...
++      if (!DoProfileApproveChange(aSubject)) {
++        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled\n"));
++        return NS_OK;
++      }
+ 
+-    if (prefName.Equals("security.enable_ssl2")) {
+-      mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
+-      SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
+-      clearSessionCache = PR_TRUE;
+-    } else if (prefName.Equals("security.enable_ssl3")) {
+-      mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
+-      clearSessionCache = PR_TRUE;
+-    } else if (prefName.Equals("security.enable_tls")) {
+-      mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
+-      clearSessionCache = PR_TRUE;
+-    } else if (prefName.Equals("security.enable_tls_session_tickets")) {
+-      mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
+-    } else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
+-      mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
+-      SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
+-    } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
+-      mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, 
+-        enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
+-    } else if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
+-      char *unrestricted_hosts=nsnull;
+-      mPrefBranch->GetCharPref("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
+-      if (unrestricted_hosts) {
+-        nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(nsDependentCString(unrestricted_hosts));
+-        nsMemory::Free(unrestricted_hosts);
++      DoProfileChangeNetTeardown();
++      if (!DoProfileChangeTeardown(aSubject)) {
++        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled\n"));
++        return NS_OK;
++      }
++
++      if (!DoProfileBeforeChange(aSubject)) {
++        PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CertDB change canceled by NSS shutdown\n"));
++        // Need to re-add observer because ShutdownNSS removed it.
++        nsCOMPtr<nsIPrefBranch2> pbi = do_QueryInterface(mPrefBranch);
++        pbi->AddObserver("security.", this, PR_FALSE);
++        return NS_OK;
+       }
+-    } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
+-      mPrefBranch->GetBoolPref("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
+-      nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(enabled);
+-    } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
+-      PRInt32 warnLevel = 1;
+-      mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
+-      nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
++
++      DoProfileChangeNetRestore();
++      InitializeNSS(PR_FALSE);
++      InitializeCRLUpdateTimer();
++      return NS_OK;
++    } else {
++      nsNSSShutDownPreventionLock locker;
++      PRBool clearSessionCache = PR_FALSE;
++      PRBool enabled;
++
++      if (prefName.Equals("security.enable_ssl2")) {
++        mPrefBranch->GetBoolPref("security.enable_ssl2", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_SSL2, enabled);
++        SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, enabled);
++        clearSessionCache = PR_TRUE;
++      } else if (prefName.Equals("security.enable_ssl3")) {
++        mPrefBranch->GetBoolPref("security.enable_ssl3", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_SSL3, enabled);
++        clearSessionCache = PR_TRUE;
++      } else if (prefName.Equals("security.enable_tls")) {
++        mPrefBranch->GetBoolPref("security.enable_tls", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_TLS, enabled);
++        clearSessionCache = PR_TRUE;
++      } else if (prefName.Equals("security.enable_tls_session_tickets")) {
++        mPrefBranch->GetBoolPref("security.enable_tls_session_tickets", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_SESSION_TICKETS, enabled);
++      } else if (prefName.Equals("security.ssl.require_safe_negotiation")) {
++        mPrefBranch->GetBoolPref("security.ssl.require_safe_negotiation", &enabled);
++        SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, enabled);
++      } else if (prefName.Equals("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref")) {
++        mPrefBranch->GetBoolPref("security.ssl.allow_unrestricted_renego_everywhere__temporarily_available_pref", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_RENEGOTIATION, 
++          enabled ? SSL_RENEGOTIATE_UNRESTRICTED : SSL_RENEGOTIATE_REQUIRES_XTN);
++      } else if (prefName.Equals("security.ssl.renego_unrestricted_hosts")) {
++        char *unrestricted_hosts=nsnull;
++        mPrefBranch->GetCharPref("security.ssl.renego_unrestricted_hosts", &unrestricted_hosts);
++        if (unrestricted_hosts) {
++          nsSSLIOLayerHelpers::setRenegoUnrestrictedSites(nsDependentCString(unrestricted_hosts));
++          nsMemory::Free(unrestricted_hosts);
++        }
++      } else if (prefName.Equals("security.ssl.treat_unsafe_negotiation_as_broken")) {
++        mPrefBranch->GetBoolPref("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
++        nsSSLIOLayerHelpers::setTreatUnsafeNegotiationAsBroken(enabled);
++      } else if (prefName.Equals("security.ssl.warn_missing_rfc5746")) {
++        PRInt32 warnLevel = 1;
++        mPrefBranch->GetIntPref("security.ssl.warn_missing_rfc5746", &warnLevel);
++        nsSSLIOLayerHelpers::setWarnLevelMissingRFC5746(warnLevel);
+ #ifdef SSL_ENABLE_FALSE_START // Requires NSS 3.12.8
+-    } else if (prefName.Equals("security.ssl.enable_false_start")) {
+-      mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
+-      SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
++      } else if (prefName.Equals("security.ssl.enable_false_start")) {
++        mPrefBranch->GetBoolPref("security.ssl.enable_false_start", &enabled);
++        SSL_OptionSetDefault(SSL_ENABLE_FALSE_START, enabled);
+ #endif
+-    } else if (prefName.Equals("security.OCSP.enabled")
+-               || prefName.Equals("security.OCSP.require")) {
+-      setOCSPOptions(mPrefBranch);
+-    } else {
+-      /* Look through the cipher table and set according to pref setting */
+-      for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
+-        if (prefName.Equals(cp->pref)) {
+-          mPrefBranch->GetBoolPref(cp->pref, &enabled);
+-          SSL_CipherPrefSetDefault(cp->id, enabled);
+-          clearSessionCache = PR_TRUE;
+-          break;
++      } else if (prefName.Equals("security.OCSP.enabled")
++                 || prefName.Equals("security.OCSP.require")) {
++        setOCSPOptions(mPrefBranch);
++      } else {
++        /* Look through the cipher table and set according to pref setting */
++        for (CipherPref* cp = CipherPrefs; cp->pref; ++cp) {
++          if (prefName.Equals(cp->pref)) {
++            mPrefBranch->GetBoolPref(cp->pref, &enabled);
++            SSL_CipherPrefSetDefault(cp->id, enabled);
++            clearSessionCache = PR_TRUE;
++            break;
++          }
+         }
+       }
++      if (clearSessionCache)
++        SSL_ClearSessionCache();
+     }
+-    if (clearSessionCache)
+-      SSL_ClearSessionCache();
+   }
+   else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_NET_TEARDOWN_TOPIC) == 0) {
+     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving network teardown topic\n"));
+@@ -2463,16 +2512,19 @@ nsNSSComponent::RememberCert(CERTCertificate *cert)
+   return NS_OK;
+ }
+ 
+-void
++PRBool
+ nsNSSComponent::DoProfileApproveChange(nsISupports* aSubject)
+ {
+   if (mShutdownObjectList->isUIActive()) {
++    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS UI active in profile change!\n"));
+     ShowAlert(ai_crypto_ui_active);
+     nsCOMPtr<nsIProfileChangeStatus> status = do_QueryInterface(aSubject);
+     if (status) {
+       status->VetoChange();
+     }
++    return false;
+   }
++  return true;
+ }
+ 
+ void
+@@ -2485,16 +2537,18 @@ nsNSSComponent::DoProfileChangeNetTeardown()
+   mIsNetworkDown = PR_TRUE;
+ }
+ 
+-void
++PRBool
+ nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
+ {
+   PRBool callVeto = PR_FALSE;
+ 
+   if (!mShutdownObjectList->ifPossibleDisallowUI()) {
+     callVeto = PR_TRUE;
++    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: Not possible to disallow UI!\n"));
+     ShowAlert(ai_crypto_ui_active);
+   }
+   else if (mShutdownObjectList->areSSLSocketsActive()) {
++    PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: SSL Sockets are active!\n"));
+     callVeto = PR_TRUE;
+     ShowAlert(ai_sockets_still_active);
+   }
+@@ -2505,9 +2559,11 @@ nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject)
+       status->VetoChange();
+     }
+   }
++
++  return !callVeto;
+ }
+ 
+-void
++PRBool
+ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
+ {
+   NS_ASSERTION(mIsNetworkDown, "nsNSSComponent relies on profile manager to wait for synchronous shutdown of all network activity");
+@@ -2526,16 +2582,20 @@ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject)
+   }
+     
+   StopCRLUpdateTimer();
++  PRBool allow_change = PR_TRUE;
+ 
+   if (needsCleanup) {
+     if (NS_FAILED(ShutdownNSS())) {
++      PR_LOG(gPIPNSSLog, PR_LOG_WARN, ("NSS: Shutdown failed\n"));
+       nsCOMPtr<nsIProfileChangeStatus> status = do_QueryInterface(aSubject);
+       if (status) {
+         status->ChangeFailed();
+       }
++      allow_change = PR_FALSE;
+     }
+   }
+   mShutdownObjectList->allowUI();
++  return allow_change;
+ }
+ 
+ void
+-- 
+1.7.3.4
+
diff --git a/src/current-patches/0003-Firefox5-Make-Permissions-Manager-memory-only.patch b/src/current-patches/0003-Firefox5-Make-Permissions-Manager-memory-only.patch
new file mode 100644
index 0000000..1384245
--- /dev/null
+++ b/src/current-patches/0003-Firefox5-Make-Permissions-Manager-memory-only.patch
@@ -0,0 +1,94 @@
+From 16bafbf39c89cce901af6500255822677bc4c36d Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:56 -0700
+Subject: [PATCH 3/3] Firefox5: Make Permissions Manager memory-only
+
+This patch exposes a pref 'permissions.memory_only' that properly isolates the
+permissions manager to memory, which is responsible for all user specified
+site permissions, as well as stored STS policy.
+
+The pref does successfully clear the permissions manager memory if toggled. It
+does not need to be set in prefs.js, and can be handled by Torbutton.
+
+https://trac.torproject.org/projects/tor/ticket/2950
+---
+ extensions/cookie/nsPermissionManager.cpp |   34 ++++++++++++++++++++++++++--
+ 1 files changed, 31 insertions(+), 3 deletions(-)
+
+diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp
+index 773a973..5387397 100644
+--- a/extensions/cookie/nsPermissionManager.cpp
++++ b/extensions/cookie/nsPermissionManager.cpp
+@@ -58,6 +58,10 @@
+ #include "mozStorageHelper.h"
+ #include "mozStorageCID.h"
+ #include "nsXULAppAPI.h"
++#include "nsCOMPtr.h"
++#include "nsIPrefService.h"
++#include "nsIPrefBranch.h"
++#include "nsIPrefBranch2.h"
+ 
+ static nsPermissionManager *gPermissionManager = nsnull;
+ 
+@@ -227,6 +231,11 @@ nsPermissionManager::Init()
+     mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
+   }
+ 
++  nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID);
++  if (pbi) {
++    pbi->AddObserver("permissions.", this, PR_FALSE);
++  }
++
+   if (IsChildProcess()) {
+     // Get the permissions from the parent process
+     InfallibleTArray<IPC::Permission> perms;
+@@ -275,8 +284,18 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
+   if (!storage)
+     return NS_ERROR_UNEXPECTED;
+ 
++  PRBool memory_db = false;
++  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
++  if (prefs) {
++    prefs->GetBoolPref("permissions.memory_only", &memory_db); 
++  }
++
+   // cache a connection to the hosts database
+-  rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++  if (memory_db) {
++    rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
++  } else {
++    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++  }
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   PRBool ready;
+@@ -286,7 +305,11 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
+     rv = permissionsFile->Remove(PR_FALSE);
+     NS_ENSURE_SUCCESS(rv, rv);
+ 
+-    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++    if (memory_db) {
++      rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
++    } else {
++      rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++    }
+     NS_ENSURE_SUCCESS(rv, rv);
+ 
+     mDBConn->GetConnectionReady(&ready);
+@@ -805,7 +828,12 @@ NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aT
+ {
+   ENSURE_NOT_CHILD_PROCESS;
+ 
+-  if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
++  if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
++    if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) {
++      // XXX: Should we remove the file? Probably not..
++      InitDB(PR_FALSE);
++    }
++  } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
+     // The profile is about to change,
+     // or is going away because the application is shutting down.
+     if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
+-- 
+1.7.3.4
+
diff --git a/src/current-patches/0004-Firefox-Make-Permissions-Manager-memory-only.patch b/src/current-patches/0004-Firefox-Make-Permissions-Manager-memory-only.patch
deleted file mode 100644
index 241e4eb..0000000
--- a/src/current-patches/0004-Firefox-Make-Permissions-Manager-memory-only.patch
+++ /dev/null
@@ -1,94 +0,0 @@
-From acf065599f1ab270eed4846fbc916f245002da0d Mon Sep 17 00:00:00 2001
-From: Mike Perry <mikeperry-git@xxxxxxxxxx>
-Date: Mon, 20 Jun 2011 17:07:56 -0700
-Subject: [PATCH 4/4] Firefox: Make Permissions Manager memory-only
-
-This patch exposes a pref 'permissions.memory_only' that properly isolates the
-permissions manager to memory, which is responsible for all user specified
-site permissions, as well as stored STS policy.
-
-The pref does successfully clear the permissions manager memory if toggled. It
-does not need to be set in prefs.js, and can be handled by Torbutton.
-
-https://trac.torproject.org/projects/tor/ticket/2950
----
- extensions/cookie/nsPermissionManager.cpp |   34 ++++++++++++++++++++++++++--
- 1 files changed, 31 insertions(+), 3 deletions(-)
-
-diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp
-index d182013..0a1aea6 100644
---- a/extensions/cookie/nsPermissionManager.cpp
-+++ b/extensions/cookie/nsPermissionManager.cpp
-@@ -60,6 +60,10 @@
- #include "mozStorageHelper.h"
- #include "mozStorageCID.h"
- #include "nsXULAppAPI.h"
-+#include "nsCOMPtr.h"
-+#include "nsIPrefService.h"
-+#include "nsIPrefBranch.h"
-+#include "nsIPrefBranch2.h"
- 
- static nsPermissionManager *gPermissionManager = nsnull;
- 
-@@ -233,6 +237,11 @@ nsPermissionManager::Init()
-     mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
-   }
- 
-+  nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID);
-+  if (pbi) {
-+    pbi->AddObserver("permissions.", this, PR_FALSE);
-+  }
-+
- #ifdef MOZ_IPC
-   if (IsChildProcess()) {
-     // Get the permissions from the parent process
-@@ -283,8 +292,18 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
-   if (!storage)
-     return NS_ERROR_UNEXPECTED;
- 
-+  PRBool memory_db = false;
-+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
-+  if (prefs) {
-+    prefs->GetBoolPref("permissions.memory_only", &memory_db); 
-+  }
-+
-   // cache a connection to the hosts database
--  rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
-+  if (memory_db) {
-+    rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
-+  } else {
-+    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
-+  }
-   NS_ENSURE_SUCCESS(rv, rv);
- 
-   PRBool ready;
-@@ -294,7 +313,11 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
-     rv = permissionsFile->Remove(PR_FALSE);
-     NS_ENSURE_SUCCESS(rv, rv);
- 
--    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
-+    if (memory_db) {
-+      rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
-+    } else {
-+      rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
-+    }
-     NS_ENSURE_SUCCESS(rv, rv);
- 
-     mDBConn->GetConnectionReady(&ready);
-@@ -825,7 +848,12 @@ NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aT
-   ENSURE_NOT_CHILD_PROCESS;
- #endif
- 
--  if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
-+  if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
-+    if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) {
-+      // XXX: Should we remove the file? Probably not..
-+      InitDB(PR_FALSE);
-+    }
-+  } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
-     // The profile is about to change,
-     // or is going away because the application is shutting down.
-     if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
--- 
-1.7.3.4
-
diff --git a/src/current-patches/0004-Firefox4-Make-Permissions-Manager-memory-only.patch b/src/current-patches/0004-Firefox4-Make-Permissions-Manager-memory-only.patch
new file mode 100644
index 0000000..8f7ddd9
--- /dev/null
+++ b/src/current-patches/0004-Firefox4-Make-Permissions-Manager-memory-only.patch
@@ -0,0 +1,94 @@
+From 6f37edd80181906c37ace589fc26eabf6731b09d Mon Sep 17 00:00:00 2001
+From: Mike Perry <mikeperry-git@xxxxxxxxxx>
+Date: Mon, 20 Jun 2011 17:07:56 -0700
+Subject: [PATCH 4/4] Firefox4: Make Permissions Manager memory-only
+
+This patch exposes a pref 'permissions.memory_only' that properly isolates the
+permissions manager to memory, which is responsible for all user specified
+site permissions, as well as stored STS policy.
+
+The pref does successfully clear the permissions manager memory if toggled. It
+does not need to be set in prefs.js, and can be handled by Torbutton.
+
+https://trac.torproject.org/projects/tor/ticket/2950
+---
+ extensions/cookie/nsPermissionManager.cpp |   34 ++++++++++++++++++++++++++--
+ 1 files changed, 31 insertions(+), 3 deletions(-)
+
+diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp
+index d182013..0a1aea6 100644
+--- a/extensions/cookie/nsPermissionManager.cpp
++++ b/extensions/cookie/nsPermissionManager.cpp
+@@ -60,6 +60,10 @@
+ #include "mozStorageHelper.h"
+ #include "mozStorageCID.h"
+ #include "nsXULAppAPI.h"
++#include "nsCOMPtr.h"
++#include "nsIPrefService.h"
++#include "nsIPrefBranch.h"
++#include "nsIPrefBranch2.h"
+ 
+ static nsPermissionManager *gPermissionManager = nsnull;
+ 
+@@ -233,6 +237,11 @@ nsPermissionManager::Init()
+     mObserverService->AddObserver(this, "profile-do-change", PR_TRUE);
+   }
+ 
++  nsCOMPtr<nsIPrefBranch2> pbi = do_GetService(NS_PREFSERVICE_CONTRACTID);
++  if (pbi) {
++    pbi->AddObserver("permissions.", this, PR_FALSE);
++  }
++
+ #ifdef MOZ_IPC
+   if (IsChildProcess()) {
+     // Get the permissions from the parent process
+@@ -283,8 +292,18 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
+   if (!storage)
+     return NS_ERROR_UNEXPECTED;
+ 
++  PRBool memory_db = false;
++  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
++  if (prefs) {
++    prefs->GetBoolPref("permissions.memory_only", &memory_db); 
++  }
++
+   // cache a connection to the hosts database
+-  rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++  if (memory_db) {
++    rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
++  } else {
++    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++  }
+   NS_ENSURE_SUCCESS(rv, rv);
+ 
+   PRBool ready;
+@@ -294,7 +313,11 @@ nsPermissionManager::InitDB(PRBool aRemoveFile)
+     rv = permissionsFile->Remove(PR_FALSE);
+     NS_ENSURE_SUCCESS(rv, rv);
+ 
+-    rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++    if (memory_db) {
++      rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
++    } else {
++      rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
++    }
+     NS_ENSURE_SUCCESS(rv, rv);
+ 
+     mDBConn->GetConnectionReady(&ready);
+@@ -825,7 +848,12 @@ NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aT
+   ENSURE_NOT_CHILD_PROCESS;
+ #endif
+ 
+-  if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
++  if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
++    if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("permissions.memory_only").get())) {
++      // XXX: Should we remove the file? Probably not..
++      InitDB(PR_FALSE);
++    }
++  } else if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
+     // The profile is about to change,
+     // or is going away because the application is shutting down.
+     if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
+-- 
+1.7.3.4
+



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits