Search code examples
javanode.jssocketshttpssocks

SSLException from HTTPS Request through socket over SOCKS Proxy


Internet networking newbie here. I'm attempting to send https requests from a Node JS server using the 'socks' npm module through a socks proxy to a Java HTTPS server. I can make individual https requests through the proxy without a socket fine, for example using the 'socks5-https-client' npm module.

I expect back a 200 OK response from my request but instead the Java server throws an SSLException: Unrecognized SSL message, plaintext connection error.

My Node JS code is very simple:

const options  = {
  proxy: {
    host: '<proxy ip>',
    port: 1080,
    type: 5
  },

  destination: {
    host: '<java server ip>',
    port: 443
  },

  command: 'connect'
};

try {
  const info = await SocksClient.createConnection(options);

  info.socket.write('GET /users HTTP/1.1\nAuthorization: Basic <token>\n\n');
  info.socket.on('data', (data) => {
    console.log(data.toString());
} catch (err) {
  // Handle errors
}

The Java server code is:

SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
SSLServerSocket listenSocket = (SSLServerSocket) ssf.createServerSocket(portNum, 50);

boolean done = false;
while (!done) {
    clientSocket = listenSocket.accept();
    if (Thread.currentThread().isInterrupted()) {
        Logger.logMessage("interrupted during accept()");
        done = true;
        break;
    }

    InputStream in  = clientSocket.getInputStream();

    // Read the initial request line from the client
    ByteArrayOutputStream inLineA = new ByteArrayOutputStream(100);
    int ch = -1;
    try {
        clientSocket.setSoTimeout(10000);
        while ((ch = in.read()) != '\n' && ch != -1)
            inLineA.write((byte) ch);
    }
    catch (Exception e) {
        e.printStackTrace(System.out);
        clientSocket.close();
        continue;
    }

[ ... process HTTP request here ]
}

After a lot of googling, I thought the issue was because the 'socks' module uses net.Socket which is not secure. However, when I switch it out for a tls.TLSSocket, it results in a proxy connection timeout.

How can I connect a socket through the proxy and send https requests? Is there an issue with my thinking?


Solution

  • Big thanks to dave_thompson_085. It worked after wrapping the TCP socket returned in a TLS socket like so:

    Socks.SocksClient.createConnection(options)
              .then(info => {
                const tlsSocket= tls.connect(
                  {
                    host: <java server ip>,
                    port: 443,
                    socket: info.socket
                  },
                  () => {
                    tlsSocket.write('GET /users HTTP/1.1\r\nAuthorization: Basic <token>\r\nHost: <java server ip>:443\r\n\r\n');
                  }
                );
    
                tlsSocket.on("data", function(data) {
                  // do something with data
                });
              })
              .catch(err => {
                console.log(err);
              });