$OpenBSD$ index 34d29c3..f3250b9 100644 --- netwerk/socket/nsSOCKSIOLayer.cpp.orig Fri Feb 20 15:40:37 2015 +++ netwerk/socket/nsSOCKSIOLayer.cpp Fri Feb 20 15:40:37 2015 @@ -50,6 +50,8 @@ class nsSOCKSSocketInfo : public nsISOCKSSocketInfo SOCKS4_READ_CONNECT_RESPONSE, SOCKS5_WRITE_AUTH_REQUEST, SOCKS5_READ_AUTH_RESPONSE, + SOCKS5_WRITE_USERNAME_REQUEST, + SOCKS5_READ_USERNAME_RESPONSE, SOCKS5_WRITE_CONNECT_REQUEST, SOCKS5_READ_CONNECT_RESPONSE_TOP, SOCKS5_READ_CONNECT_RESPONSE_BOTTOM, @@ -57,10 +59,12 @@ class nsSOCKSSocketInfo : public nsISOCKSSocketInfo SOCKS_FAILED }; - // A buffer of 262 bytes should be enough for any request and response + // A buffer of 520 bytes should be enough for any request and response // in case of SOCKS4 as well as SOCKS5 - static const uint32_t BUFFER_SIZE = 262; + static const uint32_t BUFFER_SIZE = 520; static const uint32_t MAX_HOSTNAME_LEN = 255; + static const uint32_t MAX_USERNAME_LEN = 255; + static const uint32_t MAX_PASSWORD_LEN = 255; public: nsSOCKSSocketInfo(); @@ -72,15 +76,16 @@ public: void Init(int32_t version, int32_t family, - const char *proxyHost, - int32_t proxyPort, + nsIProxyInfo *proxy, const char *destinationHost, uint32_t flags); void SetConnectTimeout(PRIntervalTime to); PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1); int16_t GetPollFlags() const; - bool IsConnected() const { return mState == SOCKS_CONNECTED; } + bool IsConnected() const { return (mState == SOCKS_CONNECTED || + mState == SOCKS5_READ_CONNECT_RESPONSE_TOP); } + void ForgetFD() { mFD = nullptr; } private: @@ -93,6 +98,8 @@ private: PRStatus ReadV4ConnectResponse(); PRStatus WriteV5AuthRequest(); PRStatus ReadV5AuthResponse(); + PRStatus WriteV5UsernameRequest(); + PRStatus ReadV5UsernameResponse(); PRStatus WriteV5ConnectRequest(); PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len); PRStatus ReadV5ConnectResponseTop(); @@ -128,8 +135,7 @@ private: PRFileDesc *mFD; nsCString mDestinationHost; - nsCString mProxyHost; - int32_t mProxyPort; + nsCOMPtr mProxy; int32_t mVersion; // SOCKS version 4 or 5 int32_t mDestinationFamily; uint32_t mFlags; @@ -137,6 +143,7 @@ private: NetAddr mExternalProxyAddr; NetAddr mDestinationAddr; PRIntervalTime mTimeout; + nsCString mProxyUsername; // Cache, from mProxy }; nsSOCKSSocketInfo::nsSOCKSSocketInfo() @@ -145,7 +152,6 @@ nsSOCKSSocketInfo::nsSOCKSSocketInfo() , mDataLength(0) , mReadOffset(0) , mAmountToRead(0) - , mProxyPort(-1) , mVersion(-1) , mDestinationFamily(AF_INET) , mFlags(0) @@ -167,14 +173,14 @@ nsSOCKSSocketInfo::nsSOCKSSocketInfo() } void -nsSOCKSSocketInfo::Init(int32_t version, int32_t family, const char *proxyHost, int32_t proxyPort, const char *host, uint32_t flags) +nsSOCKSSocketInfo::Init(int32_t version, int32_t family, nsIProxyInfo *proxy, const char *host, uint32_t flags) { mVersion = version; mDestinationFamily = family; - mProxyHost = proxyHost; - mProxyPort = proxyPort; + mProxy = proxy; mDestinationHost = host; mFlags = flags; + mProxy->GetUsername(mProxyUsername); // cache } NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener) @@ -261,14 +267,17 @@ nsSOCKSSocketInfo::StartDNS(PRFileDesc *fd) if (!dns) return PR_FAILURE; + nsCString proxyHost; + mProxy->GetHost(proxyHost); + mFD = fd; - nsresult rv = dns->AsyncResolve(mProxyHost, 0, this, + nsresult rv = dns->AsyncResolve(proxyHost, 0, this, NS_GetCurrentThread(), getter_AddRefs(mLookup)); if (NS_FAILED(rv)) { LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed", - mProxyHost.get())); + proxyHost.get())); return PR_FAILURE; } mState = SOCKS_DNS_IN_PROGRESS; @@ -313,16 +322,21 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd) mVersion = 5; } + int32_t proxyPort; + mProxy->GetPort(&proxyPort); + int32_t addresses = 0; do { if (addresses++) - mDnsRec->ReportUnusable(mProxyPort); + mDnsRec->ReportUnusable(proxyPort); - rv = mDnsRec->GetNextAddr(mProxyPort, &mInternalProxyAddr); + rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr); // No more addresses to try? If so, we'll need to bail if (NS_FAILED(rv)) { + nsCString proxyHost; + mProxy->GetHost(proxyHost); LOGERROR(("socks: unable to connect to SOCKS proxy, %s", - mProxyHost.get())); + proxyHost.get())); return PR_FAILURE; } @@ -438,6 +452,12 @@ nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags) PRStatus nsSOCKSSocketInfo::WriteV4ConnectRequest() { + if (mProxyUsername.Length() > MAX_USERNAME_LEN) { + LOGERROR(("socks username is too long")); + HandshakeFinished(PR_UNKNOWN_ERROR); + return PR_FAILURE; + } + NetAddr *addr = &mDestinationAddr; int32_t proxy_resolve; @@ -463,7 +483,9 @@ nsSOCKSSocketInfo::WriteV4ConnectRequest() // 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(htonl(0x00000001)); // Fake IP - WriteUint8(0x00); // Send an emtpy username + WriteString(mProxyUsername); // Send username. May be empty. + WriteUint8(0x00); // Null-terminate username + // Password not supported by V4. if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) { LOGERROR(("socks4: destination host name is too long!")); HandshakeFinished(PR_BAD_ADDRESS_ERROR); @@ -473,7 +495,9 @@ nsSOCKSSocketInfo::WriteV4ConnectRequest() WriteUint8(0x00); } else if (addr->raw.family == AF_INET) { WriteNetAddr(addr); // Add the IPv4 address - WriteUint8(0x00); // Send an emtpy username + WriteString(mProxyUsername); // Send username. May be empty. + WriteUint8(0x00); // Null-terminate username + // Password not supported by V4. } else if (addr->raw.family == AF_INET6) { LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!")); HandshakeFinished(PR_BAD_ADDRESS_ERROR); @@ -488,11 +512,15 @@ 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(("socks4: checking connection reply")); + if (mDataLength != 8) { + LOGERROR(("SOCKS 4 connection reply must be 8 bytes!")); + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); + return PR_FAILURE; + } + if (ReadUint8() != 0x00) { LOGERROR(("socks4: wrong connection reply")); HandshakeFinished(PR_CONNECT_REFUSED_ERROR); @@ -516,13 +544,18 @@ nsSOCKSSocketInfo::WriteV5AuthRequest() { NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!"); + mDataLength = 0; mState = SOCKS5_WRITE_AUTH_REQUEST; // 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 + WriteUint8(0x01); // # of auth methods -- 1 + if (mProxyUsername.IsEmpty()) { + WriteUint8(0x00); // no authentication + } else { + WriteUint8(0x02); // username/password + } return PR_SUCCESS; } @@ -532,10 +565,12 @@ 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!"); - LOGDEBUG(("socks5: checking auth method reply")); + if (mDataLength != 2) { + LOGERROR(("SOCKS 5 auth method reply must be 2 bytes")); + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); + return PR_FAILURE; + } // Check version number if (ReadUint8() != 0x05) { @@ -544,12 +579,82 @@ nsSOCKSSocketInfo::ReadV5AuthResponse() return PR_FAILURE; } - // Make sure our authentication choice was accepted - if (ReadUint8() != 0x00) { + // Make sure our authentication choice was accepted, + // and continue accordingly + uint8_t authMethod = ReadUint8(); + if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth + LOGDEBUG(("socks5: server allows connection without authentication")); + return WriteV5ConnectRequest(); + } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw + LOGDEBUG(("socks5: auth method accepted by server")); + return WriteV5UsernameRequest(); + } else { // 0xFF signals error LOGERROR(("socks5: server did not accept our authentication method")); HandshakeFinished(PR_CONNECT_REFUSED_ERROR); return PR_FAILURE; } +} + +PRStatus +nsSOCKSSocketInfo::WriteV5UsernameRequest() +{ + NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!"); + + if (mProxyUsername.Length() > MAX_USERNAME_LEN) { + LOGERROR(("socks username is too long")); + HandshakeFinished(PR_UNKNOWN_ERROR); + return PR_FAILURE; + } + + nsCString password; + mProxy->GetPassword(password); + if (password.Length() > MAX_PASSWORD_LEN) { + LOGERROR(("socks password is too long")); + HandshakeFinished(PR_UNKNOWN_ERROR); + return PR_FAILURE; + } + + mDataLength = 0; + mState = SOCKS5_WRITE_USERNAME_REQUEST; + + LOGDEBUG(("socks5: sending username and password")); + // RFC 1929 Username/password auth for SOCKS 5 + WriteUint8(0x01); // version 1 (not 5) + WriteUint8(mProxyUsername.Length()); // username length + WriteString(mProxyUsername); // username + WriteUint8(password.Length()); // password length + WriteString(password); // password. WARNING: Sent unencrypted! + + return PR_SUCCESS; +} + +PRStatus +nsSOCKSSocketInfo::ReadV5UsernameResponse() +{ + NS_ABORT_IF_FALSE(mState == SOCKS5_READ_USERNAME_RESPONSE, + "Handling SOCKS 5 username/password reply in wrong state!"); + + if (mDataLength != 2) { + LOGERROR(("SOCKS 5 username reply must be 2 bytes")); + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); + return PR_FAILURE; + } + + // Check version number, must be 1 (not 5) + if (ReadUint8() != 0x01) { + LOGERROR(("socks5: unexpected version in the reply")); + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); + return PR_FAILURE; + } + + // Check whether username/password were accepted + if (ReadUint8() != 0x00) { // 0 = success + LOGERROR(("socks5: username/password not accepted")); + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); + return PR_FAILURE; + } + + LOGDEBUG(("socks5: username/password accepted by server")); return WriteV5ConnectRequest(); } @@ -793,6 +898,16 @@ nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags) if (ReadFromSocket(fd) != PR_SUCCESS) return PR_FAILURE; return ReadV5AuthResponse(); + case SOCKS5_WRITE_USERNAME_REQUEST: + if (WriteToSocket(fd) != PR_SUCCESS) + return PR_FAILURE; + WantRead(2); + mState = SOCKS5_READ_USERNAME_RESPONSE; + return PR_SUCCESS; + case SOCKS5_READ_USERNAME_RESPONSE: + if (ReadFromSocket(fd) != PR_SUCCESS) + return PR_FAILURE; + return ReadV5UsernameResponse(); case SOCKS5_WRITE_CONNECT_REQUEST: if (WriteToSocket(fd) != PR_SUCCESS) return PR_FAILURE; @@ -837,10 +952,12 @@ nsSOCKSSocketInfo::GetPollFlags() const return PR_POLL_EXCEPT | PR_POLL_WRITE; case SOCKS4_WRITE_CONNECT_REQUEST: case SOCKS5_WRITE_AUTH_REQUEST: + case SOCKS5_WRITE_USERNAME_REQUEST: case SOCKS5_WRITE_CONNECT_REQUEST: return PR_POLL_WRITE; case SOCKS4_READ_CONNECT_RESPONSE: case SOCKS5_READ_AUTH_RESPONSE: + case SOCKS5_READ_USERNAME_RESPONSE: case SOCKS5_READ_CONNECT_RESPONSE_TOP: case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM: return PR_POLL_READ; @@ -854,7 +971,7 @@ nsSOCKSSocketInfo::GetPollFlags() const inline void nsSOCKSSocketInfo::WriteUint8(uint8_t v) { - NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE, + MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE, "Can't write that much data!"); mData[mDataLength] = v; mDataLength += sizeof(v); @@ -863,7 +980,7 @@ nsSOCKSSocketInfo::WriteUint8(uint8_t v) inline void nsSOCKSSocketInfo::WriteUint16(uint16_t v) { - NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE, + MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE, "Can't write that much data!"); memcpy(mData + mDataLength, &v, sizeof(v)); mDataLength += sizeof(v); @@ -872,7 +989,7 @@ nsSOCKSSocketInfo::WriteUint16(uint16_t v) inline void nsSOCKSSocketInfo::WriteUint32(uint32_t v) { - NS_ABORT_IF_FALSE(mDataLength + sizeof(v) <= BUFFER_SIZE, + MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE, "Can't write that much data!"); memcpy(mData + mDataLength, &v, sizeof(v)); mDataLength += sizeof(v); @@ -892,8 +1009,8 @@ nsSOCKSSocketInfo::WriteNetAddr(const NetAddr *addr) len = sizeof(addr->inet6.ip.u8); } - NS_ABORT_IF_FALSE(ip != nullptr, "Unknown address"); - NS_ABORT_IF_FALSE(mDataLength + len <= BUFFER_SIZE, + MOZ_RELEASE_ASSERT(ip != nullptr, "Unknown address"); + MOZ_RELEASE_ASSERT(mDataLength + len <= BUFFER_SIZE, "Can't write that much data!"); memcpy(mData + mDataLength, ip, len); @@ -909,7 +1026,7 @@ nsSOCKSSocketInfo::WriteNetPort(const NetAddr *addr) void nsSOCKSSocketInfo::WriteString(const nsACString &str) { - NS_ABORT_IF_FALSE(mDataLength + str.Length() <= BUFFER_SIZE, + MOZ_RELEASE_ASSERT(mDataLength + str.Length() <= BUFFER_SIZE, "Can't write that much data!"); memcpy(mData + mDataLength, str.Data(), str.Length()); mDataLength += str.Length(); @@ -919,7 +1036,7 @@ inline uint8_t nsSOCKSSocketInfo::ReadUint8() { uint8_t rv; - NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength, + MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength, "Not enough space to pop a uint8_t!"); rv = mData[mReadOffset]; mReadOffset += sizeof(rv); @@ -930,7 +1047,7 @@ inline uint16_t nsSOCKSSocketInfo::ReadUint16() { uint16_t rv; - NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength, + MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength, "Not enough space to pop a uint16_t!"); memcpy(&rv, mData + mReadOffset, sizeof(rv)); mReadOffset += sizeof(rv); @@ -941,7 +1058,7 @@ inline uint32_t nsSOCKSSocketInfo::ReadUint32() { uint32_t rv; - NS_ABORT_IF_FALSE(mReadOffset + sizeof(rv) <= mDataLength, + MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength, "Not enough space to pop a uint32_t!"); memcpy(&rv, mData + mReadOffset, sizeof(rv)); mReadOffset += sizeof(rv); @@ -957,12 +1074,12 @@ nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam) addr->raw.family = fam; if (fam == AF_INET) { amt = sizeof(addr->inet.ip); - NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength, + MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength, "Not enough space to pop an ipv4 addr!"); memcpy(&addr->inet.ip, ip, amt); } else if (fam == AF_INET6) { amt = sizeof(addr->inet6.ip.u8); - NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength, + MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength, "Not enough space to pop an ipv6 addr!"); memcpy(addr->inet6.ip.u8, ip, amt); } @@ -981,7 +1098,7 @@ nsSOCKSSocketInfo::WantRead(uint32_t sz) { NS_ABORT_IF_FALSE(mDataIoPtr == nullptr, "WantRead() called while I/O already in progress!"); - NS_ABORT_IF_FALSE(mDataLength + sz <= BUFFER_SIZE, + MOZ_RELEASE_ASSERT(mDataLength + sz <= BUFFER_SIZE, "Can't read that much data!"); mAmountToRead = sz; } @@ -1219,8 +1336,7 @@ nsresult nsSOCKSIOLayerAddToSocket(int32_t family, const char *host, int32_t port, - const char *proxyHost, - int32_t proxyPort, + nsIProxyInfo *proxy, int32_t socksVersion, uint32_t flags, PRFileDesc *fd, @@ -1287,7 +1403,7 @@ nsSOCKSIOLayerAddToSocket(int32_t family, } NS_ADDREF(infoObject); - infoObject->Init(socksVersion, family, proxyHost, proxyPort, host, flags); + infoObject->Init(socksVersion, family, proxy, host, flags); layer->secret = (PRFilePrivate*) infoObject; rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);