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; | ||
|  | }; |