Backed out changeset 88fbb17e3c20 (bug 1865637) for causing animation related mochite...
[gecko.git] / netwerk / socket / nsSOCKSIOLayer.cpp
blobb92de4e87abc734cd5a3f47d1137f9dbb1662645
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 "nsISocketProvider.h"
15 #include "nsNamedPipeIOLayer.h"
16 #include "nsSOCKSIOLayer.h"
17 #include "nsNetCID.h"
18 #include "nsIDNSListener.h"
19 #include "nsICancelable.h"
20 #include "nsThreadUtils.h"
21 #include "nsIFile.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 {
40 enum State {
41 SOCKS_INITIAL,
42 SOCKS_DNS_IN_PROGRESS,
43 SOCKS_DNS_COMPLETE,
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,
54 SOCKS_CONNECTED,
55 SOCKS_FAILED
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;
65 public:
66 nsSOCKSSocketInfo();
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);
85 private:
86 virtual ~nsSOCKSSocketInfo() {
87 ForgetFD();
88 HandshakeFinished();
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();
107 uint8_t ReadUint8();
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) {
125 #ifdef XP_UNIX
126 nsresult rv;
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))) {
132 return rv;
135 nsCOMPtr<nsIFileProtocolHandler> fileHandler(
136 do_QueryInterface(protocolHandler, &rv));
137 if (NS_WARN_IF(NS_FAILED(rv))) {
138 return rv;
141 nsCOMPtr<nsIFile> socketFile;
142 rv = fileHandler->GetFileFromURLSpec(aLocalProxyPath,
143 getter_AddRefs(socketFile));
144 if (NS_WARN_IF(NS_FAILED(rv))) {
145 return rv;
148 nsAutoCString path;
149 if (NS_WARN_IF(NS_FAILED(rv = socketFile->GetNativePath(path)))) {
150 return rv;
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());
161 return NS_OK;
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());
172 return NS_OK;
173 #else
174 mozilla::Unused << aLocalProxyPath;
175 mozilla::Unused << aProxyAddr;
176 return NS_ERROR_NOT_IMPLEMENTED;
177 #endif
180 bool SetupNamedPipeLayer(PRFileDesc* fd) {
181 #if defined(XP_WIN)
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);
186 return true;
188 #endif
189 return false;
192 private:
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};
208 uint32_t mFlags{0};
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.
236 * Example usage:
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)
245 * .WriteUint16(1)
246 * .WriteUint8(2);
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>
263 class Buffer {
264 public:
265 Buffer() = default;
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");
300 return *this;
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());
311 size_t Written() {
312 MOZ_ASSERT(mBuf);
313 return mLength;
316 explicit operator bool() { return !!mBuf; }
318 private:
319 template <size_t Size2>
320 friend class Buffer;
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);
331 MOZ_ASSERT(mBuf);
332 memcpy(mBuf, aValue, aCopyLength);
333 Buffer<Size - Length> result(mBuf + aCopyLength, mLength + aCopyLength);
334 mBuf = nullptr;
335 mLength = 0;
336 return result;
339 uint8_t* mBuf{nullptr};
340 size_t mLength{0};
343 void nsSOCKSSocketInfo::Init(int32_t version, int32_t family,
344 nsIProxyInfo* proxy, const char* host,
345 uint32_t flags, uint32_t tlsFlags) {
346 mVersion = version;
347 mDestinationFamily = family;
348 mProxy = proxy;
349 mDestinationHost = host;
350 mFlags = flags;
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) {
377 if (err == 0) {
378 mState = SOCKS_CONNECTED;
379 #if defined(XP_WIN)
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);
386 mFD = nullptr;
388 #endif
389 } else {
390 mState = SOCKS_FAILED;
391 PR_SetError(PR_UNKNOWN_ERROR, err);
394 // We don't need the buffer any longer, so free it.
395 delete[] mData;
396 mData = nullptr;
397 mDataIoPtr = nullptr;
398 mDataLength = 0;
399 mReadOffset = 0;
400 mAmountToRead = 0;
401 if (mLookup) {
402 mLookup->Cancel(NS_ERROR_FAILURE);
403 mLookup = nullptr;
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;
414 nsCString proxyHost;
415 mProxy->GetHost(proxyHost);
417 mozilla::OriginAttributes attrs;
419 mFD = fd;
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));
425 if (NS_FAILED(rv)) {
426 LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed", proxyHost.get()));
427 return PR_FAILURE;
429 mState = SOCKS_DNS_IN_PROGRESS;
430 PR_SetError(PR_IN_PROGRESS_ERROR, 0);
431 return PR_FAILURE;
434 NS_IMETHODIMP
435 nsSOCKSSocketInfo::OnLookupComplete(nsICancelable* aRequest,
436 nsIDNSRecord* aRecord, nsresult aStatus) {
437 MOZ_ASSERT(aRequest == mLookup, "wrong DNS query");
438 mLookup = nullptr;
439 mLookupStatus = aStatus;
440 mDnsRec = aRecord;
441 mState = SOCKS_DNS_COMPLETE;
442 if (mFD) {
443 ConnectToProxy(mFD);
444 ForgetFD();
446 return NS_OK;
449 PRStatus nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc* fd) {
450 PRStatus status;
451 nsresult rv;
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);
457 return PR_FAILURE;
460 // Try socks5 if the destination addrress is IPv6
461 if (mVersion == 4 && mDestinationAddr.raw.family == AF_INET6) {
462 mVersion = 5;
465 nsAutoCString proxyHost;
466 mProxy->GetHost(proxyHost);
468 int32_t proxyPort;
469 mProxy->GetPort(&proxyPort);
471 int32_t addresses = 0;
472 do {
473 if (IsLocalProxy()) {
474 rv = SetLocalProxyPath(proxyHost, &mInternalProxyAddr);
475 if (NS_FAILED(rv)) {
476 LOGERROR(
477 ("socks: unable to connect to SOCKS proxy, %s", proxyHost.get()));
478 return PR_FAILURE;
480 } else {
481 nsCOMPtr<nsIDNSAddrRecord> record = do_QueryInterface(mDnsRec);
482 MOZ_ASSERT(record);
483 if (addresses++) {
484 record->ReportUnusable(proxyPort);
487 rv = record->GetNextAddr(proxyPort, &mInternalProxyAddr);
488 // No more addresses to try? If so, we'll need to bail
489 if (NS_FAILED(rv)) {
490 LOGERROR(
491 ("socks: unable to connect to SOCKS proxy, %s", proxyHost.get()));
492 return PR_FAILURE;
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);
505 PRNetAddr prProxy;
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;
514 return status;
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;
520 return status;
523 } while (status != PR_SUCCESS);
525 #if defined(XP_WIN)
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);
533 #endif
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) {
544 return;
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) {
549 return;
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
562 return;
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)) {
567 return;
570 // Get an OS native handle from a specified FileDesc
571 PROsfd osfd = PR_FileDesc2NativeHandle(fd);
572 if (osfd == -1) {
573 return;
576 // Create a new FileDesc with a specified family
577 PRFileDesc* tmpfd = PR_OpenTCPSocket(proxyFamily);
578 if (!tmpfd) {
579 return;
581 PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd);
582 if (newsd == -1) {
583 PR_Close(tmpfd);
584 return;
586 // Must succeed because PR_FileDesc2NativeHandle succeeded
587 fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
588 MOZ_ASSERT(fd);
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
594 PR_Close(tmpfd);
595 mDestinationFamily = proxyFamily;
598 PRStatus nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc* fd,
599 int16_t oflags) {
600 PRStatus status;
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
617 return PR_FAILURE;
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);
629 return PR_FAILURE;
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;
639 mDataLength = 0;
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
649 .WriteNetPort(addr);
651 // We don't have anything more to write after the if, so we can
652 // use a buffer with no further writes allowed.
653 Buffer<0> buf3;
654 if (proxy_resolve) {
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.
661 auto buf2 =
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
666 if (!buf2) {
667 LOGERROR(("socks4: destination host name is too long!"));
668 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
669 return PR_FAILURE;
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
677 } else {
678 LOGERROR(("socks: SOCKS 4 can only handle IPv4 addresses!"));
679 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
680 return PR_FAILURE;
683 mDataLength = buf3.Written();
684 return PR_SUCCESS;
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);
697 return PR_FAILURE;
700 // See if our connection request was granted
701 if (ReadUint8() == 90) {
702 LOGDEBUG(("socks4: connection successful!"));
703 HandshakeFinished();
704 return PR_SUCCESS;
707 LOGERROR(("socks4: unable to connect"));
708 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
709 return PR_FAILURE;
712 PRStatus nsSOCKSSocketInfo::WriteV5AuthRequest() {
713 MOZ_ASSERT(mVersion == 5, "SOCKS version must be 5!");
715 mDataLength = 0;
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)
725 .Written();
727 return PR_SUCCESS;
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);
741 return PR_FAILURE;
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);
757 return PR_FAILURE;
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);
766 return PR_FAILURE;
769 nsCString password;
770 mProxy->GetPassword(password);
771 if (password.Length() > MAX_PASSWORD_LEN) {
772 LOGERROR(("socks password is too long"));
773 HandshakeFinished(PR_UNKNOWN_ERROR);
774 return PR_FAILURE;
777 mDataLength = 0;
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!
789 .Written();
791 return PR_SUCCESS;
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);
804 return PR_FAILURE;
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);
811 return PR_FAILURE;
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"));
828 mDataLength = 0;
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.
841 if (proxy_resolve) {
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
847 if (!buf2) {
848 LOGERROR(("socks5: destination host name is too long!"));
849 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
850 return PR_FAILURE;
852 } else if (addr->raw.family == AF_INET) {
853 buf2 = buf.WriteUint8(0x01) // addr type -- IPv4
854 .WriteNetAddr(addr);
855 } else if (addr->raw.family == AF_INET6) {
856 buf2 = buf.WriteUint8(0x04) // addr type -- IPv6
857 .WriteNetAddr(addr);
858 } else {
859 LOGERROR(("socks5: destination address of unknown type!"));
860 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
861 return PR_FAILURE;
864 auto buf3 = buf2.WriteNetPort(addr); // port
865 mDataLength = buf3.Written();
867 return PR_SUCCESS;
870 PRStatus nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t* type,
871 uint32_t* len) {
872 MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
873 mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
874 "Invalid state!");
875 MOZ_ASSERT(mDataLength >= 5,
876 "SOCKS 5 connection reply must be at least 5 bytes!");
878 // Seek to the address location
879 mReadOffset = 3;
881 *type = ReadUint8();
883 switch (*type) {
884 case 0x01: // ipv4
885 *len = 4 - 1;
886 break;
887 case 0x04: // ipv6
888 *len = 16 - 1;
889 break;
890 case 0x03: // fqdn
891 *len = ReadUint8();
892 break;
893 default: // wrong address type
894 LOGERROR(("socks5: wrong address type in connection reply!"));
895 return PR_FAILURE;
898 return PR_SUCCESS;
901 PRStatus nsSOCKSSocketInfo::ReadV5ConnectResponseTop() {
902 uint8_t res;
903 uint32_t len;
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);
915 return PR_FAILURE;
918 // Check response
919 res = ReadUint8();
920 if (res != 0x00) {
921 PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
923 switch (res) {
924 case 0x01:
925 LOGERROR(
926 ("socks5: connect failed: "
927 "01, General SOCKS server failure."));
928 break;
929 case 0x02:
930 LOGERROR(
931 ("socks5: connect failed: "
932 "02, Connection not allowed by ruleset."));
933 break;
934 case 0x03:
935 LOGERROR(("socks5: connect failed: 03, Network unreachable."));
936 c = PR_NETWORK_UNREACHABLE_ERROR;
937 break;
938 case 0x04:
939 LOGERROR(("socks5: connect failed: 04, Host unreachable."));
940 c = PR_BAD_ADDRESS_ERROR;
941 break;
942 case 0x05:
943 LOGERROR(("socks5: connect failed: 05, Connection refused."));
944 break;
945 case 0x06:
946 LOGERROR(("socks5: connect failed: 06, TTL expired."));
947 c = PR_CONNECT_TIMEOUT_ERROR;
948 break;
949 case 0x07:
950 LOGERROR(
951 ("socks5: connect failed: "
952 "07, Command not supported."));
953 break;
954 case 0x08:
955 LOGERROR(
956 ("socks5: connect failed: "
957 "08, Address type not supported."));
958 c = PR_BAD_ADDRESS_ERROR;
959 break;
960 default:
961 LOGERROR(("socks5: connect failed."));
962 break;
965 HandshakeFinished(c);
966 return PR_FAILURE;
969 if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
970 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
971 return PR_FAILURE;
974 mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
975 WantRead(len + 2);
977 return PR_SUCCESS;
980 PRStatus nsSOCKSSocketInfo::ReadV5ConnectResponseBottom() {
981 uint8_t type;
982 uint32_t len;
984 MOZ_ASSERT(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM, "Invalid state!");
986 if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
987 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
988 return PR_FAILURE;
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
996 switch (type) {
997 case 0x01: // ipv4
998 ReadNetAddr(&mExternalProxyAddr, AF_INET);
999 break;
1000 case 0x04: // ipv6
1001 ReadNetAddr(&mExternalProxyAddr, AF_INET6);
1002 break;
1003 case 0x03: // fqdn (skip)
1004 mReadOffset += len;
1005 mExternalProxyAddr.raw.family = AF_INET;
1006 break;
1009 ReadNetPort(&mExternalProxyAddr);
1011 LOGDEBUG(("socks5: connected!"));
1012 HandshakeFinished();
1014 return PR_SUCCESS;
1017 void nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to) { mTimeout = to; }
1019 PRStatus nsSOCKSSocketInfo::DoHandshake(PRFileDesc* fd, int16_t oflags) {
1020 LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
1022 switch (mState) {
1023 case SOCKS_INITIAL:
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);
1033 return PR_FAILURE;
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;
1040 WantRead(8);
1041 mState = SOCKS4_READ_CONNECT_RESPONSE;
1042 return PR_SUCCESS;
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;
1049 WantRead(2);
1050 mState = SOCKS5_READ_AUTH_RESPONSE;
1051 return PR_SUCCESS;
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;
1057 WantRead(2);
1058 mState = SOCKS5_READ_USERNAME_RESPONSE;
1059 return PR_SUCCESS;
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.
1069 WantRead(5);
1070 mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
1071 return PR_SUCCESS;
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);
1082 return PR_FAILURE;
1083 case SOCKS_FAILED:
1084 LOGERROR(("socks: already failed"));
1085 return PR_FAILURE;
1088 LOGERROR(("socks: executing handshake in invalid state, %d", mState));
1089 HandshakeFinished(PR_INVALID_STATE_ERROR);
1091 return PR_FAILURE;
1094 int16_t nsSOCKSSocketInfo::GetPollFlags() const {
1095 switch (mState) {
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;
1111 default:
1112 break;
1115 return 0;
1118 inline uint8_t nsSOCKSSocketInfo::ReadUint8() {
1119 uint8_t rv;
1120 MOZ_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1121 "Not enough space to pop a uint8_t!");
1122 rv = mData[mReadOffset];
1123 mReadOffset += sizeof(rv);
1124 return rv;
1127 inline uint16_t nsSOCKSSocketInfo::ReadUint16() {
1128 uint16_t rv;
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);
1133 return rv;
1136 inline uint32_t nsSOCKSSocketInfo::ReadUint32() {
1137 uint32_t rv;
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);
1142 return rv;
1145 void nsSOCKSSocketInfo::ReadNetAddr(NetAddr* addr, uint16_t fam) {
1146 uint32_t amt = 0;
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);
1162 mReadOffset += 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!");
1173 mAmountToRead = sz;
1176 PRStatus nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc* fd) {
1177 int32_t rc;
1178 const uint8_t* end;
1180 if (!mAmountToRead) {
1181 LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
1182 return PR_SUCCESS;
1185 if (!mDataIoPtr) {
1186 mDataIoPtr = mData + mDataLength;
1187 mDataLength += mAmountToRead;
1190 end = mData + mDataLength;
1192 while (mDataIoPtr < end) {
1193 rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
1194 if (rc <= 0) {
1195 if (rc == 0) {
1196 LOGERROR(("socks: proxy server closed connection"));
1197 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
1198 return PR_FAILURE;
1200 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
1201 LOGDEBUG(("socks: ReadFromSocket(), want read"));
1203 break;
1206 mDataIoPtr += rc;
1209 LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
1210 unsigned(mDataIoPtr - mData)));
1211 if (mDataIoPtr == end) {
1212 mDataIoPtr = nullptr;
1213 mAmountToRead = 0;
1214 mReadOffset = 0;
1215 return PR_SUCCESS;
1218 return PR_FAILURE;
1221 PRStatus nsSOCKSSocketInfo::WriteToSocket(PRFileDesc* fd) {
1222 int32_t rc;
1223 const uint8_t* end;
1225 if (!mDataLength) {
1226 LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
1227 return PR_SUCCESS;
1230 if (!mDataIoPtr) mDataIoPtr = mData;
1232 end = mData + mDataLength;
1234 while (mDataIoPtr < end) {
1235 rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
1236 if (rc < 0) {
1237 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
1238 LOGDEBUG(("socks: WriteToSocket(), want write"));
1240 break;
1243 mDataIoPtr += rc;
1246 if (mDataIoPtr == end) {
1247 mDataIoPtr = nullptr;
1248 mDataLength = 0;
1249 mReadOffset = 0;
1250 return PR_SUCCESS;
1253 return PR_FAILURE;
1256 static PRStatus nsSOCKSIOLayerConnect(PRFileDesc* fd, const PRNetAddr* addr,
1257 PRIntervalTime to) {
1258 PRStatus status;
1259 NetAddr dst;
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;
1278 } else {
1279 memcpy(&dst, addr, sizeof(dst));
1282 info->SetDestinationAddr(dst);
1283 info->SetConnectTimeout(to);
1285 do {
1286 status = info->DoHandshake(fd, -1);
1287 } while (status == PR_SUCCESS && !info->IsConnected());
1289 return status;
1292 static PRStatus nsSOCKSIOLayerConnectContinue(PRFileDesc* fd, int16_t oflags) {
1293 PRStatus status;
1295 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1296 if (info == nullptr) return PR_FAILURE;
1298 do {
1299 status = info->DoHandshake(fd, oflags);
1300 } while (status == PR_SUCCESS && !info->IsConnected());
1302 return status;
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()) {
1311 *out_flags = 0;
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) {
1323 info->ForgetFD();
1324 NS_RELEASE(info);
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,
1339 int32_t amount,
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,
1343 timeout);
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) {
1355 NetAddr temp;
1356 info->GetExternalProxyAddr(temp);
1357 NetAddrToPRNetAddr(&temp, addr);
1358 return PR_SUCCESS;
1361 return PR_FAILURE;
1364 static PRStatus nsSOCKSIOLayerGetPeerName(PRFileDesc* fd, PRNetAddr* addr) {
1365 nsSOCKSSocketInfo* info = (nsSOCKSSocketInfo*)fd->secret;
1367 if (info != nullptr && addr != nullptr) {
1368 NetAddr temp;
1369 info->GetDestinationAddr(temp);
1370 NetAddrToPRNetAddr(&temp, addr);
1371 return PR_SUCCESS;
1374 return PR_FAILURE;
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);
1390 if (firstTime) {
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);
1394 if (!tmpfd) {
1395 ipv6Supported = false;
1396 } else {
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;
1400 PR_Close(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;
1417 firstTime = false;
1420 LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
1422 PRFileDesc* layer;
1423 PRStatus rv;
1425 layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
1426 if (!layer) {
1427 LOGERROR(("PR_CreateIOLayerStub() failed."));
1428 return NS_ERROR_FAILURE;
1431 nsSOCKSSocketInfo* infoObject = new nsSOCKSSocketInfo();
1432 if (!infoObject) {
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);
1444 #if defined(XP_WIN)
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);
1450 #endif
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;
1460 return NS_OK;
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);
1468 #else
1469 return false;
1470 #endif // XP_UNIX