Networking

SOCKS4

SOCKS4 is an early version of the SOCKS protocol that provides basic proxy functionality for TCP connections, offering simple IP-based authentication and IPv4 support without advanced features like UDP or DNS resolution.

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';
  };
}

Learn More

Create a free Account to use SOCKS4 in Production

Say goodbye to CORS errors and get back to building great web applications. It's free!

CORSPROXY Dashboard

Related Terms

More in Networking

Related guides

Back to Glossary