diff --git a/README.md b/README.md index b20799d..c405306 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,10 @@ A request can complete in a number of ways, for example the request timed out, a response was received from a host other than the targeted host (i.e. a gateway) or an error occurred when sending the request. -All errors excluding a timed out error are passed to the callback function as -an instance of the `Error` object. For timed out errors the error passed to -the callback function will be an instance of the `ping.RequestTimedOutError` -object, with the exposed `message` attribute set to `Request timed out`. +All errors are sub-classes of the `Error` class. For timed out errors the +error passed to the callback function will be an instance of the +`ping.RequestTimedOutError` class, with the exposed `message` attribute set +to `Request timed out`. This makes it easy to determine if a host responded or whether an error occurred: @@ -58,16 +58,29 @@ occurred: console.log (target + ": Alive"); }); -If a host other than the target reports an error its address will be included -in the error, i.e.: +In addition to the the `ping.RequestTimedOutError` class, the following errors +are also exported by this module to wrap ICMP error responses: + + * `DestinationUnreachableError` + * `PacketTooBigError` + * `ParameterProblemError` + * `RedirectReceivedError` + * `SourceQuenchError` + * `TimeExceededError` + +These errors are typically reported by hosts other than the intended target. +In all cases class exposes a `source` attribute which will specify the +host who reported the error (which could be the intended target). This will +also be included in the errors `message` attribute, i.e.: $ sudo node example/ping-ttl.js 1 192.168.2.10 192.168.2.20 192.168.2.30 192.168.2.10: Alive - 192.168.2.20: Error: Time exceeded (source=192.168.1.1) + 192.168.2.20: TimeExceededError: Time exceeded (source=192.168.1.1) 192.168.2.30: Not alive The `Session` class will emit an `error` event for any other error not -directly associated with a request. +directly associated with a request. This is typically an instance of the +`Error` class with the errors `message` attribute specifying the reason. # Packet Size @@ -131,7 +144,7 @@ items: packets) * `retries` - Number of times to re-send a ping requests, defaults to `1` * `sessionId` - A unique ID used to identify request and response packets sent - by this instance of the `Session` class, valid numbers of in the range of + by this instance of the `Session` class, valid numbers are in the range of `1` to `65535`, defaults to the value of `process.pid % 65535` * `timeout` - Number of milliseconds to wait for a response before re-trying or failing, defaults to `2000` @@ -279,7 +292,7 @@ Bug reports should be sent to . * Add the `packetSize` option to the `createSession()` method to specify how many bytes each ICMP echo request packet should be -## Version 1.1.5 - ? +## Version 1.1.5 - 17/05/2013 * Incorrectly parsing ICMP error responses resulting in responses matching the wrong request @@ -290,6 +303,15 @@ Bug reports should be sent to . * Added example programs `ping-ttl.js` and `ping6-ttl.js` * Use MIT license instead of GPL +## Version 1.1.6 - 17/05/2013 + + * Session IDs are now 2 bytes (previously 1 byte), and request IDs are also + now 2 bytes long (previously 3 bytes) + * Each ICMP error response now has an associated error class (i.e. the + `Time exceeded` response maps onto the `ping.TimeExceededError` class) + * Call request callbacks with an error when there are no free request IDs + because of too many outstanding requests + # Roadmap In no particular order: diff --git a/index.js b/index.js index 3e7a90a..685d3dc 100644 --- a/index.js +++ b/index.js @@ -19,12 +19,54 @@ var NetworkProtocol = { _expandConstantObject (NetworkProtocol); -function RequestTimedOutError (message) { +function DestinationUnreachableError (source) { + this.name = "DestinationUnreachableError"; + this.message = "Destination unreachable (source=" + source + ")"; + this.source = source; +} +util.inherits (DestinationUnreachableError, Error); + +function PacketTooBigError (source) { + this.name = "PacketTooBigError"; + this.message = "Packet too big (source=" + source + ")"; + this.source = source; +} +util.inherits (PacketTooBigError, Error); + +function ParameterProblemError (source) { + this.name = "ParameterProblemError"; + this.message = "Parameter problem (source=" + source + ")"; + this.source = source; +} +util.inherits (ParameterProblemError, Error); + +function RedirectReceivedError (source) { + this.name = "RedirectReceivedError"; + this.message = "Redireect received (source=" + source + ")"; + this.source = source; +} +util.inherits (RedirectReceivedError, Error); + +function RequestTimedOutError () { this.name = "RequestTimedOutError"; - this.message = message; + this.message = "Request timed out"; } util.inherits (RequestTimedOutError, Error); +function SourceQuenchError (source) { + this.name = "SourceQuenchError"; + this.message = "Source quench (source=" + source + ")"; + this.source = source; +} +util.inherits (SourceQuenchError, Error); + +function TimeExceededError (source) { + this.name = "TimeExceededError"; + this.message = "Time exceeded (source=" + source + ")"; + this.source = source; +} +util.inherits (TimeExceededError, Error); + function Session (options) { this.retries = (options && options.retries) ? options.retries : 1; this.timeout = (options && options.timeout) ? options.timeout : 2000; @@ -45,7 +87,7 @@ function Session (options) { ? options.sessionId : process.pid; - this.sessionId = this.sessionId % 255; + this.sessionId = this.sessionId % 65535; this.nextId = 1; @@ -176,12 +218,12 @@ Session.prototype.fromBuffer = function (buffer) { } // Response is not for a request we generated - if (buffer.readUInt8 (offset + 4) != this.sessionId) + if (buffer.readUInt16BE (offset + 4) != this.sessionId) return; buffer[offset + 4] = 0; - var id = buffer.readUInt32BE (offset + 4); + var id = buffer.readUInt16BE (offset + 6); var req = this.reqs[id]; if (req) { @@ -211,17 +253,13 @@ Session.prototype.onSocketMessage = function (buffer, source) { this.reqRemove (req.id); if (this.addressFamily == raw.AddressFamily.IPv6) { if (req.type == 1) { - req.callback (new Error ("Destination unreachable (source=" - + source + ")"), req.target); + req.callback (new DestinationUnreachableError (source), req.target); } else if (req.type == 2) { - req.callback (new Error ("Packet too big (source=" + source + ")"), - req.target); + req.callback (new PacketTooBigError (source), req.target); } else if (req.type == 3) { - req.callback (new Error ("Time exceeded (source=" + source + ")"), - req.target); + req.callback (new TimeExceededError (source), req.target); } else if (req.type == 4) { - req.callback (new Error ("Parameter problem (source=" + source - + ")"), req.target); + req.callback (new ParameterProblemError (source), req.target); } else if (req.type == 129) { req.callback (null, req.target); } else { @@ -232,17 +270,13 @@ Session.prototype.onSocketMessage = function (buffer, source) { if (req.type == 0) { req.callback (null, req.target); } else if (req.type == 3) { - req.callback (new Error ("Destination unreachable (source=" - + source + ")"), req.target); + req.callback (new DestinationUnreachableError (source), req.target); } else if (req.type == 4) { - req.callback (new Error ("Source quench (source=" + source + ")"), - req.target); + req.callback (new SourceQuenchError (source), req.target); } else if (req.type == 5) { - req.callback (new Error ("Redirect received (source=" + source - + ")"), req.target); + req.callback (new RedirectReceivedError (source), req.target); } else if (req.type == 11) { - req.callback (new Error ("Time exceeded (source=" + source + ")"), - req.target); + req.callback (new TimeExceededError (source), req.target); } else { req.callback (new Error ("Unknown response type '" + req.type + "' (source=" + source + ")"), req.target); @@ -274,19 +308,28 @@ Session.prototype.onTimeout = function (req) { // Keep searching for an ID which is not in use Session.prototype._generateId = function () { + var startId = this.nextId; while (1) { - if (this.nextId > 16777215) + if (this.nextId > 65535) this.nextId = 1; if (this.reqs[this.nextId]) { this.nextId++ - continue; } else { return this.nextId; } + // No free request IDs + if (this.nextId == startId) + return; } } Session.prototype.pingHost = function (target, callback) { + var id = this._generateId (); + if (! id) { + callback (new Error ("Too many requests outstanding"), target); + return this; + } + var req = { id: this._generateId (), retries: this.retries, @@ -345,9 +388,8 @@ Session.prototype.toBuffer = function (req) { buffer.writeUInt8 (type, 0); buffer.writeUInt8 (0, 1); buffer.writeUInt16BE (0, 2); - buffer.writeUInt32BE (req.id, 4); - - buffer[4] = this.sessionId; + buffer.writeUInt16BE (this.sessionId, 4); + buffer.writeUInt16BE (req.id, 6); // Checksums are be generated by our raw.Socket instance @@ -362,4 +404,10 @@ exports.NetworkProtocol = NetworkProtocol; exports.Session = Session; +exports.DestinationUnreachableError = DestinationUnreachableError; +exports.PacketTooBigError = PacketTooBigError; +exports.ParameterProblemError = ParameterProblemError; +exports.RedirectReceivedError = RedirectReceivedError; exports.RequestTimedOutError = RequestTimedOutError; +exports.SourceQuenchError = SourceQuenchError; +exports.TimeExceededError = TimeExceededError; diff --git a/package.json b/package.json index 2242c01..4cbf365 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "net-ping", - "version": "1.1.5", + "version": "1.1.6", "description": "Ping many hosts at once.", "main": "index.js", "directories": {