4

I'm using an HTTP request library that can use an http.Agent instance in order to route your requests through a specific proxy. Let me give you an example:

const SocksProxyAgent = require('socks-proxy-agent')
const HttpProxyAgent = require('http-proxy-agent') // not used
const axios = require('axios')

// requires Tor service to be running
const torProxyAgent = new SocksProxyAgent('socks://circuit1@127.0.0.1:9050')
// not used
const otherProxyAgent = new HttpProxyAgent('http://admin:1234@localhost:8888')

const axiosConfig = {
    httpsAgent: torProxyAgent,
    httpAgent: torProxyAgent
}

const axiosInstance = axios.create(axiosConfig)

axiosInstance.get('https://ifconfig.io/ip')
  .then(res => {
    console.log('Public IP address:', res.data)
  }).catch(err => {
    console.log(err)
  })

As you can see, I specified multiple Agents (torProxyAgent and otherProxyAgent), but I'm only able to use one.

So what I'm looking for is a sort of super Agent that I can pass to my HTTP request library and this super Agent then chains an arbitrary number of normal Agents together by routing all my requests through the first, then through the second, then through the third and so on...

Is that even possible? Are there any existing solutions?

Is there maybe a way to use the Proxy class to create a fake Agent that passes all method calls to one Agent and then to the second Agent?

Edit:
While I appreciate lifeisfoo's answer, it only solved a theoretical problem where I happened to just use two proxies (one HTTP proxy and one socks proxy). In reality I don't want to be limited to two proxies and I would like to be able to specify the order.

Forivin
  • 14,780
  • 27
  • 106
  • 199
  • Why do you want to make this? What result are you trying to achieve? Passing a connection through multiple nodes? – lifeisfoo Nov 29 '20 at 15:56
  • Yes, I want to be able to create proxy chains that supports different types of proxies like socks and http proxies. E.g. PC->Tor-socks-proxy->HTTP-proxy->Server – Forivin Nov 29 '20 at 18:10
  • I updated my asnwer, this should solve you problem – lifeisfoo Dec 04 '20 at 08:41

1 Answers1

4

Updated answer - axios + socks-proxy-agent + http proxy

Looks like the solution is easier than I thoght:

const SocksProxyAgent = require('socks-proxy-agent')
const axios = require('axios')

const torProxyAgent = new SocksProxyAgent('socks://circuit1@127.0.0.1:9050')

const axiosConfig = {
    httpsAgent: torProxyAgent,
    httpAgent: torProxyAgent,
    proxy: {
        protocol: 'http', //'http',
        host: '89.208.35.81', // your http proxy ip
        port: 3128, // your http proxy port
        // optional - add it if your proxy require auth
        auth: {
          username: 'myuser',
          password: 'mypass'
        }
    }
}

const axiosInstance = axios.create(axiosConfig)

axiosInstance.get('https://ifconfig.io/ip')
  .then(res => {
    console.log('Public IP address:', res.data)
  }).catch(err => {
    console.log(err)
  })

Be sure to use a localhost HTTP proxy ONLY if your socks proxy is running locally, otherwise it will not be reached. Obviously socks and proxy IPs should be update with working ones, I used some IPs found on a public proxy list as a test but they're unreliable.

How it works

The nodejs docs sasys that an Agent:

[...] is responsible for managing connection persistence and reuse for HTTP clients. It maintains a queue of pending requests for a given host and port, reusing a single socket connection for each until the queue is empty.

Basically, it exposes a createConnection function that return a socket:

This method is guaranteed to return an instance of the <net.Socket> class, a subclass of <stream.Duplex>, unless the user specifies a socket type other than <net.Socket>.

This behaviour it's easy to see if you look at the source code of another socks agent like socks5-http-client/lib/Agent:

var http = require('http');
var inherits = require('util').inherits;
var socksClient = require('socks5-client');

function Agent(options) {
    http.Agent.call(this, options);
    this.createConnection = socksClient.createConnection;
}

inherits(Agent, http.Agent);

module.exports = Agent;

Try debugging the request flow and you'll see that both socks connection data and http data are written on the socket. Add a breakpoint or a log in Socks5ClientSocket.prototype.write function

Socks5ClientSocket.prototype.write = function(data, encoding, cb) {
    console.log('Writing', data);
    return this.socket.write(data, encoding, cb);
};

And you'll see something like this:

Writing <Buffer 05 01 00>
Writing <Buffer 05 01 00 01 59 d0 23 51 0c 38>
Writing GET http://ip-api.com/json HTTP/1.1 ....// http request data

The first two lines are bytes to establish the socks connection to the socks proxy, then the http data are written on the same socket but using the http proxy as the target host.

So, from the http library point of view, an agent is just a socket provider and so you can create this socket as you want, possibly chaining more connections on the same socket (see my original answer).

axios + socks5-http-client + http proxy

var Agent = require('socks5-http-client/lib/Agent');
var axios = require('axios');

const socksProxyOpts = {
    socksHost: '5.189.130.21',
    socksPort: 1080
};
const socksAgent = new Agent(socksProxyOpts);
const axiosConfig = {
    httpAgent: socksAgent,
    proxy: {
        protocol: 'http',
        host: '89.208.35.81',// or '45.33.99.194',
        port: 3128 // or 8888
      }
}

const axiosInstance = axios.create(axiosConfig)

axiosInstance.get('http://ip-api.com/json')
.then(res => {
    console.log('Public IP address:', res.data)
  }).catch(err => {
    console.log(err)
  })

Original answer

Yes, it's possible as you can see from this C proxychains application and from its configuration file:

[ProxyList]
# based on you configuration
socks4  127.0.0.1 9050  circuit1
http    localhost 8888  admin 1234

But looks like there isn't an existing node modules capable to handle a chain of mixed kind of proxies (socks, http).

The socks library can handle a chain of socks proxies but this feature isn't already exposed to the socks-proxy-agent you're using.

A http proxies chain can be achieved using http-proxy and code from this gist.

So you have three options:

  • use the existing proxychains application
  • use a chain of socks proxies using socks
  • hack the socks library to add support for http proxies in the chain

If you choose the last one be sure to check the socks library source code for the createConnectionChain function for the chain creation logic.

See also:

lifeisfoo
  • 15,478
  • 6
  • 74
  • 115