|
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 } |