UNPKG

lightsword

Version:

LightSword Secure SOCKS5 Proxy / iOS VPN Server

199 lines (153 loc) 5.79 kB
//----------------------------------- // Copyright(c) 2015 Neko //----------------------------------- 'use strict' import * as net from 'net'; import { EventEmitter } from 'events'; import * as crypto from 'crypto'; import * as cryptoEx from '../common/cipher'; import { VPN_TYPE, HandshakeOptions, defaultCipherAlgorithm } from '../common/constant' import { handleSocks5 } from './socks5/index'; import { handleOSXSocks5 } from './osxcl5/index'; import { handleAppleVPN } from './aplvpn/index'; import * as kinq from 'kinq'; export type ServerOptions = { cipherAlgorithm: string, password: string, port: number, timeout?: number, expireDate?: string, disableSelfProtection?: boolean, speed?: number } export type UpdateServerOptions = { expireDate?: string, disableSelfProtection?: boolean, speed?: number } export class LsServer extends EventEmitter { disableSelfProtection = false; cipherAlgorithm: string; password: string; port: number; timeout: number; expireDate: string; speed: number; blackIPs = new Set<string>(); private static expireRefreshInterval = 60 * 60 * 1000; private remainingTime: number; // Unit: ms private remainingTimer: NodeJS.Timer; private blacklistIntervalTimer: NodeJS.Timer; private blacklist = new Map<string, Set<number>>(); private server: net.Server; private requestHandlers = new Map<VPN_TYPE, (client: net.Socket, data: Buffer, options: HandshakeOptions) => boolean>(); constructor(options: ServerOptions) { super() let me = this; Object.getOwnPropertyNames(options).forEach(n => me[n] = options[n]); this.requestHandlers.set(VPN_TYPE.APLVPN, handleAppleVPN); this.requestHandlers.set(VPN_TYPE.SOCKS5, handleSocks5); this.requestHandlers.set(VPN_TYPE.OSXCL5, handleOSXSocks5); } start() { let me = this; let server = net.createServer(async (client) => { if (me.blacklist.has(client.remoteAddress) && me.blacklist.get(client.remoteAddress).size > 20) return client.dispose(); let data = await client.readAsync(); if (!data) return client.dispose(); let meta = cryptoEx.SupportedCiphers[me.cipherAlgorithm]; if (!meta) meta = cryptoEx.SupportedCiphers[defaultCipherAlgorithm]; let ivLength = meta[1]; if (data.length < ivLength) { console.warn(client.remoteAddress, 'Malicious Access'); return me.addToBlacklist(client); } let iv = data.slice(0, ivLength); let decipher = cryptoEx.createDecipher(me.cipherAlgorithm, me.password, iv); let et = data.slice(ivLength, data.length); let dt = Buffer.concat([decipher.update(et), decipher.final()]); if (dt.length < 2) { console.warn(client.remoteAddress, 'Malicious Access') return me.addToBlacklist(client); } let vpnType = dt[0]; let paddingSize = dt[1]; let options = { iv, password: me.password, cipherAlgorithm: me.cipherAlgorithm, timeout: me.timeout, xorNum: paddingSize, speed: me.speed, ivLength, }; let request = dt.slice(2 + paddingSize, data.length); let handler = me.requestHandlers.get(vpnType); if (!handler) return me.addToBlacklist(client); let handled = handler(client, request, options); if (handled) return; me.addToBlacklist(client); }); this.server = server; server.listen(this.port); server.on('error', (err) => { console.error(err.message); me.stop(); }); this.blacklistIntervalTimer = setInterval(() => me.blacklist.clear(), 10 * 60 * 1000); this.blacklistIntervalTimer.unref(); setInterval(() => me.blackIPs.clear(), 24 * 60 * 60 * 1000).unref(); this.startRemainingTimer(); } stop() { if (!this.server) return; this.server.removeAllListeners(); this.server.close(); this.server = undefined; this.stopRemainingTimer(); this.emit('close'); this.blacklist.clear(); if (this.blacklistIntervalTimer) clearInterval(this.blacklistIntervalTimer); this.blacklistIntervalTimer = undefined; } updateConfiguration(options: UpdateServerOptions) { this.disableSelfProtection = options.disableSelfProtection; this.expireDate = options.expireDate; this.startRemainingTimer(); } private addToBlacklist(client: net.Socket) { if (this.disableSelfProtection) return; let ports = this.blacklist.get(client.remoteAddress); if (!ports) { ports = new Set<number>(); this.blacklist.set(client.remoteAddress, ports); } ports.add(client.remotePort); client.dispose(); this.blackIPs.add(client.remoteAddress); } private startRemainingTimer() { let me = this; this.remainingTime = this.expireDate ? (<any>(new Date(this.expireDate)) - <any>new Date()) : undefined; if (!this.remainingTime) return me.stopRemainingTimer(); if (this.remainingTime <= 0) { return process.nextTick(() => { console.info(`${me.port} expired. ${me.expireDate} ${me.remainingTime}`); me.stop(); }); } this.stopRemainingTimer(); this.remainingTimer = setInterval(() => { me.remainingTime -= LsServer.expireRefreshInterval; if (me.remainingTime > 0) return; console.info(`${me.port} expired. ${me.expireDate} ${me.remainingTime}`); me.stop(); }, LsServer.expireRefreshInterval); this.remainingTimer.unref(); } private stopRemainingTimer() { if (!this.remainingTimer) return; clearInterval(this.remainingTimer); this.remainingTimer = undefined; } }