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