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/. */
8 #include "private/pprio.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"
19 #include "nsIDNSListener.h"
20 #include "nsICancelable.h"
21 #include "nsThreadUtils.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
{
43 SOCKS_DNS_IN_PROGRESS
,
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
,
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;
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
; }
84 virtual ~nsSOCKSSocketInfo() {
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();
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
) {
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
))) {
133 nsCOMPtr
<nsIFileProtocolHandler
> fileHandler(
134 do_QueryInterface(protocolHandler
, &rv
));
135 if (NS_WARN_IF(NS_FAILED(rv
))) {
139 nsCOMPtr
<nsIFile
> socketFile
;
140 rv
= fileHandler
->GetFileFromURLSpec(aLocalProxyPath
,
141 getter_AddRefs(socketFile
));
142 if (NS_WARN_IF(NS_FAILED(rv
))) {
147 if (NS_WARN_IF(NS_FAILED(rv
= socketFile
->GetNativePath(path
)))) {
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());
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());
172 mozilla::Unused
<< aLocalProxyPath
;
173 mozilla::Unused
<< aProxyAddr
;
174 return NS_ERROR_NOT_IMPLEMENTED
;
178 bool SetupNamedPipeLayer(PRFileDesc
* fd
) {
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
);
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
};
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.
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)
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
>
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");
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());
314 explicit operator bool() { return !!mBuf
; }
317 template <size_t Size2
>
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
);
330 memcpy(mBuf
, aValue
, aCopyLength
);
331 Buffer
<Size
- Length
> result(mBuf
+ aCopyLength
, mLength
+ aCopyLength
);
337 uint8_t* mBuf
{nullptr};
341 void nsSOCKSSocketInfo::Init(int32_t version
, int32_t family
,
342 nsIProxyInfo
* proxy
, const char* host
,
343 uint32_t flags
, uint32_t tlsFlags
) {
345 mDestinationFamily
= family
;
347 mDestinationHost
= host
;
349 mTlsFlags
= tlsFlags
;
350 mProxy
->GetUsername(mProxyUsername
); // cache
353 NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo
, nsISOCKSSocketInfo
, nsIDNSListener
)
356 nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr
** aExternalProxyAddr
) {
357 memcpy(*aExternalProxyAddr
, &mExternalProxyAddr
, sizeof(NetAddr
));
362 nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr
* aExternalProxyAddr
) {
363 memcpy(&mExternalProxyAddr
, aExternalProxyAddr
, sizeof(NetAddr
));
368 nsSOCKSSocketInfo::GetDestinationAddr(NetAddr
** aDestinationAddr
) {
369 memcpy(*aDestinationAddr
, &mDestinationAddr
, sizeof(NetAddr
));
374 nsSOCKSSocketInfo::SetDestinationAddr(NetAddr
* aDestinationAddr
) {
375 memcpy(&mDestinationAddr
, aDestinationAddr
, sizeof(NetAddr
));
380 nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr
** aInternalProxyAddr
) {
381 memcpy(*aInternalProxyAddr
, &mInternalProxyAddr
, sizeof(NetAddr
));
386 nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr
* aInternalProxyAddr
) {
387 memcpy(&mInternalProxyAddr
, aInternalProxyAddr
, sizeof(NetAddr
));
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
) {
400 mState
= SOCKS_CONNECTED
;
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
);
412 mState
= SOCKS_FAILED
;
413 PR_SetError(PR_UNKNOWN_ERROR
, err
);
416 // We don't need the buffer any longer, so free it.
419 mDataIoPtr
= nullptr;
424 mLookup
->Cancel(NS_ERROR_FAILURE
);
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
;
437 mProxy
->GetHost(proxyHost
);
439 mozilla::OriginAttributes attrs
;
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
));
448 LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed", proxyHost
.get()));
451 mState
= SOCKS_DNS_IN_PROGRESS
;
452 PR_SetError(PR_IN_PROGRESS_ERROR
, 0);
457 nsSOCKSSocketInfo::OnLookupComplete(nsICancelable
* aRequest
,
458 nsIDNSRecord
* aRecord
, nsresult aStatus
) {
459 MOZ_ASSERT(aRequest
== mLookup
, "wrong DNS query");
461 mLookupStatus
= aStatus
;
463 mState
= SOCKS_DNS_COMPLETE
;
471 PRStatus
nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc
* fd
) {
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);
482 // Try socks5 if the destination addrress is IPv6
483 if (mVersion
== 4 && mDestinationAddr
.raw
.family
== AF_INET6
) {
487 nsAutoCString proxyHost
;
488 mProxy
->GetHost(proxyHost
);
491 mProxy
->GetPort(&proxyPort
);
493 int32_t addresses
= 0;
495 if (IsLocalProxy()) {
496 rv
= SetLocalProxyPath(proxyHost
, &mInternalProxyAddr
);
499 ("socks: unable to connect to SOCKS proxy, %s", proxyHost
.get()));
503 nsCOMPtr
<nsIDNSAddrRecord
> record
= do_QueryInterface(mDnsRec
);
506 record
->ReportUnusable(proxyPort
);
509 rv
= record
->GetNextAddr(proxyPort
, &mInternalProxyAddr
);
510 // No more addresses to try? If so, we'll need to bail
513 ("socks: unable to connect to SOCKS proxy, %s", proxyHost
.get()));
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
);
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
;
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
;
545 } while (status
!= PR_SUCCESS
);
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
);
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
) {
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
) {
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
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
)) {
592 // Get an OS native handle from a specified FileDesc
593 PROsfd osfd
= PR_FileDesc2NativeHandle(fd
);
598 // Create a new FileDesc with a specified family
599 PRFileDesc
* tmpfd
= PR_OpenTCPSocket(proxyFamily
);
603 PROsfd newsd
= PR_FileDesc2NativeHandle(tmpfd
);
608 // Must succeed because PR_FileDesc2NativeHandle succeeded
609 fd
= PR_GetIdentitiesLayer(fd
, PR_NSPR_IO_LAYER
);
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
617 mDestinationFamily
= proxyFamily
;
620 PRStatus
nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc
* fd
,
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
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
);
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
;
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
673 // We don't have anything more to write after the if, so we can
674 // use a buffer with no further writes allowed.
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.
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
689 LOGERROR(("socks4: destination host name is too long!"));
690 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
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
700 LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
701 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
705 mDataLength
= buf3
.Written();
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
);
722 // See if our connection request was granted
723 if (ReadUint8() == 90) {
724 LOGDEBUG(("socks4: connection successful!"));
729 LOGERROR(("socks4: unable to connect"));
730 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
734 PRStatus
nsSOCKSSocketInfo::WriteV5AuthRequest() {
735 MOZ_ASSERT(mVersion
== 5, "SOCKS version must be 5!");
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)
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
);
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
);
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
);
792 mProxy
->GetPassword(password
);
793 if (password
.Length() > MAX_PASSWORD_LEN
) {
794 LOGERROR(("socks password is too long"));
795 HandshakeFinished(PR_UNKNOWN_ERROR
);
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!
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
);
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
);
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"));
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.
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
870 LOGERROR(("socks5: destination host name is too long!"));
871 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
874 } else if (addr
->raw
.family
== AF_INET
) {
875 buf2
= buf
.WriteUint8(0x01) // addr type -- IPv4
877 } else if (addr
->raw
.family
== AF_INET6
) {
878 buf2
= buf
.WriteUint8(0x04) // addr type -- IPv6
881 LOGERROR(("socks5: destination address of unknown type!"));
882 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
886 auto buf3
= buf2
.WriteNetPort(addr
); // port
887 mDataLength
= buf3
.Written();
892 PRStatus
nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t* type
,
894 MOZ_ASSERT(mState
== SOCKS5_READ_CONNECT_RESPONSE_TOP
||
895 mState
== SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
,
897 MOZ_ASSERT(mDataLength
>= 5,
898 "SOCKS 5 connection reply must be at least 5 bytes!");
900 // Seek to the address location
915 default: // wrong address type
916 LOGERROR(("socks5: wrong address type in connection reply!"));
923 PRStatus
nsSOCKSSocketInfo::ReadV5ConnectResponseTop() {
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
);
943 PRErrorCode c
= PR_CONNECT_REFUSED_ERROR
;
948 ("socks5: connect failed: "
949 "01, General SOCKS server failure."));
953 ("socks5: connect failed: "
954 "02, Connection not allowed by ruleset."));
957 LOGERROR(("socks5: connect failed: 03, Network unreachable."));
958 c
= PR_NETWORK_UNREACHABLE_ERROR
;
961 LOGERROR(("socks5: connect failed: 04, Host unreachable."));
962 c
= PR_BAD_ADDRESS_ERROR
;
965 LOGERROR(("socks5: connect failed: 05, Connection refused."));
968 LOGERROR(("socks5: connect failed: 06, TTL expired."));
969 c
= PR_CONNECT_TIMEOUT_ERROR
;
973 ("socks5: connect failed: "
974 "07, Command not supported."));
978 ("socks5: connect failed: "
979 "08, Address type not supported."));
980 c
= PR_BAD_ADDRESS_ERROR
;
983 LOGERROR(("socks5: connect failed."));
987 HandshakeFinished(c
);
991 if (ReadV5AddrTypeAndLength(&res
, &len
) != PR_SUCCESS
) {
992 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
996 mState
= SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
;
1002 PRStatus
nsSOCKSSocketInfo::ReadV5ConnectResponseBottom() {
1006 MOZ_ASSERT(mState
== SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
, "Invalid state!");
1008 if (ReadV5AddrTypeAndLength(&type
, &len
) != PR_SUCCESS
) {
1009 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
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
1020 ReadNetAddr(&mExternalProxyAddr
, AF_INET
);
1023 ReadNetAddr(&mExternalProxyAddr
, AF_INET6
);
1025 case 0x03: // fqdn (skip)
1027 mExternalProxyAddr
.raw
.family
= AF_INET
;
1031 ReadNetPort(&mExternalProxyAddr
);
1033 LOGDEBUG(("socks5: connected!"));
1034 HandshakeFinished();
1039 void nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to
) { mTimeout
= to
; }
1041 PRStatus
nsSOCKSSocketInfo::DoHandshake(PRFileDesc
* fd
, int16_t oflags
) {
1042 LOGDEBUG(("socks: DoHandshake(), state = %d", mState
));
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);
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
;
1063 mState
= SOCKS4_READ_CONNECT_RESPONSE
;
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
;
1072 mState
= SOCKS5_READ_AUTH_RESPONSE
;
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
;
1080 mState
= SOCKS5_READ_USERNAME_RESPONSE
;
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.
1092 mState
= SOCKS5_READ_CONNECT_RESPONSE_TOP
;
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
);
1106 LOGERROR(("socks: already failed"));
1110 LOGERROR(("socks: executing handshake in invalid state, %d", mState
));
1111 HandshakeFinished(PR_INVALID_STATE_ERROR
);
1116 int16_t nsSOCKSSocketInfo::GetPollFlags() const {
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
;
1140 inline uint8_t nsSOCKSSocketInfo::ReadUint8() {
1142 MOZ_ASSERT(mReadOffset
+ sizeof(rv
) <= mDataLength
,
1143 "Not enough space to pop a uint8_t!");
1144 rv
= mData
[mReadOffset
];
1145 mReadOffset
+= sizeof(rv
);
1149 inline uint16_t nsSOCKSSocketInfo::ReadUint16() {
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
);
1158 inline uint32_t nsSOCKSSocketInfo::ReadUint32() {
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
);
1167 void nsSOCKSSocketInfo::ReadNetAddr(NetAddr
* addr
, uint16_t fam
) {
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
);
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!");
1198 PRStatus
nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc
* fd
) {
1202 if (!mAmountToRead
) {
1203 LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
1208 mDataIoPtr
= mData
+ mDataLength
;
1209 mDataLength
+= mAmountToRead
;
1212 end
= mData
+ mDataLength
;
1214 while (mDataIoPtr
< end
) {
1215 rc
= PR_Read(fd
, mDataIoPtr
, end
- mDataIoPtr
);
1218 LOGERROR(("socks: proxy server closed connection"));
1219 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
1222 if (PR_GetError() == PR_WOULD_BLOCK_ERROR
) {
1223 LOGDEBUG(("socks: ReadFromSocket(), want read"));
1231 LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
1232 unsigned(mDataIoPtr
- mData
)));
1233 if (mDataIoPtr
== end
) {
1234 mDataIoPtr
= nullptr;
1243 PRStatus
nsSOCKSSocketInfo::WriteToSocket(PRFileDesc
* fd
) {
1248 LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
1252 if (!mDataIoPtr
) mDataIoPtr
= mData
;
1254 end
= mData
+ mDataLength
;
1256 while (mDataIoPtr
< end
) {
1257 rc
= PR_Write(fd
, mDataIoPtr
, end
- mDataIoPtr
);
1259 if (PR_GetError() == PR_WOULD_BLOCK_ERROR
) {
1260 LOGDEBUG(("socks: WriteToSocket(), want write"));
1268 if (mDataIoPtr
== end
) {
1269 mDataIoPtr
= nullptr;
1278 static PRStatus
nsSOCKSIOLayerConnect(PRFileDesc
* fd
, const PRNetAddr
* addr
,
1279 PRIntervalTime to
) {
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
;
1301 memcpy(&dst
, addr
, sizeof(dst
));
1304 info
->SetDestinationAddr(&dst
);
1305 info
->SetConnectTimeout(to
);
1308 status
= info
->DoHandshake(fd
, -1);
1309 } while (status
== PR_SUCCESS
&& !info
->IsConnected());
1314 static PRStatus
nsSOCKSIOLayerConnectContinue(PRFileDesc
* fd
, int16_t oflags
) {
1317 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1318 if (info
== nullptr) return PR_FAILURE
;
1321 status
= info
->DoHandshake(fd
, oflags
);
1322 } while (status
== PR_SUCCESS
&& !info
->IsConnected());
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()) {
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
) {
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
,
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
,
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) {
1378 NetAddr
* tempPtr
= &temp
;
1379 if (info
->GetExternalProxyAddr(&tempPtr
) == NS_OK
) {
1380 NetAddrToPRNetAddr(tempPtr
, addr
);
1388 static PRStatus
nsSOCKSIOLayerGetPeerName(PRFileDesc
* fd
, PRNetAddr
* addr
) {
1389 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1391 if (info
!= nullptr && addr
!= nullptr) {
1393 NetAddr
* tempPtr
= &temp
;
1394 if (info
->GetDestinationAddr(&tempPtr
) == NS_OK
) {
1395 NetAddrToPRNetAddr(tempPtr
, addr
);
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
);
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
);
1422 ipv6Supported
= false;
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
;
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
;
1447 LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
1452 layer
= PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity
, &nsSOCKSIOLayerMethods
);
1454 LOGERROR(("PR_CreateIOLayerStub() failed."));
1455 return NS_ERROR_FAILURE
;
1458 nsSOCKSSocketInfo
* infoObject
= new nsSOCKSSocketInfo();
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
);
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
);
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
);
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
);