1 changed files with 134 additions and 0 deletions
@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var UDP_PORT_START = 9994; |
||||
var UDP_PORT_COUNT = 16384; |
||||
var HTTP_PORT = 8080; |
||||
var LONG_POLLING_TIMEOUT = 25000; |
||||
|
||||
var http = require('http'); |
||||
var dgram = require('dgram'); |
||||
|
||||
// clients[token] = [ most recent HTTP activity, assigned UDP socket ]
|
||||
var clients = {}; |
||||
|
||||
// GETs[token] = [ [ request, timestamp ], ... ]
|
||||
var GETs = {}; |
||||
|
||||
// mappings[localPort+'/'+remoteIp+'/'+remotePort] = { ZT source: [ token ] }
|
||||
var mappings = {}; |
||||
|
||||
// Array of available UDP sockets to assign randomly to clients
|
||||
var udpSocketPool = []; |
||||
|
||||
function onIncomingUdp(socket,message,remoteIp,remotePort) |
||||
{ |
||||
if (message.length > 16) { |
||||
var mappingKey = socket.localPort + '/' + remoteIp + '/' + remotePort; |
||||
var mapping = mappings[mappingKey]; |
||||
if (mapping) { |
||||
var ztDestination = message.readUIntBE(8,5); |
||||
if (ztDestination in mapping) { |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
function onOutgoingUdp(token,socket,message,remoteIp,remotePort) |
||||
{ |
||||
if (message.length > 16) { |
||||
var ztDestination = message.readUIntBE(8,5); |
||||
var ztSource = (message.length >= 28) ? message.readUIntBE(13,5) ? 0; |
||||
if ((ztSource & 0xff00000000) == 0xff00000000) // fragment
|
||||
ztSource = 0; |
||||
|
||||
if ((ztDestination !== 0)&&((ztDestination & 0xff00000000) !== 0xff00000000)) { |
||||
socket.send(message,0,message.length,remotePort,remoteIp); |
||||
} |
||||
} |
||||
} |
||||
|
||||
function doHousekeeping() |
||||
{ |
||||
} |
||||
|
||||
for(var udpPort=UDP_PORT_START;udpPort<(UDP_PORT_START+UDP_PORT_COUNT)++udpPort) { |
||||
var socket = dgram.createSocket('udp4',function(message,rinfo) { onIncomingUdp(socket,message,rinfo.address,rinfo.port); }); |
||||
socket.on('listening',function() { |
||||
console.log('Listening on '+socket.localPort); |
||||
udpSocketPool.push(socket); |
||||
} |
||||
socket.on('error',function() { |
||||
console.log('Error listening on '+socket.localPort); |
||||
socket.close(); |
||||
}) |
||||
socket.bind(udpPort); |
||||
} |
||||
|
||||
server = http.createServer(function(request,response) { |
||||
console.log(request.socket.remoteAddress+" "+request.method+" "+request.url); |
||||
|
||||
try { |
||||
// /<proxy token>/<ignored>/...
|
||||
var urlSp = request.url.split('/'); |
||||
if ((urlSp.length >= 3)&&(udpSocketPool.length > 0)) { |
||||
var token = urlSp[1]; // urlSp[0] == '' since URLs start with /
|
||||
|
||||
if (token.length >= 8) { |
||||
var client = clients[token]; |
||||
if (!Array.isArray(client)) { |
||||
client = [ Date.now(),udpSocketPool[Math.floor(Math.random() * udpSocketPool.length)] ]; |
||||
clients[token] = client; |
||||
} else client[0] = Date.now(); |
||||
|
||||
if (request.method === "GET") { |
||||
|
||||
// /<proxy token>/<ignored> ... waits via old skool long polling
|
||||
|
||||
} else if (request.method === "POST") { |
||||
|
||||
// /<proxy token>/<ignored>/<dest ip>/<dest port>
|
||||
if (urlSp.length === 5) { |
||||
var ipSp = urlSp[3].split('.'); |
||||
var port = parseInt(urlSp[4],10); |
||||
// Note: do not allow the use of this proxy to talk to privileged ports
|
||||
if ((ipSp.length === 4)&&(port >= 1024)&&(port <= 0xffff)) { |
||||
var ip = [ parseInt(ipSp[0]),parseInt(ipSp[1]),parseInt(ipSp[2]),parseInt(ipSp[3]) ]; |
||||
if ( (ip[0] > 0) |
||||
&&(ip[0] < 240) |
||||
&&(ip[0] !== 127) |
||||
&&(ip[1] >= 0) |
||||
&&(ip[1] <= 255) |
||||
&&(ip[2] >= 0) |
||||
&&(ip[2] <= 255) |
||||
&&(ip[3] > 0) |
||||
&&(ip[3] < 255) ) { |
||||
var postData = null; |
||||
request.on('data',function(chunk) { |
||||
postData = ((postData === null) ? chunk : Buffer.concat([ postData,chunk ])); |
||||
}); |
||||
request.on('end',function() { |
||||
if (postData !== null) |
||||
onOutgoingUdp(token,client[1],postData,urlSp[3],port); |
||||
response.writeHead(200,{'Content-Length':0,'Pragma':'no-cache','Cache-Control':'no-cache'}); |
||||
response.end(); |
||||
}); |
||||
return; // no 400 -- read from stream
|
||||
} // else 400
|
||||
} // else 400
|
||||
} // else 400
|
||||
|
||||
} // else 400
|
||||
|
||||
} // else 400
|
||||
} // else 400
|
||||
} catch (e) {} // 400
|
||||
|
||||
response.writeHead(400,{'Content-Length':0,'Pragma':'no-cache','Cache-Control':'no-cache'}); |
||||
response.end(); |
||||
return; |
||||
}); |
||||
|
||||
setInterval(doHousekeeping,5000); |
||||
|
||||
server.setTimeout(120000); |
||||
server.listen(HTTP_PORT); |
||||
Loading…
Reference in new issue