Ticket #6733: socks-username-6733-protocol-1.diff

File socks-username-6733-protocol-1.diff, 15.9 KB (added by ben, 7 years ago)

Implement SOCKS username and password, with pref - separate patch, for easier review

  • netwerk/base/src/nsProtocolProxyService.cpp

    diff --git a/netwerk/base/src/nsProtocolProxyService.cpp b/netwerk/base/src/nsProtocolProxyService.cpp
    a b nsProtocolProxyService::PrefsChanged(nsI 
    497497        proxy_GetIntPref(prefBranch, PROXY_PREF("ftp_port"), mFTPProxyPort);
    498498
    499499    if (!pref || !strcmp(pref, PROXY_PREF("socks")))
    500500        proxy_GetStringPref(prefBranch, PROXY_PREF("socks"), mSOCKSProxyHost);
    501501   
    502502    if (!pref || !strcmp(pref, PROXY_PREF("socks_port")))
    503503        proxy_GetIntPref(prefBranch, PROXY_PREF("socks_port"), mSOCKSProxyPort);
    504504
     505    if (!pref || !strcmp(pref, PROXY_PREF("socks_username")))
     506        proxy_GetStringPref(prefBranch, PROXY_PREF("socks_username"), mSOCKSProxyUsername);
     507
     508    if (!pref || !strcmp(pref, PROXY_PREF("socks_password")))
     509        proxy_GetStringPref(prefBranch, PROXY_PREF("socks_password"), mSOCKSProxyPassword);
     510
    505511    if (!pref || !strcmp(pref, PROXY_PREF("socks_version"))) {
    506512        int32_t version;
    507513        proxy_GetIntPref(prefBranch, PROXY_PREF("socks_version"), version);
    508514        // make sure this preference value remains sane
    509515        if (version == 5)
    510516            mSOCKSProxyVersion = 5;
    511517        else
    512518            mSOCKSProxyVersion = 4;
    nsProtocolProxyService::NewProxyInfo(con 
    11431149            break;
    11441150        }
    11451151    }
    11461152    NS_ENSURE_TRUE(type, NS_ERROR_INVALID_ARG);
    11471153
    11481154    if (aPort <= 0)
    11491155        aPort = -1;
    11501156
    1151     return NewProxyInfo_Internal(type, aHost, aPort, aFlags, aFailoverTimeout,
     1157    return NewProxyInfo_Internal(type, aHost, aPort,
     1158                                 EmptyCString(), EmptyCString(),
     1159                                 aFlags, aFailoverTimeout,
    11521160                                 aFailoverProxy, 0, aResult);
    11531161}
    11541162
    11551163NS_IMETHODIMP
    11561164nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
    11571165                                            nsIURI        *aURI,
    11581166                                            nsresult       aStatus,
    11591167                                            nsIProxyInfo **aResult)
    nsProtocolProxyService::GetProtocolInfo( 
    14201428    rv = handler->GetDefaultPort(&info->defaultPort);
    14211429    return rv;
    14221430}
    14231431
    14241432nsresult
    14251433nsProtocolProxyService::NewProxyInfo_Internal(const char *aType,
    14261434                                              const nsACString &aHost,
    14271435                                              int32_t aPort,
     1436                                              const nsACString &aUsername,
     1437                                              const nsACString &aPassword,
    14281438                                              uint32_t aFlags,
    14291439                                              uint32_t aFailoverTimeout,
    14301440                                              nsIProxyInfo *aFailoverProxy,
    14311441                                              uint32_t aResolveFlags,
    14321442                                              nsIProxyInfo **aResult)
    14331443{
    14341444    nsCOMPtr<nsProxyInfo> failover;
    14351445    if (aFailoverProxy) {
    nsProtocolProxyService::NewProxyInfo_Int 
    14391449
    14401450    nsProxyInfo *proxyInfo = new nsProxyInfo();
    14411451    if (!proxyInfo)
    14421452        return NS_ERROR_OUT_OF_MEMORY;
    14431453
    14441454    proxyInfo->mType = aType;
    14451455    proxyInfo->mHost = aHost;
    14461456    proxyInfo->mPort = aPort;
     1457    proxyInfo->mUsername = aUsername;
     1458    proxyInfo->mPassword = aPassword;
    14471459    proxyInfo->mFlags = aFlags;
    14481460    proxyInfo->mResolveFlags = aResolveFlags;
    14491461    proxyInfo->mTimeout = aFailoverTimeout == UINT32_MAX
    14501462        ? mFailedProxyTimeout : aFailoverTimeout;
    14511463    failover.swap(proxyInfo->mNext);
    14521464
    14531465    NS_ADDREF(*aResult = proxyInfo);
    14541466    return NS_OK;
    nsProtocolProxyService::Resolve_Internal 
    15961608        else
    15971609            type = kProxyType_SOCKS;
    15981610        port = mSOCKSProxyPort;
    15991611        if (mSOCKSProxyRemoteDNS)
    16001612            proxyFlags |= nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST;
    16011613    }
    16021614
    16031615    if (type) {
    1604         rv = NewProxyInfo_Internal(type, *host, port, proxyFlags,
    1605                                    UINT32_MAX, nullptr, flags,
     1616        rv = NewProxyInfo_Internal(type, *host, port,
     1617                                   mSOCKSProxyUsername, mSOCKSProxyPassword,
     1618                                   proxyFlags, UINT32_MAX, nullptr, flags,
    16061619                                   result);
    16071620        if (NS_FAILED(rv))
    16081621            return rv;
    16091622    }
    16101623
    16111624    return NS_OK;
    16121625}
    16131626
  • netwerk/base/src/nsProtocolProxyService.h

    diff --git a/netwerk/base/src/nsProtocolProxyService.h b/netwerk/base/src/nsProtocolProxyService.h
    a b protected: 
    162162     * that expects a string literal for the type.
    163163     *
    164164     * @param type
    165165     *        The proxy type.
    166166     * @param host
    167167     *        The proxy host name (UTF-8 ok).
    168168     * @param port
    169169     *        The proxy port number.
     170     * @param username
     171     *        The username for the proxy (ASCII). May be "", but not null.
     172     * @param password
     173     *        The password for the proxy (ASCII). May be "", but not null.
    170174     * @param flags
    171175     *        The proxy flags (nsIProxyInfo::flags).
    172176     * @param timeout
    173177     *        The failover timeout for this proxy.
    174178     * @param next
    175179     *        The next proxy to try if this one fails.
    176180     * @param aResolveFlags
    177181     *        The flags passed to resolve (from nsIProtocolProxyService).
    178182     * @param result
    179183     *        The resulting nsIProxyInfo object.
    180184     */
    181185    NS_HIDDEN_(nsresult) NewProxyInfo_Internal(const char *type,
    182186                                               const nsACString &host,
    183187                                               int32_t port,
     188                                               const nsACString &username,
     189                                               const nsACString &password,
    184190                                               uint32_t flags,
    185191                                               uint32_t timeout,
    186192                                               nsIProxyInfo *next,
    187193                                               uint32_t aResolveFlags,
    188194                                               nsIProxyInfo **result);
    189195
    190196    /**
    191197     * This method is an internal version of Resolve that does not query PAC.
    protected: 
    351357
    352358    nsCString                    mHTTPSProxyHost;
    353359    int32_t                      mHTTPSProxyPort;
    354360   
    355361    nsCString                    mSOCKSProxyHost;
    356362    int32_t                      mSOCKSProxyPort;
    357363    int32_t                      mSOCKSProxyVersion;
    358364    bool                         mSOCKSProxyRemoteDNS;
     365    nsCString                    mSOCKSProxyUsername;
     366    nsCString                    mSOCKSProxyPassword;
    359367
    360368    nsRefPtr<nsPACMan>           mPACMan;  // non-null if we are using PAC
    361369    nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
    362370
    363371    PRTime                       mSessionStart;
    364372    nsFailedProxyTable           mFailedProxies;
    365373    int32_t                      mFailedProxyTimeout;
    366374};
  • netwerk/socket/nsSOCKSIOLayer.cpp

    diff --git a/netwerk/socket/nsSOCKSIOLayer.cpp b/netwerk/socket/nsSOCKSIOLayer.cpp
    a b class nsSOCKSSocketInfo : public nsISOCK 
    4242        SOCKS_INITIAL,
    4343        SOCKS_DNS_IN_PROGRESS,
    4444        SOCKS_DNS_COMPLETE,
    4545        SOCKS_CONNECTING_TO_PROXY,
    4646        SOCKS4_WRITE_CONNECT_REQUEST,
    4747        SOCKS4_READ_CONNECT_RESPONSE,
    4848        SOCKS5_WRITE_AUTH_REQUEST,
    4949        SOCKS5_READ_AUTH_RESPONSE,
     50        SOCKS5_WRITE_USERNAME_REQUEST,
     51        SOCKS5_READ_USERNAME_RESPONSE,
    5052        SOCKS5_WRITE_CONNECT_REQUEST,
    5153        SOCKS5_READ_CONNECT_RESPONSE_TOP,
    5254        SOCKS5_READ_CONNECT_RESPONSE_BOTTOM,
    5355        SOCKS_CONNECTED,
    5456        SOCKS_FAILED
    5557    };
    5658
    5759    // A buffer of 262 bytes should be enough for any request and response
    private: 
    8486    PRStatus StartDNS(PRFileDesc *fd);
    8587    PRStatus ConnectToProxy(PRFileDesc *fd);
    8688    void FixupAddressFamily(PRFileDesc *fd, PRNetAddr *proxy);
    8789    PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags);
    8890    PRStatus WriteV4ConnectRequest();
    8991    PRStatus ReadV4ConnectResponse();
    9092    PRStatus WriteV5AuthRequest();
    9193    PRStatus ReadV5AuthResponse();
     94    PRStatus WriteV5UsernameRequest();
     95    PRStatus ReadV5UsernameResponse();
    9296    PRStatus WriteV5ConnectRequest();
    9397    PRStatus ReadV5AddrTypeAndLength(uint8_t *type, uint32_t *len);
    9498    PRStatus ReadV5ConnectResponseTop();
    9599    PRStatus ReadV5ConnectResponseBottom();
    96100
    97101    void WriteUint8(uint8_t d);
    98102    void WriteUint16(uint16_t d);
    99103    void WriteUint32(uint32_t d);
    private: 
    128132    int32_t   mProxyPort;
    129133    int32_t   mVersion;   // SOCKS version 4 or 5
    130134    int32_t   mDestinationFamily;
    131135    uint32_t  mFlags;
    132136    PRNetAddr mInternalProxyAddr;
    133137    PRNetAddr mExternalProxyAddr;
    134138    PRNetAddr mDestinationAddr;
    135139    PRIntervalTime mTimeout;
     140    nsCString mProxyUsername; // Cache, from mProxy
    136141};
    137142
    138143nsSOCKSSocketInfo::nsSOCKSSocketInfo()
    139144    : mState(SOCKS_INITIAL)
    140145    , mDataIoPtr(nullptr)
    141146    , mDataLength(0)
    142147    , mReadOffset(0)
    143148    , mAmountToRead(0)
    void 
    157162nsSOCKSSocketInfo::Init(int32_t version, int32_t family, const char *proxyHost, int32_t proxyPort, const char *host, uint32_t flags)
    158163{
    159164    mVersion         = version;
    160165    mDestinationFamily = family;
    161166    mProxyHost       = proxyHost;
    162167    mProxyPort       = proxyPort;
    163168    mDestinationHost = host;
    164169    mFlags           = flags;
     170    mProxy->GetUsername(mProxyUsername); // cache
    165171}
    166172
    167173NS_IMPL_THREADSAFE_ISUPPORTS2(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener)
    168174
    169175NS_IMETHODIMP
    170176nsSOCKSSocketInfo::GetExternalProxyAddr(PRNetAddr * *aExternalProxyAddr)
    171177{
    172178    memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(PRNetAddr));
    nsSOCKSSocketInfo::WriteV4ConnectRequest 
    441447    WriteNetPort(addr);
    442448    if (proxy_resolve) {
    443449        // Add the full name, null-terminated, to the request
    444450        // according to SOCKS 4a. A fake IP address, with the first
    445451        // four bytes set to 0 and the last byte set to something other
    446452        // than 0, is used to notify the proxy that this is a SOCKS 4a
    447453        // request. This request type works for Tor and perhaps others.
    448454        WriteUint32(PR_htonl(0x00000001)); // Fake IP
    449         WriteUint8(0x00); // Send an emtpy username
     455        WriteString(mProxyUsername); // Send username. May be empty.
     456        WriteUint8(0x00); // Null-terminate username
     457        // Password not supported by V4.
    450458        if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) {
    451459            LOGERROR(("socks4: destination host name is too long!"));
    452460            HandshakeFinished(PR_BAD_ADDRESS_ERROR);
    453461            return PR_FAILURE;
    454462        }
    455463        WriteString(mDestinationHost); // Hostname
    456464        WriteUint8(0x00);
    457465    } else if (PR_NetAddrFamily(addr) == PR_AF_INET) {
    458466        WriteNetAddr(addr); // Add the IPv4 address
    459         WriteUint8(0x00); // Send an emtpy username
     467        WriteString(mProxyUsername); // Send username. May be empty.
     468        WriteUint8(0x00); // Null-terminate username
     469        // Password not supported by V4.
    460470    } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) {
    461471        LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!"));
    462472        HandshakeFinished(PR_BAD_ADDRESS_ERROR);
    463473        return PR_FAILURE;
    464474    }
    465475
    466476    return PR_SUCCESS;
    467477}
    nsSOCKSSocketInfo::WriteV5AuthRequest() 
    499509{
    500510    NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
    501511
    502512    mState = SOCKS5_WRITE_AUTH_REQUEST;
    503513
    504514    // Send an initial SOCKS 5 greeting
    505515    LOGDEBUG(("socks5: sending auth methods"));
    506516    WriteUint8(0x05); // version -- 5
    507     WriteUint8(0x01); // # auth methods -- 1
    508     WriteUint8(0x00); // we don't support authentication
     517    WriteUint8(0x01); // # of auth methods -- 1
     518    if (mProxyUsername.IsEmpty()) {
     519      WriteUint8(0x00); // no authentication
     520    } else {
     521      WriteUint8(0x02); // username/password
     522    }
    509523
    510524    return PR_SUCCESS;
    511525}
    512526
    513527PRStatus
    514528nsSOCKSSocketInfo::ReadV5AuthResponse()
    515529{
    516530    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_AUTH_RESPONSE,
    517531                      "Handling SOCKS 5 auth method reply in wrong state!");
    518532    NS_ABORT_IF_FALSE(mDataLength == 2,
    519533                      "SOCKS 5 auth method reply must be 2 bytes!");
    520534
    521     LOGDEBUG(("socks5: checking auth method reply"));
    522 
    523535    // Check version number
    524536    if (ReadUint8() != 0x05) {
    525537        LOGERROR(("socks5: unexpected version in the reply"));
    526538        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
    527539        return PR_FAILURE;
    528540    }
    529541
    530     // Make sure our authentication choice was accepted
    531     if (ReadUint8() != 0x00) {
     542    // Make sure our authentication choice was accepted,
     543    // and continue accordingly
     544    uint8_t authMethod = ReadUint8(); // don't call Read() twice
     545    if (mProxyUsername.IsEmpty() && authMethod == 0x00) { // no auth
     546        LOGDEBUG(("socks5: server allows connection without authentication"));
     547        return WriteV5ConnectRequest();
     548    } else if (!mProxyUsername.IsEmpty() && authMethod == 0x02) { // username/pw
     549        LOGDEBUG(("socks5: auth method accepted by server"));
     550        return WriteV5UsernameRequest();
     551    } else { // 0xFF signals error
    532552        LOGERROR(("socks5: server did not accept our authentication method"));
    533553        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
    534554        return PR_FAILURE;
    535555    }
     556}
    536557
    537     return WriteV5ConnectRequest();
     558PRStatus
     559nsSOCKSSocketInfo::WriteV5UsernameRequest()
     560{
     561    NS_ABORT_IF_FALSE(mVersion == 5, "SOCKS version must be 5!");
     562
     563    mState = SOCKS5_WRITE_USERNAME_REQUEST;
     564
     565    LOGDEBUG(("socks5: sending username and password"));
     566    // RFC 1929 Username/password auth for SOCKS 5
     567    WriteUint8(0x01); // version 1 (not 5)
     568    WriteUint8(mProxyUsername.Length()); // username length
     569    WriteString(mProxyUsername); // username
     570    nsCString password;
     571    mProxy->GetPassword(password);
     572    WriteUint8(password.Length()); // password length
     573    WriteString(password); // password. WARNING: Sent unencrypted!
     574
     575    return PR_SUCCESS;
     576}
     577
     578PRStatus
     579nsSOCKSSocketInfo::ReadV5UsernameResponse()
     580{
     581    NS_ABORT_IF_FALSE(mState == SOCKS5_READ_USERNAME_RESPONSE,
     582                      "Handling SOCKS 5 username/password reply in wrong state!");
     583    NS_ABORT_IF_FALSE(mDataLength == 2,
     584                      "SOCKS 5 username reply must be 2 bytes!");
     585
     586    ReadUint8(); // version, ignore
     587
     588    // Check whether username/password were accepted
     589    if (ReadUint8() != 0x00) { // 0 = success
     590        LOGERROR(("socks5: username/password not accepted"));
     591        HandshakeFinished(PR_CONNECT_REFUSED_ERROR);
     592        return PR_FAILURE;
     593    }
     594
     595    LOGDEBUG(("socks5: username/password accepted by server"));
     596
     597    return WriteV5UsernameRequest();
    538598}
    539599
    540600PRStatus
    541601nsSOCKSSocketInfo::WriteV5ConnectRequest()
    542602{
    543603    // Send SOCKS 5 connect request
    544604    PRNetAddr *addr = &mDestinationAddr;
    545605    int32_t proxy_resolve;
    nsSOCKSSocketInfo::DoHandshake(PRFileDes 
    771831                return PR_FAILURE;
    772832            WantRead(2);
    773833            mState = SOCKS5_READ_AUTH_RESPONSE;
    774834            return PR_SUCCESS;
    775835        case SOCKS5_READ_AUTH_RESPONSE:
    776836            if (ReadFromSocket(fd) != PR_SUCCESS)
    777837                return PR_FAILURE;
    778838            return ReadV5AuthResponse();
     839        case SOCKS5_WRITE_USERNAME_REQUEST:
     840            if (WriteToSocket(fd) != PR_SUCCESS)
     841                return PR_FAILURE;
     842            WantRead(2);
     843            mState = SOCKS5_READ_USERNAME_RESPONSE;
     844            return PR_SUCCESS;
     845        case SOCKS5_READ_USERNAME_RESPONSE:
     846            if (ReadFromSocket(fd) != PR_SUCCESS)
     847                return PR_FAILURE;
     848            return ReadV5UsernameResponse();
    779849        case SOCKS5_WRITE_CONNECT_REQUEST:
    780850            if (WriteToSocket(fd) != PR_SUCCESS)
    781851                return PR_FAILURE;
    782852
    783853            // The SOCKS 5 response to the connection request is variable
    784854            // length. First, we'll read enough to tell how long the response
    785855            // is, and will read the rest later.
    786856            WantRead(5);