200 lines
5.2 KiB
JavaScript
200 lines
5.2 KiB
JavaScript
// Copyright 2012 Timothy J Fontaine <tjfontaine@gmail.com>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE
|
|
|
|
var dgram = require('dgram'),
|
|
EventEmitter = require('events').EventEmitter,
|
|
ipaddr = require('ipaddr.js'),
|
|
net = require('net'),
|
|
util = require('util');
|
|
|
|
var UDPSocket = exports.UDPSocket = function(socket, remote) {
|
|
this._socket = socket;
|
|
this._remote = remote;
|
|
this._buff = undefined;
|
|
this.base_size = 512;
|
|
this.bound = false;
|
|
this.unref = undefined;
|
|
this.ref = undefined;
|
|
};
|
|
util.inherits(UDPSocket, EventEmitter);
|
|
|
|
UDPSocket.prototype.buffer = function(size) {
|
|
this._buff = new Buffer(size);
|
|
return this._buff;
|
|
};
|
|
|
|
UDPSocket.prototype.send = function(len) {
|
|
this._socket.send(this._buff, 0, len, this._remote.port,
|
|
this._remote.address);
|
|
};
|
|
|
|
UDPSocket.prototype.bind = function(type) {
|
|
var self = this;
|
|
|
|
if (this.bound) {
|
|
this.emit('ready');
|
|
} else {
|
|
this._socket = dgram.createSocket(type);
|
|
this._socket.on('listening', function() {
|
|
self.bound = true;
|
|
if (self._socket.unref) {
|
|
self.unref = function() {
|
|
self._socket.unref();
|
|
}
|
|
self.ref = function() {
|
|
self._socket.ref();
|
|
}
|
|
}
|
|
self.emit('ready');
|
|
});
|
|
|
|
this._socket.on('message', this.emit.bind(this, 'message'));
|
|
|
|
this._socket.on('close', function() {
|
|
self.bound = false;
|
|
self.emit('close');
|
|
});
|
|
|
|
this._socket.bind();
|
|
}
|
|
};
|
|
|
|
UDPSocket.prototype.close = function() {
|
|
this._socket.close();
|
|
};
|
|
|
|
UDPSocket.prototype.remote = function(remote) {
|
|
return new UDPSocket(this._socket, remote);
|
|
};
|
|
|
|
var TCPSocket = exports.TCPSocket = function(socket) {
|
|
UDPSocket.call(this, socket);
|
|
this.base_size = 4096;
|
|
this._rest = undefined;
|
|
};
|
|
util.inherits(TCPSocket, UDPSocket);
|
|
|
|
TCPSocket.prototype.buffer = function(size) {
|
|
this._buff = new Buffer(size + 2);
|
|
return this._buff.slice(2);
|
|
};
|
|
|
|
TCPSocket.prototype.send = function(len) {
|
|
this._buff.writeUInt16BE(len, 0);
|
|
this._socket.write(this._buff.slice(0, len + 2));
|
|
};
|
|
|
|
TCPSocket.prototype.bind = function(server) {
|
|
var self = this;
|
|
|
|
if (this.bound) {
|
|
this.emit('ready');
|
|
} else {
|
|
this._socket = net.connect(server.port, server.address);
|
|
|
|
this._socket.on('connect', function() {
|
|
self.bound = true;
|
|
if (self._socket.unref) {
|
|
self.unref = function() {
|
|
self._socket.unref();
|
|
}
|
|
self.ref = function() {
|
|
self._socket.ref();
|
|
}
|
|
}
|
|
self.emit('ready');
|
|
});
|
|
|
|
this._socket.on('timeout', function() {
|
|
self.bound = false;
|
|
self.emit('close');
|
|
});
|
|
|
|
this._socket.on('close', function() {
|
|
self.bound = false;
|
|
self.emit('close');
|
|
});
|
|
|
|
this.catchMessages();
|
|
}
|
|
};
|
|
|
|
TCPSocket.prototype.catchMessages = function() {
|
|
var self = this;
|
|
this._socket.on('data', function(data) {
|
|
var len, tmp;
|
|
if (!self._rest) {
|
|
self._rest = data;
|
|
} else {
|
|
tmp = new Buffer(self._rest.length + data.length);
|
|
self._rest.copy(tmp, 0);
|
|
data.copy(tmp, self._rest.length);
|
|
self._rest = tmp;
|
|
}
|
|
while (self._rest && self._rest.length > 2) {
|
|
len = self._rest.readUInt16BE(0);
|
|
if (self._rest.length >= len + 2) {
|
|
self.emit('message', self._rest.slice(2, len + 2), self);
|
|
self._rest = self._rest.slice(len + 2);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
TCPSocket.prototype.close = function() {
|
|
this._socket.end();
|
|
};
|
|
|
|
TCPSocket.prototype.remote = function() {
|
|
return this;
|
|
};
|
|
|
|
exports.reverseIP = function(ip) {
|
|
var address, kind, reverseip, parts;
|
|
address = ipaddr.parse(ip.split(/%/)[0]);
|
|
kind = address.kind();
|
|
|
|
switch (kind) {
|
|
case 'ipv4':
|
|
address = address.toByteArray();
|
|
address.reverse();
|
|
reverseip = address.join('.') + '.IN-ADDR.ARPA';
|
|
break;
|
|
case 'ipv6':
|
|
parts = [];
|
|
address.toNormalizedString().split(':').forEach(function(part) {
|
|
var i, pad = 4 - part.length;
|
|
for (i = 0; i < pad; i++) {
|
|
part = '0' + part;
|
|
}
|
|
part.split('').forEach(function(p) {
|
|
parts.push(p);
|
|
});
|
|
});
|
|
parts.reverse();
|
|
reverseip = parts.join('.') + '.IP6.ARPA';
|
|
break;
|
|
}
|
|
|
|
return reverseip;
|
|
};
|