netwerk/socket/nsSOCKSIOLayer.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:26bb077afe70
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 sw=4 sts=4 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/. */
6
7 #include "nspr.h"
8 #include "private/pprio.h"
9 #include "nsString.h"
10 #include "nsCRT.h"
11
12 #include "nsIServiceManager.h"
13 #include "nsIDNSService.h"
14 #include "nsIDNSRecord.h"
15 #include "nsISOCKSSocketInfo.h"
16 #include "nsISocketProvider.h"
17 #include "nsSOCKSIOLayer.h"
18 #include "nsNetCID.h"
19 #include "nsIDNSListener.h"
20 #include "nsICancelable.h"
21 #include "nsThreadUtils.h"
22 #include "mozilla/net/DNS.h"
23
24 using namespace mozilla::net;
25
26 static PRDescIdentity nsSOCKSIOLayerIdentity;
27 static PRIOMethods nsSOCKSIOLayerMethods;
28 static bool firstTime = true;
29 static bool ipv6Supported = true;
30
31 #if defined(PR_LOGGING)
32 static PRLogModuleInfo *gSOCKSLog;
33 #define LOGDEBUG(args) PR_LOG(gSOCKSLog, PR_LOG_DEBUG, args)
34 #define LOGERROR(args) PR_LOG(gSOCKSLog, PR_LOG_ERROR , args)
35
36 #else
37 #define LOGDEBUG(args)
38 #define LOGERROR(args)
39 #endif
40
41 class nsSOCKSSocketInfo : public nsISOCKSSocketInfo
42 , public nsIDNSListener
43 {
44 enum State {
45 SOCKS_INITIAL,
46 SOCKS_DNS_IN_PROGRESS,
47 SOCKS_DNS_COMPLETE,
48 SOCKS_CONNECTING_TO_PROXY,
49 SOCKS4_WRITE_CONNECT_REQUEST,
50 SOCKS4_READ_CONNECT_RESPONSE,
51 SOCKS5_WRITE_AUTH_REQUEST,
52 SOCKS5_READ_AUTH_RESPONSE,
53 SOCKS5_WRITE_USERNAME_REQUEST,
54 SOCKS5_READ_USERNAME_RESPONSE,
55 SOCKS5_WRITE_CONNECT_REQUEST,
56 SOCKS5_READ_CONNECT_RESPONSE_TOP,
57 SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
58 SOCKS_CONNECTED,
59 SOCKS_FAILED
60 };
61
62 // A buffer of 520 bytes should be enough for any request and response
63 // in case of SOCKS4 as well as SOCKS5
64 static const uint32_t BUFFER_SIZE = 520;
65 static const uint32_t MAX_HOSTNAME_LEN = 255;
66 static const uint32_t MAX_USERNAME_LEN = 255;
67 static const uint32_t MAX_PASSWORD_LEN = 255;
68
69 public:
70 nsSOCKSSocketInfo();
71 virtual ~nsSOCKSSocketInfo() { HandshakeFinished(); }
72
73 NS_DECL_THREADSAFE_ISUPPORTS
74 NS_DECL_NSISOCKSSOCKETINFO
75 NS_DECL_NSIDNSLISTENER
76
77 void Init(int32_t version,
78 int32_t family,
79 nsIProxyInfo *proxy,
80 const char *destinationHost,
81 uint32_t flags);
82
83 void SetConnectTimeout(PRIntervalTime to);
84 PRStatus DoHandshake(PRFileDesc *fd, int16_t oflags = -1);
85 int16_t GetPollFlags() const;
86 bool IsConnected() const { return (mState == SOCKS_CONNECTED ||
87 mState == SOCKS5_READ_CONNECT_RESPONSE_TOP); }
88
89 void ForgetFD() { mFD = nullptr; }
90
91 private:
92 void HandshakeFinished(PRErrorCode err = 0);
93 PRStatus StartDNS(PRFileDesc *fd);
94 PRStatus ConnectToProxy(PRFileDesc *fd);
95 void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy);
96 PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags);
97 PRStatus WriteV4ConnectRequest();
98 PRStatus ReadV4ConnectResponse();
99 PRStatus WriteV5AuthRequest();
100 PRStatus ReadV5AuthResponse();
101 PRStatus WriteV5UsernameRequest();
102 PRStatus ReadV5UsernameResponse();
103 PRStatus WriteV5ConnectRequest();
104 PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len);
105 PRStatus ReadV5ConnectResponseTop();
106 PRStatus ReadV5ConnectResponseBottom();
107
108 void WriteUint8(uint8_t d);
109 void WriteUint16(uint16_t d);
110 void WriteUint32(uint32_t d);
111 void WriteNetAddr(const NetAddr *addr);
112 void WriteNetPort(const NetAddr *addr);
113 void WriteString(const nsACString &str);
114
115 uint8_t ReadUint8();
116 uint16_t ReadUint16();
117 uint32_t ReadUint32();
118 void ReadNetAddr(NetAddr *addr, uint16_t fam);
119 void ReadNetPort(NetAddr *addr);
120
121 void WantRead(uint32_t sz);
122 PRStatus ReadFromSocket(PRFileDesc *fd);
123 PRStatus WriteToSocket(PRFileDesc *fd);
124
125 private:
126 State mState;
127 uint8_t * mData;
128 uint8_t * mDataIoPtr;
129 uint32_t mDataLength;
130 uint32_t mReadOffset;
131 uint32_t mAmountToRead;
132 nsCOMPtr<nsIDNSRecord> mDnsRec;
133 nsCOMPtr<nsICancelable> mLookup;
134 nsresult mLookupStatus;
135 PRFileDesc *mFD;
136
137 nsCString mDestinationHost;
138 nsCOMPtr<nsIProxyInfo> mProxy;
139 int32_t mVersion; // SOCKS version 4 or 5
140 int32_t mDestinationFamily;
141 uint32_t mFlags;
142 NetAddr mInternalProxyAddr;
143 NetAddr mExternalProxyAddr;
144 NetAddr mDestinationAddr;
145 PRIntervalTime mTimeout;
146 nsCString mProxyUsername; // Cache, from mProxy
147 };
148
149 nsSOCKSSocketInfo::nsSOCKSSocketInfo()
150 : mState(SOCKS_INITIAL)
151 , mDataIoPtr(nullptr)
152 , mDataLength(0)
153 , mReadOffset(0)
154 , mAmountToRead(0)
155 , mVersion(-1)
156 , mDestinationFamily(AF_INET)
157 , mFlags(0)
158 , mTimeout(PR_INTERVAL_NO_TIMEOUT)
159 {
160 mData = new uint8_t[BUFFER_SIZE];
161
162 mInternalProxyAddr.raw.family = AF_INET;
163 mInternalProxyAddr.inet.ip = htonl(INADDR_ANY);
164 mInternalProxyAddr.inet.port = htons(0);
165
166 mExternalProxyAddr.raw.family = AF_INET;
167 mExternalProxyAddr.inet.ip = htonl(INADDR_ANY);
168 mExternalProxyAddr.inet.port = htons(0);
169
170 mDestinationAddr.raw.family = AF_INET;
171 mDestinationAddr.inet.ip = htonl(INADDR_ANY);
172 mDestinationAddr.inet.port = htons(0);
173 }
174
175 void
176 nsSOCKSSocketInfo::Init(int32_t version, int32_t family, nsIProxyInfo *proxy, const char *host, uint32_t flags)
177 {
178 mVersion = version;
179 mDestinationFamily = family;
180 mProxy = proxy;
181 mDestinationHost = host;
182 mFlags = flags;
183 mProxy->GetUsername(mProxyUsername); // cache
184 }
185
186 NS_IMPL_ISUPPORTS(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
187
188 NS_IMETHODIMP
189 nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr)
190 {
191 memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr));
192 return NS_OK;
193 }
194
195 NS_IMETHODIMP
196 nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr *aExternalProxyAddr)
197 {
198 memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr));
199 return NS_OK;
200 }
201
202 NS_IMETHODIMP
203 nsSOCKSSocketInfo::GetDestinationAddr(NetAddr * *aDestinationAddr)
204 {
205 memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr));
206 return NS_OK;
207 }
208
209 NS_IMETHODIMP
210 nsSOCKSSocketInfo::SetDestinationAddr(NetAddr *aDestinationAddr)
211 {
212 memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr));
213 return NS_OK;
214 }
215
216 NS_IMETHODIMP
217 nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr * *aInternalProxyAddr)
218 {
219 memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr));
220 return NS_OK;
221 }
222
223 NS_IMETHODIMP
224 nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr *aInternalProxyAddr)
225 {
226 memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr));
227 return NS_OK;
228 }
229
230 // There needs to be a means of distinguishing between connection errors
231 // that the SOCKS server reports when it rejects a connection request, and
232 // connection errors that happen while attempting to connect to the SOCKS
233 // server. Otherwise, Firefox will report incorrectly that the proxy server
234 // is refusing connections when a SOCKS request is rejected by the proxy.
235 // When a SOCKS handshake failure occurs, the PR error is set to
236 // PR_UNKNOWN_ERROR, and the real error code is returned via the OS error.
237 void
238 nsSOCKSSocketInfo::HandshakeFinished(PRErrorCode err)
239 {
240 if (err == 0) {
241 mState = SOCKS_CONNECTED;
242 } else {
243 mState = SOCKS_FAILED;
244 PR_SetError(PR_UNKNOWN_ERROR, err);
245 }
246
247 // We don't need the buffer any longer, so free it.
248 delete [] mData;
249 mData = nullptr;
250 mDataIoPtr = nullptr;
251 mDataLength = 0;
252 mReadOffset = 0;
253 mAmountToRead = 0;
254 if (mLookup) {
255 mLookup->Cancel(NS_ERROR_FAILURE);
256 mLookup = nullptr;
257 }
258 }
259
260 PRStatus
261 nsSOCKSSocketInfo::StartDNS(PRFileDesc *fd)
262 {
263 NS_ABORT_IF_FALSE(!mDnsRec && mState == SOCKS_INITIAL,
264 "Must be in initial state to make DNS Lookup");
265
266 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
267 if (!dns)
268 return PR_FAILURE;
269
270 nsCString proxyHost;
271 mProxy->GetHost(proxyHost);
272
273 mFD = fd;
274 nsresult rv = dns->AsyncResolve(proxyHost, 0, this,
275 NS_GetCurrentThread(),
276 getter_AddRefs(mLookup));
277
278 if (NS_FAILED(rv)) {
279 LOGERROR(("socks: DNS lookup for SOCKS proxy %s failed",
280 proxyHost.get()));
281 return PR_FAILURE;
282 }
283 mState = SOCKS_DNS_IN_PROGRESS;
284 PR_SetError(PR_IN_PROGRESS_ERROR, 0);
285 return PR_FAILURE;
286 }
287
288 NS_IMETHODIMP
289 nsSOCKSSocketInfo::OnLookupComplete(nsICancelable *aRequest,
290 nsIDNSRecord *aRecord,
291 nsresult aStatus)
292 {
293 NS_ABORT_IF_FALSE(aRequest == mLookup, "wrong DNS query");
294 mLookup = nullptr;
295 mLookupStatus = aStatus;
296 mDnsRec = aRecord;
297 mState = SOCKS_DNS_COMPLETE;
298 if (mFD) {
299 ConnectToProxy(mFD);
300 ForgetFD();
301 }
302 return NS_OK;
303 }
304
305 PRStatus
306 nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd)
307 {
308 PRStatus status;
309 nsresult rv;
310
311 NS_ABORT_IF_FALSE(mState == SOCKS_DNS_COMPLETE,
312 "Must have DNS to make connection!");
313
314 if (NS_FAILED(mLookupStatus)) {
315 PR_SetError(PR_BAD_ADDRESS_ERROR, 0);
316 return PR_FAILURE;
317 }
318
319 // Try socks5 if the destination addrress is IPv6
320 if (mVersion == 4 &&
321 mDestinationAddr.raw.family == AF_INET6) {
322 mVersion = 5;
323 }
324
325 int32_t proxyPort;
326 mProxy->GetPort(&proxyPort);
327
328 int32_t addresses = 0;
329 do {
330 if (addresses++)
331 mDnsRec->ReportUnusable(proxyPort);
332
333 rv = mDnsRec->GetNextAddr(proxyPort, &mInternalProxyAddr);
334 // No more addresses to try? If so, we'll need to bail
335 if (NS_FAILED(rv)) {
336 nsCString proxyHost;
337 mProxy->GetHost(proxyHost);
338 LOGERROR(("socks: unable to connect to SOCKS proxy, %s",
339 proxyHost.get()));
340 return PR_FAILURE;
341 }
342
343 #if defined(PR_LOGGING)
344 char buf[kIPv6CStrBufSize];
345 NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf));
346 LOGDEBUG(("socks: trying proxy server, %s:%hu",
347 buf, ntohs(mInternalProxyAddr.inet.port)));
348 #endif
349 NetAddr proxy = mInternalProxyAddr;
350 FixupAddressFamily(fd, &proxy);
351 PRNetAddr prProxy;
352 NetAddrToPRNetAddr(&proxy, &prProxy);
353 status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout);
354 if (status != PR_SUCCESS) {
355 PRErrorCode c = PR_GetError();
356 // If EINPROGRESS, return now and check back later after polling
357 if (c == PR_WOULD_BLOCK_ERROR || c == PR_IN_PROGRESS_ERROR) {
358 mState = SOCKS_CONNECTING_TO_PROXY;
359 return status;
360 }
361 }
362 } while (status != PR_SUCCESS);
363
364 // Connected now, start SOCKS
365 if (mVersion == 4)
366 return WriteV4ConnectRequest();
367 return WriteV5AuthRequest();
368 }
369
370 void
371 nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy)
372 {
373 int32_t proxyFamily = mInternalProxyAddr.raw.family;
374 // Do nothing if the address family is already matched
375 if (proxyFamily == mDestinationFamily) {
376 return;
377 }
378 // If the system does not support IPv6 and the proxy address is IPv6,
379 // We can do nothing here.
380 if (proxyFamily == AF_INET6 && !ipv6Supported) {
381 return;
382 }
383 // If the system does not support IPv6 and the destination address is
384 // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy
385 // the emulation layer
386 if (mDestinationFamily == AF_INET6 && !ipv6Supported) {
387 proxy->inet6.family = AF_INET6;
388 proxy->inet6.port = mInternalProxyAddr.inet.port;
389 uint8_t *proxyp = proxy->inet6.ip.u8;
390 memset(proxyp, 0, 10);
391 memset(proxyp + 10, 0xff, 2);
392 memcpy(proxyp + 12,(char *) &mInternalProxyAddr.inet.ip, 4);
393 // mDestinationFamily should not be updated
394 return;
395 }
396 // Get an OS native handle from a specified FileDesc
397 PROsfd osfd = PR_FileDesc2NativeHandle(fd);
398 if (osfd == -1) {
399 return;
400 }
401 // Create a new FileDesc with a specified family
402 PRFileDesc *tmpfd = PR_OpenTCPSocket(proxyFamily);
403 if (!tmpfd) {
404 return;
405 }
406 PROsfd newsd = PR_FileDesc2NativeHandle(tmpfd);
407 if (newsd == -1) {
408 PR_Close(tmpfd);
409 return;
410 }
411 // Must succeed because PR_FileDesc2NativeHandle succeeded
412 fd = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
413 MOZ_ASSERT(fd);
414 // Swap OS native handles
415 PR_ChangeFileDescNativeHandle(fd, newsd);
416 PR_ChangeFileDescNativeHandle(tmpfd, osfd);
417 // Close temporary FileDesc which is now associated with
418 // old OS native handle
419 PR_Close(tmpfd);
420 mDestinationFamily = proxyFamily;
421 }
422
423 PRStatus
424 nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags)
425 {
426 PRStatus status;
427
428 NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
429 "Continuing connection in wrong state!");
430
431 LOGDEBUG(("socks: continuing connection to proxy"));
432
433 status = fd->lower->methods->connectcontinue(fd->lower, oflags);
434 if (status != PR_SUCCESS) {
435 PRErrorCode c = PR_GetError();
436 if (c != PR_WOULD_BLOCK_ERROR && c != PR_IN_PROGRESS_ERROR) {
437 // A connection failure occured, try another address
438 mState = SOCKS_DNS_COMPLETE;
439 return ConnectToProxy(fd);
440 }
441
442 // We're still connecting
443 return PR_FAILURE;
444 }
445
446 // Connected now, start SOCKS
447 if (mVersion == 4)
448 return WriteV4ConnectRequest();
449 return WriteV5AuthRequest();
450 }
451
452 PRStatus
453 nsSOCKSSocketInfo::WriteV4ConnectRequest()
454 {
455 if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
456 LOGERROR(("socks username is too long"));
457 HandshakeFinished(PR_UNKNOWN_ERROR);
458 return PR_FAILURE;
459 }
460
461 NetAddr *addr = &mDestinationAddr;
462 int32_t proxy_resolve;
463
464 NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY,
465 "Invalid state!");
466
467 proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
468
469 mDataLength = 0;
470 mState = SOCKS4_WRITE_CONNECT_REQUEST;
471
472 LOGDEBUG(("socks4: sending connection request (socks4a resolve? %s)",
473 proxy_resolve? "yes" : "no"));
474
475 // Send a SOCKS 4 connect request.
476 WriteUint8(0x04); // version -- 4
477 WriteUint8(0x01); // command -- connect
478 WriteNetPort(addr);
479 if (proxy_resolve) {
480 // Add the full name, null-terminated, to the request
481 // according to SOCKS 4a. A fake IP address, with the first
482 // four bytes set to 0 and the last byte set to something other
483 // than 0, is used to notify the proxy that this is a SOCKS 4a
484 // request. This request type works for Tor and perhaps others.
485 WriteUint32(htonl(0x00000001)); // Fake IP
486 WriteString(mProxyUsername); // Send username. May be empty.
487 WriteUint8(0x00); // Null-terminate username
488 // Password not supported by V4.
489 if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
490 LOGERROR(("socks4: destination host name is too long!"));
491 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
492 return PR_FAILURE;
493 }
494 WriteString(mDestinationHost); // Hostname
495 WriteUint8(0x00);
496 } else if (addr->raw.family == AF_INET) {
497 WriteNetAddr(addr); // Add the IPv4 address
498 WriteString(mProxyUsername); // Send username. May be empty.
499 WriteUint8(0x00); // Null-terminate username
500 // Password not supported by V4.
501 } else if (addr->raw.family == AF_INET6) {
502 LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!"));
503 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
504 return PR_FAILURE;
505 }
506
507 return PR_SUCCESS;
508 }
509
510 PRStatus
511 nsSOCKSSocketInfo::ReadV4ConnectResponse()
512 {
513 NS_ABORT_IF_FALSE(mState == SOCKS4_READ_CONNECT_RESPONSE,
514 "Handling SOCKS 4 connection reply in wrong state!");
515
516 LOGDEBUG(("socks4: checking connection reply"));
517
518 if (mDataLength != 8) {
519 LOGERROR(("SOCKS 4 connection reply must be 8 bytes!"));
520 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
521 return PR_FAILURE;
522 }
523
524 if (ReadUint8() != 0x00) {
525 LOGERROR(("socks4: wrong connection reply"));
526 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
527 return PR_FAILURE;
528 }
529
530 // See if our connection request was granted
531 if (ReadUint8() == 90) {
532 LOGDEBUG(("socks4: connection successful!"));
533 HandshakeFinished();
534 return PR_SUCCESS;
535 }
536
537 LOGERROR(("socks4: unable to connect"));
538 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
539 return PR_FAILURE;
540 }
541
542 PRStatus
543 nsSOCKSSocketInfo::WriteV5AuthRequest()
544 {
545 NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
546
547 mDataLength = 0;
548 mState = SOCKS5_WRITE_AUTH_REQUEST;
549
550 // Send an initial SOCKS 5 greeting
551 LOGDEBUG(("socks5: sending auth methods"));
552 WriteUint8(0x05); // version -- 5
553 WriteUint8(0x01); // # of auth methods -- 1
554 if (mProxyUsername.IsEmpty()) {
555 WriteUint8(0x00); // no authentication
556 } else {
557 WriteUint8(0x02); // username/password
558 }
559
560 return PR_SUCCESS;
561 }
562
563 PRStatus
564 nsSOCKSSocketInfo::ReadV5AuthResponse()
565 {
566 NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE,
567 "Handling SOCKS 5 auth method reply in wrong state!");
568
569 if (mDataLength != 2) {
570 LOGERROR(("SOCKS 5 auth method reply must be 2 bytes"));
571 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
572 return PR_FAILURE;
573 }
574
575 // Check version number
576 if (ReadUint8() != 0x05) {
577 LOGERROR(("socks5: unexpected version in the reply"));
578 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
579 return PR_FAILURE;
580 }
581
582 // Make sure our authentication choice was accepted,
583 // and continue accordingly
584 uint8_t authMethod = ReadUint8();
585 if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth
586 LOGDEBUG(("socks5: server allows connection without authentication"));
587 return WriteV5ConnectRequest();
588 } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw
589 LOGDEBUG(("socks5: auth method accepted by server"));
590 return WriteV5UsernameRequest();
591 } else { // 0xFF signals error
592 LOGERROR(("socks5: server did not accept our authentication method"));
593 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
594 return PR_FAILURE;
595 }
596 }
597
598 PRStatus
599 nsSOCKSSocketInfo::WriteV5UsernameRequest()
600 {
601 NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
602
603 if (mProxyUsername.Length() > MAX_USERNAME_LEN) {
604 LOGERROR(("socks username is too long"));
605 HandshakeFinished(PR_UNKNOWN_ERROR);
606 return PR_FAILURE;
607 }
608
609 nsCString password;
610 mProxy->GetPassword(password);
611 if (password.Length() > MAX_PASSWORD_LEN) {
612 LOGERROR(("socks password is too long"));
613 HandshakeFinished(PR_UNKNOWN_ERROR);
614 return PR_FAILURE;
615 }
616
617 mDataLength = 0;
618 mState = SOCKS5_WRITE_USERNAME_REQUEST;
619
620 LOGDEBUG(("socks5: sending username and password"));
621 // RFC 1929 Username/password auth for SOCKS 5
622 WriteUint8(0x01); // version 1 (not 5)
623 WriteUint8(mProxyUsername.Length()); // username length
624 WriteString(mProxyUsername); // username
625 WriteUint8(password.Length()); // password length
626 WriteString(password); // password. WARNING: Sent unencrypted!
627
628 return PR_SUCCESS;
629 }
630
631 PRStatus
632 nsSOCKSSocketInfo::ReadV5UsernameResponse()
633 {
634 NS_ABORT_IF_FALSE(mState == SOCKS5_READ_USERNAME_RESPONSE,
635 "Handling SOCKS 5 username/password reply in wrong state!");
636
637 if (mDataLength != 2) {
638 LOGERROR(("SOCKS 5 username reply must be 2 bytes"));
639 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
640 return PR_FAILURE;
641 }
642
643 // Check version number, must be 1 (not 5)
644 if (ReadUint8() != 0x01) {
645 LOGERROR(("socks5: unexpected version in the reply"));
646 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
647 return PR_FAILURE;
648 }
649
650 // Check whether username/password were accepted
651 if (ReadUint8() != 0x00) { // 0 = success
652 LOGERROR(("socks5: username/password not accepted"));
653 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
654 return PR_FAILURE;
655 }
656
657 LOGDEBUG(("socks5: username/password accepted by server"));
658
659 return WriteV5ConnectRequest();
660 }
661
662 PRStatus
663 nsSOCKSSocketInfo::WriteV5ConnectRequest()
664 {
665 // Send SOCKS 5 connect request
666 NetAddr *addr = &mDestinationAddr;
667 int32_t proxy_resolve;
668 proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST;
669
670 LOGDEBUG(("socks5: sending connection request (socks5 resolve? %s)",
671 proxy_resolve? "yes" : "no"));
672
673 mDataLength = 0;
674 mState = SOCKS5_WRITE_CONNECT_REQUEST;
675
676 WriteUint8(0x05); // version -- 5
677 WriteUint8(0x01); // command -- connect
678 WriteUint8(0x00); // reserved
679
680 // Add the address to the SOCKS 5 request. SOCKS 5 supports several
681 // address types, so we pick the one that works best for us.
682 if (proxy_resolve) {
683 // Add the host name. Only a single byte is used to store the length,
684 // so we must prevent long names from being used.
685 if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
686 LOGERROR(("socks5: destination host name is too long!"));
687 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
688 return PR_FAILURE;
689 }
690 WriteUint8(0x03); // addr type -- domainname
691 WriteUint8(mDestinationHost.Length()); // name length
692 WriteString(mDestinationHost);
693 } else if (addr->raw.family == AF_INET) {
694 WriteUint8(0x01); // addr type -- IPv4
695 WriteNetAddr(addr);
696 } else if (addr->raw.family == AF_INET6) {
697 WriteUint8(0x04); // addr type -- IPv6
698 WriteNetAddr(addr);
699 } else {
700 LOGERROR(("socks5: destination address of unknown type!"));
701 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
702 return PR_FAILURE;
703 }
704
705 WriteNetPort(addr); // port
706
707 return PR_SUCCESS;
708 }
709
710 PRStatus
711 nsSOCKSSocketInfo::ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len)
712 {
713 NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP ||
714 mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
715 "Invalid state!");
716 NS_ABORT_IF_FALSE(mDataLength >= 5,
717 "SOCKS 5 connection reply must be at least 5 bytes!");
718
719 // Seek to the address location
720 mReadOffset = 3;
721
722 *type = ReadUint8();
723
724 switch (*type) {
725 case 0x01: // ipv4
726 *len = 4 - 1;
727 break;
728 case 0x04: // ipv6
729 *len = 16 - 1;
730 break;
731 case 0x03: // fqdn
732 *len = ReadUint8();
733 break;
734 default: // wrong address type
735 LOGERROR(("socks5: wrong address type in connection reply!"));
736 return PR_FAILURE;
737 }
738
739 return PR_SUCCESS;
740 }
741
742 PRStatus
743 nsSOCKSSocketInfo::ReadV5ConnectResponseTop()
744 {
745 uint8_t res;
746 uint32_t len;
747
748 NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_TOP,
749 "Invalid state!");
750 NS_ABORT_IF_FALSE(mDataLength == 5,
751 "SOCKS 5 connection reply must be exactly 5 bytes!");
752
753 LOGDEBUG(("socks5: checking connection reply"));
754
755 // Check version number
756 if (ReadUint8() != 0x05) {
757 LOGERROR(("socks5: unexpected version in the reply"));
758 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
759 return PR_FAILURE;
760 }
761
762 // Check response
763 res = ReadUint8();
764 if (res != 0x00) {
765 PRErrorCode c = PR_CONNECT_REFUSED_ERROR;
766
767 switch (res) {
768 case 0x01:
769 LOGERROR(("socks5: connect failed: "
770 "01, General SOCKS server failure."));
771 break;
772 case 0x02:
773 LOGERROR(("socks5: connect failed: "
774 "02, Connection not allowed by ruleset."));
775 break;
776 case 0x03:
777 LOGERROR(("socks5: connect failed: 03, Network unreachable."));
778 c = PR_NETWORK_UNREACHABLE_ERROR;
779 break;
780 case 0x04:
781 LOGERROR(("socks5: connect failed: 04, Host unreachable."));
782 break;
783 case 0x05:
784 LOGERROR(("socks5: connect failed: 05, Connection refused."));
785 break;
786 case 0x06:
787 LOGERROR(("socks5: connect failed: 06, TTL expired."));
788 c = PR_CONNECT_TIMEOUT_ERROR;
789 break;
790 case 0x07:
791 LOGERROR(("socks5: connect failed: "
792 "07, Command not supported."));
793 break;
794 case 0x08:
795 LOGERROR(("socks5: connect failed: "
796 "08, Address type not supported."));
797 c = PR_BAD_ADDRESS_ERROR;
798 break;
799 default:
800 LOGERROR(("socks5: connect failed."));
801 break;
802 }
803
804 HandshakeFinished(c);
805 return PR_FAILURE;
806 }
807
808 if (ReadV5AddrTypeAndLength(&res, &len) != PR_SUCCESS) {
809 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
810 return PR_FAILURE;
811 }
812
813 mState = SOCKS5_READ_CONNECT_RESPONSE_BOTTOM;
814 WantRead(len + 2);
815
816 return PR_SUCCESS;
817 }
818
819 PRStatus
820 nsSOCKSSocketInfo::ReadV5ConnectResponseBottom()
821 {
822 uint8_t type;
823 uint32_t len;
824
825 NS_ABORT_IF_FALSE(mState == SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
826 "Invalid state!");
827
828 if (ReadV5AddrTypeAndLength(&type, &len) != PR_SUCCESS) {
829 HandshakeFinished(PR_BAD_ADDRESS_ERROR);
830 return PR_FAILURE;
831 }
832
833 NS_ABORT_IF_FALSE(mDataLength == 7+len,
834 "SOCKS 5 unexpected length of connection reply!");
835
836 LOGDEBUG(("socks5: loading source addr and port"));
837 // Read what the proxy says is our source address
838 switch (type) {
839 case 0x01: // ipv4
840 ReadNetAddr(&mExternalProxyAddr, AF_INET);
841 break;
842 case 0x04: // ipv6
843 ReadNetAddr(&mExternalProxyAddr, AF_INET6);
844 break;
845 case 0x03: // fqdn (skip)
846 mReadOffset += len;
847 mExternalProxyAddr.raw.family = AF_INET;
848 break;
849 }
850
851 ReadNetPort(&mExternalProxyAddr);
852
853 LOGDEBUG(("socks5: connected!"));
854 HandshakeFinished();
855
856 return PR_SUCCESS;
857 }
858
859 void
860 nsSOCKSSocketInfo::SetConnectTimeout(PRIntervalTime to)
861 {
862 mTimeout = to;
863 }
864
865 PRStatus
866 nsSOCKSSocketInfo::DoHandshake(PRFileDesc *fd, int16_t oflags)
867 {
868 LOGDEBUG(("socks: DoHandshake(), state = %d", mState));
869
870 switch (mState) {
871 case SOCKS_INITIAL:
872 return StartDNS(fd);
873 case SOCKS_DNS_IN_PROGRESS:
874 PR_SetError(PR_IN_PROGRESS_ERROR, 0);
875 return PR_FAILURE;
876 case SOCKS_DNS_COMPLETE:
877 return ConnectToProxy(fd);
878 case SOCKS_CONNECTING_TO_PROXY:
879 return ContinueConnectingToProxy(fd, oflags);
880 case SOCKS4_WRITE_CONNECT_REQUEST:
881 if (WriteToSocket(fd) != PR_SUCCESS)
882 return PR_FAILURE;
883 WantRead(8);
884 mState = SOCKS4_READ_CONNECT_RESPONSE;
885 return PR_SUCCESS;
886 case SOCKS4_READ_CONNECT_RESPONSE:
887 if (ReadFromSocket(fd) != PR_SUCCESS)
888 return PR_FAILURE;
889 return ReadV4ConnectResponse();
890
891 case SOCKS5_WRITE_AUTH_REQUEST:
892 if (WriteToSocket(fd) != PR_SUCCESS)
893 return PR_FAILURE;
894 WantRead(2);
895 mState = SOCKS5_READ_AUTH_RESPONSE;
896 return PR_SUCCESS;
897 case SOCKS5_READ_AUTH_RESPONSE:
898 if (ReadFromSocket(fd) != PR_SUCCESS)
899 return PR_FAILURE;
900 return ReadV5AuthResponse();
901 case SOCKS5_WRITE_USERNAME_REQUEST:
902 if (WriteToSocket(fd) != PR_SUCCESS)
903 return PR_FAILURE;
904 WantRead(2);
905 mState = SOCKS5_READ_USERNAME_RESPONSE;
906 return PR_SUCCESS;
907 case SOCKS5_READ_USERNAME_RESPONSE:
908 if (ReadFromSocket(fd) != PR_SUCCESS)
909 return PR_FAILURE;
910 return ReadV5UsernameResponse();
911 case SOCKS5_WRITE_CONNECT_REQUEST:
912 if (WriteToSocket(fd) != PR_SUCCESS)
913 return PR_FAILURE;
914
915 // The SOCKS 5 response to the connection request is variable
916 // length. First, we'll read enough to tell how long the response
917 // is, and will read the rest later.
918 WantRead(5);
919 mState = SOCKS5_READ_CONNECT_RESPONSE_TOP;
920 return PR_SUCCESS;
921 case SOCKS5_READ_CONNECT_RESPONSE_TOP:
922 if (ReadFromSocket(fd) != PR_SUCCESS)
923 return PR_FAILURE;
924 return ReadV5ConnectResponseTop();
925 case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
926 if (ReadFromSocket(fd) != PR_SUCCESS)
927 return PR_FAILURE;
928 return ReadV5ConnectResponseBottom();
929
930 case SOCKS_CONNECTED:
931 LOGERROR(("socks: already connected"));
932 HandshakeFinished(PR_IS_CONNECTED_ERROR);
933 return PR_FAILURE;
934 case SOCKS_FAILED:
935 LOGERROR(("socks: already failed"));
936 return PR_FAILURE;
937 }
938
939 LOGERROR(("socks: executing handshake in invalid state, %d", mState));
940 HandshakeFinished(PR_INVALID_STATE_ERROR);
941
942 return PR_FAILURE;
943 }
944
945 int16_t
946 nsSOCKSSocketInfo::GetPollFlags() const
947 {
948 switch (mState) {
949 case SOCKS_DNS_IN_PROGRESS:
950 case SOCKS_DNS_COMPLETE:
951 case SOCKS_CONNECTING_TO_PROXY:
952 return PR_POLL_EXCEPT | PR_POLL_WRITE;
953 case SOCKS4_WRITE_CONNECT_REQUEST:
954 case SOCKS5_WRITE_AUTH_REQUEST:
955 case SOCKS5_WRITE_USERNAME_REQUEST:
956 case SOCKS5_WRITE_CONNECT_REQUEST:
957 return PR_POLL_WRITE;
958 case SOCKS4_READ_CONNECT_RESPONSE:
959 case SOCKS5_READ_AUTH_RESPONSE:
960 case SOCKS5_READ_USERNAME_RESPONSE:
961 case SOCKS5_READ_CONNECT_RESPONSE_TOP:
962 case SOCKS5_READ_CONNECT_RESPONSE_BOTTOM:
963 return PR_POLL_READ;
964 default:
965 break;
966 }
967
968 return 0;
969 }
970
971 inline void
972 nsSOCKSSocketInfo::WriteUint8(uint8_t v)
973 {
974 MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE,
975 "Can't write that much data!");
976 mData[mDataLength] = v;
977 mDataLength += sizeof(v);
978 }
979
980 inline void
981 nsSOCKSSocketInfo::WriteUint16(uint16_t v)
982 {
983 MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE,
984 "Can't write that much data!");
985 memcpy(mData + mDataLength, &v, sizeof(v));
986 mDataLength += sizeof(v);
987 }
988
989 inline void
990 nsSOCKSSocketInfo::WriteUint32(uint32_t v)
991 {
992 MOZ_RELEASE_ASSERT(mDataLength + sizeof(v) <= BUFFER_SIZE,
993 "Can't write that much data!");
994 memcpy(mData + mDataLength, &v, sizeof(v));
995 mDataLength += sizeof(v);
996 }
997
998 void
999 nsSOCKSSocketInfo::WriteNetAddr(const NetAddr *addr)
1000 {
1001 const char *ip = nullptr;
1002 uint32_t len = 0;
1003
1004 if (addr->raw.family == AF_INET) {
1005 ip = (const char*)&addr->inet.ip;
1006 len = sizeof(addr->inet.ip);
1007 } else if (addr->raw.family == AF_INET6) {
1008 ip = (const char*)addr->inet6.ip.u8;
1009 len = sizeof(addr->inet6.ip.u8);
1010 }
1011
1012 MOZ_RELEASE_ASSERT(ip != nullptr, "Unknown address");
1013 MOZ_RELEASE_ASSERT(mDataLength + len <= BUFFER_SIZE,
1014 "Can't write that much data!");
1015
1016 memcpy(mData + mDataLength, ip, len);
1017 mDataLength += len;
1018 }
1019
1020 void
1021 nsSOCKSSocketInfo::WriteNetPort(const NetAddr *addr)
1022 {
1023 WriteUint16(addr->inet.port);
1024 }
1025
1026 void
1027 nsSOCKSSocketInfo::WriteString(const nsACString &str)
1028 {
1029 MOZ_RELEASE_ASSERT(mDataLength + str.Length() <= BUFFER_SIZE,
1030 "Can't write that much data!");
1031 memcpy(mData + mDataLength, str.Data(), str.Length());
1032 mDataLength += str.Length();
1033 }
1034
1035 inline uint8_t
1036 nsSOCKSSocketInfo::ReadUint8()
1037 {
1038 uint8_t rv;
1039 MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1040 "Not enough space to pop a uint8_t!");
1041 rv = mData[mReadOffset];
1042 mReadOffset += sizeof(rv);
1043 return rv;
1044 }
1045
1046 inline uint16_t
1047 nsSOCKSSocketInfo::ReadUint16()
1048 {
1049 uint16_t rv;
1050 MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1051 "Not enough space to pop a uint16_t!");
1052 memcpy(&rv, mData + mReadOffset, sizeof(rv));
1053 mReadOffset += sizeof(rv);
1054 return rv;
1055 }
1056
1057 inline uint32_t
1058 nsSOCKSSocketInfo::ReadUint32()
1059 {
1060 uint32_t rv;
1061 MOZ_RELEASE_ASSERT(mReadOffset + sizeof(rv) <= mDataLength,
1062 "Not enough space to pop a uint32_t!");
1063 memcpy(&rv, mData + mReadOffset, sizeof(rv));
1064 mReadOffset += sizeof(rv);
1065 return rv;
1066 }
1067
1068 void
1069 nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam)
1070 {
1071 uint32_t amt = 0;
1072 const uint8_t *ip = mData + mReadOffset;
1073
1074 addr->raw.family = fam;
1075 if (fam == AF_INET) {
1076 amt = sizeof(addr->inet.ip);
1077 MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength,
1078 "Not enough space to pop an ipv4 addr!");
1079 memcpy(&addr->inet.ip, ip, amt);
1080 } else if (fam == AF_INET6) {
1081 amt = sizeof(addr->inet6.ip.u8);
1082 MOZ_RELEASE_ASSERT(mReadOffset + amt <= mDataLength,
1083 "Not enough space to pop an ipv6 addr!");
1084 memcpy(addr->inet6.ip.u8, ip, amt);
1085 }
1086
1087 mReadOffset += amt;
1088 }
1089
1090 void
1091 nsSOCKSSocketInfo::ReadNetPort(NetAddr *addr)
1092 {
1093 addr->inet.port = ReadUint16();
1094 }
1095
1096 void
1097 nsSOCKSSocketInfo::WantRead(uint32_t sz)
1098 {
1099 NS_ABORT_IF_FALSE(mDataIoPtr == nullptr,
1100 "WantRead() called while I/O already in progress!");
1101 MOZ_RELEASE_ASSERT(mDataLength + sz <= BUFFER_SIZE,
1102 "Can't read that much data!");
1103 mAmountToRead = sz;
1104 }
1105
1106 PRStatus
1107 nsSOCKSSocketInfo::ReadFromSocket(PRFileDesc *fd)
1108 {
1109 int32_t rc;
1110 const uint8_t *end;
1111
1112 if (!mAmountToRead) {
1113 LOGDEBUG(("socks: ReadFromSocket(), nothing to do"));
1114 return PR_SUCCESS;
1115 }
1116
1117 if (!mDataIoPtr) {
1118 mDataIoPtr = mData + mDataLength;
1119 mDataLength += mAmountToRead;
1120 }
1121
1122 end = mData + mDataLength;
1123
1124 while (mDataIoPtr < end) {
1125 rc = PR_Read(fd, mDataIoPtr, end - mDataIoPtr);
1126 if (rc <= 0) {
1127 if (rc == 0) {
1128 LOGERROR(("socks: proxy server closed connection"));
1129 HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
1130 return PR_FAILURE;
1131 } else if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
1132 LOGDEBUG(("socks: ReadFromSocket(), want read"));
1133 }
1134 break;
1135 }
1136
1137 mDataIoPtr += rc;
1138 }
1139
1140 LOGDEBUG(("socks: ReadFromSocket(), have %u bytes total",
1141 unsigned(mDataIoPtr - mData)));
1142 if (mDataIoPtr == end) {
1143 mDataIoPtr = nullptr;
1144 mAmountToRead = 0;
1145 mReadOffset = 0;
1146 return PR_SUCCESS;
1147 }
1148
1149 return PR_FAILURE;
1150 }
1151
1152 PRStatus
1153 nsSOCKSSocketInfo::WriteToSocket(PRFileDesc *fd)
1154 {
1155 int32_t rc;
1156 const uint8_t *end;
1157
1158 if (!mDataLength) {
1159 LOGDEBUG(("socks: WriteToSocket(), nothing to do"));
1160 return PR_SUCCESS;
1161 }
1162
1163 if (!mDataIoPtr)
1164 mDataIoPtr = mData;
1165
1166 end = mData + mDataLength;
1167
1168 while (mDataIoPtr < end) {
1169 rc = PR_Write(fd, mDataIoPtr, end - mDataIoPtr);
1170 if (rc < 0) {
1171 if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
1172 LOGDEBUG(("socks: WriteToSocket(), want write"));
1173 }
1174 break;
1175 }
1176
1177 mDataIoPtr += rc;
1178 }
1179
1180 if (mDataIoPtr == end) {
1181 mDataIoPtr = nullptr;
1182 mDataLength = 0;
1183 mReadOffset = 0;
1184 return PR_SUCCESS;
1185 }
1186
1187 return PR_FAILURE;
1188 }
1189
1190 static PRStatus
1191 nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to)
1192 {
1193 PRStatus status;
1194 NetAddr dst;
1195
1196 nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
1197 if (info == nullptr) return PR_FAILURE;
1198
1199 if (addr->raw.family == PR_AF_INET6 &&
1200 PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
1201 const uint8_t *srcp;
1202
1203 LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4"));
1204
1205 // copied from _PR_ConvertToIpv4NetAddr()
1206 dst.raw.family = AF_INET;
1207 dst.inet.ip = htonl(INADDR_ANY);
1208 dst.inet.port = htons(0);
1209 srcp = addr->ipv6.ip.pr_s6_addr;
1210 memcpy(&dst.inet.ip, srcp + 12, 4);
1211 dst.inet.family = AF_INET;
1212 dst.inet.port = addr->ipv6.port;
1213 } else {
1214 memcpy(&dst, addr, sizeof(dst));
1215 }
1216
1217 info->SetDestinationAddr(&dst);
1218 info->SetConnectTimeout(to);
1219
1220 do {
1221 status = info->DoHandshake(fd, -1);
1222 } while (status == PR_SUCCESS && !info->IsConnected());
1223
1224 return status;
1225 }
1226
1227 static PRStatus
1228 nsSOCKSIOLayerConnectContinue(PRFileDesc *fd, int16_t oflags)
1229 {
1230 PRStatus status;
1231
1232 nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
1233 if (info == nullptr) return PR_FAILURE;
1234
1235 do {
1236 status = info->DoHandshake(fd, oflags);
1237 } while (status == PR_SUCCESS && !info->IsConnected());
1238
1239 return status;
1240 }
1241
1242 static int16_t
1243 nsSOCKSIOLayerPoll(PRFileDesc *fd, int16_t in_flags, int16_t *out_flags)
1244 {
1245 nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
1246 if (info == nullptr) return PR_FAILURE;
1247
1248 if (!info->IsConnected()) {
1249 *out_flags = 0;
1250 return info->GetPollFlags();
1251 }
1252
1253 return fd->lower->methods->poll(fd->lower, in_flags, out_flags);
1254 }
1255
1256 static PRStatus
1257 nsSOCKSIOLayerClose(PRFileDesc *fd)
1258 {
1259 nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
1260 PRDescIdentity id = PR_GetLayersIdentity(fd);
1261
1262 if (info && id == nsSOCKSIOLayerIdentity)
1263 {
1264 info->ForgetFD();
1265 NS_RELEASE(info);
1266 fd->identity = PR_INVALID_IO_LAYER;
1267 }
1268
1269 return fd->lower->methods->close(fd->lower);
1270 }
1271
1272 static PRFileDesc*
1273 nsSOCKSIOLayerAccept(PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout)
1274 {
1275 // TODO: implement SOCKS support for accept
1276 return fd->lower->methods->accept(fd->lower, addr, timeout);
1277 }
1278
1279 static int32_t
1280 nsSOCKSIOLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, int32_t amount, PRIntervalTime timeout)
1281 {
1282 // TODO: implement SOCKS support for accept, then read from it
1283 return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, timeout);
1284 }
1285
1286 static PRStatus
1287 nsSOCKSIOLayerBind(PRFileDesc *fd, const PRNetAddr *addr)
1288 {
1289 // TODO: implement SOCKS support for bind (very similar to connect)
1290 return fd->lower->methods->bind(fd->lower, addr);
1291 }
1292
1293 static PRStatus
1294 nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr)
1295 {
1296 nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
1297
1298 if (info != nullptr && addr != nullptr) {
1299 NetAddr temp;
1300 NetAddr *tempPtr = &temp;
1301 if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) {
1302 NetAddrToPRNetAddr(tempPtr, addr);
1303 return PR_SUCCESS;
1304 }
1305 }
1306
1307 return PR_FAILURE;
1308 }
1309
1310 static PRStatus
1311 nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr)
1312 {
1313 nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret;
1314
1315 if (info != nullptr && addr != nullptr) {
1316 NetAddr temp;
1317 NetAddr *tempPtr = &temp;
1318 if (info->GetDestinationAddr(&tempPtr) == NS_OK) {
1319 NetAddrToPRNetAddr(tempPtr, addr);
1320 return PR_SUCCESS;
1321 }
1322 }
1323
1324 return PR_FAILURE;
1325 }
1326
1327 static PRStatus
1328 nsSOCKSIOLayerListen(PRFileDesc *fd, int backlog)
1329 {
1330 // TODO: implement SOCKS support for listen
1331 return fd->lower->methods->listen(fd->lower, backlog);
1332 }
1333
1334 // add SOCKS IO layer to an existing socket
1335 nsresult
1336 nsSOCKSIOLayerAddToSocket(int32_t family,
1337 const char *host,
1338 int32_t port,
1339 nsIProxyInfo *proxy,
1340 int32_t socksVersion,
1341 uint32_t flags,
1342 PRFileDesc *fd,
1343 nsISupports** info)
1344 {
1345 NS_ENSURE_TRUE((socksVersion == 4) || (socksVersion == 5), NS_ERROR_NOT_INITIALIZED);
1346
1347
1348 if (firstTime)
1349 {
1350 //XXX hack until NSPR provides an official way to detect system IPv6
1351 // support (bug 388519)
1352 PRFileDesc *tmpfd = PR_OpenTCPSocket(PR_AF_INET6);
1353 if (!tmpfd) {
1354 ipv6Supported = false;
1355 } else {
1356 // If the system does not support IPv6, NSPR will push
1357 // IPv6-to-IPv4 emulation layer onto the native layer
1358 ipv6Supported = PR_GetIdentitiesLayer(tmpfd, PR_NSPR_IO_LAYER) == tmpfd;
1359 PR_Close(tmpfd);
1360 }
1361
1362 nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer");
1363 nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods();
1364
1365 nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect;
1366 nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue;
1367 nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll;
1368 nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind;
1369 nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead;
1370 nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName;
1371 nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName;
1372 nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept;
1373 nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen;
1374 nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose;
1375
1376 firstTime = false;
1377
1378 #if defined(PR_LOGGING)
1379 gSOCKSLog = PR_NewLogModule("SOCKS");
1380 #endif
1381
1382 }
1383
1384 LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket()."));
1385
1386 PRFileDesc *layer;
1387 PRStatus rv;
1388
1389 layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods);
1390 if (! layer)
1391 {
1392 LOGERROR(("PR_CreateIOLayerStub() failed."));
1393 return NS_ERROR_FAILURE;
1394 }
1395
1396 nsSOCKSSocketInfo * infoObject = new nsSOCKSSocketInfo();
1397 if (!infoObject)
1398 {
1399 // clean up IOLayerStub
1400 LOGERROR(("Failed to create nsSOCKSSocketInfo()."));
1401 PR_DELETE(layer);
1402 return NS_ERROR_FAILURE;
1403 }
1404
1405 NS_ADDREF(infoObject);
1406 infoObject->Init(socksVersion, family, proxy, host, flags);
1407 layer->secret = (PRFilePrivate*) infoObject;
1408 rv = PR_PushIOLayer(fd, PR_GetLayersIdentity(fd), layer);
1409
1410 if (rv == PR_FAILURE) {
1411 LOGERROR(("PR_PushIOLayer() failed. rv = %x.", rv));
1412 NS_RELEASE(infoObject);
1413 PR_DELETE(layer);
1414 return NS_ERROR_FAILURE;
1415 }
1416
1417 *info = static_cast<nsISOCKSSocketInfo*>(infoObject);
1418 NS_ADDREF(*info);
1419 return NS_OK;
1420 }

mercurial