vpn.email.client.gfw
Version:
vpn client gfw mode
875 lines (689 loc) • 25.5 kB
text/typescript
/*!
* Copyright 2017 Vpn.Email network security technology Canada Inc. All Rights Reserved.
*
* Vpn.Email network technolog Canada Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as Net from 'net'
import * as Http from 'http'
import * as Dns from 'dns'
import * as Ip from 'ip'
import * as Nekudo from '../util/nekudo'
import HttpProxyHeader from './httpProxy'
import * as Async from 'async'
import * as Compress from './compress'
import * as Os from 'os'
import * as util from 'util'
import * as Rfc1928 from './rfc1928'
import * as shortId from 'shortid'
import * as Stream from 'stream'
import * as cluster from 'cluster'
import * as Fs from 'fs'
import * as Path from 'path'
const whiteIpFile = 'whiteIpList.json'
Http.globalAgent.maxSockets = 1024
const ipConnectResetTime = 1000 * 60 * 5
const Day = 1000 * 60 * 60 * 24
const managerPagePort = 8001
const _HTTP_502 = `HTTP/1.1 502 Bad Gateway
Content-Length: 0
Connection: close
Proxy-Connection: close
Content-Type: text/html; charset=UTF-8
Cache-Control: private, max-age=0
`
const _HTTP_404 = `HTTP/1.1 404 Not Found
Content-Length: 0
Connection: close
Proxy-Connection: close
Content-Type: text/html; charset=UTF-8
Cache-Control: private, max-age=0
`
const _HTTP_599_body = 'Have not internet.\r\n無互聯網,請檢查您的網絡連結\r\nネットワークはオフラインです\r\n'
const _HTTP_599 = `HTTP/1.1 599 Have not internet
Content-Length: 100
Connection: close
Proxy-Connection: close
Content-Type: text/html; charset=UTF-8
Cache-Control: private, max-age=0
${ _HTTP_599_body }
`
const _HTTP_598_body = `Domain name can't find.\r\n無此域名\r\nこのドメイン名が見つからないです\r\n`
const _HTTP_598 = `HTTP/1.1 598 Domain name can't find
Content-Length: 100
Connection: close
Proxy-Connection: close
Content-Type: text/html; charset=UTF-8
Cache-Control: private, max-age=0
${ _HTTP_598_body }
`
const _HTTP_200 = ( body: string ) => {
return `HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Content-Length: ${ body.length }
${ body }\r\n\r\n`
}
const body_403 = '<!DOCTYPE html><html><p>This domain in proxy blacklist.</p><p>這個域名被代理服務器列入黑名單</p><p>このサイドはプロクシーの禁止リストにあります</p></html>'
const HTTP_403 = `HTTP/1.1 403 Forbidden
Content-Type: text/html; charset=UTF-8
Connection: close
Proxy-Connection: close
Content-Length: 300
${ body_403 }
`
const _HTTP_PROXY_200 = `HTTP/1.1 200 Connection Established
Content-Type: text/html; charset=UTF-8
`
interface domainData {
dns: Dns.address [];
expire: number;
}
const testGatewayDomainName = 'www.google.com'
// socks 5 headers
const res_NO_AUTHENTICATION_REQUIRED = new Buffer ( '0500', 'hex' )
const respon_se = new Buffer ( '05000001000000000000', 'hex' )
// -
const IsSslConnect = ( buffer: Buffer ) => {
const kk = buffer.toString ( 'hex', 0, 4 )
return /^1603(01|02|03|00)|^80..0103|^(14|15|17)03(00|01)/.test (kk)
}
const checkDomain = ( domainList: string[], domain: string, CallBack ) => {
if ( Net.isIP ( domain )) {
return CallBack ( null, domainList.find ( n => { return n === domain }) ? true : false )
}
const domainS = domain.split ('.')
return Async.some ( domainList, ( n, next ) => {
const nS = n.split ('.')
let ret = false
for ( let i = nS.length - 1, ni = domainS.length - 1 ; i >= 0 && ni >= 0 ; i --, ni -- ) {
const ns = nS [i]
if ( domainS [ni].toLowerCase () !== nS [i].toLowerCase ()) {
break
}
if ( i === 0 )
ret = true
}
return next ( null, ret )
}, ( err, result ) => {
return CallBack ( null, result )
})
}
const otherRespon = ( path: string, host: string, port: number, UserAgent: string ) => {
const option = {
host: host,
port: port,
path: '/' + path,
method: 'GET',
headers: {
//'Upgrade-Insecure-Requests': 1,
Host: host + ':' + port,
'Accept': '*/*',
'Accept-Language': 'en-US',
'Connection': 'keep-alive',
'Accept-Encoding': 'gzip, deflate',
'User-Agent': UserAgent || 'Mozilla/5.0',
}
}
return option
}
const otherRequestForNet = ( path: string, host: string, port: number, UserAgent: string ) => {
if ( path.length < 2048)
return `GET /${ path } HTTP/1.1\r\n` +
`Host: ${ host }:${ port }\r\n` +
`Accept: */*\r\n` +
`Accept-Language: en-ca\r\n` +
`Connection: keep-alive\r\n` +
`Accept-Encoding: gzip, deflate\r\n` +
`User-Agent: ${ UserAgent ? UserAgent : 'Mozilla/5.0' }\r\n\r\n`
return `POST /${ Buffer.allocUnsafe ( 10 + Math.random()).toString('base64') } HTTP/1.1\r\n` +
`Host: ${ host }:${ port }\r\n` +
`Content-Length: ${ path.length }\r\n\r\n` +
path + '\r\n\r\n'
}
const testLogin = ( req: Buffer, loginUserList: string ) => {
const header = new HttpProxyHeader ( req )
if ( header.isGet && header.Url.path === loginUserList )
return true
return false
}
const closeClientSocket = ( socket: Net.Socket, status: number, body: string ) => {
if ( !socket || ! socket.writable )
return
let stat = _HTTP_404
switch ( status ) {
case 502:
stat = _HTTP_502
break;
case 599:
stat = _HTTP_599
break;
case 598:
stat = _HTTP_598
break;
case -200:
stat = _HTTP_PROXY_200
socket.write ( stat )
return socket.resume ()
default:
break;
}
socket.end ( stat )
return socket.resume ()
}
const _connect = ( hostname: string, hostIp: string, port: number, clientSocket: Net.Socket, data: Buffer, connectHostTimeOut: number, CallBack ) => {
const socket = new Net.Socket()
const ip = clientSocket.remoteAddress.split (':')[3]
let err = null
const id = `[${ ip }] => [${ hostname }:${ port }] `
const hostInfo = `{${ hostIp }:${ port }}`
let callbacked = false
const startTime = new Date ().getTime ()
const callBack = ( err ) => {
if ( callbacked )
return
callbacked = true
if ( socket && typeof socket.unpipe === 'function' )
socket.unpipe ()
if ( socket && typeof socket.end === 'function' )
socket.end ()
if ( socket && typeof socket.destroy === 'function' )
socket.destroy ()
CallBack ( err )
}
socket.on ( 'connect', () => {
console.log (`${ id } socket.on connect!`)
clearTimeout ( timeout )
if ( callbacked ) {
const stopTime = new Date ().getTime ()
const connectTimeOutTime = stopTime - startTime + 500
console.log (` connectHostTimeOut need change [${ connectHostTimeOut }] => [${ connectTimeOutTime }]`)
connectHostTimeOut = connectTimeOutTime
return socket.end ()
}
socket.pipe ( clientSocket ).pipe ( socket )
if ( socket && socket.writable ) {
socket.write ( data )
return socket.resume ()
}
return callBack ( null )
})
clientSocket.on ( 'error', err => {
callBack ( null )
return console.log ( 'clientSocket on error', err.message )
})
socket.on ( 'error', err => {
console.log ( '_connect socket on error', err.message )
return callBack ( err )
})
socket.on ( 'end', () => {
return callBack ( null )
})
const timeout = setTimeout (() => {
err = new Error ( `${ id } _connect timeout!` )
return callBack ( err )
}, connectHostTimeOut )
return socket.connect ( port, hostIp )
}
const tryConnectHost = ( hostname: string, hostIp: domainData, port: number, data: Buffer, clientSocket: Net.Socket, isSSLConnect: boolean,
checkAgainTimeOut: number, connectTimeOut: number, gateway: boolean, CallBack ) => {
if ( isSSLConnect ) {
clientSocket.once ( 'data', ( _data: Buffer ) => {
return tryConnectHost ( hostname, hostIp, port, _data, clientSocket, false, checkAgainTimeOut, connectTimeOut, gateway, CallBack )
})
return closeClientSocket ( clientSocket, -200, '' )
}
if ( ! hostIp ) {
console.log ( hostname, ' tryConnectHost have not hostIp CallBack!' )
return CallBack ( new Error ( 'not hostIp' ), data )
}
const now = new Date ().getTime ()
console.log ( 'tryConnectHost do Async.someSeries hostIp:' )
console.log ( hostIp )
Async.someSeries ( hostIp.dns, ( n, next ) => {
console.log ( n )
if ( n.family === 6 && ! this.hostGlobalIpV6 ) {
return next ( null, false )
}
if ( n.connect && n.connect.length ) {
const last = n.connect [0]
if ( now - last < ipConnectResetTime ) {
console.log ( n.address, ' cant connect in time range!')
return next ( null, false )
}
}
return _connect ( hostname, n.address, port, clientSocket, data, connectTimeOut, err => {
if ( err ) {
console.log ( '_connect callback error', err.message )
if ( ! n.connect )
n.connect = []
n.connect.unshift ( new Date().getTime() )
return next ( null, false )
}
return next ( null, true )
})
}, ( err, fin ) => {
if ( fin )
return CallBack ()
return CallBack ( new Error ( 'all ip cant direct connect' ), data )
})
}
class hostLookupResponse extends Stream.Writable {
constructor ( private CallBack: ( err?: Error, dns?: domainData ) => void ) { super ()}
public _write ( chunk: Buffer, enc, next ) {
const ns = chunk.toString ( 'utf8' )
try {
const _ret = JSON.parse ( ns )
const ret: domainData = {
expire: new Date().getTime () + Day,
dns: _ret
}
this.CallBack ( null, ret )
next ()
return this.end ()
} catch ( e ) {
return next ( e )
}
}
}
class gateWay {
private userAgent = null
private request ( str: string ) {
return Buffer.from ( otherRequestForNet ( str, this.serverIp, this.serverPort, this.userAgent ), 'utf8' )
}
constructor ( public serverIp: string, public serverPort: number, private password: string ) {
}
public hostLookup ( hostName: string, userAgent: string, CallBack: ( err?: Error, data?: domainData ) => void ) {
const _data = new Buffer ( JSON.stringify ({ hostName: hostName }), 'utf8' )
const encrypt = new Compress.encryptStream ( this.password, 0, ( str: string ) => {
return this.request ( str )
}, err => {
if ( err ) {
return CallBack ( err )
}
const finish = new hostLookupResponse ( CallBack )
const httpBlock = new Compress.getDecryptClientStreamFromHttp ()
const decrypt = new Compress.decryptStream ( this.password )
const _socket = Net.connect ({ port: this.serverPort, host: this.serverIp }, () => {
httpBlock.on ( 'error', err => {
_socket.end ( _HTTP_502 )
return CallBack ( err )
})
encrypt.pipe ( _socket ).pipe ( httpBlock ).pipe ( decrypt ).pipe ( finish )
encrypt.write ( _data )
})
})
}
public requestGetWay ( id: string, uuuu: VE_IPptpStream, userAgent: string, socket: Net.Socket ) {
this.userAgent = userAgent
const decrypt = new Compress.decryptStream ( this.password )
const encrypt = new Compress.encryptStream ( this.password, 0, ( str: string ) => {
return this.request ( str )
}, err => {
if ( err ) {
return console.log ( 'requestGetWay new Compress.encryptStream got ERROR: ', err.message )
}
const httpBlock = new Compress.getDecryptClientStreamFromHttp ()
httpBlock.on ( 'error', err => {
socket.end ( _HTTP_404 )
})
const _socket = Net.connect ({ port: this.serverPort, host: this.serverIp }, () => {
console.log ( 'requestGetWay connect:', uuuu.host, uuuu.port )
encrypt.pipe ( _socket ).pipe ( httpBlock ).pipe ( decrypt ).pipe ( socket ).pipe ( encrypt )
encrypt.write ( Buffer.from ( JSON.stringify ( uuuu ), 'utf8' ))
})
})
}
public requestGetWayTest ( id: string, uuuu: VE_IPptpStream, userAgent: string, socket: Net.Socket ) {
console.log ('connect to test port!')
const _socket = Net.createConnection ({ port: this.serverPort + 1, host: this.serverIp })
_socket.on ( 'connect', () => {
const ls = new Compress.printStream ('>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
const ls1 = new Compress.printStream ('<<<<<<<<<<<<<<<<<<<<<<<<<<<<')
_socket.pipe ( socket ).pipe ( _socket )
const _buf = Buffer.from ( otherRequestForNet ( Buffer.from ( JSON.stringify ( uuuu ), 'utf8' ).toString ( 'base64' ), this.serverIp, this.serverPort, this.userAgent ), 'utf8' )
_socket.write ( _buf )
})
_socket.on ( 'end', () => {
return console.log ( 'test gateway END' )
})
_socket.on ( 'error', error => {
return console.log ( 'test gateway ERROR:', error.message )
})
}
}
const isAllBlackedByFireWall = ( hostName: string, ip6: boolean, checkAgainTime: number, gatway: gateWay, userAgent: string, domainListPool: Map < string, domainData >,
CallBack: ( err?: Error, hostIp?: domainData ) => void ) => {
const hostIp = domainListPool.get ( hostName )
const now = new Date ().getTime ()
if ( ! hostIp || hostIp.expire < now)
return gatway.hostLookup ( hostName, userAgent, CallBack )
return CallBack ( null, hostIp )
}
class socks5 {
private host: string;
public ATYP: number;
public port: number;
public cmd: number;
private keep = false
private closeSocks5 ( buffer: Buffer ) {
if ( this.socket ) {
if ( this.socket.writable ) {
this.socket.end ( buffer )
}
if ( typeof this.socket.removeAllListeners === 'function')
this.socket.removeAllListeners()
}
}
private connectStat2_after ( retBuffer: Rfc1928.Requests, cmd: string ) {
if ( this.keep ) {
this.socket.once ( 'data', ( data: Buffer ) => {
/*
const header = new HttpProxyHeader.httpProxy ( data )
if ( this.hostList.putListSock5 ( this.host, IsSslConnect ? null : header ))
return this.closeSocks5 ( HTTP_403 )
if ( header.cachePath && this.cacheKeepTime ) {
return this.proxyCacheSave ( header, ( err, data1: Buffer ) => {
if ( !data1 ) {
this.mainSsWrite = new mainSSWrite ( this.vpnServerSocket, this.masterPassword, IsSslConnect ( data ), this.id, 0 )
this.socket.pipe ( this.mainSsWrite )
if ( this.savePath )
return this.save ( header.BufferWithOutKeepAlife )
return this.save ( data )
}
this.ending = true
return this.endConnect ( data1 )
})
}
*/
})
return this.socket.write ( retBuffer.buffer )
}
return this.closeSocks5 ( retBuffer.buffer )
}
private connectStat2 ( data: Buffer ) {
const req = new Rfc1928.Requests ( data )
this.ATYP = req.ATYP
this.host = req.host
this.port = req.port
this.cmd = req.cmd
const localIp = this.socket.localAddress.split (':')[3]
const retBuffer = new Rfc1928.Requests ( respon_se )
retBuffer.ATYP_IP4Address = localIp
let cmd = ''
switch ( this.cmd ) {
case Rfc1928.CMD.CONNECT:
this.keep = true
console.log ('got Rfc1928.CMD.CONNECT',this.ATYP)
break
case Rfc1928.CMD.BIND:
cmd = 'Rfc1928.CMD.BIND'
console.log ('Rfc1928.CMD.BIND request')
retBuffer.REP = Rfc1928.Replies.COMMAND_NOT_SUPPORTED_or_PROTOCOL_ERROR
break
case Rfc1928.CMD.UDP_ASSOCIATE:
cmd = 'Rfc1928.CMD.UDP_ASSOCIATE'
console.log ('Rfc1928.CMD.UDP_ASSOCIATE')
retBuffer.REP = Rfc1928.Replies.COMMAND_NOT_SUPPORTED_or_PROTOCOL_ERROR
break
default:
retBuffer.REP = Rfc1928.Replies.COMMAND_NOT_SUPPORTED_or_PROTOCOL_ERROR
break
}
return this.connectStat2_after ( retBuffer, cmd )
}
constructor ( private socket: Net.Socket ) {
this.socket.once ( 'data', ( chunk: Buffer ) => {
return this.connectStat2 ( chunk )
})
this.socket.write ( res_NO_AUTHENTICATION_REQUIRED )
}
}
const httpProxy = ( clientSocket: Net.Socket, buffer: Buffer, useGatWay: boolean, ip6: boolean, connectTimeOut: number,
domainListPool: Map < string, domainData >, gatway: gateWay, checkAgainTime: number, blackDomainList: string[] ) => {
const httpHead = new HttpProxyHeader ( buffer )
const hostName = httpHead.Url.hostname
const userAgent = httpHead.headers [ 'user-agent' ]
const CallBack = ( err?: Error, _data?: Buffer ) => {
if ( err ) {
if ( useGatWay && _data && _data.length && clientSocket.writable ) {
const uuuu : VE_IPptpStream = {
uuid: shortId.generate (),
host: hostName,
buffer: _data.toString ( 'base64' ),
cmd: Rfc1928.CMD.CONNECT,
ATYP: Rfc1928.ATYP.IP_V4,
port: parseInt ( httpHead.Url.port || httpHead.isHttps ? '443' : '80' )
}
const id = `[${ clientSocket.remoteAddress.split(':')[3] }:${ clientSocket.remotePort }][${ uuuu.uuid }] `
console.log ( ` ${id} [${ hostName }]`, 'try use gateway\n' )
return gatway.requestGetWay ( id, uuuu, userAgent, clientSocket )
}
return clientSocket.end ( HTTP_403 )
}
return
}
return checkDomain ( blackDomainList, hostName, ( err, result: boolean ) => {
if ( result ) {
return clientSocket.end ( HTTP_403 )
}
const port = parseInt ( httpHead.Url.port || httpHead.isHttps ? '443' : '80' )
const isIp = Net.isIP ( hostName )
const hostIp: domainData = ! isIp ? domainListPool.get ( hostName ) : { dns: [{ family: isIp, address: hostName, expire: null, connect: [] }], expire: null }
if ( ! hostIp ) {
return isAllBlackedByFireWall ( hostName, ip6, checkAgainTime, gatway, userAgent, domainListPool, ( err, _hostIp ) => {
if ( err ) {
return closeClientSocket ( clientSocket, 504, null )
}
if ( ! _hostIp ) {
console.log ( 'isAllBlackedByFireWall back no _hostIp' )
return CallBack ( new Error ( 'have not host info' ))
}
domainListPool.set ( hostName, _hostIp )
return tryConnectHost ( hostName, _hostIp, port, buffer, clientSocket, httpHead.isConnect, checkAgainTime, connectTimeOut, useGatWay, CallBack )
})
}
return tryConnectHost ( hostName, hostIp, port, buffer, clientSocket, httpHead.isConnect, checkAgainTime, connectTimeOut, useGatWay, CallBack )
})
}
const httpProxyTest = ( socket: Net.Socket, data: Buffer, gateway: gateWay ) => {
const httpHead = new HttpProxyHeader ( data )
const port = parseInt ( httpHead.Url.port || httpHead.isHttps ? '443' : '80' )
const hostName = httpHead.Url.hostname
const userAgent = httpHead.headers [ 'user-agent' ]
let first = true
const localrequest = ( buf: Buffer ) => {
if ( httpHead.isHttps && first ) {
first = false
console.log ('https connect!')
socket.once ( 'data', _data => {
return localrequest ( _data )
})
return closeClientSocket ( socket, -200, '' )
}
const net = Net.createConnection ( port, hostName, () => {
const ls = new Compress.printStream ('>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
const ls1 = new Compress.printStream ('<<<<<<<<<<<<<<<<<<<<<<<<<<<<')
net.pipe ( socket ).pipe( ls ).pipe ( net )
net.write ( buf )
})
}
const connectTestPort = ( buf: Buffer ) => {
if ( httpHead.isHttps && first ) {
first = false
console.log ('https connect!')
socket.once ( 'data', _data => {
return connectTestPort ( _data )
})
return closeClientSocket ( socket, -200, '' )
}
const uuuu : VE_IPptpStream = {
uuid: shortId.generate (),
host: hostName,
buffer: buf.toString ( 'base64' ),
cmd: Rfc1928.CMD.CONNECT,
ATYP: Rfc1928.ATYP.IP_V4,
port: port
}
const id = `[${ socket.remoteAddress.split(':')[3] }:${ socket.remotePort }][${ uuuu.uuid }]`
console.log ( uuuu.buffer )
return gateway.requestGetWayTest ( id, uuuu, userAgent, socket )
}
return connectTestPort ( data )
}
export default class proxyServer {
private hostLocalIpv4: { network: Ip.network, address: string } []= null
private hostLocalIpv6: string = null
private hostGlobalIpV4: string = null
private hostGlobalIpV6: string = null
private network = false
private getGlobalIpRunning = false
private saveWhiteIpList () {
if ( this.whiteIpList.length > 0 )
Fs.writeFile ( Path.join( __dirname, whiteIpFile ), JSON.stringify( this.whiteIpList ), { encoding: 'utf8' }, err => {
if ( err ) {
return console.log ( `saveWhiteIpList save file error : ${ err.message }`)
}
})
}
private isHostIp ( ip: string ) {
if ( Net.isIPv4 ( ip )) {
if ( Ip.address ) {
}
}
}
private isLocalRequest ( buffer: Buffer, path: string ) {
const header = new HttpProxyHeader ( buffer )
const ip = header.Url.hostname
if ( Net.isIP ( ip )) {
if ( ! Ip.isPrivate ( ip ) || ! this.isHostIp ( ip )) {
return false
}
if ( testLogin ( header.buffer, path )) {
return true
}
return null
}
return false
}
private getGlobalIp = ( gateWay: gateWay ) => {
if ( this.getGlobalIpRunning )
return
this.getGlobalIpRunning = true
const reqult = Nekudo.localNetInterFace ()
this.hostLocalIpv4 = reqult.PrivateIp4
this.hostLocalIpv6 = reqult.PrivateIp6
this.hostGlobalIpV4 = reqult.PublicIp4
this.hostGlobalIpV6 = reqult.publicIp6
gateWay.hostLookup ( testGatewayDomainName, null, ( err, data ) => {
if ( err )
return console.log ( 'getGlobalIp ERROR:', err.message )
this.network = true
this.hostLocalIpv6 ? console.log ( `LocalIpv6[ ${ this.hostLocalIpv6 } ]`) : null
this.hostLocalIpv4.forEach ( n => {
return console.log ( `LocalIpv4[ ${ n.address }]`)
})
this.hostGlobalIpV6 ? console.log ( `GlobalIpv6[ ${ this.hostGlobalIpV6 } ]`) : null
this.hostGlobalIpV4 ? console.log ( `GlobalIpv4[ ${ this.hostGlobalIpV4 } ]`) : null
const domain = data
if ( ! domain )
return console.log ( `[${ gateWay.serverIp } : ${ gateWay.serverPort }] Gateway connect Error!` )
console.log ( `[${ gateWay.serverIp } : ${ gateWay.serverPort }] Gateway connect success!` )
console.log ( '****************************************' )
})
}
private getPac ( remoteIp: string, port: string ) {
const ip6 = Net.isIP ( remoteIp )
const hostIp = Ip.isPrivate ( remoteIp ) ? ip6 === 6 ? this.hostLocalIpv6 : Nekudo.getLocalNetWorkIp ( this.hostLocalIpv4, remoteIp ) : ip6 === 6 ? this.hostGlobalIpV6: this.hostGlobalIpV4
const FindProxyForURL = `function FindProxyForURL ( url, host ) {return SOCKS5 ${ hostIp }:${ port };}`
return _HTTP_200 ( FindProxyForURL )
}
constructor ( private whiteIpList: string[], private domainListPool: Map < string, domainData >,
private port: number, private securityPath: string, private serverIp: string, private serverPort: number, private password: string, private checkAgainTimeOut: number,
private connectHostTimeOut: number, useGatWay: boolean, domainBlackList: string[] ) {
const gateway = new gateWay ( serverIp, serverPort, password )
this.getGlobalIp ( gateway )
const server = Net.createServer ( socket => {
const ip = socket.remoteAddress
const isWhiteIp = this.whiteIpList.find ( n => { return n === ip }) ? true : false
const isLocalIp = Ip.isPrivate ( ip )
socket.once ( 'data', ( data: Buffer ) => {
if ( ! isWhiteIp ) {
console.log ('! isWhiteIp', data.toString ('utf8'))
if ( testLogin ( data, this.securityPath )) {
this.whiteIpList.push ( ip )
this.saveWhiteIpList ()
return socket.end ( this.getPac ( ip, port.toString ()))
}
return socket.end ()
}
switch ( data.readUInt8 ( 0 )) {
case 0x4:
return console.log ( 'SOCK4 connect' )
case 0x5:
console.log ( 'socks5 connect' )
return new socks5 ( socket )
default:
return httpProxy ( socket, data, useGatWay, this.hostGlobalIpV6 ? true : false, connectHostTimeOut, domainListPool, gateway, checkAgainTimeOut, domainBlackList )
}
})
socket.on ( 'error', err => {
console.log ( `[${ip}] socket.on error`, err.message )
})
})
server.on ( 'error', err => {
console.log ( 'proxy server :', err )
return process.exit ( 1 )
})
server.listen ( port, () => {
console.log ( '****************************************' )
return console.log ( 'proxy start success on port :', port, 'security path = ', securityPath )
})
const serverTest = Net.createServer ( socket => {
const ip = socket.remoteAddress
const isWhiteIp = this.whiteIpList.find ( n => { return n === ip }) ? true : false
const isLocalIp = Ip.isPrivate ( ip )
socket.once ( 'data', ( data: Buffer ) => {
if ( ! isLocalIp && ! isWhiteIp ) {
if ( testLogin ( data, this.securityPath )) {
this.whiteIpList.push ( ip )
this.saveWhiteIpList ()
return socket.end ( this.getPac ( ip, port.toString ()))
}
return socket.end ()
}
console.log ('new connect from test port!')
switch ( data.readUInt8 ( 0 )) {
case 0x4:
return console.log ( 'SOCK4 connect' )
case 0x5:
console.log ( 'socks5 connect' )
return new socks5 ( socket )
default:
return httpProxyTest ( socket, data, gateway )
}
})
socket.on ( 'error', err => {
console.log ( `[${ip}] test socket.on error`, err.message )
})
})
serverTest.on ( 'error', err => {
console.log ( 'test proxy server :', err )
return process.exit ( 1 )
})
serverTest.listen ( port + 1, () => {
console.log ( '****************************************' )
return console.log ( 'proxy start success on port :', port + 1, 'security path = ', securityPath )
})
}
}