Benni
Benni

Reputation: 788

NodeJS - Error: write EPIPE on SOCKS5 proxy

I get the following stacktrace when trying to make net.Socket() connection to a SOCKS5-server:

events.js:141
      throw er; // Unhandled 'error' event
      ^

Error: write EPIPE
    at exports._errnoException (util.js:870:11)
    at WriteWrap.afterWrite (net.js:769:14)

My code is as follows:

var net = require('net');

var proxy = {
    host: '115.159.155.96',
    port: 1080,
    dstHost: 'www.google.com',
    dstPort: 80
};

var socket = new net.connect({
    host: proxy.host,
    port: proxy.port
}, function() {
    console.log('connected!');

    var addrlen = Buffer.byteLength(proxy.dstHost);
    var reqbuf = new Buffer(7 + addrlen);
    var p;

    reqbuf[0] = 0x05;
    reqbuf[1] = 0x01;
    reqbuf[2] = 0x00;
    reqbuf[3] = 0x03;
    reqbuf[4] = addrlen;
    reqbuf.write(proxy.dstHost, 5, addrlen);
    p = 5 + addrlen;

    reqbuf.writeUInt16BE(proxy.dstPort, p, true);
    socket.write(reqbuf);

    socket.on('data', function(data) {
        if(data.toString('hex') == '0500') { // This means 0x05 for version 5 and 0x00 for 'succeeded', right?
            var bfr = new Buffer('GET / HTTP/1.1\r\nHost: www.google.com');
            socket.write(bfr);
            // I also tried "socket.write('GET / HTTP/1.1\r\nHost: www.google.com');",
            // but I got the same error.
        }

        console.log(data.toString());
    });
});

I'm sure this is not the right way to do a connection to the socket. What would be the correct method to do it?

I took the code above out of lib/client.js in the socksv5 package.

By the way, I do not want to use an existing package (I know there are lots of SOCKS-Client packages for NodeJS), because I would like to learn SOCKS by doing it.

Edit: Here is the output of curl -vv --socks5-hostname 115.159.155.96:1080 http://api.ipify.org?format=json, like Quedaro suggested below:

root@mymachine:~# curl -vv --socks5-hostname 115.159.155.96:1080
http://api.ipify.org?format=json
* Rebuilt URL to: http://api.ipify.org/?format=json
* Hostname was NOT found in DNS cache
*   Trying 115.159.155.96...
* Connected to 115.159.155.96 (115.159.155.96) port 1080 (#0)
> GET /?format=json HTTP/1.1
> User-Agent: curl/7.38.0
> Host: api.ipify.org
> Accept: */*
> 
< HTTP/1.1 200 OK
* Server Cowboy is not blacklisted
< Server: Cowboy
< Connection: keep-alive
< Content-Type: application/json
< Date: Wed, 06 Apr 2016 18:15:59 GMT
< Content-Length: 23
< Via: 1.1 vegur
< 
* Connection #0 to host api.ipify.org left intact
{"ip":"115.159.155.96"}
root@mymachine:~#

This means, that the proxy should work fine, doesn't it?

I also added an error-event to the socket, simply logging err to console. This is, what I get:

{ [Error: write EPIPE] code: 'EPIPE', errno: 'EPIPE', syscall: 'write' }

Upvotes: 2

Views: 2918

Answers (2)

Punit Mittal
Punit Mittal

Reputation: 171

Adding the working code here if someone stumbles upon this question.

As @Quedaro had mentioned, the socks5 handshake was missing.

var net = require('net');

var proxy = {
  host: 'localhost', // You can create a socks5 server by running `ssh -D 8001 xyz.com`
  port: 8001,
  destinationHost: 'www.google.com',
  destinationPort: 80,
  destinationPath: '/'
};

var socks5Handshake = new Buffer(3);
socks5Handshake[0] = 0x05; // SOCKS version number (must be 0x05 for this version)
socks5Handshake[1] = 0x01; // Number of authentication methods supported.
socks5Handshake[2] = 0x00; // 0x00: No authentication

var serverHandshakeResponse = "0500"; // SOCKS version number followed by chosen authentication method, 1 byte, or 0xFF if no acceptable methods were offered.
var serverConnectionResponse = "05000001000000000000";

var socket = new net.connect({
  host: proxy.host,
  port: proxy.port
}, function() {
  // Socket connect done. Initiating Socks5 handshake
  socket.write(socks5Handshake);

  // Error event handler to handle error in case of ECONNRESET, ECONNREFUSED etc.
  socket.on('error', function(err){
    console.log('error!', err);
  });

  socket.once('data', function(data) {
    if(data.toString('hex') == serverHandshakeResponse) {
      var addressLength = Buffer.byteLength(proxy.destinationHost);
      var requestBuffer = new Buffer(7 + addressLength);
      var portOffset;

      requestBuffer[0] = 0x05; // SOCKS version number (must be 0x05 for this version)
      requestBuffer[1] = 0x01; // establish a TCP/IP stream connection
      requestBuffer[2] = 0x00; // reserved, must be 0x00
      requestBuffer[3] = 0x03; // address type, 1 byte. 0x03 = Domain name
      requestBuffer[4] = addressLength; // 1 byte of name length followed by the name for domain name
      requestBuffer.write(proxy.destinationHost, 5, addressLength);
      portOffset = 5 + addressLength;

      requestBuffer.writeUInt16BE(proxy.destinationPort, portOffset, true);
      socket.write(requestBuffer);
      socket.once('data', function(data){
        if(data.toString('hex') == serverConnectionResponse) {
          var requestBuffer = new Buffer("GET " + proxy.destinationPath + " HTTP/1.1\r\nHost: " + proxy.destinationHost + ":" + proxy.destinationPort + "\r\n\r\n");
          socket.write(requestBuffer);
          socket.once('data', function(data){
            console.log("Response for the url:", proxy.destinationHost + ":" + proxy.destinationPort, "=>", data.toString().match(/^HTTP\/1\.. \d+/i)[0]);
            socket.end();
          });
        }
        else {
          console.log("Socks5 connection request failed. Closing the socket");
          socket.end();
        }
      });
    }
    else {
      console.log("Socks5 handshake failed. Closing the socket");
      socket.end();
    }
  });
});

Upvotes: 4

Hipocoro
Hipocoro

Reputation: 49

Are you sure that socks server actually works? Write EPIPE means the socket cannot be written because the connection had been dropped, hopefully closed by the server. Try it testing this command (on Linux systems):

curl -vv --socks5-hostname 115.159.155.96:1080 http://api.ipify.org?format=json

Also, before sending that data you may want to say 'hello' to the socks server, like sending these bytes: 0x05 0x02 0x00 0x01

To don't get thrown on any socket exception, try to capture the 'error' event, like this:

socket.on('error', function(err){        
    console.log('error!', err);
});

Upvotes: 1

Related Questions