1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/socket/nsSOCKSIOLayer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1420 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:set expandtab ts=4 sw=4 sts=4 cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nspr.h" 1.11 +#include "private/pprio.h" 1.12 +#include "nsString.h" 1.13 +#include "nsCRT.h" 1.14 + 1.15 +#include "nsIServiceManager.h" 1.16 +#include "nsIDNSService.h" 1.17 +#include "nsIDNSRecord.h" 1.18 +#include "nsISOCKSSocketInfo.h" 1.19 +#include "nsISocketProvider.h" 1.20 +#include "nsSOCKSIOLayer.h" 1.21 +#include "nsNetCID.h" 1.22 +#include "nsIDNSListener.h" 1.23 +#include "nsICancelable.h" 1.24 +#include "nsThreadUtils.h" 1.25 +#include "mozilla/net/DNS.h" 1.26 + 1.27 +using namespace mozilla::net; 1.28 + 1.29 +static PRDescIdentity nsSOCKSIOLayerIdentity; 1.30 +static PRIOMethods nsSOCKSIOLayerMethods; 1.31 +static bool firstTime = true; 1.32 +static bool ipv6Supported = true; 1.33 + 1.34 +#if defined(PR_LOGGING) 1.35 +static PRLogModuleInfo *gSOCKSLog; 1.36 +#define LOGDEBUG(args) PR_LOG(gSOCKSLog, PR_LOG_DEBUG, args) 1.37 +#define LOGERROR(args) PR_LOG(gSOCKSLog, PR_LOG_ERROR , args) 1.38 + 1.39 +#else 1.40 +#define LOGDEBUG(args) 1.41 +#define LOGERROR(args) 1.42 +#endif 1.43 + 1.44 +class nsSOCKSSocketInfo : public nsISOCKSSocketInfo 1.45 + , public nsIDNSListener 1.46 +{ 1.47 + enum State { 1.48 + SOCKS_INITIAL, 1.49 + SOCKS_DNS_IN_PROGRESS, 1.50 + SOCKS_DNS_COMPLETE, 1.51 + SOCKS_CONNECTING_TO_PROXY, 1.52 + SOCKS4_WRITE_CONNECT_REQUEST, 1.53 + SOCKS4_READ_CONNECT_RESPONSE, 1.54 + SOCKS5_WRITE_AUTH_REQUEST, 1.55 + SOCKS5_READ_AUTH_RESPONSE, 1.56 + SOCKS5_WRITE_USERNAME_REQUEST, 1.57 + SOCKS5_READ_USERNAME_RESPONSE, 1.58 + SOCKS5_WRITE_CONNECT_REQUEST, 1.59 + SOCKS5_READ_CONNECT_RESPONSE_TOP, 1.60 + SOCKS5_READ_CONNECT_RESPONSE_BOTTOM, 1.61 + SOCKS_CONNECTED, 1.62 + SOCKS_FAILED 1.63 + }; 1.64 + 1.65 + // A buffer of 520 bytes should be enough for any request and response 1.66 + // in case of SOCKS4 as well as SOCKS5 1.67 + static const uint32_t BUFFER_SIZE = 520; 1.68 + static const uint32_t MAX_HOSTNAME_LEN = 255; 1.69 + static const uint32_t MAX_USERNAME_LEN = 255; 1.70 + static const uint32_t MAX_PASSWORD_LEN = 255; 1.71 + 1.72 +public: 1.73 + nsSOCKSSocketInfo(); 1.74 + virtual ~nsSOCKSSocketInfo() { HandshakeFinished(); } 1.75 + 1.76 + NS_DECL_THREADSAFE_ISUPPORTS 1.77 + NS_DECL_NSISOCKSSOCKETINFO 1.78 + NS_DECL_NSIDNSLISTENER 1.79 + 1.80 + void Init(int32_t version, 1.81 + int32_t family, 1.82 + nsIProxyInfo *proxy, 1.83 + const char *destinationHost, 1.84 + uint32_t flags); 1.85 + 1.86 + void SetConnectTimeout(PRIntervalTime to); 1.87 + PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1); 1.88 + int16_t GetPollFlags() const; 1.89 + bool IsConnected() const { return (mState == SOCKS_CONNECTED || 1.90 + mState == SOCKS5_READ_CONNECT_RESPONSE_TOP); } 1.91 + 1.92 + void ForgetFD() { mFD = nullptr; } 1.93 + 1.94 +private: 1.95 + void HandshakeFinished(PRErrorCode err = 0); 1.96 + PRStatus StartDNS(PRFileDesc *fd); 1.97 + PRStatus ConnectToProxy(PRFileDesc *fd); 1.98 + void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy); 1.99 + PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags); 1.100 + PRStatus WriteV4ConnectRequest(); 1.101 + PRStatus ReadV4ConnectResponse(); 1.102 + PRStatus WriteV5AuthRequest(); 1.103 + PRStatus ReadV5AuthResponse(); 1.104 + PRStatus WriteV5UsernameRequest(); 1.105 + PRStatus ReadV5UsernameResponse(); 1.106 + PRStatus WriteV5ConnectRequest(); 1.107 + PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len); 1.108 + PRStatus ReadV5ConnectResponseTop(); 1.109 + PRStatus ReadV5ConnectResponseBottom(); 1.110 + 1.111 + void WriteUint8(uint8_t d); 1.112 + void WriteUint16(uint16_t d); 1.113 + void WriteUint32(uint32_t d); 1.114 + void WriteNetAddr(const NetAddr *addr); 1.115 + void WriteNetPort(const NetAddr *addr); 1.116 + void WriteString(const nsACString &str); 1.117 + 1.118 + uint8_t ReadUint8(); 1.119 + uint16_t ReadUint16(); 1.120 + uint32_t ReadUint32(); 1.121 + void ReadNetAddr(NetAddr *addr, uint16_t fam); 1.122 + void ReadNetPort(NetAddr *addr); 1.123 + 1.124 + void WantRead(uint32_t sz); 1.125 + PRStatus ReadFromSocket(PRFileDesc *fd); 1.126 + PRStatus WriteToSocket(PRFileDesc *fd); 1.127 + 1.128 +private: 1.129 + State mState; 1.130 + uint8_t * mData; 1.131 + uint8_t * mDataIoPtr; 1.132 + uint32_t mDataLength; 1.133 + uint32_t mReadOffset; 1.134 + uint32_t mAmountToRead; 1.135 + nsCOMPtr<nsIDNSRecord> mDnsRec; 1.136 + nsCOMPtr<nsICancelable> mLookup; 1.137 + nsresult mLookupStatus; 1.138 + PRFileDesc *mFD; 1.139 + 1.140 + nsCString mDestinationHost; 1.141 + nsCOMPtr<nsIProxyInfo> mProxy; 1.142 + int32_t mVersion; // SOCKS version 4 or 5 1.143 + int32_t mDestinationFamily; 1.144 + uint32_t mFlags; 1.145 + NetAddr mInternalProxyAddr; 1.146 + NetAddr mExternalProxyAddr; 1.147 + NetAddr mDestinationAddr; 1.148 + PRIntervalTime mTimeout; 1.149 + nsCString mProxyUsername; // Cache, from mProxy 1.150 +}; 1.151 + 1.152 +nsSOCKSSocketInfo::nsSOCKSSocketInfo() 1.153 + : mState(SOCKS_INITIAL) 1.154 + , mDataIoPtr(nullptr) 1.155 + , mDataLength(0) 1.156 + , mReadOffset(0) 1.157 + , mAmountToRead(0) 1.158 + , mVersion(-1) 1.159 + , mDestinationFamily(AF_INET) 1.160 + , mFlags(0) 1.161 + , mTimeout(PR_INTERVAL_NO_TIMEOUT) 1.162 +{ 1.163 + mData = new uint8_t[BUFFER_SIZE]; 1.164 + 1.165 + mInternalProxyAddr.raw.family = AF_INET; 1.166 + mInternalProxyAddr.inet.ip = htonl(INADDR_ANY); 1.167 + mInternalProxyAddr.inet.port = htons(0); 1.168 + 1.169 + mExternalProxyAddr.raw.family = AF_INET; 1.170 + mExternalProxyAddr.inet.ip = htonl(INADDR_ANY); 1.171 + mExternalProxyAddr.inet.port = htons(0); 1.172 + 1.173 + mDestinationAddr.raw.family = AF_INET; 1.174 + mDestinationAddr.inet.ip = htonl(INADDR_ANY); 1.175 + mDestinationAddr.inet.port = htons(0); 1.176 +} 1.177 + 1.178 +void 1.179 +nsSOCKSSocketInfo::Init(int32_t version, int32_t family, nsIProxyInfo *proxy, const char *host, uint32_t flags) 1.180 +{ 1.181 + mVersion = version; 1.182 + mDestinationFamily = family; 1.183 + mProxy = proxy; 1.184 + mDestinationHost = host; 1.185 + mFlags = flags; 1.186 + mProxy->GetUsername(mProxyUsername); // cache 1.187 +} 1.188 + 1.189 +NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener) 1.190 + 1.191 +NS_IMETHODIMP 1.192 +nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr) 1.193 +{ 1.194 + memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr)); 1.195 + return NS_OK; 1.196 +} 1.197 + 1.198 +NS_IMETHODIMP 1.199 +nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr *aExternalProxyAddr) 1.200 +{ 1.201 + memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr)); 1.202 + return NS_OK; 1.203 +} 1.204 + 1.205 +NS_IMETHODIMP 1.206 +nsSOCKSSocketInfo::GetDestinationAddr(NetAddr * *aDestinationAddr) 1.207 +{ 1.208 + memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr)); 1.209 + return NS_OK; 1.210 +} 1.211 + 1.212 +NS_IMETHODIMP 1.213 +nsSOCKSSocketInfo::SetDestinationAddr(NetAddr *aDestinationAddr) 1.214 +{ 1.215 + memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr)); 1.216 + return NS_OK; 1.217 +} 1.218 + 1.219 +NS_IMETHODIMP 1.220 +nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr * *aInternalProxyAddr) 1.221 +{ 1.222 + memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr)); 1.223 + return NS_OK; 1.224 +} 1.225 + 1.226 +NS_IMETHODIMP 1.227 +nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr *aInternalProxyAddr) 1.228 +{ 1.229 + memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr)); 1.230 + return NS_OK; 1.231 +} 1.232 + 1.233 +// There needs to be a means of distinguishing between connection errors 1.234 +// that the SOCKS server reports when it rejects a connection request, and 1.235 +// connection errors that happen while attempting to connect to the SOCKS 1.236 +// server. Otherwise, Firefox will report incorrectly that the proxy server 1.237 +// is refusing connections when a SOCKS request is rejected by the proxy. 1.238 +// When a SOCKS handshake failure occurs, the PR error is set to 1.239 +// PR_UNKNOWN_ERROR, and the real error code is returned via the OS error. 1.240 +void 1.241 +nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err) 1.242 +{ 1.243 + if (err == 0) { 1.244 + mState = SOCKS_CONNECTED; 1.245 + } else { 1.246 + mState = SOCKS_FAILED; 1.247 + PR_SetError(PR_UNKNOWN_ERROR, err); 1.248 + } 1.249 + 1.250 + // We don't need the buffer any longer, so free it. 1.251 + delete [] mData; 1.252 + mData = nullptr; 1.253 + mDataIoPtr = nullptr; 1.254 + mDataLength = 0; 1.255 + mReadOffset = 0; 1.256 + mAmountToRead = 0; 1.257 + if (mLookup) { 1.258 + mLookup->Cancel(NS_ERROR_FAILURE); 1.259 + mLookup = nullptr; 1.260 + } 1.261 +} 1.262 + 1.263 +PRStatus 1.264 +nsSOCKSSocketInfo::StartDNS(PRFileDesc *fd) 1.265 +{ 1.266 + NS_ABORT_IF_FALSE(!mDnsRec && mState == SOCKS_INITIAL, 1.267 + "Must be in initial state to make DNS Lookup"); 1.268 + 1.269 + nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); 1.270 + if (!dns) 1.271 + return PR_FAILURE; 1.272 + 1.273 + nsCString proxyHost; 1.274 + mProxy->GetHost(proxyHost); 1.275 + 1.276 + mFD = fd; 1.277 + nsresult rv = dns->AsyncResolve(proxyHost, 0, this, 1.278 + NS_GetCurrentThread(), 1.279 + getter_AddRefs(mLookup)); 1.280 + 1.281 + if (NS_FAILED(rv)) { 1.282 + LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed", 1.283 + proxyHost.get())); 1.284 + return PR_FAILURE; 1.285 + } 1.286 + mState = SOCKS_DNS_IN_PROGRESS; 1.287 + PR_SetError(PR_IN_PROGRESS_ERROR, 0); 1.288 + return PR_FAILURE; 1.289 +} 1.290 + 1.291 +NS_IMETHODIMP 1.292 +nsSOCKSSocketInfo::OnLookupComplete(nsICancelable *aRequest, 1.293 + nsIDNSRecord *aRecord, 1.294 + nsresult aStatus) 1.295 +{ 1.296 + NS_ABORT_IF_FALSE(aRequest == mLookup, "wrong DNS query"); 1.297 + mLookup = nullptr; 1.298 + mLookupStatus = aStatus; 1.299 + mDnsRec = aRecord; 1.300 + mState = SOCKS_DNS_COMPLETE; 1.301 + if (mFD) { 1.302 + ConnectToProxy(mFD); 1.303 + ForgetFD(); 1.304 + } 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +PRStatus 1.309 +nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd) 1.310 +{ 1.311 + PRStatus status; 1.312 + nsresult rv; 1.313 + 1.314 + NS_ABORT_IF_FALSE(mState == SOCKS_DNS_COMPLETE, 1.315 + "Must have DNS to make connection!"); 1.316 + 1.317 + if (NS_FAILED(mLookupStatus)) { 1.318 + PR_SetError(PR_BAD_ADDRESS_ERROR, 0); 1.319 + return PR_FAILURE; 1.320 + } 1.321 + 1.322 + // Try socks5 if the destination addrress is IPv6 1.323 + if (mVersion == 4 && 1.324 + mDestinationAddr.raw.family == AF_INET6) { 1.325 + mVersion = 5; 1.326 + } 1.327 + 1.328 + int32_t proxyPort; 1.329 + mProxy->GetPort(&proxyPort); 1.330 + 1.331 + int32_t addresses = 0; 1.332 + do { 1.333 + if (addresses++) 1.334 + mDnsRec->ReportUnusable(proxyPort); 1.335 + 1.336 + rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr); 1.337 + // No more addresses to try? If so, we'll need to bail 1.338 + if (NS_FAILED(rv)) { 1.339 + nsCString proxyHost; 1.340 + mProxy->GetHost(proxyHost); 1.341 + LOGERROR(("socks: unable to connect to SOCKS proxy, %s", 1.342 + proxyHost.get())); 1.343 + return PR_FAILURE; 1.344 + } 1.345 + 1.346 +#if defined(PR_LOGGING) 1.347 + char buf[kIPv6CStrBufSize]; 1.348 + NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf)); 1.349 + LOGDEBUG(("socks: trying proxy server, %s:%hu", 1.350 + buf, ntohs(mInternalProxyAddr.inet.port))); 1.351 +#endif 1.352 + NetAddr proxy = mInternalProxyAddr; 1.353 + FixupAddressFamily(fd, &proxy); 1.354 + PRNetAddr prProxy; 1.355 + NetAddrToPRNetAddr(&proxy, &prProxy); 1.356 + status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout); 1.357 + if (status != PR_SUCCESS) { 1.358 + PRErrorCode c = PR_GetError(); 1.359 + // If EINPROGRESS, return now and check back later after polling 1.360 + if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) { 1.361 + mState = SOCKS_CONNECTING_TO_PROXY; 1.362 + return status; 1.363 + } 1.364 + } 1.365 + } while (status != PR_SUCCESS); 1.366 + 1.367 + // Connected now, start SOCKS 1.368 + if (mVersion == 4) 1.369 + return WriteV4ConnectRequest(); 1.370 + return WriteV5AuthRequest(); 1.371 +} 1.372 + 1.373 +void 1.374 +nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy) 1.375 +{ 1.376 + int32_t proxyFamily = mInternalProxyAddr.raw.family; 1.377 + // Do nothing if the address family is already matched 1.378 + if (proxyFamily == mDestinationFamily) { 1.379 + return; 1.380 + } 1.381 + // If the system does not support IPv6 and the proxy address is IPv6, 1.382 + // We can do nothing here. 1.383 + if (proxyFamily == AF_INET6 && !ipv6Supported) { 1.384 + return; 1.385 + } 1.386 + // If the system does not support IPv6 and the destination address is 1.387 + // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy 1.388 + // the emulation layer 1.389 + if (mDestinationFamily == AF_INET6 && !ipv6Supported) { 1.390 + proxy->inet6.family = AF_INET6; 1.391 + proxy->inet6.port = mInternalProxyAddr.inet.port; 1.392 + uint8_t *proxyp = proxy->inet6.ip.u8; 1.393 + memset(proxyp, 0, 10); 1.394 + memset(proxyp + 10, 0xff, 2); 1.395 + memcpy(proxyp + 12,(char *) &mInternalProxyAddr.inet.ip, 4); 1.396 + // mDestinationFamily should not be updated 1.397 + return; 1.398 + } 1.399 + // Get an OS native handle from a specified FileDesc 1.400 + PROsfd osfd = PR_FileDesc2NativeHandle(fd); 1.401 + if (osfd == -1) { 1.402 + return; 1.403 + } 1.404 + // Create a new FileDesc with a specified family 1.405 + PRFileDesc *tmpfd = PR_OpenTCPSocket(proxyFamily); 1.406 + if (!tmpfd) { 1.407 + return; 1.408 + } 1.409 + PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd); 1.410 + if (newsd == -1) { 1.411 + PR_Close(tmpfd); 1.412 + return; 1.413 + } 1.414 + // Must succeed because PR_FileDesc2NativeHandle succeeded 1.415 + fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER); 1.416 + MOZ_ASSERT(fd); 1.417 + // Swap OS native handles 1.418 + PR_ChangeFileDescNativeHandle(fd, newsd); 1.419 + PR_ChangeFileDescNativeHandle(tmpfd, osfd); 1.420 + // Close temporary FileDesc which is now associated with 1.421 + // old OS native handle 1.422 + PR_Close(tmpfd); 1.423 + mDestinationFamily = proxyFamily; 1.424 +} 1.425 + 1.426 +PRStatus 1.427 +nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags) 1.428 +{ 1.429 + PRStatus status; 1.430 + 1.431 + NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY, 1.432 + "Continuing connection in wrong state!"); 1.433 + 1.434 + LOGDEBUG(("socks: continuing connection to proxy")); 1.435 + 1.436 + status = fd->lower->methods->connectcontinue(fd->lower, oflags); 1.437 + if (status != PR_SUCCESS) { 1.438 + PRErrorCode c = PR_GetError(); 1.439 + if (c != PR_WOULD_BLOCK_ERROR && c != PR_IN_PROGRESS_ERROR) { 1.440 + // A connection failure occured, try another address 1.441 + mState = SOCKS_DNS_COMPLETE; 1.442 + return ConnectToProxy(fd); 1.443 + } 1.444 + 1.445 + // We're still connecting 1.446 + return PR_FAILURE; 1.447 + } 1.448 + 1.449 + // Connected now, start SOCKS 1.450 + if (mVersion == 4) 1.451 + return WriteV4ConnectRequest(); 1.452 + return WriteV5AuthRequest(); 1.453 +} 1.454 + 1.455 +PRStatus 1.456 +nsSOCKSSocketInfo::WriteV4ConnectRequest() 1.457 +{ 1.458 + if (mProxyUsername.Length() > MAX_USERNAME_LEN) { 1.459 + LOGERROR(("socks username is too long")); 1.460 + HandshakeFinished(PR_UNKNOWN_ERROR); 1.461 + return PR_FAILURE; 1.462 + } 1.463 + 1.464 + NetAddr *addr = &mDestinationAddr; 1.465 + int32_t proxy_resolve; 1.466 + 1.467 + NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY, 1.468 + "Invalid state!"); 1.469 + 1.470 + proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST; 1.471 + 1.472 + mDataLength = 0; 1.473 + mState = SOCKS4_WRITE_CONNECT_REQUEST; 1.474 + 1.475 + LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)", 1.476 + proxy_resolve? "yes" : "no")); 1.477 + 1.478 + // Send a SOCKS 4 connect request. 1.479 + WriteUint8(0x04); // version -- 4 1.480 + WriteUint8(0x01); // command -- connect 1.481 + WriteNetPort(addr); 1.482 + if (proxy_resolve) { 1.483 + // Add the full name, null-terminated, to the request 1.484 + // according to SOCKS 4a. A fake IP address, with the first 1.485 + // four bytes set to 0 and the last byte set to something other 1.486 + // than 0, is used to notify the proxy that this is a SOCKS 4a 1.487 + // request. This request type works for Tor and perhaps others. 1.488 + WriteUint32(htonl(0x00000001)); // Fake IP 1.489 + WriteString(mProxyUsername); // Send username. May be empty. 1.490 + WriteUint8(0x00); // Null-terminate username 1.491 + // Password not supported by V4. 1.492 + if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) { 1.493 + LOGERROR(("socks4: destination host name is too long!")); 1.494 + HandshakeFinished(PR_BAD_ADDRESS_ERROR); 1.495 + return PR_FAILURE; 1.496 + } 1.497 + WriteString(mDestinationHost); // Hostname 1.498 + WriteUint8(0x00); 1.499 + } else if (addr->raw.family == AF_INET) { 1.500 + WriteNetAddr(addr); // Add the IPv4 address 1.501 + WriteString(mProxyUsername); // Send username. May be empty. 1.502 + WriteUint8(0x00); // Null-terminate username 1.503 + // Password not supported by V4. 1.504 + } else if (addr->raw.family == AF_INET6) { 1.505 + LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!")); 1.506 + HandshakeFinished(PR_BAD_ADDRESS_ERROR); 1.507 + return PR_FAILURE; 1.508 + } 1.509 + 1.510 + return PR_SUCCESS; 1.511 +} 1.512 + 1.513 +PRStatus 1.514 +nsSOCKSSocketInfo::ReadV4ConnectResponse() 1.515 +{ 1.516 + NS_ABORT_IF_FALSE(mState == SOCKS4_READ_CONNECT_RESPONSE, 1.517 + "Handling SOCKS 4 connection reply in wrong state!"); 1.518 + 1.519 + LOGDEBUG(("socks4: checking connection reply")); 1.520 + 1.521 + if (mDataLength != 8) { 1.522 + LOGERROR(("SOCKS 4 connection reply must be 8 bytes!")); 1.523 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.524 + return PR_FAILURE; 1.525 + } 1.526 + 1.527 + if (ReadUint8() != 0x00) { 1.528 + LOGERROR(("socks4: wrong connection reply")); 1.529 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.530 + return PR_FAILURE; 1.531 + } 1.532 + 1.533 + // See if our connection request was granted 1.534 + if (ReadUint8() == 90) { 1.535 + LOGDEBUG(("socks4: connection successful!")); 1.536 + HandshakeFinished(); 1.537 + return PR_SUCCESS; 1.538 + } 1.539 + 1.540 + LOGERROR(("socks4: unable to connect")); 1.541 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.542 + return PR_FAILURE; 1.543 +} 1.544 + 1.545 +PRStatus 1.546 +nsSOCKSSocketInfo::WriteV5AuthRequest() 1.547 +{ 1.548 + NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!"); 1.549 + 1.550 + mDataLength = 0; 1.551 + mState = SOCKS5_WRITE_AUTH_REQUEST; 1.552 + 1.553 + // Send an initial SOCKS 5 greeting 1.554 + LOGDEBUG(("socks5: sending auth methods")); 1.555 + WriteUint8(0x05); // version -- 5 1.556 + WriteUint8(0x01); // # of auth methods -- 1 1.557 + if (mProxyUsername.IsEmpty()) { 1.558 + WriteUint8(0x00); // no authentication 1.559 + } else { 1.560 + WriteUint8(0x02); // username/password 1.561 + } 1.562 + 1.563 + return PR_SUCCESS; 1.564 +} 1.565 + 1.566 +PRStatus 1.567 +nsSOCKSSocketInfo::ReadV5AuthResponse() 1.568 +{ 1.569 + NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE, 1.570 + "Handling SOCKS 5 auth method reply in wrong state!"); 1.571 + 1.572 + if (mDataLength != 2) { 1.573 + LOGERROR(("SOCKS 5 auth method reply must be 2 bytes")); 1.574 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.575 + return PR_FAILURE; 1.576 + } 1.577 + 1.578 + // Check version number 1.579 + if (ReadUint8() != 0x05) { 1.580 + LOGERROR(("socks5: unexpected version in the reply")); 1.581 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.582 + return PR_FAILURE; 1.583 + } 1.584 + 1.585 + // Make sure our authentication choice was accepted, 1.586 + // and continue accordingly 1.587 + uint8_t authMethod = ReadUint8(); 1.588 + if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth 1.589 + LOGDEBUG(("socks5: server allows connection without authentication")); 1.590 + return WriteV5ConnectRequest(); 1.591 + } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw 1.592 + LOGDEBUG(("socks5: auth method accepted by server")); 1.593 + return WriteV5UsernameRequest(); 1.594 + } else { // 0xFF signals error 1.595 + LOGERROR(("socks5: server did not accept our authentication method")); 1.596 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.597 + return PR_FAILURE; 1.598 + } 1.599 +} 1.600 + 1.601 +PRStatus 1.602 +nsSOCKSSocketInfo::WriteV5UsernameRequest() 1.603 +{ 1.604 + NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!"); 1.605 + 1.606 + if (mProxyUsername.Length() > MAX_USERNAME_LEN) { 1.607 + LOGERROR(("socks username is too long")); 1.608 + HandshakeFinished(PR_UNKNOWN_ERROR); 1.609 + return PR_FAILURE; 1.610 + } 1.611 + 1.612 + nsCString password; 1.613 + mProxy->GetPassword(password); 1.614 + if (password.Length() > MAX_PASSWORD_LEN) { 1.615 + LOGERROR(("socks password is too long")); 1.616 + HandshakeFinished(PR_UNKNOWN_ERROR); 1.617 + return PR_FAILURE; 1.618 + } 1.619 + 1.620 + mDataLength = 0; 1.621 + mState = SOCKS5_WRITE_USERNAME_REQUEST; 1.622 + 1.623 + LOGDEBUG(("socks5: sending username and password")); 1.624 + // RFC 1929 Username/password auth for SOCKS 5 1.625 + WriteUint8(0x01); // version 1 (not 5) 1.626 + WriteUint8(mProxyUsername.Length()); // username length 1.627 + WriteString(mProxyUsername); // username 1.628 + WriteUint8(password.Length()); // password length 1.629 + WriteString(password); // password. WARNING: Sent unencrypted! 1.630 + 1.631 + return PR_SUCCESS; 1.632 +} 1.633 + 1.634 +PRStatus 1.635 +nsSOCKSSocketInfo::ReadV5UsernameResponse() 1.636 +{ 1.637 + NS_ABORT_IF_FALSE(mState == SOCKS5_READ_USERNAME_RESPONSE, 1.638 + "Handling SOCKS 5 username/password reply in wrong state!"); 1.639 + 1.640 + if (mDataLength != 2) { 1.641 + LOGERROR(("SOCKS 5 username reply must be 2 bytes")); 1.642 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.643 + return PR_FAILURE; 1.644 + } 1.645 + 1.646 + // Check version number, must be 1 (not 5) 1.647 + if (ReadUint8() != 0x01) { 1.648 + LOGERROR(("socks5: unexpected version in the reply")); 1.649 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.650 + return PR_FAILURE; 1.651 + } 1.652 + 1.653 + // Check whether username/password were accepted 1.654 + if (ReadUint8() != 0x00) { // 0 = success 1.655 + LOGERROR(("socks5: username/password not accepted")); 1.656 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.657 + return PR_FAILURE; 1.658 + } 1.659 + 1.660 + LOGDEBUG(("socks5: username/password accepted by server")); 1.661 + 1.662 + return WriteV5ConnectRequest(); 1.663 +} 1.664 + 1.665 +PRStatus 1.666 +nsSOCKSSocketInfo::WriteV5ConnectRequest() 1.667 +{ 1.668 + // Send SOCKS 5 connect request 1.669 + NetAddr *addr = &mDestinationAddr; 1.670 + int32_t proxy_resolve; 1.671 + proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST; 1.672 + 1.673 + LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)", 1.674 + proxy_resolve? "yes" : "no")); 1.675 + 1.676 + mDataLength = 0; 1.677 + mState = SOCKS5_WRITE_CONNECT_REQUEST; 1.678 + 1.679 + WriteUint8(0x05); // version -- 5 1.680 + WriteUint8(0x01); // command -- connect 1.681 + WriteUint8(0x00); // reserved 1.682 + 1.683 + // Add the address to the SOCKS 5 request. SOCKS 5 supports several 1.684 + // address types, so we pick the one that works best for us. 1.685 + if (proxy_resolve) { 1.686 + // Add the host name. Only a single byte is used to store the length, 1.687 + // so we must prevent long names from being used. 1.688 + if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) { 1.689 + LOGERROR(("socks5: destination host name is too long!")); 1.690 + HandshakeFinished(PR_BAD_ADDRESS_ERROR); 1.691 + return PR_FAILURE; 1.692 + } 1.693 + WriteUint8(0x03); // addr type -- domainname 1.694 + WriteUint8(mDestinationHost.Length()); // name length 1.695 + WriteString(mDestinationHost); 1.696 + } else if (addr->raw.family == AF_INET) { 1.697 + WriteUint8(0x01); // addr type -- IPv4 1.698 + WriteNetAddr(addr); 1.699 + } else if (addr->raw.family == AF_INET6) { 1.700 + WriteUint8(0x04); // addr type -- IPv6 1.701 + WriteNetAddr(addr); 1.702 + } else { 1.703 + LOGERROR(("socks5: destination address of unknown type!")); 1.704 + HandshakeFinished(PR_BAD_ADDRESS_ERROR); 1.705 + return PR_FAILURE; 1.706 + } 1.707 + 1.708 + WriteNetPort(addr); // port 1.709 + 1.710 + return PR_SUCCESS; 1.711 +} 1.712 + 1.713 +PRStatus 1.714 +nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len) 1.715 +{ 1.716 + NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP || 1.717 + mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM, 1.718 + "Invalid state!"); 1.719 + NS_ABORT_IF_FALSE(mDataLength >= 5, 1.720 + "SOCKS 5 connection reply must be at least 5 bytes!"); 1.721 + 1.722 + // Seek to the address location 1.723 + mReadOffset = 3; 1.724 + 1.725 + *type = ReadUint8(); 1.726 + 1.727 + switch (*type) { 1.728 + case 0x01: // ipv4 1.729 + *len = 4 - 1; 1.730 + break; 1.731 + case 0x04: // ipv6 1.732 + *len = 16 - 1; 1.733 + break; 1.734 + case 0x03: // fqdn 1.735 + *len = ReadUint8(); 1.736 + break; 1.737 + default: // wrong address type 1.738 + LOGERROR(("socks5: wrong address type in connection reply!")); 1.739 + return PR_FAILURE; 1.740 + } 1.741 + 1.742 + return PR_SUCCESS; 1.743 +} 1.744 + 1.745 +PRStatus 1.746 +nsSOCKSSocketInfo::ReadV5ConnectResponseTop() 1.747 +{ 1.748 + uint8_t res; 1.749 + uint32_t len; 1.750 + 1.751 + NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP, 1.752 + "Invalid state!"); 1.753 + NS_ABORT_IF_FALSE(mDataLength == 5, 1.754 + "SOCKS 5 connection reply must be exactly 5 bytes!"); 1.755 + 1.756 + LOGDEBUG(("socks5: checking connection reply")); 1.757 + 1.758 + // Check version number 1.759 + if (ReadUint8() != 0x05) { 1.760 + LOGERROR(("socks5: unexpected version in the reply")); 1.761 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.762 + return PR_FAILURE; 1.763 + } 1.764 + 1.765 + // Check response 1.766 + res = ReadUint8(); 1.767 + if (res != 0x00) { 1.768 + PRErrorCode c = PR_CONNECT_REFUSED_ERROR; 1.769 + 1.770 + switch (res) { 1.771 + case 0x01: 1.772 + LOGERROR(("socks5: connect failed: " 1.773 + "01, General SOCKS server failure.")); 1.774 + break; 1.775 + case 0x02: 1.776 + LOGERROR(("socks5: connect failed: " 1.777 + "02, Connection not allowed by ruleset.")); 1.778 + break; 1.779 + case 0x03: 1.780 + LOGERROR(("socks5: connect failed: 03, Network unreachable.")); 1.781 + c = PR_NETWORK_UNREACHABLE_ERROR; 1.782 + break; 1.783 + case 0x04: 1.784 + LOGERROR(("socks5: connect failed: 04, Host unreachable.")); 1.785 + break; 1.786 + case 0x05: 1.787 + LOGERROR(("socks5: connect failed: 05, Connection refused.")); 1.788 + break; 1.789 + case 0x06: 1.790 + LOGERROR(("socks5: connect failed: 06, TTL expired.")); 1.791 + c = PR_CONNECT_TIMEOUT_ERROR; 1.792 + break; 1.793 + case 0x07: 1.794 + LOGERROR(("socks5: connect failed: " 1.795 + "07, Command not supported.")); 1.796 + break; 1.797 + case 0x08: 1.798 + LOGERROR(("socks5: connect failed: " 1.799 + "08, Address type not supported.")); 1.800 + c = PR_BAD_ADDRESS_ERROR; 1.801 + break; 1.802 + default: 1.803 + LOGERROR(("socks5: connect failed.")); 1.804 + break; 1.805 + } 1.806 + 1.807 + HandshakeFinished(c); 1.808 + return PR_FAILURE; 1.809 + } 1.810 + 1.811 + if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) { 1.812 + HandshakeFinished(PR_BAD_ADDRESS_ERROR); 1.813 + return PR_FAILURE; 1.814 + } 1.815 + 1.816 + mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM; 1.817 + WantRead(len + 2); 1.818 + 1.819 + return PR_SUCCESS; 1.820 +} 1.821 + 1.822 +PRStatus 1.823 +nsSOCKSSocketInfo::ReadV5ConnectResponseBottom() 1.824 +{ 1.825 + uint8_t type; 1.826 + uint32_t len; 1.827 + 1.828 + NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM, 1.829 + "Invalid state!"); 1.830 + 1.831 + if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) { 1.832 + HandshakeFinished(PR_BAD_ADDRESS_ERROR); 1.833 + return PR_FAILURE; 1.834 + } 1.835 + 1.836 + NS_ABORT_IF_FALSE(mDataLength == 7+len, 1.837 + "SOCKS 5 unexpected length of connection reply!"); 1.838 + 1.839 + LOGDEBUG(("socks5: loading source addr and port")); 1.840 + // Read what the proxy says is our source address 1.841 + switch (type) { 1.842 + case 0x01: // ipv4 1.843 + ReadNetAddr(&mExternalProxyAddr, AF_INET); 1.844 + break; 1.845 + case 0x04: // ipv6 1.846 + ReadNetAddr(&mExternalProxyAddr, AF_INET6); 1.847 + break; 1.848 + case 0x03: // fqdn (skip) 1.849 + mReadOffset += len; 1.850 + mExternalProxyAddr.raw.family = AF_INET; 1.851 + break; 1.852 + } 1.853 + 1.854 + ReadNetPort(&mExternalProxyAddr); 1.855 + 1.856 + LOGDEBUG(("socks5: connected!")); 1.857 + HandshakeFinished(); 1.858 + 1.859 + return PR_SUCCESS; 1.860 +} 1.861 + 1.862 +void 1.863 +nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to) 1.864 +{ 1.865 + mTimeout = to; 1.866 +} 1.867 + 1.868 +PRStatus 1.869 +nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags) 1.870 +{ 1.871 + LOGDEBUG(("socks: DoHandshake(), state = %d", mState)); 1.872 + 1.873 + switch (mState) { 1.874 + case SOCKS_INITIAL: 1.875 + return StartDNS(fd); 1.876 + case SOCKS_DNS_IN_PROGRESS: 1.877 + PR_SetError(PR_IN_PROGRESS_ERROR, 0); 1.878 + return PR_FAILURE; 1.879 + case SOCKS_DNS_COMPLETE: 1.880 + return ConnectToProxy(fd); 1.881 + case SOCKS_CONNECTING_TO_PROXY: 1.882 + return ContinueConnectingToProxy(fd, oflags); 1.883 + case SOCKS4_WRITE_CONNECT_REQUEST: 1.884 + if (WriteToSocket(fd) != PR_SUCCESS) 1.885 + return PR_FAILURE; 1.886 + WantRead(8); 1.887 + mState = SOCKS4_READ_CONNECT_RESPONSE; 1.888 + return PR_SUCCESS; 1.889 + case SOCKS4_READ_CONNECT_RESPONSE: 1.890 + if (ReadFromSocket(fd) != PR_SUCCESS) 1.891 + return PR_FAILURE; 1.892 + return ReadV4ConnectResponse(); 1.893 + 1.894 + case SOCKS5_WRITE_AUTH_REQUEST: 1.895 + if (WriteToSocket(fd) != PR_SUCCESS) 1.896 + return PR_FAILURE; 1.897 + WantRead(2); 1.898 + mState = SOCKS5_READ_AUTH_RESPONSE; 1.899 + return PR_SUCCESS; 1.900 + case SOCKS5_READ_AUTH_RESPONSE: 1.901 + if (ReadFromSocket(fd) != PR_SUCCESS) 1.902 + return PR_FAILURE; 1.903 + return ReadV5AuthResponse(); 1.904 + case SOCKS5_WRITE_USERNAME_REQUEST: 1.905 + if (WriteToSocket(fd) != PR_SUCCESS) 1.906 + return PR_FAILURE; 1.907 + WantRead(2); 1.908 + mState = SOCKS5_READ_USERNAME_RESPONSE; 1.909 + return PR_SUCCESS; 1.910 + case SOCKS5_READ_USERNAME_RESPONSE: 1.911 + if (ReadFromSocket(fd) != PR_SUCCESS) 1.912 + return PR_FAILURE; 1.913 + return ReadV5UsernameResponse(); 1.914 + case SOCKS5_WRITE_CONNECT_REQUEST: 1.915 + if (WriteToSocket(fd) != PR_SUCCESS) 1.916 + return PR_FAILURE; 1.917 + 1.918 + // The SOCKS 5 response to the connection request is variable 1.919 + // length. First, we'll read enough to tell how long the response 1.920 + // is, and will read the rest later. 1.921 + WantRead(5); 1.922 + mState = SOCKS5_READ_CONNECT_RESPONSE_TOP; 1.923 + return PR_SUCCESS; 1.924 + case SOCKS5_READ_CONNECT_RESPONSE_TOP: 1.925 + if (ReadFromSocket(fd) != PR_SUCCESS) 1.926 + return PR_FAILURE; 1.927 + return ReadV5ConnectResponseTop(); 1.928 + case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM: 1.929 + if (ReadFromSocket(fd) != PR_SUCCESS) 1.930 + return PR_FAILURE; 1.931 + return ReadV5ConnectResponseBottom(); 1.932 + 1.933 + case SOCKS_CONNECTED: 1.934 + LOGERROR(("socks: already connected")); 1.935 + HandshakeFinished(PR_IS_CONNECTED_ERROR); 1.936 + return PR_FAILURE; 1.937 + case SOCKS_FAILED: 1.938 + LOGERROR(("socks: already failed")); 1.939 + return PR_FAILURE; 1.940 + } 1.941 + 1.942 + LOGERROR(("socks: executing handshake in invalid state, %d", mState)); 1.943 + HandshakeFinished(PR_INVALID_STATE_ERROR); 1.944 + 1.945 + return PR_FAILURE; 1.946 +} 1.947 + 1.948 +int16_t 1.949 +nsSOCKSSocketInfo::GetPollFlags() const 1.950 +{ 1.951 + switch (mState) { 1.952 + case SOCKS_DNS_IN_PROGRESS: 1.953 + case SOCKS_DNS_COMPLETE: 1.954 + case SOCKS_CONNECTING_TO_PROXY: 1.955 + return PR_POLL_EXCEPT | PR_POLL_WRITE; 1.956 + case SOCKS4_WRITE_CONNECT_REQUEST: 1.957 + case SOCKS5_WRITE_AUTH_REQUEST: 1.958 + case SOCKS5_WRITE_USERNAME_REQUEST: 1.959 + case SOCKS5_WRITE_CONNECT_REQUEST: 1.960 + return PR_POLL_WRITE; 1.961 + case SOCKS4_READ_CONNECT_RESPONSE: 1.962 + case SOCKS5_READ_AUTH_RESPONSE: 1.963 + case SOCKS5_READ_USERNAME_RESPONSE: 1.964 + case SOCKS5_READ_CONNECT_RESPONSE_TOP: 1.965 + case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM: 1.966 + return PR_POLL_READ; 1.967 + default: 1.968 + break; 1.969 + } 1.970 + 1.971 + return 0; 1.972 +} 1.973 + 1.974 +inline void 1.975 +nsSOCKSSocketInfo::WriteUint8(uint8_t v) 1.976 +{ 1.977 + MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE, 1.978 + "Can't write that much data!"); 1.979 + mData[mDataLength] = v; 1.980 + mDataLength += sizeof(v); 1.981 +} 1.982 + 1.983 +inline void 1.984 +nsSOCKSSocketInfo::WriteUint16(uint16_t v) 1.985 +{ 1.986 + MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE, 1.987 + "Can't write that much data!"); 1.988 + memcpy(mData + mDataLength, &v, sizeof(v)); 1.989 + mDataLength += sizeof(v); 1.990 +} 1.991 + 1.992 +inline void 1.993 +nsSOCKSSocketInfo::WriteUint32(uint32_t v) 1.994 +{ 1.995 + MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE, 1.996 + "Can't write that much data!"); 1.997 + memcpy(mData + mDataLength, &v, sizeof(v)); 1.998 + mDataLength += sizeof(v); 1.999 +} 1.1000 + 1.1001 +void 1.1002 +nsSOCKSSocketInfo::WriteNetAddr(const NetAddr *addr) 1.1003 +{ 1.1004 + const char *ip = nullptr; 1.1005 + uint32_t len = 0; 1.1006 + 1.1007 + if (addr->raw.family == AF_INET) { 1.1008 + ip = (const char*)&addr->inet.ip; 1.1009 + len = sizeof(addr->inet.ip); 1.1010 + } else if (addr->raw.family == AF_INET6) { 1.1011 + ip = (const char*)addr->inet6.ip.u8; 1.1012 + len = sizeof(addr->inet6.ip.u8); 1.1013 + } 1.1014 + 1.1015 + MOZ_RELEASE_ASSERT(ip != nullptr, "Unknown address"); 1.1016 + MOZ_RELEASE_ASSERT(mDataLength + len <= BUFFER_SIZE, 1.1017 + "Can't write that much data!"); 1.1018 + 1.1019 + memcpy(mData + mDataLength, ip, len); 1.1020 + mDataLength += len; 1.1021 +} 1.1022 + 1.1023 +void 1.1024 +nsSOCKSSocketInfo::WriteNetPort(const NetAddr *addr) 1.1025 +{ 1.1026 + WriteUint16(addr->inet.port); 1.1027 +} 1.1028 + 1.1029 +void 1.1030 +nsSOCKSSocketInfo::WriteString(const nsACString &str) 1.1031 +{ 1.1032 + MOZ_RELEASE_ASSERT(mDataLength + str.Length() <= BUFFER_SIZE, 1.1033 + "Can't write that much data!"); 1.1034 + memcpy(mData + mDataLength, str.Data(), str.Length()); 1.1035 + mDataLength += str.Length(); 1.1036 +} 1.1037 + 1.1038 +inline uint8_t 1.1039 +nsSOCKSSocketInfo::ReadUint8() 1.1040 +{ 1.1041 + uint8_t rv; 1.1042 + MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength, 1.1043 + "Not enough space to pop a uint8_t!"); 1.1044 + rv = mData[mReadOffset]; 1.1045 + mReadOffset += sizeof(rv); 1.1046 + return rv; 1.1047 +} 1.1048 + 1.1049 +inline uint16_t 1.1050 +nsSOCKSSocketInfo::ReadUint16() 1.1051 +{ 1.1052 + uint16_t rv; 1.1053 + MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength, 1.1054 + "Not enough space to pop a uint16_t!"); 1.1055 + memcpy(&rv, mData + mReadOffset, sizeof(rv)); 1.1056 + mReadOffset += sizeof(rv); 1.1057 + return rv; 1.1058 +} 1.1059 + 1.1060 +inline uint32_t 1.1061 +nsSOCKSSocketInfo::ReadUint32() 1.1062 +{ 1.1063 + uint32_t rv; 1.1064 + MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength, 1.1065 + "Not enough space to pop a uint32_t!"); 1.1066 + memcpy(&rv, mData + mReadOffset, sizeof(rv)); 1.1067 + mReadOffset += sizeof(rv); 1.1068 + return rv; 1.1069 +} 1.1070 + 1.1071 +void 1.1072 +nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam) 1.1073 +{ 1.1074 + uint32_t amt = 0; 1.1075 + const uint8_t *ip = mData + mReadOffset; 1.1076 + 1.1077 + addr->raw.family = fam; 1.1078 + if (fam == AF_INET) { 1.1079 + amt = sizeof(addr->inet.ip); 1.1080 + MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength, 1.1081 + "Not enough space to pop an ipv4 addr!"); 1.1082 + memcpy(&addr->inet.ip, ip, amt); 1.1083 + } else if (fam == AF_INET6) { 1.1084 + amt = sizeof(addr->inet6.ip.u8); 1.1085 + MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength, 1.1086 + "Not enough space to pop an ipv6 addr!"); 1.1087 + memcpy(addr->inet6.ip.u8, ip, amt); 1.1088 + } 1.1089 + 1.1090 + mReadOffset += amt; 1.1091 +} 1.1092 + 1.1093 +void 1.1094 +nsSOCKSSocketInfo::ReadNetPort(NetAddr *addr) 1.1095 +{ 1.1096 + addr->inet.port = ReadUint16(); 1.1097 +} 1.1098 + 1.1099 +void 1.1100 +nsSOCKSSocketInfo::WantRead(uint32_t sz) 1.1101 +{ 1.1102 + NS_ABORT_IF_FALSE(mDataIoPtr == nullptr, 1.1103 + "WantRead() called while I/O already in progress!"); 1.1104 + MOZ_RELEASE_ASSERT(mDataLength + sz <= BUFFER_SIZE, 1.1105 + "Can't read that much data!"); 1.1106 + mAmountToRead = sz; 1.1107 +} 1.1108 + 1.1109 +PRStatus 1.1110 +nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd) 1.1111 +{ 1.1112 + int32_t rc; 1.1113 + const uint8_t *end; 1.1114 + 1.1115 + if (!mAmountToRead) { 1.1116 + LOGDEBUG(("socks: ReadFromSocket(), nothing to do")); 1.1117 + return PR_SUCCESS; 1.1118 + } 1.1119 + 1.1120 + if (!mDataIoPtr) { 1.1121 + mDataIoPtr = mData + mDataLength; 1.1122 + mDataLength += mAmountToRead; 1.1123 + } 1.1124 + 1.1125 + end = mData + mDataLength; 1.1126 + 1.1127 + while (mDataIoPtr < end) { 1.1128 + rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr); 1.1129 + if (rc <= 0) { 1.1130 + if (rc == 0) { 1.1131 + LOGERROR(("socks: proxy server closed connection")); 1.1132 + HandshakeFinished(PR_CONNECT_REFUSED_ERROR); 1.1133 + return PR_FAILURE; 1.1134 + } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) { 1.1135 + LOGDEBUG(("socks: ReadFromSocket(), want read")); 1.1136 + } 1.1137 + break; 1.1138 + } 1.1139 + 1.1140 + mDataIoPtr += rc; 1.1141 + } 1.1142 + 1.1143 + LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total", 1.1144 + unsigned(mDataIoPtr - mData))); 1.1145 + if (mDataIoPtr == end) { 1.1146 + mDataIoPtr = nullptr; 1.1147 + mAmountToRead = 0; 1.1148 + mReadOffset = 0; 1.1149 + return PR_SUCCESS; 1.1150 + } 1.1151 + 1.1152 + return PR_FAILURE; 1.1153 +} 1.1154 + 1.1155 +PRStatus 1.1156 +nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd) 1.1157 +{ 1.1158 + int32_t rc; 1.1159 + const uint8_t *end; 1.1160 + 1.1161 + if (!mDataLength) { 1.1162 + LOGDEBUG(("socks: WriteToSocket(), nothing to do")); 1.1163 + return PR_SUCCESS; 1.1164 + } 1.1165 + 1.1166 + if (!mDataIoPtr) 1.1167 + mDataIoPtr = mData; 1.1168 + 1.1169 + end = mData + mDataLength; 1.1170 + 1.1171 + while (mDataIoPtr < end) { 1.1172 + rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr); 1.1173 + if (rc < 0) { 1.1174 + if (PR_GetError() == PR_WOULD_BLOCK_ERROR) { 1.1175 + LOGDEBUG(("socks: WriteToSocket(), want write")); 1.1176 + } 1.1177 + break; 1.1178 + } 1.1179 + 1.1180 + mDataIoPtr += rc; 1.1181 + } 1.1182 + 1.1183 + if (mDataIoPtr == end) { 1.1184 + mDataIoPtr = nullptr; 1.1185 + mDataLength = 0; 1.1186 + mReadOffset = 0; 1.1187 + return PR_SUCCESS; 1.1188 + } 1.1189 + 1.1190 + return PR_FAILURE; 1.1191 +} 1.1192 + 1.1193 +static PRStatus 1.1194 +nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to) 1.1195 +{ 1.1196 + PRStatus status; 1.1197 + NetAddr dst; 1.1198 + 1.1199 + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; 1.1200 + if (info == nullptr) return PR_FAILURE; 1.1201 + 1.1202 + if (addr->raw.family == PR_AF_INET6 && 1.1203 + PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) { 1.1204 + const uint8_t *srcp; 1.1205 + 1.1206 + LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4")); 1.1207 + 1.1208 + // copied from _PR_ConvertToIpv4NetAddr() 1.1209 + dst.raw.family = AF_INET; 1.1210 + dst.inet.ip = htonl(INADDR_ANY); 1.1211 + dst.inet.port = htons(0); 1.1212 + srcp = addr->ipv6.ip.pr_s6_addr; 1.1213 + memcpy(&dst.inet.ip, srcp + 12, 4); 1.1214 + dst.inet.family = AF_INET; 1.1215 + dst.inet.port = addr->ipv6.port; 1.1216 + } else { 1.1217 + memcpy(&dst, addr, sizeof(dst)); 1.1218 + } 1.1219 + 1.1220 + info->SetDestinationAddr(&dst); 1.1221 + info->SetConnectTimeout(to); 1.1222 + 1.1223 + do { 1.1224 + status = info->DoHandshake(fd, -1); 1.1225 + } while (status == PR_SUCCESS && !info->IsConnected()); 1.1226 + 1.1227 + return status; 1.1228 +} 1.1229 + 1.1230 +static PRStatus 1.1231 +nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, int16_t oflags) 1.1232 +{ 1.1233 + PRStatus status; 1.1234 + 1.1235 + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; 1.1236 + if (info == nullptr) return PR_FAILURE; 1.1237 + 1.1238 + do { 1.1239 + status = info->DoHandshake(fd, oflags); 1.1240 + } while (status == PR_SUCCESS && !info->IsConnected()); 1.1241 + 1.1242 + return status; 1.1243 +} 1.1244 + 1.1245 +static int16_t 1.1246 +nsSOCKSIOLayerPoll(PRFileDesc *fd, int16_t in_flags, int16_t *out_flags) 1.1247 +{ 1.1248 + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; 1.1249 + if (info == nullptr) return PR_FAILURE; 1.1250 + 1.1251 + if (!info->IsConnected()) { 1.1252 + *out_flags = 0; 1.1253 + return info->GetPollFlags(); 1.1254 + } 1.1255 + 1.1256 + return fd->lower->methods->poll(fd->lower, in_flags, out_flags); 1.1257 +} 1.1258 + 1.1259 +static PRStatus 1.1260 +nsSOCKSIOLayerClose(PRFileDesc *fd) 1.1261 +{ 1.1262 + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; 1.1263 + PRDescIdentity id = PR_GetLayersIdentity(fd); 1.1264 + 1.1265 + if (info && id == nsSOCKSIOLayerIdentity) 1.1266 + { 1.1267 + info->ForgetFD(); 1.1268 + NS_RELEASE(info); 1.1269 + fd->identity = PR_INVALID_IO_LAYER; 1.1270 + } 1.1271 + 1.1272 + return fd->lower->methods->close(fd->lower); 1.1273 +} 1.1274 + 1.1275 +static PRFileDesc* 1.1276 +nsSOCKSIOLayerAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) 1.1277 +{ 1.1278 + // TODO: implement SOCKS support for accept 1.1279 + return fd->lower->methods->accept(fd->lower, addr, timeout); 1.1280 +} 1.1281 + 1.1282 +static int32_t 1.1283 +nsSOCKSIOLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, int32_t amount, PRIntervalTime timeout) 1.1284 +{ 1.1285 + // TODO: implement SOCKS support for accept, then read from it 1.1286 + return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, timeout); 1.1287 +} 1.1288 + 1.1289 +static PRStatus 1.1290 +nsSOCKSIOLayerBind(PRFileDesc *fd, const PRNetAddr *addr) 1.1291 +{ 1.1292 + // TODO: implement SOCKS support for bind (very similar to connect) 1.1293 + return fd->lower->methods->bind(fd->lower, addr); 1.1294 +} 1.1295 + 1.1296 +static PRStatus 1.1297 +nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr) 1.1298 +{ 1.1299 + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; 1.1300 + 1.1301 + if (info != nullptr && addr != nullptr) { 1.1302 + NetAddr temp; 1.1303 + NetAddr *tempPtr = &temp; 1.1304 + if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) { 1.1305 + NetAddrToPRNetAddr(tempPtr, addr); 1.1306 + return PR_SUCCESS; 1.1307 + } 1.1308 + } 1.1309 + 1.1310 + return PR_FAILURE; 1.1311 +} 1.1312 + 1.1313 +static PRStatus 1.1314 +nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr) 1.1315 +{ 1.1316 + nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; 1.1317 + 1.1318 + if (info != nullptr && addr != nullptr) { 1.1319 + NetAddr temp; 1.1320 + NetAddr *tempPtr = &temp; 1.1321 + if (info->GetDestinationAddr(&tempPtr) == NS_OK) { 1.1322 + NetAddrToPRNetAddr(tempPtr, addr); 1.1323 + return PR_SUCCESS; 1.1324 + } 1.1325 + } 1.1326 + 1.1327 + return PR_FAILURE; 1.1328 +} 1.1329 + 1.1330 +static PRStatus 1.1331 +nsSOCKSIOLayerListen(PRFileDesc *fd, int backlog) 1.1332 +{ 1.1333 + // TODO: implement SOCKS support for listen 1.1334 + return fd->lower->methods->listen(fd->lower, backlog); 1.1335 +} 1.1336 + 1.1337 +// add SOCKS IO layer to an existing socket 1.1338 +nsresult 1.1339 +nsSOCKSIOLayerAddToSocket(int32_t family, 1.1340 + const char *host, 1.1341 + int32_t port, 1.1342 + nsIProxyInfo *proxy, 1.1343 + int32_t socksVersion, 1.1344 + uint32_t flags, 1.1345 + PRFileDesc *fd, 1.1346 + nsISupports** info) 1.1347 +{ 1.1348 + NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED); 1.1349 + 1.1350 + 1.1351 + if (firstTime) 1.1352 + { 1.1353 + //XXX hack until NSPR provides an official way to detect system IPv6 1.1354 + // support (bug 388519) 1.1355 + PRFileDesc *tmpfd = PR_OpenTCPSocket(PR_AF_INET6); 1.1356 + if (!tmpfd) { 1.1357 + ipv6Supported = false; 1.1358 + } else { 1.1359 + // If the system does not support IPv6, NSPR will push 1.1360 + // IPv6-to-IPv4 emulation layer onto the native layer 1.1361 + ipv6Supported = PR_GetIdentitiesLayer(tmpfd, PR_NSPR_IO_LAYER) == tmpfd; 1.1362 + PR_Close(tmpfd); 1.1363 + } 1.1364 + 1.1365 + nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer"); 1.1366 + nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods(); 1.1367 + 1.1368 + nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect; 1.1369 + nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue; 1.1370 + nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll; 1.1371 + nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind; 1.1372 + nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead; 1.1373 + nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName; 1.1374 + nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName; 1.1375 + nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept; 1.1376 + nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen; 1.1377 + nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose; 1.1378 + 1.1379 + firstTime = false; 1.1380 + 1.1381 +#if defined(PR_LOGGING) 1.1382 + gSOCKSLog = PR_NewLogModule("SOCKS"); 1.1383 +#endif 1.1384 + 1.1385 + } 1.1386 + 1.1387 + LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket().")); 1.1388 + 1.1389 + PRFileDesc *layer; 1.1390 + PRStatus rv; 1.1391 + 1.1392 + layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods); 1.1393 + if (! layer) 1.1394 + { 1.1395 + LOGERROR(("PR_CreateIOLayerStub() failed.")); 1.1396 + return NS_ERROR_FAILURE; 1.1397 + } 1.1398 + 1.1399 + nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo(); 1.1400 + if (!infoObject) 1.1401 + { 1.1402 + // clean up IOLayerStub 1.1403 + LOGERROR(("Failed to create nsSOCKSSocketInfo().")); 1.1404 + PR_DELETE(layer); 1.1405 + return NS_ERROR_FAILURE; 1.1406 + } 1.1407 + 1.1408 + NS_ADDREF(infoObject); 1.1409 + infoObject->Init(socksVersion, family, proxy, host, flags); 1.1410 + layer->secret = (PRFilePrivate*) infoObject; 1.1411 + rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer); 1.1412 + 1.1413 + if (rv == PR_FAILURE) { 1.1414 + LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv)); 1.1415 + NS_RELEASE(infoObject); 1.1416 + PR_DELETE(layer); 1.1417 + return NS_ERROR_FAILURE; 1.1418 + } 1.1419 + 1.1420 + *info = static_cast<nsISOCKSSocketInfo*>(infoObject); 1.1421 + NS_ADDREF(*info); 1.1422 + return NS_OK; 1.1423 +}