Skip to content

Commit

Permalink
Session IDs are now 2 bytes, Request IDs are now 2 bytes, Each ICMP e…
Browse files Browse the repository at this point in the history
…rror response now has an associated error class, Call request callbacks with an error when there are no free request IDs, Update version package.json to 1.1.6, Update README.md for 1.1.6
  • Loading branch information
stephenwvickers committed May 17, 2013
1 parent 8af0445 commit a5b7e0e
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 37 deletions.
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -279,7 +292,7 @@ Bug reports should be sent to <stephen.vickers.sv@gmail.com>.
* 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
Expand All @@ -290,6 +303,15 @@ Bug reports should be sent to <stephen.vickers.sv@gmail.com>.
* 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:
Expand Down
100 changes: 74 additions & 26 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,7 +87,7 @@ function Session (options) {
? options.sessionId
: process.pid;

this.sessionId = this.sessionId % 255;
this.sessionId = this.sessionId % 65535;

this.nextId = 1;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand All @@ -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;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down

0 comments on commit a5b7e0e

Please sign in to comment.