|
|
@ -1,19 +1,19 @@ |
|
|
|
var util = require("util"); |
|
|
|
var fs = require("fs"); |
|
|
|
var events = require("events"); |
|
|
|
var util = require('util'); |
|
|
|
var fs = require('fs'); |
|
|
|
var events = require('events'); |
|
|
|
var dns = require('dns'); |
|
|
|
|
|
|
|
var IOWatcher = process.binding('io_watcher').IOWatcher; |
|
|
|
var binding = process.binding('net'); |
|
|
|
var constants = process.binding('constants'); |
|
|
|
var IOWatcher = process.binding('io_watcher').IOWatcher; |
|
|
|
var binding = process.binding('net'); |
|
|
|
var constants = process.binding('constants'); |
|
|
|
|
|
|
|
var socket = binding.socket; |
|
|
|
var recvfrom = binding.recvfrom; |
|
|
|
var close = binding.close; |
|
|
|
var socket = binding.socket; |
|
|
|
var recvfrom = binding.recvfrom; |
|
|
|
var close = binding.close; |
|
|
|
|
|
|
|
var ENOENT = constants.ENOENT; |
|
|
|
var ENOENT = constants.ENOENT; |
|
|
|
|
|
|
|
function isPort (x) { return parseInt(x) >= 0; } |
|
|
|
function isPort(x) { return parseInt(x) >= 0; } |
|
|
|
var pool = null; |
|
|
|
|
|
|
|
function getPool() { |
|
|
@ -22,7 +22,7 @@ function getPool() { |
|
|
|
|
|
|
|
var poolSize = 1024 * 64; |
|
|
|
|
|
|
|
if (pool === null || (pool.used + minPoolAvail > pool.length)) { |
|
|
|
if (pool === null || (pool.used + minPoolAvail > pool.length)) { |
|
|
|
pool = new Buffer(poolSize); |
|
|
|
pool.used = 0; |
|
|
|
} |
|
|
@ -31,24 +31,26 @@ function getPool() { |
|
|
|
} |
|
|
|
|
|
|
|
function dnsLookup(type, hostname, callback) { |
|
|
|
var family = (type ? ((type === "udp6") ? 6 : 4) : null); |
|
|
|
dns.lookup(hostname, family, function (err, ip, addressFamily) { |
|
|
|
var family = (type ? ((type === 'udp6') ? 6 : 4) : null); |
|
|
|
dns.lookup(hostname, family, function(err, ip, addressFamily) { |
|
|
|
if (!err && family && addressFamily !== family) { |
|
|
|
err = new Error('no address found in family '+type+' for '+hostname); |
|
|
|
err = new Error('no address found in family ' + type + |
|
|
|
' for ' + hostname); |
|
|
|
} |
|
|
|
callback(err, ip, addressFamily); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function Socket (type, listener) { |
|
|
|
function Socket(type, listener) { |
|
|
|
events.EventEmitter.call(this); |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self.type = type; |
|
|
|
if (type === "unix_dgram" || type === "udp4" || type === "udp6") { |
|
|
|
self.fd = socket(self.type); |
|
|
|
if (type === 'unix_dgram' || type === 'udp4' || type === 'udp6') { |
|
|
|
self.fd = socket(self.type); |
|
|
|
} else { |
|
|
|
throw new Error("Bad socket type specified. Valid types are: unix_dgram, udp4, udp6"); |
|
|
|
throw new Error('Bad socket type specified. Valid types are: ' + |
|
|
|
'unix_dgram, udp4, udp6'); |
|
|
|
} |
|
|
|
|
|
|
|
if (typeof listener === 'function') { |
|
|
@ -57,7 +59,7 @@ function Socket (type, listener) { |
|
|
|
|
|
|
|
self.watcher = new IOWatcher(); |
|
|
|
self.watcher.host = self; |
|
|
|
self.watcher.callback = function () { |
|
|
|
self.watcher.callback = function() { |
|
|
|
while (self.fd) { |
|
|
|
var p = getPool(); |
|
|
|
var rinfo = recvfrom(self.fd, p, p.used, p.length - p.used, 0); |
|
|
@ -69,8 +71,8 @@ function Socket (type, listener) { |
|
|
|
p.used += rinfo.size; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
if (self.type === "udp4" || self.type === "udp6") { |
|
|
|
|
|
|
|
if (self.type === 'udp4' || self.type === 'udp6') { |
|
|
|
self._startWatcher(); |
|
|
|
} |
|
|
|
} |
|
|
@ -78,65 +80,70 @@ function Socket (type, listener) { |
|
|
|
util.inherits(Socket, events.EventEmitter); |
|
|
|
exports.Socket = Socket; |
|
|
|
|
|
|
|
exports.createSocket = function (type, listener) { |
|
|
|
exports.createSocket = function(type, listener) { |
|
|
|
return new Socket(type, listener); |
|
|
|
}; |
|
|
|
|
|
|
|
Socket.prototype.bind = function () { |
|
|
|
Socket.prototype.bind = function() { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (this.type === "unix_dgram") { // bind(path)
|
|
|
|
if (typeof arguments[0] !== "string") { |
|
|
|
throw new Error("unix_dgram sockets must be bound to a path in the filesystem"); |
|
|
|
if (this.type === 'unix_dgram') { |
|
|
|
// bind(path)
|
|
|
|
if (typeof arguments[0] !== 'string') { |
|
|
|
throw new Error('unix_dgram sockets must be bound to a path in ' + |
|
|
|
'the filesystem'); |
|
|
|
} |
|
|
|
this.path = arguments[0]; |
|
|
|
|
|
|
|
fs.unlink(this.path, function (err) { // unlink old file, OK if it doesn't exist
|
|
|
|
// unlink old file, OK if it doesn't exist
|
|
|
|
fs.unlink(this.path, function(err) { |
|
|
|
if (err && err.errno !== ENOENT) { |
|
|
|
throw err; |
|
|
|
} else { |
|
|
|
try { |
|
|
|
binding.bind(self.fd, self.path); |
|
|
|
self._startWatcher(); |
|
|
|
self.emit("listening"); |
|
|
|
self.emit('listening'); |
|
|
|
} catch (err) { |
|
|
|
console.log("Error in unix_dgram bind of " + self.path); |
|
|
|
console.log('Error in unix_dgram bind of ' + self.path); |
|
|
|
console.log(err.stack); |
|
|
|
throw err; |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} else if (this.type === "udp4" || this.type === "udp6") { // bind(port, [address])
|
|
|
|
} else if (this.type === 'udp4' || this.type === 'udp6') { |
|
|
|
// bind(port, [address])
|
|
|
|
if (arguments[1] === undefined) { |
|
|
|
// Not bind()ing a specific address. Use INADDR_ANY and OS will pick one.
|
|
|
|
// The address can be found with server.address()
|
|
|
|
binding.bind(self.fd, arguments[0]); |
|
|
|
this.emit("listening"); |
|
|
|
this.emit('listening'); |
|
|
|
} else { |
|
|
|
// the first argument is the port, the second an address
|
|
|
|
this.port = arguments[0]; |
|
|
|
dnsLookup(this.type, arguments[1], function (err, ip, addressFamily) { |
|
|
|
dnsLookup(this.type, arguments[1], function(err, ip, addressFamily) { |
|
|
|
if (err) { |
|
|
|
self.emit('error', err); |
|
|
|
} else { |
|
|
|
self.ip = ip; |
|
|
|
binding.bind(self.fd, self.port, ip); |
|
|
|
self.emit("listening"); |
|
|
|
self.emit('listening'); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Socket.prototype._startWatcher = function () { |
|
|
|
Socket.prototype._startWatcher = function() { |
|
|
|
if (! this._watcherStarted) { |
|
|
|
this.watcher.set(this.fd, true, false); // listen for read ready, not write ready
|
|
|
|
// listen for read ready, not write ready
|
|
|
|
this.watcher.set(this.fd, true, false); |
|
|
|
this.watcher.start(); |
|
|
|
this._watcherStarted = true; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Socket.prototype.address = function () { |
|
|
|
Socket.prototype.address = function() { |
|
|
|
return binding.getsockname(this.fd); |
|
|
|
}; |
|
|
|
|
|
|
@ -150,11 +157,11 @@ Socket.prototype.setBroadcast = function(arg) { |
|
|
|
|
|
|
|
Socket.prototype.setTTL = function(arg) { |
|
|
|
var newttl = parseInt(arg); |
|
|
|
|
|
|
|
|
|
|
|
if (newttl > 0 && newttl < 256) { |
|
|
|
return binding.setTTL(this.fd, newttl); |
|
|
|
} else { |
|
|
|
throw new Error("New TTL must be between 1 and 255"); |
|
|
|
throw new Error('New TTL must be between 1 and 255'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -162,28 +169,31 @@ Socket.prototype.setTTL = function(arg) { |
|
|
|
Socket.prototype.send = function(buffer, offset, length) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (typeof offset !== "number" || typeof length !== "number") { |
|
|
|
throw new Error("send takes offset and length as args 2 and 3"); |
|
|
|
if (typeof offset !== 'number' || typeof length !== 'number') { |
|
|
|
throw new Error('send takes offset and length as args 2 and 3'); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.type === "unix_dgram") { // send(buffer, offset, length, path [, callback])
|
|
|
|
if (typeof arguments[3] !== "string") { |
|
|
|
throw new Error("unix_dgram sockets must send to a path in the filesystem"); |
|
|
|
if (this.type === 'unix_dgram') { |
|
|
|
// send(buffer, offset, length, path [, callback])
|
|
|
|
if (typeof arguments[3] !== 'string') { |
|
|
|
throw new Error('unix_dgram sockets must send to a path ' + |
|
|
|
'in the filesystem'); |
|
|
|
} |
|
|
|
|
|
|
|
self.sendto(buffer, offset, length, arguments[3], null, arguments[4]); |
|
|
|
} else if (this.type === "udp4" || this.type === "udp6") { // send(buffer, offset, length, port, address [, callback])
|
|
|
|
if (typeof arguments[4] !== "string") { |
|
|
|
throw new Error(this.type + " sockets must send to port, address"); |
|
|
|
} else if (this.type === 'udp4' || this.type === 'udp6') { |
|
|
|
// send(buffer, offset, length, port, address [, callback])
|
|
|
|
if (typeof arguments[4] !== 'string') { |
|
|
|
throw new Error(this.type + ' sockets must send to port, address'); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (binding.isIP(arguments[4])) { |
|
|
|
self.sendto(arguments[0], arguments[1], arguments[2], arguments[3], |
|
|
|
arguments[4], arguments[5]); |
|
|
|
} else { |
|
|
|
var port = arguments[3], |
|
|
|
callback = arguments[5]; |
|
|
|
dnsLookup(this.type, arguments[4], function (err, ip, addressFamily) { |
|
|
|
dnsLookup(this.type, arguments[4], function(err, ip, addressFamily) { |
|
|
|
if (err) { // DNS error
|
|
|
|
if (callback) { |
|
|
|
callback(err); |
|
|
@ -197,7 +207,12 @@ Socket.prototype.send = function(buffer, offset, length) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Socket.prototype.sendto = function(buffer, offset, length, port, addr, callback) { |
|
|
|
Socket.prototype.sendto = function(buffer, |
|
|
|
offset, |
|
|
|
length, |
|
|
|
port, |
|
|
|
addr, |
|
|
|
callback) { |
|
|
|
try { |
|
|
|
var bytes = binding.sendto(this.fd, buffer, offset, length, 0, port, addr); |
|
|
|
} catch (err) { |
|
|
@ -212,7 +227,7 @@ Socket.prototype.sendto = function(buffer, offset, length, port, addr, callback) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Socket.prototype.close = function () { |
|
|
|
Socket.prototype.close = function() { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (!this.fd) throw new Error('Not running'); |
|
|
@ -223,11 +238,11 @@ Socket.prototype.close = function () { |
|
|
|
close(this.fd); |
|
|
|
this.fd = null; |
|
|
|
|
|
|
|
if (this.type === "unix_dgram" && this.path) { |
|
|
|
fs.unlink(this.path, function () { |
|
|
|
self.emit("close"); |
|
|
|
if (this.type === 'unix_dgram' && this.path) { |
|
|
|
fs.unlink(this.path, function() { |
|
|
|
self.emit('close'); |
|
|
|
}); |
|
|
|
} else { |
|
|
|
this.emit("close"); |
|
|
|
this.emit('close'); |
|
|
|
} |
|
|
|
}; |
|
|
|