Skip to content

Commit

Permalink
Use a unique session ID per instance of the Session class to identify…
Browse files Browse the repository at this point in the history
… requests and responses sent by a session, Cleanup IPv4 and IPv6 response parsing
  • Loading branch information
stephenwvickers committed May 17, 2013
1 parent 7d1ea0b commit 970e54e
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 83 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ 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, defaults to the value of
`process.pid`
by this instance of the `Session` class, valid numbers of 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
141 changes: 60 additions & 81 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ function Session (options) {
this.sessionId = (options && options.sessionId)
? options.sessionId
: process.pid;

this.sessionId = this.sessionId % 255;

this.nextId = 1;

this.socket = null;

Expand Down Expand Up @@ -109,104 +113,74 @@ Session.prototype.getSocket = function () {
};

Session.prototype.fromBuffer = function (buffer) {
var offset;
var offset, type, code;

if (this.addressFamily == raw.AddressFamily.IPv6) {
// IPv6 raw sockets don't pass the IP header back to us
// IPv6 raw sockets don't pass the IPv6 header back to us
offset = 0;
} else {
// IP header too short
if (buffer.length < 20)

if (buffer.length - offset < 8)
return;

// We don't believe any IPv6 options will be passed back to us so we
// don't attempt to pass them here.

// IPv4
if ((buffer[0] & 0xf0) != 0x40)
type = buffer.readUInt8 (offset);
code = buffer.readUInt8 (offset + 1);
} else {
// Need at least 20 bytes for an IP header, and it should be IPv4
if (buffer.length < 20 || (buffer[0] & 0xf0) != 0x40)
return;

// The length of the IPv4 header is in mulitples of double words
var ip_length = (buffer[0] & 0x0f) * 4;

// ICMP message too short
if (buffer.length - ip_length < 12)
// ICMP header is 8 bytes, we don't care about the data for now
if (buffer.length - ip_length < 8)
return;

offset = ip_length;
}

if (buffer.length - offset < 8)
return;

var type = buffer.readUInt8 (offset);
var code = buffer.readUInt8 (offset + 1);

// Get the request ID from the payload in the response for some errors
if (this.addressFamily == raw.AddressFamily.IPv6) {
// The following code doesn't seem to work as expected in the wild so
// we'll comment it out for now.

/*if (type == 1 || type == 2 || type == 3 || type == 4 || type == 129) {
ip_offset = offset + 8;
// IP header too short
if (buffer.length - ip_offset < 40)
return;
var ip_icmp_offset = ip_length;

// IPv6
if ((buffer[ip_offset] & 0xf0) != 0x60)
return;
// ICMP message too short
if (buffer.length - ip_icmp_offset < 8)
return;

// Skip over all extension headers if they exist
var next_header = buffer[ip_offset + 6];
var current_offset = 40;
while (1) {
if (next_header == 58) // ICMPv6
break;
if (buffer.length - ip_offset - current_offset < 8)
return null;
var next_header = buffer[ip_offset + current_offset];
if (current_header == 44) {
current_offset += 8;
} else if (current_header == 0 || current_header == 60
|| current_header == 43 || current_header == 51
|| current_header == 50 || current_header == 60
|| current_header == 135) {
current_offset += buffer[ip_offset + current_offset + 1] * 8;
current_offset += 8;
} else {
return null;
}
}
type = buffer.readUInt8 (ip_icmp_offset);
code = buffer.readUInt8 (ip_icmp_offset + 1);

offset = ip_offset + current_offset;
}*/
} else {
// For error type responses the sequence and identifier cannot be
// extracted in the same way as echo responses, the data part contains
// the IP header from our request, followed with at least 8 bytes from
// the echo request that generated the error, so we first go to the IP
// header, then skip that to get to the ICMP packet which contains the
// sequence and identifier.
if (type == 3 || type == 4 || type == 5 || type == 11) {
ip_offset = offset + 8;

// IP header too short
if (buffer.length - ip_offset < 20)
return;
var ip_icmp_ip_offset = ip_icmp_offset + 8;

// IPv4
if ((buffer[ip_offset] & 0xf0) != 0x40)
// Need at least 20 bytes for an IP header, and it should be IPv4
if (buffer.length - ip_icmp_ip_offset < 20
|| (buffer[ip_icmp_ip_offset] & 0xf0) != 0x40)
return;

var ip_length = (buffer[ip_offset] & 0x0f) * 4;
// The length of the IPv4 header is in mulitples of double words
var ip_icmp_ip_length = (buffer[ip_icmp_ip_offset] & 0x0f) * 4;

// ICMP message too short
if (buffer.length - ip_offset - ip_length < 12)
if (buffer.length - ip_icmp_ip_offset - ip_icmp_ip_length < 8)
return;

offset = ip_offset + ip_length;
offset = ip_icmp_ip_offset + ip_icmp_ip_length;
} else {
offset = ip_icmp_offset
}
}

// Response is not for a request we generated
if (buffer.readUInt32BE (offset + 8) != this.sessionId)
if (buffer.readUInt8 (offset + 4) != this.sessionId)
return;

buffer[offset + 4] = 0;

var id = buffer.readUInt32BE (offset + 4);
var req = this.reqs[id];

Expand Down Expand Up @@ -298,18 +272,23 @@ Session.prototype.onTimeout = function (req) {
}
};

var nextId = 1;

// This will wrap after 4294967295 pings
function _generateId (req) {
if (nextId > 4294967295)
nextId = 1;
return nextId++;
// Keep searching for an ID which is not in use
Session.prototype._generateId = function () {
while (1) {
if (this.nextId > 16777215)
this.nextId = 1;
if (this.reqs[this.nextId]) {
this.nextId++
continue;
} else {
return this.nextId;
}
}
}

Session.prototype.pingHost = function (target, callback) {
var req = {
id: _generateId (),
id: this._generateId (),
retries: this.retries,
timeout: this.timeout,
callback: callback,
Expand Down Expand Up @@ -358,7 +337,7 @@ Session.prototype.toBuffer = function (req) {

// Since our buffer represents real memory we should initialise it to
// prevent its previous contents from leaking to the network.
for (var i = 12; i < this.packetSize; i++)
for (var i = 8; i < this.packetSize; i++)
buffer[i] = 0;

var type = this.addressFamily == raw.AddressFamily.IPv6 ? 128 : 8;
Expand All @@ -368,8 +347,8 @@ Session.prototype.toBuffer = function (req) {
buffer.writeUInt16BE (0, 2);
buffer.writeUInt32BE (req.id, 4);

buffer.writeUInt32BE (this.sessionId, 8);

buffer[4] = this.sessionId;
// Checksums are be generated by our raw.Socket instance

return buffer;
Expand Down

0 comments on commit 970e54e

Please sign in to comment.