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 "nsISocketProvider.h"
15 #include "nsNamedPipeIOLayer.h"
16 #include "nsSOCKSIOLayer.h"
18 #include "nsIDNSListener.h"
19 #include "nsICancelable.h"
20 #include "nsThreadUtils.h"
22 #include "nsIFileProtocolHandler.h"
23 #include "mozilla/Logging.h"
24 #include "mozilla/net/DNS.h"
25 #include "mozilla/Unused.h"
27 using mozilla::LogLevel
;
28 using namespace mozilla::net
;
30 static PRDescIdentity nsSOCKSIOLayerIdentity
;
31 static PRIOMethods nsSOCKSIOLayerMethods
;
32 static bool firstTime
= true;
33 static bool ipv6Supported
= true;
35 static mozilla::LazyLogModule
gSOCKSLog("SOCKS");
36 #define LOGDEBUG(args) MOZ_LOG(gSOCKSLog, mozilla::LogLevel::Debug, args)
37 #define LOGERROR(args) MOZ_LOG(gSOCKSLog, mozilla::LogLevel::Error, args)
39 class nsSOCKSSocketInfo
: public nsIDNSListener
{
42 SOCKS_DNS_IN_PROGRESS
,
44 SOCKS_CONNECTING_TO_PROXY
,
45 SOCKS4_WRITE_CONNECT_REQUEST
,
46 SOCKS4_READ_CONNECT_RESPONSE
,
47 SOCKS5_WRITE_AUTH_REQUEST
,
48 SOCKS5_READ_AUTH_RESPONSE
,
49 SOCKS5_WRITE_USERNAME_REQUEST
,
50 SOCKS5_READ_USERNAME_RESPONSE
,
51 SOCKS5_WRITE_CONNECT_REQUEST
,
52 SOCKS5_READ_CONNECT_RESPONSE_TOP
,
53 SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
,
58 // A buffer of 520 bytes should be enough for any request and response
59 // in case of SOCKS4 as well as SOCKS5
60 static const uint32_t BUFFER_SIZE
= 520;
61 static const uint32_t MAX_HOSTNAME_LEN
= 255;
62 static const uint32_t MAX_USERNAME_LEN
= 255;
63 static const uint32_t MAX_PASSWORD_LEN
= 255;
68 NS_DECL_THREADSAFE_ISUPPORTS
69 NS_DECL_NSIDNSLISTENER
71 void Init(int32_t version
, int32_t family
, nsIProxyInfo
* proxy
,
72 const char* destinationHost
, uint32_t flags
, uint32_t tlsFlags
);
74 void SetConnectTimeout(PRIntervalTime to
);
75 PRStatus
DoHandshake(PRFileDesc
* fd
, int16_t oflags
= -1);
76 int16_t GetPollFlags() const;
77 bool IsConnected() const { return mState
== SOCKS_CONNECTED
; }
78 void ForgetFD() { mFD
= nullptr; }
79 void SetNamedPipeFD(PRFileDesc
* fd
) { mFD
= fd
; }
81 void GetExternalProxyAddr(NetAddr
& aExternalProxyAddr
);
82 void GetDestinationAddr(NetAddr
& aDestinationAddr
);
83 void SetDestinationAddr(const NetAddr
& aDestinationAddr
);
86 virtual ~nsSOCKSSocketInfo() {
91 void HandshakeFinished(PRErrorCode err
= 0);
92 PRStatus
StartDNS(PRFileDesc
* fd
);
93 PRStatus
ConnectToProxy(PRFileDesc
* fd
);
94 void FixupAddressFamily(PRFileDesc
* fd
, NetAddr
* proxy
);
95 PRStatus
ContinueConnectingToProxy(PRFileDesc
* fd
, int16_t oflags
);
96 PRStatus
WriteV4ConnectRequest();
97 PRStatus
ReadV4ConnectResponse();
98 PRStatus
WriteV5AuthRequest();
99 PRStatus
ReadV5AuthResponse();
100 PRStatus
WriteV5UsernameRequest();
101 PRStatus
ReadV5UsernameResponse();
102 PRStatus
WriteV5ConnectRequest();
103 PRStatus
ReadV5AddrTypeAndLength(uint8_t* type
, uint32_t* len
);
104 PRStatus
ReadV5ConnectResponseTop();
105 PRStatus
ReadV5ConnectResponseBottom();
108 uint16_t ReadUint16();
109 uint32_t ReadUint32();
110 void ReadNetAddr(NetAddr
* addr
, uint16_t fam
);
111 void ReadNetPort(NetAddr
* addr
);
113 void WantRead(uint32_t sz
);
114 PRStatus
ReadFromSocket(PRFileDesc
* fd
);
115 PRStatus
WriteToSocket(PRFileDesc
* fd
);
117 bool IsLocalProxy() {
118 nsAutoCString proxyHost
;
119 mProxy
->GetHost(proxyHost
);
120 return IsHostLocalTarget(proxyHost
);
123 nsresult
SetLocalProxyPath(const nsACString
& aLocalProxyPath
,
124 NetAddr
* aProxyAddr
) {
127 MOZ_ASSERT(aProxyAddr
);
129 nsCOMPtr
<nsIProtocolHandler
> protocolHandler(
130 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX
"file", &rv
));
131 if (NS_WARN_IF(NS_FAILED(rv
))) {
135 nsCOMPtr
<nsIFileProtocolHandler
> fileHandler(
136 do_QueryInterface(protocolHandler
, &rv
));
137 if (NS_WARN_IF(NS_FAILED(rv
))) {
141 nsCOMPtr
<nsIFile
> socketFile
;
142 rv
= fileHandler
->GetFileFromURLSpec(aLocalProxyPath
,
143 getter_AddRefs(socketFile
));
144 if (NS_WARN_IF(NS_FAILED(rv
))) {
149 if (NS_WARN_IF(NS_FAILED(rv
= socketFile
->GetNativePath(path
)))) {
153 if (sizeof(aProxyAddr
->local
.path
) <= path
.Length()) {
154 NS_WARNING("domain socket path too long.");
155 return NS_ERROR_FAILURE
;
158 aProxyAddr
->raw
.family
= AF_UNIX
;
159 strcpy(aProxyAddr
->local
.path
, path
.get());
162 #elif defined(XP_WIN)
163 MOZ_ASSERT(aProxyAddr
);
165 if (sizeof(aProxyAddr
->local
.path
) <= aLocalProxyPath
.Length()) {
166 NS_WARNING("pipe path too long.");
167 return NS_ERROR_FAILURE
;
170 aProxyAddr
->raw
.family
= AF_LOCAL
;
171 strcpy(aProxyAddr
->local
.path
, PromiseFlatCString(aLocalProxyPath
).get());
174 mozilla::Unused
<< aLocalProxyPath
;
175 mozilla::Unused
<< aProxyAddr
;
176 return NS_ERROR_NOT_IMPLEMENTED
;
180 bool SetupNamedPipeLayer(PRFileDesc
* fd
) {
182 if (IsLocalProxy()) {
183 // nsSOCKSIOLayer handshaking only works under blocking mode
184 // unfortunately. Remember named pipe's FD to switch between modes.
185 SetNamedPipeFD(fd
->lower
);
193 State mState
{SOCKS_INITIAL
};
194 uint8_t* mData
{nullptr};
195 uint8_t* mDataIoPtr
{nullptr};
196 uint32_t mDataLength
{0};
197 uint32_t mReadOffset
{0};
198 uint32_t mAmountToRead
{0};
199 nsCOMPtr
<nsIDNSRecord
> mDnsRec
;
200 nsCOMPtr
<nsICancelable
> mLookup
;
201 nsresult mLookupStatus
{NS_ERROR_NOT_INITIALIZED
};
202 PRFileDesc
* mFD
{nullptr};
204 nsCString mDestinationHost
;
205 nsCOMPtr
<nsIProxyInfo
> mProxy
;
206 int32_t mVersion
{-1}; // SOCKS version 4 or 5
207 int32_t mDestinationFamily
{AF_INET
};
209 uint32_t mTlsFlags
{0};
210 NetAddr mInternalProxyAddr
;
211 NetAddr mExternalProxyAddr
;
212 NetAddr mDestinationAddr
;
213 PRIntervalTime mTimeout
{PR_INTERVAL_NO_TIMEOUT
};
214 nsCString mProxyUsername
; // Cache, from mProxy
217 nsSOCKSSocketInfo::nsSOCKSSocketInfo() {
218 mData
= new uint8_t[BUFFER_SIZE
];
220 mInternalProxyAddr
.raw
.family
= AF_INET
;
221 mInternalProxyAddr
.inet
.ip
= htonl(INADDR_ANY
);
222 mInternalProxyAddr
.inet
.port
= htons(0);
224 mExternalProxyAddr
.raw
.family
= AF_INET
;
225 mExternalProxyAddr
.inet
.ip
= htonl(INADDR_ANY
);
226 mExternalProxyAddr
.inet
.port
= htons(0);
228 mDestinationAddr
.raw
.family
= AF_INET
;
229 mDestinationAddr
.inet
.ip
= htonl(INADDR_ANY
);
230 mDestinationAddr
.inet
.port
= htons(0);
233 /* Helper template class to statically check that writes to a fixed-size
234 * buffer are not going to overflow.
237 * uint8_t real_buf[TOTAL_SIZE];
238 * Buffer<TOTAL_SIZE> buf(&real_buf);
239 * auto buf2 = buf.WriteUint16(1);
240 * auto buf3 = buf2.WriteUint8(2);
242 * It is possible to chain them, to limit the number of (error-prone)
243 * intermediate variables:
244 * auto buf = Buffer<TOTAL_SIZE>(&real_buf)
248 * Debug builds assert when intermediate variables are reused:
249 * Buffer<TOTAL_SIZE> buf(&real_buf);
250 * auto buf2 = buf.WriteUint16(1);
251 * auto buf3 = buf.WriteUint8(2); // Asserts
253 * Strings can be written, given an explicit maximum length.
254 * buf.WriteString<MAX_STRING_LENGTH>(str);
256 * The Written() method returns how many bytes have been written so far:
257 * Buffer<TOTAL_SIZE> buf(&real_buf);
258 * auto buf2 = buf.WriteUint16(1);
259 * auto buf3 = buf2.WriteUint8(2);
260 * buf3.Written(); // returns 3.
262 template <size_t Size
>
267 explicit Buffer(uint8_t* aBuf
, size_t aLength
= 0)
268 : mBuf(aBuf
), mLength(aLength
) {}
270 template <size_t Size2
>
271 MOZ_IMPLICIT
Buffer(const Buffer
<Size2
>& aBuf
)
272 : mBuf(aBuf
.mBuf
), mLength(aBuf
.mLength
) {
273 static_assert(Size2
> Size
, "Cannot cast buffer");
276 Buffer
<Size
- sizeof(uint8_t)> WriteUint8(uint8_t aValue
) {
277 return Write(aValue
);
280 Buffer
<Size
- sizeof(uint16_t)> WriteUint16(uint16_t aValue
) {
281 return Write(aValue
);
284 Buffer
<Size
- sizeof(uint32_t)> WriteUint32(uint32_t aValue
) {
285 return Write(aValue
);
288 Buffer
<Size
- sizeof(uint16_t)> WriteNetPort(const NetAddr
* aAddr
) {
289 return WriteUint16(aAddr
->inet
.port
);
292 Buffer
<Size
- sizeof(IPv6Addr
)> WriteNetAddr(const NetAddr
* aAddr
) {
293 if (aAddr
->raw
.family
== AF_INET
) {
294 return Write(aAddr
->inet
.ip
);
296 if (aAddr
->raw
.family
== AF_INET6
) {
297 return Write(aAddr
->inet6
.ip
.u8
);
299 MOZ_ASSERT_UNREACHABLE("Unknown address family");
303 template <size_t MaxLength
>
304 Buffer
<Size
- MaxLength
> WriteString(const nsACString
& aStr
) {
305 if (aStr
.Length() > MaxLength
) {
306 return Buffer
<Size
- MaxLength
>(nullptr);
308 return WritePtr
<char, MaxLength
>(aStr
.Data(), aStr
.Length());
316 explicit operator bool() { return !!mBuf
; }
319 template <size_t Size2
>
322 template <typename T
>
323 Buffer
<Size
- sizeof(T
)> Write(T
& aValue
) {
324 return WritePtr
<T
, sizeof(T
)>(&aValue
, sizeof(T
));
327 template <typename T
, size_t Length
>
328 Buffer
<Size
- Length
> WritePtr(const T
* aValue
, size_t aCopyLength
) {
329 static_assert(Size
>= Length
, "Cannot write that much");
330 MOZ_ASSERT(aCopyLength
<= Length
);
332 memcpy(mBuf
, aValue
, aCopyLength
);
333 Buffer
<Size
- Length
> result(mBuf
+ aCopyLength
, mLength
+ aCopyLength
);
339 uint8_t* mBuf
{nullptr};
343 void nsSOCKSSocketInfo::Init(int32_t version
, int32_t family
,
344 nsIProxyInfo
* proxy
, const char* host
,
345 uint32_t flags
, uint32_t tlsFlags
) {
347 mDestinationFamily
= family
;
349 mDestinationHost
= host
;
351 mTlsFlags
= tlsFlags
;
352 mProxy
->GetUsername(mProxyUsername
); // cache
355 NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo
, nsIDNSListener
)
357 void nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr
& aExternalProxyAddr
) {
358 aExternalProxyAddr
= mExternalProxyAddr
;
361 void nsSOCKSSocketInfo::GetDestinationAddr(NetAddr
& aDestinationAddr
) {
362 aDestinationAddr
= mDestinationAddr
;
365 void nsSOCKSSocketInfo::SetDestinationAddr(const NetAddr
& aDestinationAddr
) {
366 mDestinationAddr
= aDestinationAddr
;
369 // There needs to be a means of distinguishing between connection errors
370 // that the SOCKS server reports when it rejects a connection request, and
371 // connection errors that happen while attempting to connect to the SOCKS
372 // server. Otherwise, Firefox will report incorrectly that the proxy server
373 // is refusing connections when a SOCKS request is rejected by the proxy.
374 // When a SOCKS handshake failure occurs, the PR error is set to
375 // PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
376 void nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err
) {
378 mState
= SOCKS_CONNECTED
;
380 // Switch back to nonblocking mode after finishing handshaking.
381 if (IsLocalProxy() && mFD
) {
382 PRSocketOptionData opt_nonblock
;
383 opt_nonblock
.option
= PR_SockOpt_Nonblocking
;
384 opt_nonblock
.value
.non_blocking
= PR_TRUE
;
385 PR_SetSocketOption(mFD
, &opt_nonblock
);
390 mState
= SOCKS_FAILED
;
391 PR_SetError(PR_UNKNOWN_ERROR
, err
);
394 // We don't need the buffer any longer, so free it.
397 mDataIoPtr
= nullptr;
402 mLookup
->Cancel(NS_ERROR_FAILURE
);
407 PRStatus
nsSOCKSSocketInfo::StartDNS(PRFileDesc
* fd
) {
408 MOZ_ASSERT(!mDnsRec
&& mState
== SOCKS_INITIAL
,
409 "Must be in initial state to make DNS Lookup");
411 nsCOMPtr
<nsIDNSService
> dns
= do_GetService(NS_DNSSERVICE_CONTRACTID
);
412 if (!dns
) return PR_FAILURE
;
415 mProxy
->GetHost(proxyHost
);
417 mozilla::OriginAttributes attrs
;
420 nsresult rv
= dns
->AsyncResolveNative(
421 proxyHost
, nsIDNSService::RESOLVE_TYPE_DEFAULT
,
422 nsIDNSService::RESOLVE_IGNORE_SOCKS_DNS
, nullptr, this,
423 mozilla::GetCurrentSerialEventTarget(), attrs
, getter_AddRefs(mLookup
));
426 LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed", proxyHost
.get()));
429 mState
= SOCKS_DNS_IN_PROGRESS
;
430 PR_SetError(PR_IN_PROGRESS_ERROR
, 0);
435 nsSOCKSSocketInfo::OnLookupComplete(nsICancelable
* aRequest
,
436 nsIDNSRecord
* aRecord
, nsresult aStatus
) {
437 MOZ_ASSERT(aRequest
== mLookup
, "wrong DNS query");
439 mLookupStatus
= aStatus
;
441 mState
= SOCKS_DNS_COMPLETE
;
449 PRStatus
nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc
* fd
) {
453 MOZ_ASSERT(mState
== SOCKS_DNS_COMPLETE
, "Must have DNS to make connection!");
455 if (NS_FAILED(mLookupStatus
)) {
456 PR_SetError(PR_BAD_ADDRESS_ERROR
, 0);
460 // Try socks5 if the destination addrress is IPv6
461 if (mVersion
== 4 && mDestinationAddr
.raw
.family
== AF_INET6
) {
465 nsAutoCString proxyHost
;
466 mProxy
->GetHost(proxyHost
);
469 mProxy
->GetPort(&proxyPort
);
471 int32_t addresses
= 0;
473 if (IsLocalProxy()) {
474 rv
= SetLocalProxyPath(proxyHost
, &mInternalProxyAddr
);
477 ("socks: unable to connect to SOCKS proxy, %s", proxyHost
.get()));
481 nsCOMPtr
<nsIDNSAddrRecord
> record
= do_QueryInterface(mDnsRec
);
484 record
->ReportUnusable(proxyPort
);
487 rv
= record
->GetNextAddr(proxyPort
, &mInternalProxyAddr
);
488 // No more addresses to try? If so, we'll need to bail
491 ("socks: unable to connect to SOCKS proxy, %s", proxyHost
.get()));
495 if (MOZ_LOG_TEST(gSOCKSLog
, LogLevel::Debug
)) {
496 char buf
[kIPv6CStrBufSize
];
497 mInternalProxyAddr
.ToStringBuffer(buf
, sizeof(buf
));
498 LOGDEBUG(("socks: trying proxy server, %s:%hu", buf
,
499 ntohs(mInternalProxyAddr
.inet
.port
)));
503 NetAddr proxy
= mInternalProxyAddr
;
504 FixupAddressFamily(fd
, &proxy
);
506 NetAddrToPRNetAddr(&proxy
, &prProxy
);
507 status
= fd
->lower
->methods
->connect(fd
->lower
, &prProxy
, mTimeout
);
508 if (status
!= PR_SUCCESS
) {
509 PRErrorCode c
= PR_GetError();
511 // If EINPROGRESS, return now and check back later after polling
512 if (c
== PR_WOULD_BLOCK_ERROR
|| c
== PR_IN_PROGRESS_ERROR
) {
513 mState
= SOCKS_CONNECTING_TO_PROXY
;
516 if (IsLocalProxy()) {
517 LOGERROR(("socks: connect to domain socket failed (%d)", c
));
518 PR_SetError(PR_CONNECT_REFUSED_ERROR
, 0);
519 mState
= SOCKS_FAILED
;
523 } while (status
!= PR_SUCCESS
);
526 // Switch to blocking mode during handshaking
527 if (IsLocalProxy() && mFD
) {
528 PRSocketOptionData opt_nonblock
;
529 opt_nonblock
.option
= PR_SockOpt_Nonblocking
;
530 opt_nonblock
.value
.non_blocking
= PR_FALSE
;
531 PR_SetSocketOption(mFD
, &opt_nonblock
);
535 // Connected now, start SOCKS
536 if (mVersion
== 4) return WriteV4ConnectRequest();
537 return WriteV5AuthRequest();
540 void nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc
* fd
, NetAddr
* proxy
) {
541 int32_t proxyFamily
= mInternalProxyAddr
.raw
.family
;
542 // Do nothing if the address family is already matched
543 if (proxyFamily
== mDestinationFamily
) {
546 // If the system does not support IPv6 and the proxy address is IPv6,
547 // We can do nothing here.
548 if (proxyFamily
== AF_INET6
&& !ipv6Supported
) {
551 // If the system does not support IPv6 and the destination address is
552 // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy
553 // the emulation layer
554 if (mDestinationFamily
== AF_INET6
&& !ipv6Supported
) {
555 proxy
->inet6
.family
= AF_INET6
;
556 proxy
->inet6
.port
= mInternalProxyAddr
.inet
.port
;
557 uint8_t* proxyp
= proxy
->inet6
.ip
.u8
;
558 memset(proxyp
, 0, 10);
559 memset(proxyp
+ 10, 0xff, 2);
560 memcpy(proxyp
+ 12, (char*)&mInternalProxyAddr
.inet
.ip
, 4);
561 // mDestinationFamily should not be updated
564 // There's no PR_NSPR_IO_LAYER required when using named pipe,
565 // we simply ignore the TCP family here.
566 if (SetupNamedPipeLayer(fd
)) {
570 // Get an OS native handle from a specified FileDesc
571 PROsfd osfd
= PR_FileDesc2NativeHandle(fd
);
576 // Create a new FileDesc with a specified family
577 PRFileDesc
* tmpfd
= PR_OpenTCPSocket(proxyFamily
);
581 PROsfd newsd
= PR_FileDesc2NativeHandle(tmpfd
);
586 // Must succeed because PR_FileDesc2NativeHandle succeeded
587 fd
= PR_GetIdentitiesLayer(fd
, PR_NSPR_IO_LAYER
);
589 // Swap OS native handles
590 PR_ChangeFileDescNativeHandle(fd
, newsd
);
591 PR_ChangeFileDescNativeHandle(tmpfd
, osfd
);
592 // Close temporary FileDesc which is now associated with
593 // old OS native handle
595 mDestinationFamily
= proxyFamily
;
598 PRStatus
nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc
* fd
,
602 MOZ_ASSERT(mState
== SOCKS_CONNECTING_TO_PROXY
,
603 "Continuing connection in wrong state!");
605 LOGDEBUG(("socks: continuing connection to proxy"));
607 status
= fd
->lower
->methods
->connectcontinue(fd
->lower
, oflags
);
608 if (status
!= PR_SUCCESS
) {
609 PRErrorCode c
= PR_GetError();
610 if (c
!= PR_WOULD_BLOCK_ERROR
&& c
!= PR_IN_PROGRESS_ERROR
) {
611 // A connection failure occured, try another address
612 mState
= SOCKS_DNS_COMPLETE
;
613 return ConnectToProxy(fd
);
616 // We're still connecting
620 // Connected now, start SOCKS
621 if (mVersion
== 4) return WriteV4ConnectRequest();
622 return WriteV5AuthRequest();
625 PRStatus
nsSOCKSSocketInfo::WriteV4ConnectRequest() {
626 if (mProxyUsername
.Length() > MAX_USERNAME_LEN
) {
627 LOGERROR(("socks username is too long"));
628 HandshakeFinished(PR_UNKNOWN_ERROR
);
632 NetAddr
* addr
= &mDestinationAddr
;
633 int32_t proxy_resolve
;
635 MOZ_ASSERT(mState
== SOCKS_CONNECTING_TO_PROXY
, "Invalid state!");
637 proxy_resolve
= mFlags
& nsISocketProvider::PROXY_RESOLVES_HOST
;
640 mState
= SOCKS4_WRITE_CONNECT_REQUEST
;
642 LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
643 proxy_resolve
? "yes" : "no"));
645 // Send a SOCKS 4 connect request.
646 auto buf
= Buffer
<BUFFER_SIZE
>(mData
)
647 .WriteUint8(0x04) // version -- 4
648 .WriteUint8(0x01) // command -- connect
651 // We don't have anything more to write after the if, so we can
652 // use a buffer with no further writes allowed.
655 // Add the full name, null-terminated, to the request
656 // according to SOCKS 4a. A fake IP address, with the first
657 // four bytes set to 0 and the last byte set to something other
658 // than 0, is used to notify the proxy that this is a SOCKS 4a
659 // request. This request type works for Tor and perhaps others.
660 // Passwords not supported by V4.
662 buf
.WriteUint32(htonl(0x00000001)) // Fake IP
663 .WriteString
<MAX_USERNAME_LEN
>(mProxyUsername
)
664 .WriteUint8(0x00) // Null-terminate username
665 .WriteString
<MAX_HOSTNAME_LEN
>(mDestinationHost
); // Hostname
667 LOGERROR(("socks4: destination host name is too long!"));
668 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
671 buf3
= buf2
.WriteUint8(0x00);
672 } else if (addr
->raw
.family
== AF_INET
) {
673 // Passwords not supported by V4.
674 buf3
= buf
.WriteNetAddr(addr
) // Add the IPv4 address
675 .WriteString
<MAX_USERNAME_LEN
>(mProxyUsername
)
676 .WriteUint8(0x00); // Null-terminate username
678 LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
679 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
683 mDataLength
= buf3
.Written();
687 PRStatus
nsSOCKSSocketInfo::ReadV4ConnectResponse() {
688 MOZ_ASSERT(mState
== SOCKS4_READ_CONNECT_RESPONSE
,
689 "Handling SOCKS 4 connection reply in wrong state!");
690 MOZ_ASSERT(mDataLength
== 8, "SOCKS 4 connection reply must be 8 bytes!");
692 LOGDEBUG(("socks4: checking connection reply"));
694 if (ReadUint8() != 0x00) {
695 LOGERROR(("socks4: wrong connection reply"));
696 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
700 // See if our connection request was granted
701 if (ReadUint8() == 90) {
702 LOGDEBUG(("socks4: connection successful!"));
707 LOGERROR(("socks4: unable to connect"));
708 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
712 PRStatus
nsSOCKSSocketInfo::WriteV5AuthRequest() {
713 MOZ_ASSERT(mVersion
== 5, "SOCKS version must be 5!");
716 mState
= SOCKS5_WRITE_AUTH_REQUEST
;
718 // Send an initial SOCKS 5 greeting
719 LOGDEBUG(("socks5: sending auth methods"));
720 mDataLength
= Buffer
<BUFFER_SIZE
>(mData
)
721 .WriteUint8(0x05) // version -- 5
722 .WriteUint8(0x01) // # of auth methods -- 1
723 // Use authenticate iff we have a proxy username.
724 .WriteUint8(mProxyUsername
.IsEmpty() ? 0x00 : 0x02)
730 PRStatus
nsSOCKSSocketInfo::ReadV5AuthResponse() {
731 MOZ_ASSERT(mState
== SOCKS5_READ_AUTH_RESPONSE
,
732 "Handling SOCKS 5 auth method reply in wrong state!");
733 MOZ_ASSERT(mDataLength
== 2, "SOCKS 5 auth method reply must be 2 bytes!");
735 LOGDEBUG(("socks5: checking auth method reply"));
737 // Check version number
738 if (ReadUint8() != 0x05) {
739 LOGERROR(("socks5: unexpected version in the reply"));
740 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
744 // Make sure our authentication choice was accepted,
745 // and continue accordingly
746 uint8_t authMethod
= ReadUint8();
747 if (mProxyUsername
.IsEmpty() && authMethod
== 0x00) { // no auth
748 LOGDEBUG(("socks5: server allows connection without authentication"));
749 return WriteV5ConnectRequest();
751 if (!mProxyUsername
.IsEmpty() && authMethod
== 0x02) { // username/pw
752 LOGDEBUG(("socks5: auth method accepted by server"));
753 return WriteV5UsernameRequest();
754 } // 0xFF signals error
755 LOGERROR(("socks5: server did not accept our authentication method"));
756 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
760 PRStatus
nsSOCKSSocketInfo::WriteV5UsernameRequest() {
761 MOZ_ASSERT(mVersion
== 5, "SOCKS version must be 5!");
763 if (mProxyUsername
.Length() > MAX_USERNAME_LEN
) {
764 LOGERROR(("socks username is too long"));
765 HandshakeFinished(PR_UNKNOWN_ERROR
);
770 mProxy
->GetPassword(password
);
771 if (password
.Length() > MAX_PASSWORD_LEN
) {
772 LOGERROR(("socks password is too long"));
773 HandshakeFinished(PR_UNKNOWN_ERROR
);
778 mState
= SOCKS5_WRITE_USERNAME_REQUEST
;
780 // RFC 1929 Username/password auth for SOCKS 5
781 LOGDEBUG(("socks5: sending username and password"));
782 mDataLength
= Buffer
<BUFFER_SIZE
>(mData
)
783 .WriteUint8(0x01) // version 1 (not 5)
784 .WriteUint8(mProxyUsername
.Length()) // username length
785 .WriteString
<MAX_USERNAME_LEN
>(mProxyUsername
) // username
786 .WriteUint8(password
.Length()) // password length
787 .WriteString
<MAX_PASSWORD_LEN
>(
788 password
) // password. WARNING: Sent unencrypted!
794 PRStatus
nsSOCKSSocketInfo::ReadV5UsernameResponse() {
795 MOZ_ASSERT(mState
== SOCKS5_READ_USERNAME_RESPONSE
,
796 "Handling SOCKS 5 username/password reply in wrong state!");
798 MOZ_ASSERT(mDataLength
== 2, "SOCKS 5 username reply must be 2 bytes");
800 // Check version number, must be 1 (not 5)
801 if (ReadUint8() != 0x01) {
802 LOGERROR(("socks5: unexpected version in the reply"));
803 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
807 // Check whether username/password were accepted
808 if (ReadUint8() != 0x00) { // 0 = success
809 LOGERROR(("socks5: username/password not accepted"));
810 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
814 LOGDEBUG(("socks5: username/password accepted by server"));
816 return WriteV5ConnectRequest();
819 PRStatus
nsSOCKSSocketInfo::WriteV5ConnectRequest() {
820 // Send SOCKS 5 connect request
821 NetAddr
* addr
= &mDestinationAddr
;
822 int32_t proxy_resolve
;
823 proxy_resolve
= mFlags
& nsISocketProvider::PROXY_RESOLVES_HOST
;
825 LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
826 proxy_resolve
? "yes" : "no"));
829 mState
= SOCKS5_WRITE_CONNECT_REQUEST
;
831 auto buf
= Buffer
<BUFFER_SIZE
>(mData
)
832 .WriteUint8(0x05) // version -- 5
833 .WriteUint8(0x01) // command -- connect
834 .WriteUint8(0x00); // reserved
836 // We're writing a net port after the if, so we need a buffer allowing
837 // to write that much.
838 Buffer
<sizeof(uint16_t)> buf2
;
839 // Add the address to the SOCKS 5 request. SOCKS 5 supports several
840 // address types, so we pick the one that works best for us.
842 // Add the host name. Only a single byte is used to store the length,
843 // so we must prevent long names from being used.
844 buf2
= buf
.WriteUint8(0x03) // addr type -- domainname
845 .WriteUint8(mDestinationHost
.Length()) // name length
846 .WriteString
<MAX_HOSTNAME_LEN
>(mDestinationHost
); // Hostname
848 LOGERROR(("socks5: destination host name is too long!"));
849 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
852 } else if (addr
->raw
.family
== AF_INET
) {
853 buf2
= buf
.WriteUint8(0x01) // addr type -- IPv4
855 } else if (addr
->raw
.family
== AF_INET6
) {
856 buf2
= buf
.WriteUint8(0x04) // addr type -- IPv6
859 LOGERROR(("socks5: destination address of unknown type!"));
860 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
864 auto buf3
= buf2
.WriteNetPort(addr
); // port
865 mDataLength
= buf3
.Written();
870 PRStatus
nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t* type
,
872 MOZ_ASSERT(mState
== SOCKS5_READ_CONNECT_RESPONSE_TOP
||
873 mState
== SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
,
875 MOZ_ASSERT(mDataLength
>= 5,
876 "SOCKS 5 connection reply must be at least 5 bytes!");
878 // Seek to the address location
893 default: // wrong address type
894 LOGERROR(("socks5: wrong address type in connection reply!"));
901 PRStatus
nsSOCKSSocketInfo::ReadV5ConnectResponseTop() {
905 MOZ_ASSERT(mState
== SOCKS5_READ_CONNECT_RESPONSE_TOP
, "Invalid state!");
906 MOZ_ASSERT(mDataLength
== 5,
907 "SOCKS 5 connection reply must be exactly 5 bytes!");
909 LOGDEBUG(("socks5: checking connection reply"));
911 // Check version number
912 if (ReadUint8() != 0x05) {
913 LOGERROR(("socks5: unexpected version in the reply"));
914 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
921 PRErrorCode c
= PR_CONNECT_REFUSED_ERROR
;
926 ("socks5: connect failed: "
927 "01, General SOCKS server failure."));
931 ("socks5: connect failed: "
932 "02, Connection not allowed by ruleset."));
935 LOGERROR(("socks5: connect failed: 03, Network unreachable."));
936 c
= PR_NETWORK_UNREACHABLE_ERROR
;
939 LOGERROR(("socks5: connect failed: 04, Host unreachable."));
940 c
= PR_BAD_ADDRESS_ERROR
;
943 LOGERROR(("socks5: connect failed: 05, Connection refused."));
946 LOGERROR(("socks5: connect failed: 06, TTL expired."));
947 c
= PR_CONNECT_TIMEOUT_ERROR
;
951 ("socks5: connect failed: "
952 "07, Command not supported."));
956 ("socks5: connect failed: "
957 "08, Address type not supported."));
958 c
= PR_BAD_ADDRESS_ERROR
;
961 LOGERROR(("socks5: connect failed."));
965 HandshakeFinished(c
);
969 if (ReadV5AddrTypeAndLength(&res
, &len
) != PR_SUCCESS
) {
970 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
974 mState
= SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
;
980 PRStatus
nsSOCKSSocketInfo::ReadV5ConnectResponseBottom() {
984 MOZ_ASSERT(mState
== SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
, "Invalid state!");
986 if (ReadV5AddrTypeAndLength(&type
, &len
) != PR_SUCCESS
) {
987 HandshakeFinished(PR_BAD_ADDRESS_ERROR
);
991 MOZ_ASSERT(mDataLength
== 7 + len
,
992 "SOCKS 5 unexpected length of connection reply!");
994 LOGDEBUG(("socks5: loading source addr and port"));
995 // Read what the proxy says is our source address
998 ReadNetAddr(&mExternalProxyAddr
, AF_INET
);
1001 ReadNetAddr(&mExternalProxyAddr
, AF_INET6
);
1003 case 0x03: // fqdn (skip)
1005 mExternalProxyAddr
.raw
.family
= AF_INET
;
1009 ReadNetPort(&mExternalProxyAddr
);
1011 LOGDEBUG(("socks5: connected!"));
1012 HandshakeFinished();
1017 void nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to
) { mTimeout
= to
; }
1019 PRStatus
nsSOCKSSocketInfo::DoHandshake(PRFileDesc
* fd
, int16_t oflags
) {
1020 LOGDEBUG(("socks: DoHandshake(), state = %d", mState
));
1024 if (IsLocalProxy()) {
1025 mState
= SOCKS_DNS_COMPLETE
;
1026 mLookupStatus
= NS_OK
;
1027 return ConnectToProxy(fd
);
1030 return StartDNS(fd
);
1031 case SOCKS_DNS_IN_PROGRESS
:
1032 PR_SetError(PR_IN_PROGRESS_ERROR
, 0);
1034 case SOCKS_DNS_COMPLETE
:
1035 return ConnectToProxy(fd
);
1036 case SOCKS_CONNECTING_TO_PROXY
:
1037 return ContinueConnectingToProxy(fd
, oflags
);
1038 case SOCKS4_WRITE_CONNECT_REQUEST
:
1039 if (WriteToSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1041 mState
= SOCKS4_READ_CONNECT_RESPONSE
;
1043 case SOCKS4_READ_CONNECT_RESPONSE
:
1044 if (ReadFromSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1045 return ReadV4ConnectResponse();
1047 case SOCKS5_WRITE_AUTH_REQUEST
:
1048 if (WriteToSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1050 mState
= SOCKS5_READ_AUTH_RESPONSE
;
1052 case SOCKS5_READ_AUTH_RESPONSE
:
1053 if (ReadFromSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1054 return ReadV5AuthResponse();
1055 case SOCKS5_WRITE_USERNAME_REQUEST
:
1056 if (WriteToSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1058 mState
= SOCKS5_READ_USERNAME_RESPONSE
;
1060 case SOCKS5_READ_USERNAME_RESPONSE
:
1061 if (ReadFromSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1062 return ReadV5UsernameResponse();
1063 case SOCKS5_WRITE_CONNECT_REQUEST
:
1064 if (WriteToSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1066 // The SOCKS 5 response to the connection request is variable
1067 // length. First, we'll read enough to tell how long the response
1068 // is, and will read the rest later.
1070 mState
= SOCKS5_READ_CONNECT_RESPONSE_TOP
;
1072 case SOCKS5_READ_CONNECT_RESPONSE_TOP
:
1073 if (ReadFromSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1074 return ReadV5ConnectResponseTop();
1075 case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
:
1076 if (ReadFromSocket(fd
) != PR_SUCCESS
) return PR_FAILURE
;
1077 return ReadV5ConnectResponseBottom();
1079 case SOCKS_CONNECTED
:
1080 LOGERROR(("socks: already connected"));
1081 HandshakeFinished(PR_IS_CONNECTED_ERROR
);
1084 LOGERROR(("socks: already failed"));
1088 LOGERROR(("socks: executing handshake in invalid state, %d", mState
));
1089 HandshakeFinished(PR_INVALID_STATE_ERROR
);
1094 int16_t nsSOCKSSocketInfo::GetPollFlags() const {
1096 case SOCKS_DNS_IN_PROGRESS
:
1097 case SOCKS_DNS_COMPLETE
:
1098 case SOCKS_CONNECTING_TO_PROXY
:
1099 return PR_POLL_EXCEPT
| PR_POLL_WRITE
;
1100 case SOCKS4_WRITE_CONNECT_REQUEST
:
1101 case SOCKS5_WRITE_AUTH_REQUEST
:
1102 case SOCKS5_WRITE_USERNAME_REQUEST
:
1103 case SOCKS5_WRITE_CONNECT_REQUEST
:
1104 return PR_POLL_WRITE
;
1105 case SOCKS4_READ_CONNECT_RESPONSE
:
1106 case SOCKS5_READ_AUTH_RESPONSE
:
1107 case SOCKS5_READ_USERNAME_RESPONSE
:
1108 case SOCKS5_READ_CONNECT_RESPONSE_TOP
:
1109 case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM
:
1110 return PR_POLL_READ
;
1118 inline uint8_t nsSOCKSSocketInfo::ReadUint8() {
1120 MOZ_ASSERT(mReadOffset
+ sizeof(rv
) <= mDataLength
,
1121 "Not enough space to pop a uint8_t!");
1122 rv
= mData
[mReadOffset
];
1123 mReadOffset
+= sizeof(rv
);
1127 inline uint16_t nsSOCKSSocketInfo::ReadUint16() {
1129 MOZ_ASSERT(mReadOffset
+ sizeof(rv
) <= mDataLength
,
1130 "Not enough space to pop a uint16_t!");
1131 memcpy(&rv
, mData
+ mReadOffset
, sizeof(rv
));
1132 mReadOffset
+= sizeof(rv
);
1136 inline uint32_t nsSOCKSSocketInfo::ReadUint32() {
1138 MOZ_ASSERT(mReadOffset
+ sizeof(rv
) <= mDataLength
,
1139 "Not enough space to pop a uint32_t!");
1140 memcpy(&rv
, mData
+ mReadOffset
, sizeof(rv
));
1141 mReadOffset
+= sizeof(rv
);
1145 void nsSOCKSSocketInfo::ReadNetAddr(NetAddr
* addr
, uint16_t fam
) {
1147 const uint8_t* ip
= mData
+ mReadOffset
;
1149 addr
->raw
.family
= fam
;
1150 if (fam
== AF_INET
) {
1151 amt
= sizeof(addr
->inet
.ip
);
1152 MOZ_ASSERT(mReadOffset
+ amt
<= mDataLength
,
1153 "Not enough space to pop an ipv4 addr!");
1154 memcpy(&addr
->inet
.ip
, ip
, amt
);
1155 } else if (fam
== AF_INET6
) {
1156 amt
= sizeof(addr
->inet6
.ip
.u8
);
1157 MOZ_ASSERT(mReadOffset
+ amt
<= mDataLength
,
1158 "Not enough space to pop an ipv6 addr!");
1159 memcpy(addr
->inet6
.ip
.u8
, ip
, amt
);
1165 void nsSOCKSSocketInfo::ReadNetPort(NetAddr
* addr
) {
1166 addr
->inet
.port
= ReadUint16();
1169 void nsSOCKSSocketInfo::WantRead(uint32_t sz
) {
1170 MOZ_ASSERT(mDataIoPtr
== nullptr,
1171 "WantRead() called while I/O already in progress!");
1172 MOZ_ASSERT(mDataLength
+ sz
<= BUFFER_SIZE
, "Can't read that much data!");
1176 PRStatus
nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc
* fd
) {
1180 if (!mAmountToRead
) {
1181 LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
1186 mDataIoPtr
= mData
+ mDataLength
;
1187 mDataLength
+= mAmountToRead
;
1190 end
= mData
+ mDataLength
;
1192 while (mDataIoPtr
< end
) {
1193 rc
= PR_Read(fd
, mDataIoPtr
, end
- mDataIoPtr
);
1196 LOGERROR(("socks: proxy server closed connection"));
1197 HandshakeFinished(PR_CONNECT_REFUSED_ERROR
);
1200 if (PR_GetError() == PR_WOULD_BLOCK_ERROR
) {
1201 LOGDEBUG(("socks: ReadFromSocket(), want read"));
1209 LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
1210 unsigned(mDataIoPtr
- mData
)));
1211 if (mDataIoPtr
== end
) {
1212 mDataIoPtr
= nullptr;
1221 PRStatus
nsSOCKSSocketInfo::WriteToSocket(PRFileDesc
* fd
) {
1226 LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
1230 if (!mDataIoPtr
) mDataIoPtr
= mData
;
1232 end
= mData
+ mDataLength
;
1234 while (mDataIoPtr
< end
) {
1235 rc
= PR_Write(fd
, mDataIoPtr
, end
- mDataIoPtr
);
1237 if (PR_GetError() == PR_WOULD_BLOCK_ERROR
) {
1238 LOGDEBUG(("socks: WriteToSocket(), want write"));
1246 if (mDataIoPtr
== end
) {
1247 mDataIoPtr
= nullptr;
1256 static PRStatus
nsSOCKSIOLayerConnect(PRFileDesc
* fd
, const PRNetAddr
* addr
,
1257 PRIntervalTime to
) {
1261 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1262 if (info
== nullptr) return PR_FAILURE
;
1264 if (addr
->raw
.family
== PR_AF_INET6
&&
1265 PR_IsNetAddrType(addr
, PR_IpAddrV4Mapped
)) {
1266 const uint8_t* srcp
;
1268 LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
1270 // copied from _PR_ConvertToIpv4NetAddr()
1271 dst
.raw
.family
= AF_INET
;
1272 dst
.inet
.ip
= htonl(INADDR_ANY
);
1273 dst
.inet
.port
= htons(0);
1274 srcp
= addr
->ipv6
.ip
.pr_s6_addr
;
1275 memcpy(&dst
.inet
.ip
, srcp
+ 12, 4);
1276 dst
.inet
.family
= AF_INET
;
1277 dst
.inet
.port
= addr
->ipv6
.port
;
1279 memcpy(&dst
, addr
, sizeof(dst
));
1282 info
->SetDestinationAddr(dst
);
1283 info
->SetConnectTimeout(to
);
1286 status
= info
->DoHandshake(fd
, -1);
1287 } while (status
== PR_SUCCESS
&& !info
->IsConnected());
1292 static PRStatus
nsSOCKSIOLayerConnectContinue(PRFileDesc
* fd
, int16_t oflags
) {
1295 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1296 if (info
== nullptr) return PR_FAILURE
;
1299 status
= info
->DoHandshake(fd
, oflags
);
1300 } while (status
== PR_SUCCESS
&& !info
->IsConnected());
1305 static int16_t nsSOCKSIOLayerPoll(PRFileDesc
* fd
, int16_t in_flags
,
1306 int16_t* out_flags
) {
1307 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1308 if (info
== nullptr) return PR_FAILURE
;
1310 if (!info
->IsConnected()) {
1312 return info
->GetPollFlags();
1315 return fd
->lower
->methods
->poll(fd
->lower
, in_flags
, out_flags
);
1318 static PRStatus
nsSOCKSIOLayerClose(PRFileDesc
* fd
) {
1319 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1320 PRDescIdentity id
= PR_GetLayersIdentity(fd
);
1322 if (info
&& id
== nsSOCKSIOLayerIdentity
) {
1325 fd
->identity
= PR_INVALID_IO_LAYER
;
1328 return fd
->lower
->methods
->close(fd
->lower
);
1331 static PRFileDesc
* nsSOCKSIOLayerAccept(PRFileDesc
* fd
, PRNetAddr
* addr
,
1332 PRIntervalTime timeout
) {
1333 // TODO: implement SOCKS support for accept
1334 return fd
->lower
->methods
->accept(fd
->lower
, addr
, timeout
);
1337 static int32_t nsSOCKSIOLayerAcceptRead(PRFileDesc
* sd
, PRFileDesc
** nd
,
1338 PRNetAddr
** raddr
, void* buf
,
1340 PRIntervalTime timeout
) {
1341 // TODO: implement SOCKS support for accept, then read from it
1342 return sd
->lower
->methods
->acceptread(sd
->lower
, nd
, raddr
, buf
, amount
,
1346 static PRStatus
nsSOCKSIOLayerBind(PRFileDesc
* fd
, const PRNetAddr
* addr
) {
1347 // TODO: implement SOCKS support for bind (very similar to connect)
1348 return fd
->lower
->methods
->bind(fd
->lower
, addr
);
1351 static PRStatus
nsSOCKSIOLayerGetName(PRFileDesc
* fd
, PRNetAddr
* addr
) {
1352 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1354 if (info
!= nullptr && addr
!= nullptr) {
1356 info
->GetExternalProxyAddr(temp
);
1357 NetAddrToPRNetAddr(&temp
, addr
);
1364 static PRStatus
nsSOCKSIOLayerGetPeerName(PRFileDesc
* fd
, PRNetAddr
* addr
) {
1365 nsSOCKSSocketInfo
* info
= (nsSOCKSSocketInfo
*)fd
->secret
;
1367 if (info
!= nullptr && addr
!= nullptr) {
1369 info
->GetDestinationAddr(temp
);
1370 NetAddrToPRNetAddr(&temp
, addr
);
1377 static PRStatus
nsSOCKSIOLayerListen(PRFileDesc
* fd
, int backlog
) {
1378 // TODO: implement SOCKS support for listen
1379 return fd
->lower
->methods
->listen(fd
->lower
, backlog
);
1382 // add SOCKS IO layer to an existing socket
1383 nsresult
nsSOCKSIOLayerAddToSocket(int32_t family
, const char* host
,
1384 int32_t port
, nsIProxyInfo
* proxy
,
1385 int32_t socksVersion
, uint32_t flags
,
1386 uint32_t tlsFlags
, PRFileDesc
* fd
) {
1387 NS_ENSURE_TRUE((socksVersion
== 4) || (socksVersion
== 5),
1388 NS_ERROR_NOT_INITIALIZED
);
1391 // XXX hack until NSPR provides an official way to detect system IPv6
1392 // support (bug 388519)
1393 PRFileDesc
* tmpfd
= PR_OpenTCPSocket(PR_AF_INET6
);
1395 ipv6Supported
= false;
1397 // If the system does not support IPv6, NSPR will push
1398 // IPv6-to-IPv4 emulation layer onto the native layer
1399 ipv6Supported
= PR_GetIdentitiesLayer(tmpfd
, PR_NSPR_IO_LAYER
) == tmpfd
;
1403 nsSOCKSIOLayerIdentity
= PR_GetUniqueIdentity("SOCKS layer");
1404 nsSOCKSIOLayerMethods
= *PR_GetDefaultIOMethods();
1406 nsSOCKSIOLayerMethods
.connect
= nsSOCKSIOLayerConnect
;
1407 nsSOCKSIOLayerMethods
.connectcontinue
= nsSOCKSIOLayerConnectContinue
;
1408 nsSOCKSIOLayerMethods
.poll
= nsSOCKSIOLayerPoll
;
1409 nsSOCKSIOLayerMethods
.bind
= nsSOCKSIOLayerBind
;
1410 nsSOCKSIOLayerMethods
.acceptread
= nsSOCKSIOLayerAcceptRead
;
1411 nsSOCKSIOLayerMethods
.getsockname
= nsSOCKSIOLayerGetName
;
1412 nsSOCKSIOLayerMethods
.getpeername
= nsSOCKSIOLayerGetPeerName
;
1413 nsSOCKSIOLayerMethods
.accept
= nsSOCKSIOLayerAccept
;
1414 nsSOCKSIOLayerMethods
.listen
= nsSOCKSIOLayerListen
;
1415 nsSOCKSIOLayerMethods
.close
= nsSOCKSIOLayerClose
;
1420 LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
1425 layer
= PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity
, &nsSOCKSIOLayerMethods
);
1427 LOGERROR(("PR_CreateIOLayerStub() failed."));
1428 return NS_ERROR_FAILURE
;
1431 nsSOCKSSocketInfo
* infoObject
= new nsSOCKSSocketInfo();
1433 // clean up IOLayerStub
1434 LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
1435 PR_Free(layer
); // PR_CreateIOLayerStub() uses PR_Malloc().
1436 return NS_ERROR_FAILURE
;
1439 NS_ADDREF(infoObject
);
1440 infoObject
->Init(socksVersion
, family
, proxy
, host
, flags
, tlsFlags
);
1441 layer
->secret
= (PRFilePrivate
*)infoObject
;
1443 PRDescIdentity fdIdentity
= PR_GetLayersIdentity(fd
);
1445 if (fdIdentity
== mozilla::net::nsNamedPipeLayerIdentity
) {
1446 // remember named pipe fd on the info object so that we can switch
1447 // blocking and non-blocking mode on the pipe later.
1448 infoObject
->SetNamedPipeFD(fd
);
1451 rv
= PR_PushIOLayer(fd
, fdIdentity
, layer
);
1453 if (rv
== PR_FAILURE
) {
1454 LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv
));
1455 NS_RELEASE(infoObject
);
1456 PR_Free(layer
); // PR_CreateIOLayerStub() uses PR_Malloc().
1457 return NS_ERROR_FAILURE
;
1463 bool IsHostLocalTarget(const nsACString
& aHost
) {
1464 #if defined(XP_UNIX)
1465 return StringBeginsWith(aHost
, "file:"_ns
);
1466 #elif defined(XP_WIN)
1467 return IsNamedPipePath(aHost
);