Bug 1733593 [wpt PR 31046] - Add a wpt for interaction id., a=testonly
[gecko.git] / netwerk / socket / nsSOCKSIOLayer.cpp
blob119a3cbf4c510a4b031381694ea5aaf65e007220
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nspr.h"
8 #include "private/pprio.h"
9 #include "nsString.h"
10 #include "nsCRT.h"
12 #include "nsIDNSService.h"
13 #include "nsIDNSRecord.h"
14 #include "nsISOCKSSocketInfo.h"
15 #include "nsISocketProvider.h"
16 #include "nsNamedPipeIOLayer.h"
17 #include "nsSOCKSIOLayer.h"
18 #include "nsNetCID.h"
19 #include "nsIDNSListener.h"
20 #include "nsICancelable.h"
21 #include "nsThreadUtils.h"
22 #include "nsIFile.h"
23 #include "nsIFileProtocolHandler.h"
24 #include "mozilla/Logging.h"
25 #include "mozilla/net/DNS.h"
26 #include "mozilla/Unused.h"
28 using mozilla::LogLevel;
29 using namespace mozilla::net;
31 static PRDescIdentity nsSOCKSIOLayerIdentity;
32 static PRIOMethods nsSOCKSIOLayerMethods;
33 static bool firstTime = true;
34 static bool ipv6Supported = true;
36 static mozilla::LazyLogModule gSOCKSLog("SOCKS");
37 #define LOGDEBUG(args) MOZ_LOG(gSOCKSLog, mozilla::LogLevel::Debug, args)
38 #define LOGERROR(args) MOZ_LOG(gSOCKSLog, mozilla::LogLevel::Error, args)
40 class nsSOCKSSocketInfo : public nsISOCKSSocketInfo, public nsIDNSListener {
41 enum State {
42 SOCKS_INITIAL,
43 SOCKS_DNS_IN_PROGRESS,
44 SOCKS_DNS_COMPLETE,
45 SOCKS_CONNECTING_TO_PROXY,
46 SOCKS4_WRITE_CONNECT_REQUEST,
47 SOCKS4_READ_CONNECT_RESPONSE,
48 SOCKS5_WRITE_AUTH_REQUEST,
49 SOCKS5_READ_AUTH_RESPONSE,
50 SOCKS5_WRITE_USERNAME_REQUEST,
51 SOCKS5_READ_USERNAME_RESPONSE,
52 SOCKS5_WRITE_CONNECT_REQUEST,
53 SOCKS5_READ_CONNECT_RESPONSE_TOP,
54 SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
55 SOCKS_CONNECTED,
56 SOCKS_FAILED
59 // A buffer of 520 bytes should be enough for any request and response
60 // in case of SOCKS4 as well as SOCKS5
61 static const uint32_t BUFFER_SIZE = 520;
62 static const uint32_t MAX_HOSTNAME_LEN = 255;
63 static const uint32_t MAX_USERNAME_LEN = 255;
64 static const uint32_t MAX_PASSWORD_LEN = 255;
66 public:
67 nsSOCKSSocketInfo();
69 NS_DECL_THREADSAFE_ISUPPORTS
70 NS_DECL_NSISOCKSSOCKETINFO
71 NS_DECL_NSIDNSLISTENER
73 void Init(int32_t version, int32_t family, nsIProxyInfo* proxy,
74 const char* destinationHost, uint32_t flags, uint32_t tlsFlags);
76 void SetConnectTimeout(PRIntervalTime to);
77 PRStatus DoHandshake(PRFileDesc* fd, int16_t oflags = -1);
78 int16_t GetPollFlags() const;
79 bool IsConnected() const { return mState == SOCKS_CONNECTED; }
80 void ForgetFD() { mFD = nullptr; }
81 void SetNamedPipeFD(PRFileDesc* fd) { mFD = fd; }
83 private:
84 virtual ~nsSOCKSSocketInfo() {
85 ForgetFD();
86 HandshakeFinished();
89 void HandshakeFinished(PRErrorCode err = 0);
90 PRStatus StartDNS(PRFileDesc* fd);
91 PRStatus ConnectToProxy(PRFileDesc* fd);
92 void FixupAddressFamily(PRFileDesc* fd, NetAddr* proxy);
93 PRStatus ContinueConnectingToProxy(PRFileDesc* fd, int16_t oflags);
94 PRStatus WriteV4ConnectRequest();
95 PRStatus ReadV4ConnectResponse();
96 PRStatus WriteV5AuthRequest();
97 PRStatus ReadV5AuthResponse();
98 PRStatus WriteV5UsernameRequest();
99 PRStatus ReadV5UsernameResponse();
100 PRStatus WriteV5ConnectRequest();
101 PRStatus ReadV5AddrTypeAndLength(uint8_t* type, uint32_t* len);
102 PRStatus ReadV5ConnectResponseTop();
103 PRStatus ReadV5ConnectResponseBottom();
105 uint8_t ReadUint8();
106 uint16_t ReadUint16();
107 uint32_t ReadUint32();
108 void ReadNetAddr(NetAddr* addr, uint16_t fam);
109 void ReadNetPort(NetAddr* addr);
111 void WantRead(uint32_t sz);
112 PRStatus ReadFromSocket(PRFileDesc* fd);
113 PRStatus WriteToSocket(PRFileDesc* fd);
115 bool IsLocalProxy() {
116 nsAutoCString proxyHost;
117 mProxy->GetHost(proxyHost);
118 return IsHostLocalTarget(proxyHost);
121 nsresult SetLocalProxyPath(const nsACString& aLocalProxyPath,
122 NetAddr* aProxyAddr) {
123 #ifdef XP_UNIX
124 nsresult rv;
125 MOZ_ASSERT(aProxyAddr);
127 nsCOMPtr<nsIProtocolHandler> protocolHandler(
128 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file", &rv));
129 if (NS_WARN_IF(NS_FAILED(rv))) {
130 return rv;
133 nsCOMPtr<nsIFileProtocolHandler> fileHandler(
134 do_QueryInterface(protocolHandler, &rv));
135 if (NS_WARN_IF(NS_FAILED(rv))) {
136 return rv;
139 nsCOMPtr<nsIFile> socketFile;
140 rv = fileHandler->GetFileFromURLSpec(aLocalProxyPath,
141 getter_AddRefs(socketFile));
142 if (NS_WARN_IF(NS_FAILED(rv))) {
143 return rv;
146 nsAutoCString path;
147 if (NS_WARN_IF(NS_FAILED(rv = socketFile->GetNativePath(path)))) {
148 return rv;
151 if (sizeof(aProxyAddr->local.path) <= path.Length()) {
152 NS_WARNING("domain socket path too long.");
153 return NS_ERROR_FAILURE;
156 aProxyAddr->raw.family = AF_UNIX;
157 strcpy(aProxyAddr->local.path, path.get());
159 return NS_OK;
160 #elif defined(XP_WIN)
161 MOZ_ASSERT(aProxyAddr);
163 if (sizeof(aProxyAddr->local.path) <= aLocalProxyPath.Length()) {
164 NS_WARNING("pipe path too long.");
165 return NS_ERROR_FAILURE;
168 aProxyAddr->raw.family = AF_LOCAL;
169 strcpy(aProxyAddr->local.path, PromiseFlatCString(aLocalProxyPath).get());
170 return NS_OK;
171 #else
172 mozilla::Unused << aLocalProxyPath;
173 mozilla::Unused << aProxyAddr;
174 return NS_ERROR_NOT_IMPLEMENTED;
175 #endif
178 bool SetupNamedPipeLayer(PRFileDesc* fd) {
179 #if defined(XP_WIN)
180 if (IsLocalProxy()) {
181 // nsSOCKSIOLayer handshaking only works under blocking mode
182 // unfortunately. Remember named pipe's FD to switch between modes.
183 SetNamedPipeFD(fd->lower);
184 return true;
186 #endif
187 return false;
190 private:
191 State mState{SOCKS_INITIAL};
192 uint8_t* mData{nullptr};
193 uint8_t* mDataIoPtr{nullptr};
194 uint32_t mDataLength{0};
195 uint32_t mReadOffset{0};
196 uint32_t mAmountToRead{0};
197 nsCOMPtr<nsIDNSRecord> mDnsRec;
198 nsCOMPtr<nsICancelable> mLookup;
199 nsresult mLookupStatus{NS_ERROR_NOT_INITIALIZED};
200 PRFileDesc* mFD{nullptr};
202 nsCString mDestinationHost;
203 nsCOMPtr<nsIProxyInfo> mProxy;
204 int32_t mVersion{-1}; // SOCKS version 4 or 5
205 int32_t mDestinationFamily{AF_INET};
206 uint32_t mFlags{0};
207 uint32_t mTlsFlags{0};
208 NetAddr mInternalProxyAddr;
209 NetAddr mExternalProxyAddr;
210 NetAddr mDestinationAddr;
211 PRIntervalTime mTimeout{PR_INTERVAL_NO_TIMEOUT};
212 nsCString mProxyUsername; // Cache, from mProxy
215 nsSOCKSSocketInfo::nsSOCKSSocketInfo() {
216 mData = new uint8_t[BUFFER_SIZE];
218 mInternalProxyAddr.raw.family = AF_INET;
219 mInternalProxyAddr.inet.ip = htonl(INADDR_ANY);
220 mInternalProxyAddr.inet.port = htons(0);
222 mExternalProxyAddr.raw.family = AF_INET;
223 mExternalProxyAddr.inet.ip = htonl(INADDR_ANY);
224 mExternalProxyAddr.inet.port = htons(0);
226 mDestinationAddr.raw.family = AF_INET;
227 mDestinationAddr.inet.ip = htonl(INADDR_ANY);
228 mDestinationAddr.inet.port = htons(0);
231 /* Helper template class to statically check that writes to a fixed-size
232 * buffer are not going to overflow.
234 * Example usage:
235 * uint8_t real_buf[TOTAL_SIZE];
236 * Buffer<TOTAL_SIZE> buf(&real_buf);
237 * auto buf2 = buf.WriteUint16(1);
238 * auto buf3 = buf2.WriteUint8(2);
240 * It is possible to chain them, to limit the number of (error-prone)
241 * intermediate variables:
242 * auto buf = Buffer<TOTAL_SIZE>(&real_buf)
243 * .WriteUint16(1)
244 * .WriteUint8(2);
246 * Debug builds assert when intermediate variables are reused:
247 * Buffer<TOTAL_SIZE> buf(&real_buf);
248 * auto buf2 = buf.WriteUint16(1);
249 * auto buf3 = buf.WriteUint8(2); // Asserts
251 * Strings can be written, given an explicit maximum length.
252 * buf.WriteString<MAX_STRING_LENGTH>(str);
254 * The Written() method returns how many bytes have been written so far:
255 * Buffer<TOTAL_SIZE> buf(&real_buf);
256 * auto buf2 = buf.WriteUint16(1);
257 * auto buf3 = buf2.WriteUint8(2);
258 * buf3.Written(); // returns 3.
260 template <size_t Size>
261 class Buffer {
262 public:
263 Buffer() = default;
265 explicit Buffer(uint8_t* aBuf, size_t aLength = 0)
266 : mBuf(aBuf), mLength(aLength) {}
268 template <size_t Size2>
269 MOZ_IMPLICIT Buffer(const Buffer<Size2>& aBuf)
270 : mBuf(aBuf.mBuf), mLength(aBuf.mLength) {
271 static_assert(Size2 > Size, "Cannot cast buffer");
274 Buffer<Size - sizeof(uint8_t)> WriteUint8(uint8_t aValue) {
275 return Write(aValue);
278 Buffer<Size - sizeof(uint16_t)> WriteUint16(uint16_t aValue) {
279 return Write(aValue);
282 Buffer<Size - sizeof(uint32_t)> WriteUint32(uint32_t aValue) {
283 return Write(aValue);
286 Buffer<Size - sizeof(uint16_t)> WriteNetPort(const NetAddr* aAddr) {
287 return WriteUint16(aAddr->inet.port);
290 Buffer<Size - sizeof(IPv6Addr)> WriteNetAddr(const NetAddr* aAddr) {
291 if (aAddr->raw.family == AF_INET) {
292 return Write(aAddr->inet.ip);
294 if (aAddr->raw.family == AF_INET6) {
295 return Write(aAddr->inet6.ip.u8);
297 MOZ_ASSERT_UNREACHABLE("Unknown address family");
298 return *this;
301 template <size_t MaxLength>
302 Buffer<Size - MaxLength> WriteString(const nsACString& aStr) {
303 if (aStr.Length() > MaxLength) {
304 return Buffer<Size - MaxLength>(nullptr);
306 return WritePtr<char, MaxLength>(aStr.Data(), aStr.Length());
309 size_t Written() {
310 MOZ_ASSERT(mBuf);
311 return mLength;
314 explicit operator bool() { return !!mBuf; }
316 private:
317 template <size_t Size2>
318 friend class Buffer;
320 template <typename T>
321 Buffer<Size - sizeof(T)> Write(T& aValue) {
322 return WritePtr<T, sizeof(T)>(&aValue, sizeof(T));
325 template <typename T, size_t Length>
326 Buffer<Size - Length> WritePtr(const T* aValue, size_t aCopyLength) {
327 static_assert(Size >= Length, "Cannot write that much");
328 MOZ_ASSERT(aCopyLength <= Length);
329 MOZ_ASSERT(mBuf);
330 memcpy(mBuf, aValue, aCopyLength);
331 Buffer<Size - Length> result(mBuf + aCopyLength, mLength + aCopyLength);
332 mBuf = nullptr;
333 mLength = 0;
334 return result;
337 uint8_t* mBuf{nullptr};
338 size_t mLength{0};
341 void nsSOCKSSocketInfo::Init(int32_t version, int32_t family,
342 nsIProxyInfo* proxy, const char* host,
343 uint32_t flags, uint32_t tlsFlags) {
344 mVersion = version;
345 mDestinationFamily = family;
346 mProxy = proxy;
347 mDestinationHost = host;
348 mFlags = flags;
349 mTlsFlags = tlsFlags;
350 mProxy->GetUsername(mProxyUsername); // cache
353 NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
355 NS_IMETHODIMP
356 nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr** aExternalProxyAddr) {
357 memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr));
358 return NS_OK;
361 NS_IMETHODIMP
362 nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr* aExternalProxyAddr) {
363 memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr));
364 return NS_OK;
367 NS_IMETHODIMP
368 nsSOCKSSocketInfo::GetDestinationAddr(NetAddr** aDestinationAddr) {
369 memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr));
370 return NS_OK;
373 NS_IMETHODIMP
374 nsSOCKSSocketInfo::SetDestinationAddr(NetAddr* aDestinationAddr) {
375 memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr));
376 return NS_OK;
379 NS_IMETHODIMP
380 nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr** aInternalProxyAddr) {
381 memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr));
382 return NS_OK;
385 NS_IMETHODIMP
386 nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr* aInternalProxyAddr) {
387 memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr));
388 return NS_OK;
391 // There needs to be a means of distinguishing between connection errors
392 // that the SOCKS server reports when it rejects a connection request, and
393 // connection errors that happen while attempting to connect to the SOCKS
394 // server. Otherwise, Firefox will report incorrectly that the proxy server
395 // is refusing connections when a SOCKS request is rejected by the proxy.
396 // When a SOCKS handshake failure occurs, the PR error is set to
397 // PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
398 void nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err) {
399 if (err == 0) {
400 mState = SOCKS_CONNECTED;
401 #if defined(XP_WIN)
402 // Switch back to nonblocking mode after finishing handshaking.
403 if (IsLocalProxy() && mFD) {
404 PRSocketOptionData opt_nonblock;
405 opt_nonblock.option = PR_SockOpt_Nonblocking;
406 opt_nonblock.value.non_blocking = PR_TRUE;
407 PR_SetSocketOption(mFD, &opt_nonblock);
408 mFD = nullptr;
410 #endif
411 } else {
412 mState = SOCKS_FAILED;
413 PR_SetError(PR_UNKNOWN_ERROR, err);
416 // We don't need the buffer any longer, so free it.
417 delete[] mData;
418 mData = nullptr;
419 mDataIoPtr = nullptr;
420 mDataLength = 0;
421 mReadOffset = 0;
422 mAmountToRead = 0;
423 if (mLookup) {
424 mLookup->Cancel(NS_ERROR_FAILURE);
425 mLookup = nullptr;
429 PRStatus nsSOCKSSocketInfo::StartDNS(PRFileDesc* fd) {
430 MOZ_ASSERT(!mDnsRec && mState == SOCKS_INITIAL,
431 "Must be in initial state to make DNS Lookup");
433 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
434 if (!dns) return PR_FAILURE;
436 nsCString proxyHost;
437 mProxy->GetHost(proxyHost);
439 mozilla::OriginAttributes attrs;
441 mFD = fd;
442 nsresult rv = dns->AsyncResolveNative(
443 proxyHost, nsIDNSService::RESOLVE_TYPE_DEFAULT,
444 nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS, nullptr, this,
445 mozilla::GetCurrentEventTarget(), attrs, getter_AddRefs(mLookup));
447 if (NS_FAILED(rv)) {
448 LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed", proxyHost.get()));
449 return PR_FAILURE;
451 mState = SOCKS_DNS_IN_PROGRESS;
452 PR_SetError(PR_IN_PROGRESS_ERROR, 0);
453 return PR_FAILURE;
456 NS_IMETHODIMP
457 nsSOCKSSocketInfo::OnLookupComplete(nsICancelable* aRequest,
458 nsIDNSRecord* aRecord, nsresult aStatus) {
459 MOZ_ASSERT(aRequest == mLookup, "wrong DNS query");
460 mLookup = nullptr;
461 mLookupStatus = aStatus;
462 mDnsRec = aRecord;
463 mState = SOCKS_DNS_COMPLETE;
464 if (mFD) {
465 ConnectToProxy(mFD);
466 ForgetFD();
468 return NS_OK;
471 PRStatus nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc* fd) {
472 PRStatus status;
473 nsresult rv;
475 MOZ_ASSERT(mState == SOCKS_DNS_COMPLETE, "Must have DNS to make connection!");
477 if (NS_FAILED(mLookupStatus)) {
478 PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
479 return PR_FAILURE;
482 // Try socks5 if the destination addrress is IPv6
483 if (mVersion == 4 && mDestinationAddr.raw.family == AF_INET6) {
484 mVersion = 5;
487 nsAutoCString proxyHost;
488 mProxy->GetHost(proxyHost);
490 int32_t proxyPort;
491 mProxy->GetPort(&proxyPort);
493 int32_t addresses = 0;
494 do {
495 if (IsLocalProxy()) {
496 rv = SetLocalProxyPath(proxyHost, &mInternalProxyAddr);
497 if (NS_FAILED(rv)) {
498 LOGERROR(
499 ("socks: unable to connect to SOCKS proxy, %s", proxyHost.get()));
500 return PR_FAILURE;
502 } else {
503 nsCOMPtr<nsIDNSAddrRecord> record = do_QueryInterface(mDnsRec);
504 MOZ_ASSERT(record);
505 if (addresses++) {
506 record->ReportUnusable(proxyPort);
509 rv = record->GetNextAddr(proxyPort, &mInternalProxyAddr);
510 // No more addresses to try? If so, we'll need to bail
511 if (NS_FAILED(rv)) {
512 LOGERROR(
513 ("socks: unable to connect to SOCKS proxy, %s", proxyHost.get()));
514 return PR_FAILURE;
517 if (MOZ_LOG_TEST(gSOCKSLog, LogLevel::Debug)) {
518 char buf[kIPv6CStrBufSize];
519 mInternalProxyAddr.ToStringBuffer(buf, sizeof(buf));
520 LOGDEBUG(("socks: trying proxy server, %s:%hu", buf,
521 ntohs(mInternalProxyAddr.inet.port)));
525 NetAddr proxy = mInternalProxyAddr;
526 FixupAddressFamily(fd, &proxy);
527 PRNetAddr prProxy;
528 NetAddrToPRNetAddr(&proxy, &prProxy);
529 status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout);
530 if (status != PR_SUCCESS) {
531 PRErrorCode c = PR_GetError();
533 // If EINPROGRESS, return now and check back later after polling
534 if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
535 mState = SOCKS_CONNECTING_TO_PROXY;
536 return status;
538 if (IsLocalProxy()) {
539 LOGERROR(("socks: connect to domain socket failed (%d)", c));
540 PR_SetError(PR_CONNECT_REFUSED_ERROR, 0);
541 mState = SOCKS_FAILED;
542 return status;
545 } while (status != PR_SUCCESS);
547 #if defined(XP_WIN)
548 // Switch to blocking mode during handshaking
549 if (IsLocalProxy() && mFD) {
550 PRSocketOptionData opt_nonblock;
551 opt_nonblock.option = PR_SockOpt_Nonblocking;
552 opt_nonblock.value.non_blocking = PR_FALSE;
553 PR_SetSocketOption(mFD, &opt_nonblock);
555 #endif
557 // Connected now, start SOCKS
558 if (mVersion == 4) return WriteV4ConnectRequest();
559 return WriteV5AuthRequest();
562 void nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc* fd, NetAddr* proxy) {
563 int32_t proxyFamily = mInternalProxyAddr.raw.family;
564 // Do nothing if the address family is already matched
565 if (proxyFamily == mDestinationFamily) {
566 return;
568 // If the system does not support IPv6 and the proxy address is IPv6,
569 // We can do nothing here.
570 if (proxyFamily == AF_INET6 && !ipv6Supported) {
571 return;
573 // If the system does not support IPv6 and the destination address is
574 // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy
575 // the emulation layer
576 if (mDestinationFamily == AF_INET6 && !ipv6Supported) {
577 proxy->inet6.family = AF_INET6;
578 proxy->inet6.port = mInternalProxyAddr.inet.port;
579 uint8_t* proxyp = proxy->inet6.ip.u8;
580 memset(proxyp, 0, 10);
581 memset(proxyp + 10, 0xff, 2);
582 memcpy(proxyp + 12, (char*)&mInternalProxyAddr.inet.ip, 4);
583 // mDestinationFamily should not be updated
584 return;
586 // There's no PR_NSPR_IO_LAYER required when using named pipe,
587 // we simply ignore the TCP family here.
588 if (SetupNamedPipeLayer(fd)) {
589 return;
592 // Get an OS native handle from a specified FileDesc
593 PROsfd osfd = PR_FileDesc2NativeHandle(fd);
594 if (osfd == -1) {
595 return;
598 // Create a new FileDesc with a specified family
599 PRFileDesc* tmpfd = PR_OpenTCPSocket(proxyFamily);
600 if (!tmpfd) {
601 return;
603 PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd);
604 if (newsd == -1) {
605 PR_Close(tmpfd);
606 return;
608 // Must succeed because PR_FileDesc2NativeHandle succeeded
609 fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
610 MOZ_ASSERT(fd);
611 // Swap OS native handles
612 PR_ChangeFileDescNativeHandle(fd, newsd);
613 PR_ChangeFileDescNativeHandle(tmpfd, osfd);
614 // Close temporary FileDesc which is now associated with
615 // old OS native handle
616 PR_Close(tmpfd);
617 mDestinationFamily = proxyFamily;
620 PRStatus nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc* fd,
621 int16_t oflags) {
622 PRStatus status;
624 MOZ_ASSERT(mState == SOCKS_CONNECTING_TO_PROXY,
625 "Continuing connection in wrong state!");
627 LOGDEBUG(("socks: continuing connection to proxy"));
629 status = fd->lower->methods->connectcontinue(fd->lower, oflags);
630 if (status != PR_SUCCESS) {
631 PRErrorCode c = PR_GetError();
632 if (c != PR_WOULD_BLOCK_ERROR && c != PR_IN_PROGRESS_ERROR) {
633 // A connection failure occured, try another address
634 mState = SOCKS_DNS_COMPLETE;
635 return ConnectToProxy(fd);
638 // We're still connecting
639 return PR_FAILURE;
642 // Connected now, start SOCKS
643 if (mVersion == 4) return WriteV4ConnectRequest();
644 return WriteV5AuthRequest();
647 PRStatus nsSOCKSSocketInfo::WriteV4ConnectRequest() {
648 if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
649 LOGERROR(("socks username is too long"));
650 HandshakeFinished(PR_UNKNOWN_ERROR);
651 return PR_FAILURE;
654 NetAddr* addr = &mDestinationAddr;
655 int32_t proxy_resolve;
657 MOZ_ASSERT(mState == SOCKS_CONNECTING_TO_PROXY, "Invalid state!");
659 proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
661 mDataLength = 0;
662 mState = SOCKS4_WRITE_CONNECT_REQUEST;
664 LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
665 proxy_resolve ? "yes" : "no"));
667 // Send a SOCKS 4 connect request.
668 auto buf = Buffer<BUFFER_SIZE>(mData)
669 .WriteUint8(0x04) // version -- 4
670 .WriteUint8(0x01) // command -- connect
671 .WriteNetPort(addr);
673 // We don't have anything more to write after the if, so we can
674 // use a buffer with no further writes allowed.
675 Buffer<0> buf3;
676 if (proxy_resolve) {
677 // Add the full name, null-terminated, to the request
678 // according to SOCKS 4a. A fake IP address, with the first
679 // four bytes set to 0 and the last byte set to something other
680 // than 0, is used to notify the proxy that this is a SOCKS 4a
681 // request. This request type works for Tor and perhaps others.
682 // Passwords not supported by V4.
683 auto buf2 =
684 buf.WriteUint32(htonl(0x00000001)) // Fake IP
685 .WriteString<MAX_USERNAME_LEN>(mProxyUsername)
686 .WriteUint8(0x00) // Null-terminate username
687 .WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
688 if (!buf2) {
689 LOGERROR(("socks4: destination host name is too long!"));
690 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
691 return PR_FAILURE;
693 buf3 = buf2.WriteUint8(0x00);
694 } else if (addr->raw.family == AF_INET) {
695 // Passwords not supported by V4.
696 buf3 = buf.WriteNetAddr(addr) // Add the IPv4 address
697 .WriteString<MAX_USERNAME_LEN>(mProxyUsername)
698 .WriteUint8(0x00); // Null-terminate username
699 } else {
700 LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
701 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
702 return PR_FAILURE;
705 mDataLength = buf3.Written();
706 return PR_SUCCESS;
709 PRStatus nsSOCKSSocketInfo::ReadV4ConnectResponse() {
710 MOZ_ASSERT(mState == SOCKS4_READ_CONNECT_RESPONSE,
711 "Handling SOCKS 4 connection reply in wrong state!");
712 MOZ_ASSERT(mDataLength == 8, "SOCKS 4 connection reply must be 8 bytes!");
714 LOGDEBUG(("socks4: checking connection reply"));
716 if (ReadUint8() != 0x00) {
717 LOGERROR(("socks4: wrong connection reply"));
718 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
719 return PR_FAILURE;
722 // See if our connection request was granted
723 if (ReadUint8() == 90) {
724 LOGDEBUG(("socks4: connection successful!"));
725 HandshakeFinished();
726 return PR_SUCCESS;
729 LOGERROR(("socks4: unable to connect"));
730 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
731 return PR_FAILURE;
734 PRStatus nsSOCKSSocketInfo::WriteV5AuthRequest() {
735 MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
737 mDataLength = 0;
738 mState = SOCKS5_WRITE_AUTH_REQUEST;
740 // Send an initial SOCKS 5 greeting
741 LOGDEBUG(("socks5: sending auth methods"));
742 mDataLength = Buffer<BUFFER_SIZE>(mData)
743 .WriteUint8(0x05) // version -- 5
744 .WriteUint8(0x01) // # of auth methods -- 1
745 // Use authenticate iff we have a proxy username.
746 .WriteUint8(mProxyUsername.IsEmpty() ? 0x00 : 0x02)
747 .Written();
749 return PR_SUCCESS;
752 PRStatus nsSOCKSSocketInfo::ReadV5AuthResponse() {
753 MOZ_ASSERT(mState == SOCKS5_READ_AUTH_RESPONSE,
754 "Handling SOCKS 5 auth method reply in wrong state!");
755 MOZ_ASSERT(mDataLength == 2, "SOCKS 5 auth method reply must be 2 bytes!");
757 LOGDEBUG(("socks5: checking auth method reply"));
759 // Check version number
760 if (ReadUint8() != 0x05) {
761 LOGERROR(("socks5: unexpected version in the reply"));
762 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
763 return PR_FAILURE;
766 // Make sure our authentication choice was accepted,
767 // and continue accordingly
768 uint8_t authMethod = ReadUint8();
769 if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth
770 LOGDEBUG(("socks5: server allows connection without authentication"));
771 return WriteV5ConnectRequest();
773 if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw
774 LOGDEBUG(("socks5: auth method accepted by server"));
775 return WriteV5UsernameRequest();
776 } // 0xFF signals error
777 LOGERROR(("socks5: server did not accept our authentication method"));
778 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
779 return PR_FAILURE;
782 PRStatus nsSOCKSSocketInfo::WriteV5UsernameRequest() {
783 MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
785 if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
786 LOGERROR(("socks username is too long"));
787 HandshakeFinished(PR_UNKNOWN_ERROR);
788 return PR_FAILURE;
791 nsCString password;
792 mProxy->GetPassword(password);
793 if (password.Length() > MAX_PASSWORD_LEN) {
794 LOGERROR(("socks password is too long"));
795 HandshakeFinished(PR_UNKNOWN_ERROR);
796 return PR_FAILURE;
799 mDataLength = 0;
800 mState = SOCKS5_WRITE_USERNAME_REQUEST;
802 // RFC 1929 Username/password auth for SOCKS 5
803 LOGDEBUG(("socks5: sending username and password"));
804 mDataLength = Buffer<BUFFER_SIZE>(mData)
805 .WriteUint8(0x01) // version 1 (not 5)
806 .WriteUint8(mProxyUsername.Length()) // username length
807 .WriteString<MAX_USERNAME_LEN>(mProxyUsername) // username
808 .WriteUint8(password.Length()) // password length
809 .WriteString<MAX_PASSWORD_LEN>(
810 password) // password. WARNING: Sent unencrypted!
811 .Written();
813 return PR_SUCCESS;
816 PRStatus nsSOCKSSocketInfo::ReadV5UsernameResponse() {
817 MOZ_ASSERT(mState == SOCKS5_READ_USERNAME_RESPONSE,
818 "Handling SOCKS 5 username/password reply in wrong state!");
820 MOZ_ASSERT(mDataLength == 2, "SOCKS 5 username reply must be 2 bytes");
822 // Check version number, must be 1 (not 5)
823 if (ReadUint8() != 0x01) {
824 LOGERROR(("socks5: unexpected version in the reply"));
825 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
826 return PR_FAILURE;
829 // Check whether username/password were accepted
830 if (ReadUint8() != 0x00) { // 0 = success
831 LOGERROR(("socks5: username/password not accepted"));
832 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
833 return PR_FAILURE;
836 LOGDEBUG(("socks5: username/password accepted by server"));
838 return WriteV5ConnectRequest();
841 PRStatus nsSOCKSSocketInfo::WriteV5ConnectRequest() {
842 // Send SOCKS 5 connect request
843 NetAddr* addr = &mDestinationAddr;
844 int32_t proxy_resolve;
845 proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
847 LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
848 proxy_resolve ? "yes" : "no"));
850 mDataLength = 0;
851 mState = SOCKS5_WRITE_CONNECT_REQUEST;
853 auto buf = Buffer<BUFFER_SIZE>(mData)
854 .WriteUint8(0x05) // version -- 5
855 .WriteUint8(0x01) // command -- connect
856 .WriteUint8(0x00); // reserved
858 // We're writing a net port after the if, so we need a buffer allowing
859 // to write that much.
860 Buffer<sizeof(uint16_t)> buf2;
861 // Add the address to the SOCKS 5 request. SOCKS 5 supports several
862 // address types, so we pick the one that works best for us.
863 if (proxy_resolve) {
864 // Add the host name. Only a single byte is used to store the length,
865 // so we must prevent long names from being used.
866 buf2 = buf.WriteUint8(0x03) // addr type -- domainname
867 .WriteUint8(mDestinationHost.Length()) // name length
868 .WriteString<MAX_HOSTNAME_LEN>(mDestinationHost); // Hostname
869 if (!buf2) {
870 LOGERROR(("socks5: destination host name is too long!"));
871 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
872 return PR_FAILURE;
874 } else if (addr->raw.family == AF_INET) {
875 buf2 = buf.WriteUint8(0x01) // addr type -- IPv4
876 .WriteNetAddr(addr);
877 } else if (addr->raw.family == AF_INET6) {
878 buf2 = buf.WriteUint8(0x04) // addr type -- IPv6
879 .WriteNetAddr(addr);
880 } else {
881 LOGERROR(("socks5: destination address of unknown type!"));
882 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
883 return PR_FAILURE;
886 auto buf3 = buf2.WriteNetPort(addr); // port
887 mDataLength = buf3.Written();
889 return PR_SUCCESS;
892 PRStatus nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t* type,
893 uint32_t* len) {
894 MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
895 mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
896 "Invalid state!");
897 MOZ_ASSERT(mDataLength >= 5,
898 "SOCKS 5 connection reply must be at least 5 bytes!");
900 // Seek to the address location
901 mReadOffset = 3;
903 *type = ReadUint8();
905 switch (*type) {
906 case 0x01: // ipv4
907 *len = 4 - 1;
908 break;
909 case 0x04: // ipv6
910 *len = 16 - 1;
911 break;
912 case 0x03: // fqdn
913 *len = ReadUint8();
914 break;
915 default: // wrong address type
916 LOGERROR(("socks5: wrong address type in connection reply!"));
917 return PR_FAILURE;
920 return PR_SUCCESS;
923 PRStatus nsSOCKSSocketInfo::ReadV5ConnectResponseTop() {
924 uint8_t res;
925 uint32_t len;
927 MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP, "Invalid state!");
928 MOZ_ASSERT(mDataLength == 5,
929 "SOCKS 5 connection reply must be exactly 5 bytes!");
931 LOGDEBUG(("socks5: checking connection reply"));
933 // Check version number
934 if (ReadUint8() != 0x05) {
935 LOGERROR(("socks5: unexpected version in the reply"));
936 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
937 return PR_FAILURE;
940 // Check response
941 res = ReadUint8();
942 if (res != 0x00) {
943 PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
945 switch (res) {
946 case 0x01:
947 LOGERROR(
948 ("socks5: connect failed: "
949 "01, General SOCKS server failure."));
950 break;
951 case 0x02:
952 LOGERROR(
953 ("socks5: connect failed: "
954 "02, Connection not allowed by ruleset."));
955 break;
956 case 0x03:
957 LOGERROR(("socks5: connect failed: 03, Network unreachable."));
958 c = PR_NETWORK_UNREACHABLE_ERROR;
959 break;
960 case 0x04:
961 LOGERROR(("socks5: connect failed: 04, Host unreachable."));
962 c = PR_BAD_ADDRESS_ERROR;
963 break;
964 case 0x05:
965 LOGERROR(("socks5: connect failed: 05, Connection refused."));
966 break;
967 case 0x06:
968 LOGERROR(("socks5: connect failed: 06, TTL expired."));
969 c = PR_CONNECT_TIMEOUT_ERROR;
970 break;
971 case 0x07:
972 LOGERROR(
973 ("socks5: connect failed: "
974 "07, Command not supported."));
975 break;
976 case 0x08:
977 LOGERROR(
978 ("socks5: connect failed: "
979 "08, Address type not supported."));
980 c = PR_BAD_ADDRESS_ERROR;
981 break;
982 default:
983 LOGERROR(("socks5: connect failed."));
984 break;
987 HandshakeFinished(c);
988 return PR_FAILURE;
991 if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
992 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
993 return PR_FAILURE;
996 mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
997 WantRead(len + 2);
999 return PR_SUCCESS;
1002 PRStatus nsSOCKSSocketInfo::ReadV5ConnectResponseBottom() {
1003 uint8_t type;
1004 uint32_t len;
1006 MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM, "Invalid state!");
1008 if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
1009 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
1010 return PR_FAILURE;
1013 MOZ_ASSERT(mDataLength == 7 + len,
1014 "SOCKS 5 unexpected length of connection reply!");
1016 LOGDEBUG(("socks5: loading source addr and port"));
1017 // Read what the proxy says is our source address
1018 switch (type) {
1019 case 0x01: // ipv4
1020 ReadNetAddr(&mExternalProxyAddr, AF_INET);
1021 break;
1022 case 0x04: // ipv6
1023 ReadNetAddr(&mExternalProxyAddr, AF_INET6);
1024 break;
1025 case 0x03: // fqdn (skip)
1026 mReadOffset += len;
1027 mExternalProxyAddr.raw.family = AF_INET;
1028 break;
1031 ReadNetPort(&mExternalProxyAddr);
1033 LOGDEBUG(("socks5: connected!"));
1034 HandshakeFinished();
1036 return PR_SUCCESS;
1039 void nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to) { mTimeout = to; }
1041 PRStatus nsSOCKSSocketInfo::DoHandshake(PRFileDesc* fd, int16_t oflags) {
1042 LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
1044 switch (mState) {
1045 case SOCKS_INITIAL:
1046 if (IsLocalProxy()) {
1047 mState = SOCKS_DNS_COMPLETE;
1048 mLookupStatus = NS_OK;
1049 return ConnectToProxy(fd);
1052 return StartDNS(fd);
1053 case SOCKS_DNS_IN_PROGRESS:
1054 PR_SetError(PR_IN_PROGRESS_ERROR, 0);
1055 return PR_FAILURE;
1056 case SOCKS_DNS_COMPLETE:
1057 return ConnectToProxy(fd);
1058 case SOCKS_CONNECTING_TO_PROXY:
1059 return ContinueConnectingToProxy(fd, oflags);
1060 case SOCKS4_WRITE_CONNECT_REQUEST:
1061 if (WriteToSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1062 WantRead(8);
1063 mState = SOCKS4_READ_CONNECT_RESPONSE;
1064 return PR_SUCCESS;
1065 case SOCKS4_READ_CONNECT_RESPONSE:
1066 if (ReadFromSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1067 return ReadV4ConnectResponse();
1069 case SOCKS5_WRITE_AUTH_REQUEST:
1070 if (WriteToSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1071 WantRead(2);
1072 mState = SOCKS5_READ_AUTH_RESPONSE;
1073 return PR_SUCCESS;
1074 case SOCKS5_READ_AUTH_RESPONSE:
1075 if (ReadFromSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1076 return ReadV5AuthResponse();
1077 case SOCKS5_WRITE_USERNAME_REQUEST:
1078 if (WriteToSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1079 WantRead(2);
1080 mState = SOCKS5_READ_USERNAME_RESPONSE;
1081 return PR_SUCCESS;
1082 case SOCKS5_READ_USERNAME_RESPONSE:
1083 if (ReadFromSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1084 return ReadV5UsernameResponse();
1085 case SOCKS5_WRITE_CONNECT_REQUEST:
1086 if (WriteToSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1088 // The SOCKS 5 response to the connection request is variable
1089 // length. First, we'll read enough to tell how long the response
1090 // is, and will read the rest later.
1091 WantRead(5);
1092 mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
1093 return PR_SUCCESS;
1094 case SOCKS5_READ_CONNECT_RESPONSE_TOP:
1095 if (ReadFromSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1096 return ReadV5ConnectResponseTop();
1097 case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
1098 if (ReadFromSocket(fd) != PR_SUCCESS) return PR_FAILURE;
1099 return ReadV5ConnectResponseBottom();
1101 case SOCKS_CONNECTED:
1102 LOGERROR(("socks: already connected"));
1103 HandshakeFinished(PR_IS_CONNECTED_ERROR);
1104 return PR_FAILURE;
1105 case SOCKS_FAILED:
1106 LOGERROR(("socks: already failed"));
1107 return PR_FAILURE;
1110 LOGERROR(("socks: executing handshake in invalid state, %d", mState));
1111 HandshakeFinished(PR_INVALID_STATE_ERROR);
1113 return PR_FAILURE;
1116 int16_t nsSOCKSSocketInfo::GetPollFlags() const {
1117 switch (mState) {
1118 case SOCKS_DNS_IN_PROGRESS:
1119 case SOCKS_DNS_COMPLETE:
1120 case SOCKS_CONNECTING_TO_PROXY:
1121 return PR_POLL_EXCEPT | PR_POLL_WRITE;
1122 case SOCKS4_WRITE_CONNECT_REQUEST:
1123 case SOCKS5_WRITE_AUTH_REQUEST:
1124 case SOCKS5_WRITE_USERNAME_REQUEST:
1125 case SOCKS5_WRITE_CONNECT_REQUEST:
1126 return PR_POLL_WRITE;
1127 case SOCKS4_READ_CONNECT_RESPONSE:
1128 case SOCKS5_READ_AUTH_RESPONSE:
1129 case SOCKS5_READ_USERNAME_RESPONSE:
1130 case SOCKS5_READ_CONNECT_RESPONSE_TOP:
1131 case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
1132 return PR_POLL_READ;
1133 default:
1134 break;
1137 return 0;
1140 inline uint8_t nsSOCKSSocketInfo::ReadUint8() {
1141 uint8_t rv;
1142 MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1143 "Not enough space to pop a uint8_t!");
1144 rv = mData[mReadOffset];
1145 mReadOffset += sizeof(rv);
1146 return rv;
1149 inline uint16_t nsSOCKSSocketInfo::ReadUint16() {
1150 uint16_t rv;
1151 MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1152 "Not enough space to pop a uint16_t!");
1153 memcpy(&rv, mData + mReadOffset, sizeof(rv));
1154 mReadOffset += sizeof(rv);
1155 return rv;
1158 inline uint32_t nsSOCKSSocketInfo::ReadUint32() {
1159 uint32_t rv;
1160 MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1161 "Not enough space to pop a uint32_t!");
1162 memcpy(&rv, mData + mReadOffset, sizeof(rv));
1163 mReadOffset += sizeof(rv);
1164 return rv;
1167 void nsSOCKSSocketInfo::ReadNetAddr(NetAddr* addr, uint16_t fam) {
1168 uint32_t amt = 0;
1169 const uint8_t* ip = mData + mReadOffset;
1171 addr->raw.family = fam;
1172 if (fam == AF_INET) {
1173 amt = sizeof(addr->inet.ip);
1174 MOZ_ASSERT(mReadOffset + amt <= mDataLength,
1175 "Not enough space to pop an ipv4 addr!");
1176 memcpy(&addr->inet.ip, ip, amt);
1177 } else if (fam == AF_INET6) {
1178 amt = sizeof(addr->inet6.ip.u8);
1179 MOZ_ASSERT(mReadOffset + amt <= mDataLength,
1180 "Not enough space to pop an ipv6 addr!");
1181 memcpy(addr->inet6.ip.u8, ip, amt);
1184 mReadOffset += amt;
1187 void nsSOCKSSocketInfo::ReadNetPort(NetAddr* addr) {
1188 addr->inet.port = ReadUint16();
1191 void nsSOCKSSocketInfo::WantRead(uint32_t sz) {
1192 MOZ_ASSERT(mDataIoPtr == nullptr,
1193 "WantRead() called while I/O already in progress!");
1194 MOZ_ASSERT(mDataLength + sz <= BUFFER_SIZE, "Can't read that much data!");
1195 mAmountToRead = sz;
1198 PRStatus nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc* fd) {
1199 int32_t rc;
1200 const uint8_t* end;
1202 if (!mAmountToRead) {
1203 LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
1204 return PR_SUCCESS;
1207 if (!mDataIoPtr) {
1208 mDataIoPtr = mData + mDataLength;
1209 mDataLength += mAmountToRead;
1212 end = mData + mDataLength;
1214 while (mDataIoPtr < end) {
1215 rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
1216 if (rc <= 0) {
1217 if (rc == 0) {
1218 LOGERROR(("socks: proxy server closed connection"));
1219 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
1220 return PR_FAILURE;
1222 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
1223 LOGDEBUG(("socks: ReadFromSocket(), want read"));
1225 break;
1228 mDataIoPtr += rc;
1231 LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
1232 unsigned(mDataIoPtr - mData)));
1233 if (mDataIoPtr == end) {
1234 mDataIoPtr = nullptr;
1235 mAmountToRead = 0;
1236 mReadOffset = 0;
1237 return PR_SUCCESS;
1240 return PR_FAILURE;
1243 PRStatus nsSOCKSSocketInfo::WriteToSocket(PRFileDesc* fd) {
1244 int32_t rc;
1245 const uint8_t* end;
1247 if (!mDataLength) {
1248 LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
1249 return PR_SUCCESS;
1252 if (!mDataIoPtr) mDataIoPtr = mData;
1254 end = mData + mDataLength;
1256 while (mDataIoPtr < end) {
1257 rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
1258 if (rc < 0) {
1259 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
1260 LOGDEBUG(("socks: WriteToSocket(), want write"));
1262 break;
1265 mDataIoPtr += rc;
1268 if (mDataIoPtr == end) {
1269 mDataIoPtr = nullptr;
1270 mDataLength = 0;
1271 mReadOffset = 0;
1272 return PR_SUCCESS;
1275 return PR_FAILURE;
1278 static PRStatus nsSOCKSIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
1279 PRIntervalTime to) {
1280 PRStatus status;
1281 NetAddr dst;
1283 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1284 if (info == nullptr) return PR_FAILURE;
1286 if (addr->raw.family == PR_AF_INET6 &&
1287 PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
1288 const uint8_t* srcp;
1290 LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
1292 // copied from _PR_ConvertToIpv4NetAddr()
1293 dst.raw.family = AF_INET;
1294 dst.inet.ip = htonl(INADDR_ANY);
1295 dst.inet.port = htons(0);
1296 srcp = addr->ipv6.ip.pr_s6_addr;
1297 memcpy(&dst.inet.ip, srcp + 12, 4);
1298 dst.inet.family = AF_INET;
1299 dst.inet.port = addr->ipv6.port;
1300 } else {
1301 memcpy(&dst, addr, sizeof(dst));
1304 info->SetDestinationAddr(&dst);
1305 info->SetConnectTimeout(to);
1307 do {
1308 status = info->DoHandshake(fd, -1);
1309 } while (status == PR_SUCCESS && !info->IsConnected());
1311 return status;
1314 static PRStatus nsSOCKSIOLayerConnectContinue(PRFileDesc* fd, int16_t oflags) {
1315 PRStatus status;
1317 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1318 if (info == nullptr) return PR_FAILURE;
1320 do {
1321 status = info->DoHandshake(fd, oflags);
1322 } while (status == PR_SUCCESS && !info->IsConnected());
1324 return status;
1327 static int16_t nsSOCKSIOLayerPoll(PRFileDesc* fd, int16_t in_flags,
1328 int16_t* out_flags) {
1329 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1330 if (info == nullptr) return PR_FAILURE;
1332 if (!info->IsConnected()) {
1333 *out_flags = 0;
1334 return info->GetPollFlags();
1337 return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
1340 static PRStatus nsSOCKSIOLayerClose(PRFileDesc* fd) {
1341 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1342 PRDescIdentity id = PR_GetLayersIdentity(fd);
1344 if (info && id == nsSOCKSIOLayerIdentity) {
1345 info->ForgetFD();
1346 NS_RELEASE(info);
1347 fd->identity = PR_INVALID_IO_LAYER;
1350 return fd->lower->methods->close(fd->lower);
1353 static PRFileDesc* nsSOCKSIOLayerAccept(PRFileDesc* fd, PRNetAddr* addr,
1354 PRIntervalTime timeout) {
1355 // TODO: implement SOCKS support for accept
1356 return fd->lower->methods->accept(fd->lower, addr, timeout);
1359 static int32_t nsSOCKSIOLayerAcceptRead(PRFileDesc* sd, PRFileDesc** nd,
1360 PRNetAddr** raddr, void* buf,
1361 int32_t amount,
1362 PRIntervalTime timeout) {
1363 // TODO: implement SOCKS support for accept, then read from it
1364 return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount,
1365 timeout);
1368 static PRStatus nsSOCKSIOLayerBind(PRFileDesc* fd, const PRNetAddr* addr) {
1369 // TODO: implement SOCKS support for bind (very similar to connect)
1370 return fd->lower->methods->bind(fd->lower, addr);
1373 static PRStatus nsSOCKSIOLayerGetName(PRFileDesc* fd, PRNetAddr* addr) {
1374 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1376 if (info != nullptr && addr != nullptr) {
1377 NetAddr temp;
1378 NetAddr* tempPtr = &temp;
1379 if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) {
1380 NetAddrToPRNetAddr(tempPtr, addr);
1381 return PR_SUCCESS;
1385 return PR_FAILURE;
1388 static PRStatus nsSOCKSIOLayerGetPeerName(PRFileDesc* fd, PRNetAddr* addr) {
1389 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1391 if (info != nullptr && addr != nullptr) {
1392 NetAddr temp;
1393 NetAddr* tempPtr = &temp;
1394 if (info->GetDestinationAddr(&tempPtr) == NS_OK) {
1395 NetAddrToPRNetAddr(tempPtr, addr);
1396 return PR_SUCCESS;
1400 return PR_FAILURE;
1403 static PRStatus nsSOCKSIOLayerListen(PRFileDesc* fd, int backlog) {
1404 // TODO: implement SOCKS support for listen
1405 return fd->lower->methods->listen(fd->lower, backlog);
1408 // add SOCKS IO layer to an existing socket
1409 nsresult nsSOCKSIOLayerAddToSocket(int32_t family, const char* host,
1410 int32_t port, nsIProxyInfo* proxy,
1411 int32_t socksVersion, uint32_t flags,
1412 uint32_t tlsFlags, PRFileDesc* fd,
1413 nsISupports** info) {
1414 NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5),
1415 NS_ERROR_NOT_INITIALIZED);
1417 if (firstTime) {
1418 // XXX hack until NSPR provides an official way to detect system IPv6
1419 // support (bug 388519)
1420 PRFileDesc* tmpfd = PR_OpenTCPSocket(PR_AF_INET6);
1421 if (!tmpfd) {
1422 ipv6Supported = false;
1423 } else {
1424 // If the system does not support IPv6, NSPR will push
1425 // IPv6-to-IPv4 emulation layer onto the native layer
1426 ipv6Supported = PR_GetIdentitiesLayer(tmpfd, PR_NSPR_IO_LAYER) == tmpfd;
1427 PR_Close(tmpfd);
1430 nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
1431 nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
1433 nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
1434 nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue;
1435 nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll;
1436 nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
1437 nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
1438 nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
1439 nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
1440 nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
1441 nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
1442 nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
1444 firstTime = false;
1447 LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
1449 PRFileDesc* layer;
1450 PRStatus rv;
1452 layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
1453 if (!layer) {
1454 LOGERROR(("PR_CreateIOLayerStub() failed."));
1455 return NS_ERROR_FAILURE;
1458 nsSOCKSSocketInfo* infoObject = new nsSOCKSSocketInfo();
1459 if (!infoObject) {
1460 // clean up IOLayerStub
1461 LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
1462 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
1463 return NS_ERROR_FAILURE;
1466 NS_ADDREF(infoObject);
1467 infoObject->Init(socksVersion, family, proxy, host, flags, tlsFlags);
1468 layer->secret = (PRFilePrivate*)infoObject;
1470 PRDescIdentity fdIdentity = PR_GetLayersIdentity(fd);
1471 #if defined(XP_WIN)
1472 if (fdIdentity == mozilla::net::nsNamedPipeLayerIdentity) {
1473 // remember named pipe fd on the info object so that we can switch
1474 // blocking and non-blocking mode on the pipe later.
1475 infoObject->SetNamedPipeFD(fd);
1477 #endif
1478 rv = PR_PushIOLayer(fd, fdIdentity, layer);
1480 if (rv == PR_FAILURE) {
1481 LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
1482 NS_RELEASE(infoObject);
1483 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
1484 return NS_ERROR_FAILURE;
1487 *info = static_cast<nsISOCKSSocketInfo*>(infoObject);
1488 NS_ADDREF(*info);
1489 return NS_OK;
1492 bool IsHostLocalTarget(const nsACString& aHost) {
1493 #if defined(XP_UNIX)
1494 return StringBeginsWith(aHost, "file:"_ns);
1495 #elif defined(XP_WIN)
1496 return IsNamedPipePath(aHost);
1497 #else
1498 return false;
1499 #endif // XP_UNIX