What is SOCKS4?
SOCKS4 is the fourth version of the SOCKS (Socket Secure) protocol, released in 1992. It provides basic circuit-level proxying for TCP-based client-server applications, allowing clients to connect to servers through a firewall or proxy. SOCKS4 is simpler and more lightweight than SOCKS5 but lacks modern features like UDP support, IPv6, and strong authentication.
SOCKS4 vs SOCKS5
Protocol Comparison
interface SOCKSComparison {
socks4: {
released: 1992;
authentication: 'User ID only (optional)';
protocols: 'TCP only';
ipVersion: 'IPv4 only';
dns: 'Client-side resolution';
commands: ['CONNECT'];
udpSupport: false;
complexity: 'Very simple';
headerSize: '8-9 bytes';
useCases: ['Basic TCP proxying', 'Legacy systems', 'Simple firewall bypass'];
};
socks4a: {
released: 1996;
authentication: 'User ID only (optional)';
protocols: 'TCP only';
ipVersion: 'IPv4 + domain names';
dns: 'Proxy-side resolution';
commands: ['CONNECT'];
udpSupport: false;
complexity: 'Simple';
headerSize: '8+ bytes';
useCases: ['TCP proxying with DNS delegation', 'Tor network'];
};
socks5: {
released: 1996;
authentication: 'Multiple methods (None, Username/Password, GSSAPI)';
protocols: 'TCP and UDP';
ipVersion: 'IPv4, IPv6, domain names';
dns: 'Proxy-side resolution';
commands: ['CONNECT', 'BIND', 'UDP ASSOCIATE'];
udpSupport: true;
complexity: 'More complex';
headerSize: '10+ bytes';
useCases: ['Modern applications', 'Gaming', 'P2P', 'VoIP'];
};
}
SOCKS4 Protocol
Connection Flow
Client → SOCKS4 Proxy → Destination Server
1. Client → Proxy: CONNECT Request
┌────────┬────────┬────────┬─────────┬────────┬─────────┐
│ VER(1) │ CMD(1) │ PORT(2)│ IP(4) │ USER_ID│ NULL │
└────────┴────────┴────────┴─────────┴────────┴─────────┘
VER: 0x04 (SOCKS4)
CMD: 0x01 (CONNECT)
PORT: Destination port (2 bytes, big-endian)
IP: Destination IP (4 bytes)
USER_ID: Optional user identifier
NULL: 0x00 terminator
2. Proxy → Client: Response
┌────────┬────────┬────────┬─────────┐
│ VER(1) │ REP(1) │ PORT(2)│ IP(4) │
└────────┴────────┴────────┴─────────┘
VER: 0x00
REP: Status (0x5A = success, 0x5B-0x5D = errors)
PORT: Bound port (ignored)
IP: Bound IP (ignored)
3. If successful:
Client ↔ Proxy ↔ Server: Data transfer
SOCKS4 Implementation
Node.js SOCKS4 Client
import net from 'net';
class SOCKS4Client {
private proxyHost: string;
private proxyPort: number;
private userId: string;
constructor(proxyHost: string, proxyPort: number, userId: string = '') {
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
this.userId = userId;
}
async connect(
targetHost: string,
targetPort: number
): Promise<net.Socket> {
return new Promise((resolve, reject) => {
// Connect to SOCKS4 proxy
const socket = net.connect(this.proxyPort, this.proxyHost, () => {
console.log(`Connected to SOCKS4 proxy ${this.proxyHost}:${this.proxyPort}`);
// Build SOCKS4 CONNECT request
const request = this.buildConnectRequest(targetHost, targetPort);
// Send CONNECT request
socket.write(request);
});
// Handle proxy response
socket.once('data', (data) => {
const response = this.parseResponse(data);
if (response.success) {
console.log(`SOCKS4 connection established to ${targetHost}:${targetPort}`);
resolve(socket);
} else {
socket.destroy();
reject(new Error(`SOCKS4 connection failed: ${response.error}`));
}
});
socket.on('error', (err) => {
reject(new Error(`SOCKS4 proxy error: ${err.message}`));
});
});
}
private buildConnectRequest(host: string, port: number): Buffer {
// Resolve hostname to IP (SOCKS4 requires IP address)
const ip = this.resolveIPv4(host);
const ipBytes = ip.split('.').map(Number);
// Build request buffer
const userIdBytes = Buffer.from(this.userId, 'utf8');
const request = Buffer.alloc(9 + userIdBytes.length);
// VER: SOCKS4 (0x04)
request.writeUInt8(0x04, 0);
// CMD: CONNECT (0x01)
request.writeUInt8(0x01, 1);
// PORT: Destination port (big-endian)
request.writeUInt16BE(port, 2);
// IP: Destination IP address (4 bytes)
ipBytes.forEach((byte, index) => {
request.writeUInt8(byte, 4 + index);
});
// USER_ID: Optional user identifier
if (userIdBytes.length > 0) {
userIdBytes.copy(request, 8);
}
// NULL: Terminator (0x00)
request.writeUInt8(0x00, 8 + userIdBytes.length);
return request;
}
private parseResponse(data: Buffer): {
success: boolean;
error?: string;
} {
// SOCKS4 response is 8 bytes
if (data.length < 8) {
return {
success: false,
error: 'Invalid response length'
};
}
const version = data.readUInt8(0);
const reply = data.readUInt8(1);
// Check reply code
switch (reply) {
case 0x5A:
return { success: true };
case 0x5B:
return { success: false, error: 'Request rejected or failed' };
case 0x5C:
return { success: false, error: 'Request failed: client not reachable' };
case 0x5D:
return { success: false, error: 'Request failed: client user ID mismatch' };
default:
return { success: false, error: `Unknown reply code: 0x${reply.toString(16)}` };
}
}
private resolveIPv4(host: string): string {
// Check if already an IP
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host)) {
return host;
}
// For SOCKS4, must resolve DNS client-side
// This is a simplified example - real implementation needs DNS lookup
throw new Error('SOCKS4 requires IP address, not hostname. Use SOCKS4A for DNS resolution.');
}
}
// Usage
const socks4 = new SOCKS4Client('proxy.example.com', 1080, 'username');
try {
const socket = await socks4.connect('93.184.216.34', 80); // Example IP
// Use socket for communication
socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n');
socket.on('data', (data) => {
console.log(data.toString());
});
} catch (error) {
console.error('Connection failed:', error);
}
SOCKS4A Extension
DNS Resolution via Proxy
class SOCKS4AClient extends SOCKS4Client {
private buildConnectRequest(host: string, port: number): Buffer {
// SOCKS4A allows sending domain names
// Uses special IP address 0.0.0.x (where x > 0)
// to indicate that a domain name follows
const userIdBytes = Buffer.from(this.userId || '', 'utf8');
const hostBytes = Buffer.from(host, 'utf8');
const request = Buffer.alloc(9 + userIdBytes.length + hostBytes.length + 1);
// VER: SOCKS4 (0x04)
request.writeUInt8(0x04, 0);
// CMD: CONNECT (0x01)
request.writeUInt8(0x01, 1);
// PORT: Destination port
request.writeUInt16BE(port, 2);
// IP: 0.0.0.x (where x > 0) indicates SOCKS4A
request.writeUInt8(0, 4);
request.writeUInt8(0, 5);
request.writeUInt8(0, 6);
request.writeUInt8(1, 7); // Any non-zero value
// USER_ID: User identifier
let offset = 8;
if (userIdBytes.length > 0) {
userIdBytes.copy(request, offset);
offset += userIdBytes.length;
}
// NULL: Terminator for user ID
request.writeUInt8(0x00, offset++);
// DOMAIN: Target hostname
hostBytes.copy(request, offset);
offset += hostBytes.length;
// NULL: Terminator for domain
request.writeUInt8(0x00, offset);
return request;
}
}
// Usage - SOCKS4A can resolve domain names
const socks4a = new SOCKS4AClient('proxy.example.com', 1080, 'user');
const socket = await socks4a.connect('example.com', 80); // Domain name OK
SOCKS4 Proxy Server
Basic Implementation
import net from 'net';
class SOCKS4Server {
private server: net.Server;
constructor() {
this.server = net.createServer((clientSocket) => {
this.handleConnection(clientSocket);
});
}
start(port: number) {
this.server.listen(port, () => {
console.log(`SOCKS4 server listening on port ${port}`);
});
}
private async handleConnection(clientSocket: net.Socket) {
try {
// Read SOCKS4 request
const request = await this.readRequest(clientSocket);
// Parse request
const { command, targetHost, targetPort, userId } = this.parseRequest(request);
// Only support CONNECT command
if (command !== 0x01) {
this.sendResponse(clientSocket, 0x5B); // Rejected
clientSocket.end();
return;
}
console.log(`CONNECT: ${targetHost}:${targetPort} (user: ${userId})`);
// Connect to target server
const targetSocket = net.connect(targetPort, targetHost, () => {
// Send success response
this.sendResponse(clientSocket, 0x5A);
// Pipe data bidirectionally
clientSocket.pipe(targetSocket);
targetSocket.pipe(clientSocket);
});
targetSocket.on('error', (err) => {
console.error('Target connection error:', err);
this.sendResponse(clientSocket, 0x5B);
clientSocket.end();
});
} catch (error) {
console.error('SOCKS4 error:', error);
clientSocket.end();
}
}
private readRequest(socket: net.Socket): Promise<Buffer> {
return new Promise((resolve, reject) => {
let data = Buffer.alloc(0);
const onData = (chunk: Buffer) => {
data = Buffer.concat([data, chunk]);
// Check if we have complete request (at least 9 bytes)
if (data.length >= 9) {
// Find NULL terminator for user ID
const nullIndex = data.indexOf(0, 8);
if (nullIndex !== -1) {
socket.removeListener('data', onData);
resolve(data.slice(0, nullIndex + 1));
}
}
};
socket.on('data', onData);
socket.once('error', reject);
});
}
private parseRequest(data: Buffer): {
command: number;
targetHost: string;
targetPort: number;
userId: string;
} {
const version = data.readUInt8(0);
const command = data.readUInt8(1);
const port = data.readUInt16BE(2);
// Read IP address
const ip = [
data.readUInt8(4),
data.readUInt8(5),
data.readUInt8(6),
data.readUInt8(7)
].join('.');
// Read user ID (null-terminated string)
const nullIndex = data.indexOf(0, 8);
const userId = data.slice(8, nullIndex).toString('utf8');
return {
command,
targetHost: ip,
targetPort: port,
userId
};
}
private sendResponse(socket: net.Socket, replyCode: number) {
const response = Buffer.alloc(8);
// VER: 0x00
response.writeUInt8(0x00, 0);
// REP: Reply code
response.writeUInt8(replyCode, 1);
// PORT and IP (ignored, set to 0)
response.fill(0, 2);
socket.write(response);
}
}
// Usage
const server = new SOCKS4Server();
server.start(1080);
Use Cases
When to Use SOCKS4
interface SOCKS4UseCases {
goodFor: {
legacySystems: 'Applications requiring SOCKS4 specifically';
simpleTCPProxy: 'Basic TCP connection proxying';
lowOverhead: 'Minimal protocol overhead needed';
torNetwork: 'Tor uses SOCKS4A for DNS delegation';
};
notGoodFor: {
udpTraffic: 'No UDP support - use SOCKS5';
ipv6: 'No IPv6 support - use SOCKS5';
authentication: 'Weak auth - use SOCKS5';
modernApps: 'Use SOCKS5 for new applications';
};
}
Browser Configuration
# Firefox SOCKS4 proxy settings
# Preferences → Network Settings → Manual proxy configuration
# SOCKS Host: proxy.example.com
# Port: 1080
# SOCKS v4
# Chrome/Chromium with SOCKS4
chrome --proxy-server="socks4://proxy.example.com:1080"
# curl with SOCKS4
curl --socks4 proxy.example.com:1080 http://example.com
# SSH as SOCKS4 proxy
ssh -D 1080 -N user@remote-server.com
# Creates SOCKS4/5 proxy on localhost:1080
Limitations of SOCKS4
Protocol Constraints
interface SOCKS4Limitations {
noUDP: {
issue: 'Only supports TCP';
impact: 'Cannot proxy UDP traffic (gaming, VoIP, DNS)';
workaround: 'Use SOCKS5 instead';
};
noIPv6: {
issue: 'Only supports IPv4 addresses';
impact: 'Cannot connect to IPv6-only servers';
workaround: 'Use SOCKS5 or dual-stack proxy';
};
weakAuth: {
issue: 'Only optional user ID (no password)';
impact: 'Poor security, easily bypassed';
workaround: 'Use SOCKS5 with username/password auth';
};
dnsResolution: {
issue: 'Client must resolve DNS (leaks real IP)';
impact: 'DNS queries expose destination';
workaround: 'Use SOCKS4A or SOCKS5';
};
}
Migration from SOCKS4 to SOCKS5
Upgrade Path
// SOCKS4 client (old)
const socks4Client = new SOCKS4Client('proxy.example.com', 1080);
await socks4Client.connect('93.184.216.34', 80);
// SOCKS5 client (recommended)
import { SocksClient } from 'socks';
const socks5Client = await SocksClient.createConnection({
proxy: {
host: 'proxy.example.com',
port: 1080,
type: 5, // SOCKS5
userId: 'username',
password: 'password'
},
command: 'connect',
destination: {
host: 'example.com', // Can use hostname with SOCKS5
port: 80
}
});
// SOCKS5 supports:
// - UDP traffic
// - IPv6
// - Strong authentication
// - DNS resolution via proxy
Best Practices
For SOCKS4 Usage
interface SOCKS4BestPractices {
upgrade: {
recommendation: 'Migrate to SOCKS5 when possible';
reason: 'Better security and features';
};
authentication: {
recommendation: 'Use firewall rules for access control';
reason: 'SOCKS4 user ID is not secure authentication';
};
dnsLeaks: {
recommendation: 'Use SOCKS4A if DNS delegation needed';
reason: 'Prevent DNS leaks that expose destination';
};
compatibility: {
recommendation: 'Only use SOCKS4 for legacy compatibility';
reason: 'Modern applications should use SOCKS5';
};
}