Coverage Report

Created: 2018-09-25 14:53

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