udp-proxy
Version:
UDP-Proxy for node.js. Supports both IPv6 and v4 (and proxies between them)
158 lines (146 loc) • 5.25 kB
JavaScript
var dgram = require('dgram');
var events = require('events');
var util = require('util');
var net = require('net');
var UdpProxy = function (options) {
"use strict";
var proxy = this;
var localUdpType = 'udp4';
var serverPort = options.localport || 0;
var serverHost = options.localaddress || '0.0.0.0';
var proxyHost = options.proxyaddress || '0.0.0.0';
this.tOutTime = options.timeOutTime || 10000;
this.family = 'IPv4';
this.udpType = 'udp4';
this.host = options.address || 'localhost';
this.port = options.port || 41234;
this.connections = {};
if (options.ipv6) {
this.udpType = 'udp6';
this.family = 'IPv6';
proxyHost = net.isIPv6(options.proxyaddress) ? options.proxyaddress : '::0';
}
this._details = {
target: {
address: this.host,
family: this.family,
port: this.port
}
};
this._detailKeys = Object.keys(this._details);
if (options.localipv6) {
localUdpType = 'udp6';
serverHost = net.isIPv6(options.localaddress) ? options.localaddress : '::0';
}
this.middleware = options.middleware;
this._server = dgram.createSocket(localUdpType);
this._server.on('listening', function () {
var details = proxy.getDetails({server: this.address()});
setImmediate(function() {
proxy.emit('listening', details);
});
}).on('message', function (msg, sender) {
var client = proxy.createClient(msg, sender);
if (!client._bound) client.bind(0, proxyHost);
else client.emit('send', msg, sender);
}).on('error', function (err) {
this.close();
proxy.emit('error', err);
}).on('close', function () {
proxy.emit('close');
}).bind(serverPort, serverHost);
};
util.inherits(UdpProxy, events.EventEmitter);
UdpProxy.prototype.getDetails = function getDetails(initialObj) {
var self = this;
return this._detailKeys.reduce(function (obj, key) {
obj[key] = self._details[key];
return obj;
}, initialObj);
};
UdpProxy.prototype.hashD = function hashD(address) {
return (address.address + address.port).replace(/\./g, '');
};
UdpProxy.prototype.send = function send(msg, port, address, callback) {
this._server.send(msg, 0, msg.length, port, address, callback);
};
UdpProxy.prototype.createClient = function createClient(msg, sender) {
var senderD = this.hashD(sender);
var proxy = this;
var client;
if (this.connections.hasOwnProperty(senderD)) {
client = this.connections[senderD];
clearTimeout(client.t);
client.t = null;
return client;
}
client = dgram.createSocket(this.udpType);
client.once('listening', function () {
var details = proxy.getDetails({route: this.address(), peer: sender});
this.peer = sender;
this._bound = true;
proxy.emit('bound', details);
this.emit('send', msg, sender);
}).on('message', function (msg, sender) {
if (proxy.middleware) {
var self = this;
proxy.middleware.proxyMsg(msg, sender, this.peer, function (msg,sender,peer) {
proxy.handleProxyMsg(self, proxy, msg, sender, peer);
});
}
else {
proxy.handleProxyMsg(this, proxy, msg, sender, this.peer);
}
}).on('close', function () {
proxy.emit('proxyClose', this.peer);
this.removeAllListeners();
delete proxy.connections[senderD];
}).on('error', function (err) {
this.close();
proxy.emit('proxyError', err);
}).on('send', function (msg, sender) {
if (proxy.middleware) {
var self = this;
proxy.middleware.message(msg, sender, function (msg,sender) {
proxy.handleMessage(self, proxy, msg, sender);
});
}
else {
proxy.handleMessage(this, proxy, msg, sender);
}
});
this.connections[senderD] = client;
return client;
};
UdpProxy.prototype.handleProxyMsg = function handleProxyMsg(socket, proxy, msg, sender, peer) {
proxy.send(msg, peer.port, peer.address, function (err, bytes) {
if (err) socket.emit('proxyError', err);
});
proxy.emit('proxyMsg', msg, sender, peer);
};
UdpProxy.prototype.handleMessage = function handleMessage(socket, proxy, msg, sender) {
proxy.emit('message', msg, sender);
socket.send(msg, 0, msg.length, proxy.port, proxy.host, function (err, bytes) {
if (err) proxy.emit('proxyError', err);
if (!socket.t) socket.t = setTimeout(function () {
socket.close();
}, proxy.tOutTime);
});
};
UdpProxy.prototype.close = function close(callback) {
// close clients
var proxyConnections = this.connections;
Object.keys(proxyConnections).forEach(function (senderD) {
var client = proxyConnections[senderD];
if (client.t) {
clearTimeout(client.t);
client.t = null;
client.close();
}
});
this.connections = {};
this._server.close(callback || function () {});
};
exports.createServer = function (options) {
return new UdpProxy(options);
};