Disclosure: Some links on this page are affiliate links. We may earn a commission if you make a purchase through them, at no additional cost to you.
SOCKS5 is a protocol that routes network packets between a client and server through a proxy server. It supports a variety of authentication methods and is often used to mask a user’s IP address, bypass firewalls, or provide a secure communication tunnel for applications. Building a SOCKS5 proxy server in Node.js can help us understand both the inner workings of SOCKS protocols and how to handle low-level network communication using JavaScript.
In this article, we’ll explore how to build a SOCKS5 proxy server using Node.js. This will involve utilizing the Node.js net module for TCP socket handling and implementing the SOCKS5 protocol manually.
Setting Up the Environment
Before starting, make sure that you have Node.js installed. You can download it from the official website: https://nodejs.org/. We will also need the socks5-parse module for parsing and handling SOCKS5 protocol requests.
npm init -y
npm install socks5-parse
Once the environment is set up, we can begin coding the proxy server.
Creating the SOCKS5 Proxy Server
A SOCKS5 proxy server accepts requests from clients to forward network traffic. Here’s how we can implement the basic functionality:
javascript
const net = require(‘net’);
const parseSocks5Request = require(‘socks5-parse’);
// Define the SOCKS5 Proxy Server
const server = net.createServer((clientSocket) => {
clientSocket.once(‘data’, (data) => {
// Parse the SOCKS5 handshake request
const request = parseSocks5Request(data);
if (request) {
// Process the request depending on the command
handleRequest(request, clientSocket);
} else {
clientSocket.end();
}
});
});
server.listen(1080, () => {
console.log(‘SOCKS5 Proxy Server is running on port 1080’);
});
This basic setup initializes a TCP server that listens for incoming client connections. Upon receiving data, it attempts to parse the SOCKS5 handshake request.
Handling SOCKS5 Handshake
The SOCKS5 protocol begins with a handshake phase. During this phase, the client sends a version identifier followed by the authentication methods supported. After receiving the handshake, the server responds to confirm the authentication method.
javascript
function handleRequest(request, clientSocket) {
if (request.version === 5 && request.authMethods.includes(0)) {
// Respond to the handshake request (no authentication required)
const response = Buffer.from([0x05, 0x00]);
clientSocket.write(response);
// Wait for client command
clientSocket.once(‘data’, (data) => {
processClientCommand(data, clientSocket);
});
} else {
// Respond with a failure message if authentication isn’t supported
const response = Buffer.from([0x05, 0xFF]);
clientSocket.write(response);
clientSocket.end();
}
}
In this function, the server checks the version and authentication method requested by the client. If no authentication is required (method 0x00), the server responds with a success message and proceeds to the next stage of communication.
Processing SOCKS5 Client Commands
Once the handshake is successful, the client sends a command that tells the proxy server what action to perform. In this section, we’ll handle the “connect” command (0x01), which is the most common command for a SOCKS5 proxy server.
javascript
function processClientCommand(data, clientSocket) {
const cmd = data[1]; // Command byte (1 byte)
const destAddr = data.slice(4, data.length – 2); // Destination address (IPv4 or domain)
const destPort = data.readUInt16BE(data.length – 2); // Destination port (2 bytes)
if (cmd === 0x01) {
// CONNECT command
handleConnectCommand(destAddr, destPort, clientSocket);
} else {
// Unsupported command
const response = Buffer.from([0x05, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
clientSocket.write(response);
clientSocket.end();
}
}
Here, the server extracts the command, destination address, and port from the client’s data. If the command is 0x01 (CONNECT), it proceeds to connect to the destination server.
Establishing a Connection to the Destination Server
To establish a connection to the target server, the proxy server needs to create a socket connection to the destination and pipe the data between the client and the destination server.
javascript
function handleConnectCommand(destAddr, destPort, clientSocket) {
const destinationSocket = net.createConnection({ host: destAddr, port: destPort }, () => {
// Successfully connected to the destination server
const response = Buffer.from([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
clientSocket.write(response);
// Pipe data between client and destination
clientSocket.pipe(destinationSocket);
destinationSocket.pipe(clientSocket);
});
destinationSocket.on(‘error’, () => {
const response = Buffer.from([0x05, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
clientSocket.write(response);
clientSocket.end();
});
}
In this code, a connection is made to the target server using net.createConnection(). Once the connection is established, the proxy server responds with a success message and starts relaying data between the client and the destination server using pipe().
Handling Errors
Proper error handling is essential in any networking application. Here’s how we can handle connection errors both for the proxy and destination servers.
javascript
clientSocket.on(‘error’, () => {
console.error(‘Client connection error’);
clientSocket.end();
});
destinationSocket.on(‘error’, () => {
console.error(‘Destination connection error’);
clientSocket.write(Buffer.from([0x05, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
clientSocket.end();
});
Both the client and destination sockets are monitored for errors. If an error occurs, the connection is terminated, and an appropriate response is sent to the client.
Conclusion
By implementing a SOCKS5 proxy server with Node.js, we get a deep understanding of low-level network communication and the SOCKS5 protocol. This server can handle the core functions of authentication, client commands, and data forwarding, providing a basic yet functional SOCKS5 proxy service. Expanding this further, you could implement support for additional authentication methods, command types, and enhanced security features.