netwerk/socket/nsSOCKSIOLayer.cpp

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial