2017-12-08 01:21:42 +00:00
const http = require ( 'http' ) ;
const URL = require ( 'url' ) ;
2018-09-09 05:45:12 +00:00
const { Server } = http ;
2018-09-09 19:42:34 +00:00
const Promise = require ( 'bluebird' ) ;
2018-09-09 05:45:12 +00:00
const socks = require ( 'socksv5' ) ;
2018-09-10 03:44:24 +00:00
const ProxyAgent = require ( 'proxy-agent' ) ;
2017-12-08 01:21:42 +00:00
2018-09-09 05:45:12 +00:00
const TOR _ROUTER _PROXY _AGENT = 'tor-router' ;
2017-12-08 01:21:42 +00:00
class HTTPServer extends Server {
2018-09-09 19:42:34 +00:00
async listen ( ) {
return await new Promise ( ( resolve , reject ) => {
let args = Array . from ( arguments ) ;
let inner _func = super . listen ;
args . push ( ( ) => {
let args = Array . from ( arguments ) ;
resolve . apply ( args ) ;
} ) ;
inner _func . apply ( this , args ) ;
} ) ;
}
2018-09-10 03:44:24 +00:00
authenticate _user _http ( req , res ) {
return this . authenticate _user ( req , ( ) => {
res . writeHead ( 407 , { 'Proxy-Authenticate' : ` Basic realm="Name of instance to route to" ` } ) ;
res . end ( ) ;
return false ;
} )
}
authenticate _user _connect ( req , socket ) {
return this . authenticate _user ( req , ( ) => {
socket . end ( ) ;
return false ;
} )
}
authenticate _user ( req , deny ) {
if ( ! this . proxy _by _name )
return true ;
let deny _un = this . proxy _by _name . deny _unidentified _users ;
let header = req . headers [ 'authorization' ] || req . headers [ 'proxy-authorization' ] ;
if ( ! header && deny _un ) return deny ( ) ;
else if ( ! header ) return true ;
let token = header . split ( /\s+/ ) . pop ( ) ;
if ( ! token && deny _un ) return deny ( ) ;
else if ( ! token ) return true ;
let buf = new Buffer . from ( token , 'base64' ) . toString ( ) ;
let username = buf . split ( /:/ ) . shift ( ) ;
if ( ! username && deny _un ) return deny ( ) ;
else if ( ! username ) return true ;
this . logger . verbose ( ` [http]: connected attempted to instance " ${ username } " ` ) ;
let instance = this . tor _pool . instance _by _name ( username ) ;
if ( ! instance ) return deny ( ) ;
req . instance = instance ;
return true ;
}
constructor ( tor _pool , logger , proxy _by _name ) {
2017-12-08 01:21:42 +00:00
let handle _http _connections = ( req , res ) => {
2018-09-10 03:44:24 +00:00
if ( ! this . authenticate _user _http ( req , res ) )
return ;
let { instance } = req ;
2017-12-08 01:21:42 +00:00
let url = URL . parse ( req . url ) ;
url . port = url . port || 80 ;
2018-09-09 05:45:12 +00:00
let buffer = [ ] ;
2017-12-08 01:21:42 +00:00
function onIncomingData ( chunk ) {
buffer . push ( chunk ) ;
}
function preConnectClosed ( ) {
req . finished = true ;
}
req . on ( 'data' , onIncomingData ) ;
req . on ( 'end' , preConnectClosed ) ;
2018-08-10 04:46:34 +00:00
req . on ( 'error' , function ( err ) {
2018-09-09 19:42:34 +00:00
this . logger . error ( "[http-proxy]: an error occured: " + err . message ) ;
2018-08-10 04:46:34 +00:00
} ) ;
2017-12-08 01:21:42 +00:00
let connect = ( tor _instance ) => {
let socks _port = tor _instance . socks _port ;
2018-09-09 19:42:34 +00:00
this . logger . verbose ( ` [http-proxy]: ${ req . connection . remoteAddress } : ${ req . connection . remotePort } → 127.0.0.1: ${ socks _port } → ${ url . hostname } : ${ url . port } ` ) ;
2017-12-08 01:21:42 +00:00
2018-08-09 17:42:11 +00:00
let proxy _req = http . request ( {
method : req . method ,
hostname : url . hostname ,
port : url . port ,
path : url . path ,
headers : req . headers ,
2018-09-10 03:44:24 +00:00
agent : new ProxyAgent ( ` socks://127.0.0.1: ${ socks _port } ` )
2018-08-09 17:42:11 +00:00
} , ( proxy _res ) => {
proxy _res . on ( 'data' , ( chunk ) => {
res . write ( chunk ) ;
2017-12-08 01:21:42 +00:00
} ) ;
2018-08-09 17:42:11 +00:00
proxy _res . on ( 'end' , ( ) => {
res . end ( ) ;
} ) ;
2017-12-08 01:21:42 +00:00
2018-08-09 17:42:11 +00:00
res . writeHead ( proxy _res . statusCode , proxy _res . headers ) ;
} ) ;
2017-12-08 01:21:42 +00:00
2018-08-09 17:42:11 +00:00
req . removeListener ( 'data' , onIncomingData ) ;
2017-12-08 01:21:42 +00:00
2018-08-09 17:42:11 +00:00
req . on ( 'data' , ( chunk ) => {
proxy _req . write ( chunk ) ;
} )
2017-12-08 01:21:42 +00:00
2018-08-09 17:42:11 +00:00
req . on ( 'end' , ( ) => {
proxy _req . end ( ) ;
} )
while ( buffer . length ) {
proxy _req . write ( buffer . shift ( ) ) ;
}
if ( req . finished )
proxy _req . end ( ) ;
2017-12-08 01:21:42 +00:00
} ;
2018-09-10 03:44:24 +00:00
if ( instance ) {
if ( instance . ready ) {
connect ( instance ) ;
}
else {
this . logger . debug ( ` [socks]: a connection has been attempted to " ${ instance . instance _name } ", but it is not live... waiting for the instance to come online ` ) ;
instance . once ( 'ready' , ( ( ) => connect ( instance ) ) ) ;
}
}
else if ( this . tor _pool . instances . length ) {
2017-12-08 01:21:42 +00:00
connect ( tor _pool . next ( ) ) ;
} else {
2018-09-09 19:42:34 +00:00
this . logger . debug ( ` [http-proxy]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online ` ) ;
2017-12-08 01:21:42 +00:00
tor _pool . once ( 'instance_created' , connect ) ;
}
} ;
2017-12-08 01:52:10 +00:00
let handle _connect _connections = ( req , inbound _socket , head ) => {
2018-09-10 03:44:24 +00:00
if ( ! this . authenticate _user _connect ( req , inbound _socket ) )
return ;
let { instance } = req ;
2017-12-08 01:52:10 +00:00
let hostname = req . url . split ( ':' ) . shift ( ) ;
let port = Number ( req . url . split ( ':' ) . pop ( ) ) ;
2017-12-08 01:21:42 +00:00
let connect = ( tor _instance ) => {
let socks _port = tor _instance . socks _port ;
2018-09-09 19:42:34 +00:00
this . logger && this . logger . verbose ( ` [http-connect]: ${ req . connection . remoteAddress } : ${ req . connection . remotePort } → 127.0.0.1: ${ socks _port } ${ tor _instance . definition . Name ? ' (' + tor _instance . definition . Name + ')' : '' } → ${ hostname } : ${ port } ` )
2017-12-08 01:52:10 +00:00
var outbound _socket ;
2017-12-08 01:21:42 +00:00
let onClose = ( error ) => {
inbound _socket && inbound _socket . end ( ) ;
outbound _socket && outbound _socket . end ( ) ;
inbound _socket = outbound _socket = buffer = void ( 0 ) ;
if ( error )
2017-12-08 01:52:10 +00:00
this . logger . error ( ` [http-connect]: an error occured: ${ error . message } ` )
2017-12-08 01:21:42 +00:00
} ;
2017-12-08 01:52:10 +00:00
var buffer = [ head ] ;
let onInboundData = function ( data ) {
buffer . push ( data ) ;
} ;
2018-08-09 17:42:11 +00:00
socks . connect ( {
host : hostname ,
port : port ,
proxyHost : '127.0.0.1' ,
proxyPort : socks _port ,
localDNS : false ,
auths : [ socks . auth . None ( ) ]
} , ( $outbound _socket ) => {
outbound _socket = $outbound _socket ;
outbound _socket && outbound _socket . on ( 'close' , onClose ) ;
outbound _socket && outbound _socket . on ( 'error' , onClose ) ;
2018-09-09 05:45:12 +00:00
inbound _socket . write ( ` HTTP/1.1 200 Connection Established \r \n '+'Proxy-agent: ${ TOR _ROUTER _PROXY _AGENT } \r \n ` + '\r\n' ) ;
2018-08-09 17:42:11 +00:00
outbound _socket . write ( head ) ;
outbound _socket . pipe ( inbound _socket ) ;
inbound _socket . pipe ( outbound _socket ) ;
2017-12-08 01:21:42 +00:00
} ) ;
} ;
2018-09-10 03:44:24 +00:00
if ( instance ) {
if ( instance . ready ) {
connect ( instance ) ;
}
else {
this . logger . debug ( ` [socks]: a connection has been attempted to " ${ instance . instance _name } ", but it is not live... waiting for the instance to come online ` ) ;
instance . once ( 'ready' , ( ( ) => connect ( instance ) ) ) ;
}
}
else if ( this . tor _pool . instances . length ) {
connect ( this . tor _pool . next ( ) ) ;
2017-12-08 01:21:42 +00:00
} else {
2018-09-09 19:42:34 +00:00
this . logger . debug ( ` [http-connect]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online ` ) ;
2018-09-10 03:44:24 +00:00
this . tor _pool . once ( 'instance_created' , connect ) ;
2017-12-08 01:21:42 +00:00
}
} ;
super ( handle _http _connections ) ;
2017-12-08 01:52:10 +00:00
this . on ( 'connect' , handle _connect _connections ) ;
2018-09-09 19:42:34 +00:00
2018-09-09 05:45:12 +00:00
this . logger = logger || require ( './winston-silent-logger' ) ;
2017-12-08 01:21:42 +00:00
this . tor _pool = tor _pool ;
2018-09-10 03:44:24 +00:00
this . proxy _by _name = proxy _by _name ;
2017-12-08 01:21:42 +00:00
}
} ;
module . exports = HTTPServer ;