commit e98532da73eb3cbb2a9b708f68c23b0748f39305 Author: Mike Perry mikeperry-git@fscked.org 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@fscked.org -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@acm.org - * Darin Fisher darin@meer.net - * Malcolm Smith malsmith@cs.rmit.edu.au -+ * Christopher Davis chrisd@torproject.org - * - * 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@fscked.org +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@acm.org + * Darin Fisher darin@meer.net + * Malcolm Smith malsmith@cs.rmit.edu.au ++ * Christopher Davis chrisd@torproject.org + * + * 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@fscked.org +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@fscked.org -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@fscked.org +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@fscked.org +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@fscked.org -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@fscked.org +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@fscked.org +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 + InfallibleTArrayIPC::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@fscked.org -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@fscked.org +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 +