UNPKG

lightsword

Version:

LightSword Secure SOCKS5 Proxy / iOS VPN Server

123 lines (104 loc) 4.03 kB
//----------------------------------- // Copyright(c) 2015 Neko //----------------------------------- 'use strict' import * as os from 'os'; import * as net from 'net'; import * as util from 'util'; import { ATYP, REQUEST_CMD } from './socks5constant'; // // TCP // +----+-----+-------+------+----------+----------+ // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ // // UDP // +----+------+------+----------+----------+----------+ // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | // +----+------+------+----------+----------+----------+ // | 2 | 1 | 1 | Variable | 2 | Variable | // +----+------+------+----------+----------+----------+ export function refineDestination(rawData: Buffer): { cmd: REQUEST_CMD, addr: string, port: number, headerSize: number } { if (rawData.length < 5) { return null; } let cmd = rawData[1]; let atyp = rawData[3]; let addr = ''; let dnLength = 0; switch (atyp) { case ATYP.DN: dnLength = rawData[4]; addr = rawData.toString('utf8', 5, 5 + dnLength); break; case ATYP.IPV4: dnLength = 4; addr = rawData.skip(4).take(4).aggregate((c: string, n) => c.length > 1 ? c + util.format('.%d', n) : util.format('%d.%d', c, n)); break; case ATYP.IPV6: dnLength = 16; let bytes = rawData.skip(4).take(16).toArray(); let ipv6 = ''; for (let b of bytes) { ipv6 += ('0' + b.toString(16)).substr(-2); } addr = ipv6.substr(0, 4); for (let i = 1; i < 8; i++) { addr = util.format('%s:%s', addr, ipv6.substr(4 * i, 4)); } break; } let headerSize = 4 + (atyp === ATYP.DN ? 1 : 0) + dnLength + 2; let port = rawData.readUInt16BE(headerSize - 2); return { cmd, addr, port, headerSize}; } // +----+-----+-------+------+----------+----------+ // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | // +----+-----+-------+------+----------+----------+ // | 1 | 1 | X'00' | 1 | Variable | 2 | // +----+-----+-------+------+----------+----------+ export function createSocks5TcpReply(rep: number, atyp: number, fullAddr: string, port: number): Buffer { let tuple = parseAddrToBytes(fullAddr); let type = tuple.type; let addr = tuple.addrBytes; let reply = [0x05, rep, 0x00, atyp]; if (type === ATYP.DN) reply.push(addr.length); reply = reply.concat(addr).concat([0x00, 0x00]); let buf = new Buffer(reply); buf.writeUInt16BE(port, buf.length - 2); return buf; } // +----+------+------+----------+----------+----------+ // |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | // +----+------+------+----------+----------+----------+ // | 2 | 1 | 1 | Variable | 2 | Variable | // +----+------+------+----------+----------+----------+ export function createSocks5UdpHeader(dstAddr: string, dstPort: number): Buffer { let tuple = parseAddrToBytes(dstAddr); let type = tuple.type; let addr = tuple.addrBytes; let reply = [0x0, 0x0, 0x0, type]; if (type === ATYP.DN) reply.push(addr.length); reply = reply.concat(addr).concat([0x00, 0x00]); let buf = new Buffer(reply); buf.writeUInt16BE(dstPort, buf.length - 2); return buf; } function parseAddrToBytes(fullAddr: string): { addrBytes: number[], type: ATYP } { let type = net.isIP(fullAddr); let addrBytes = []; switch (type) { case 4: addrBytes = fullAddr.split('.').select(s => Number.parseInt(s)).toArray(); break; case 6: addrBytes = fullAddr.split(':').select(s => [Number.parseInt(s.substr(0, 2), 16), Number.parseInt(s.substr(2, 2), 16)]).aggregate((c: Array<number>, n) => c.concat(n)); break; case 0: fullAddr.each((c, i) => addrBytes.push(fullAddr.charCodeAt(i))); break; } return { addrBytes, type: type ? (type === 4 ? ATYP.IPV4 : ATYP.IPV6) : ATYP.DN } }