Permalink
Browse files

my first whack at p0f

renamed p0f -> connect.p0f  (as other plugins do)
whitespace/indention cleanups
added config option for adding the X-Haraka-p0f header (enable/disable/rename)
refactored several methods to remove unnecessary indentions
changed passing of 'this' in log* methods to (var plugin = this) and passing plugin, so log entries have the correct plugin name (no, I don't know why that fixes it)
consolidated 3 instances of repetition into format_results function
added a couple function names to anonymous functions (useful debug info)
  • Loading branch information...
1 parent e530934 commit 10a19f4498b0e35cd7e7d13d1a68dd139dc90179 @msimerson msimerson committed Jan 10, 2014
Showing with 145 additions and 132 deletions.
  1. +6 −0 config/connect.p0f.ini
  2. +139 −132 plugins/connect.p0f.js
@@ -1,2 +1,8 @@
+; where the p0f socket is found
+; default: socket_path=/tmp/.p0f_socket
socket_path=/tmp/.p0f_socket
+
+; add_header, add a message header with a p0f summary
+; default: X-Haraka-p0f
+add_header=X-Haraka-p0f
View
@@ -1,4 +1,4 @@
-// Javascript p0f v3 client
+// p0f v3 client - http://lcamtuf.coredump.cx/p0f3/
var net = require('net');
var ipaddr = require('ipaddr.js');
@@ -14,54 +14,49 @@ function p0f_client(path) {
this.socket_has_error = false;
this.restart_interval = false;
- var connect = function () {
- self.sock = net.createConnection(path);
+ self.sock = net.createConnection(path);
- self.sock.setTimeout(5 * 1000);
+ self.sock.setTimeout(5 * 1000);
- self.sock.on('connect', function () {
- self.sock.setTimeout(30 * 1000);
- self.connected = true;
- self.socket_has_error = false;
- self.ready = true;
- if (self.restart_interval) clearInterval(self.restart_interval);
- self.process_send_queue();
- });
+ self.sock.on('connect', function () {
+ self.sock.setTimeout(30 * 1000);
+ self.connected = true;
+ self.socket_has_error = false;
+ self.ready = true;
+ if (self.restart_interval) clearInterval(self.restart_interval);
+ self.process_send_queue();
+ });
- self.sock.on('data', function (data) {
- for (var i=0; i<data.length/232; i++) {
- self.decode_response(data.slice(((i) ? 232*i : 0), 232*(i+1)));
- }
- });
-
- self.sock.on('drain', function () {
- self.ready = true;
- self.process_send_queue();
- });
-
- self.sock.on('error', function (error) {
- self.connected = false;
- error.message = error.message + ' (socket: ' + path + ')';
- self.socket_has_error = error;
- self.sock.destroy();
- // Try and reconnect
- if (!self.restart_interval) {
- self.restart_interval = setInterval(function () {
- connect();
- }, 5 * 1000);
- }
- // Clear the receive queue
- for (var i=0; i<self.receive_queue.length; i++) {
- var item = self.receive_queue.shift();
- item.cb(self.socket_has_error);
- continue;
- }
- self.process_send_queue();
- });
- }
-
- // connect
- connect();
+ self.sock.on('data', function (data) {
+ for (var i=0; i<data.length/232; i++) {
+ self.decode_response(data.slice(((i) ? 232*i : 0), 232*(i+1)));
+ }
+ });
+
+ self.sock.on('drain', function () {
+ self.ready = true;
+ self.process_send_queue();
+ });
+
+ self.sock.on('error', function (error) {
+ self.connected = false;
+ error.message = error.message + ' (socket: ' + path + ')';
+ self.socket_has_error = error;
+ self.sock.destroy();
+ // Try and reconnect
+ if (!self.restart_interval) {
+ self.restart_interval = setInterval(function () {
+ connect();
+ }, 5 * 1000);
+ }
+ // Clear the receive queue
+ for (var i=0; i<self.receive_queue.length; i++) {
+ var item = self.receive_queue.shift();
+ item.cb(self.socket_has_error);
+ continue;
+ }
+ self.process_send_queue();
+ });
};
p0f_client.prototype.decode_response = function (data) {
@@ -76,7 +71,7 @@ p0f_client.prototype.decode_response = function (data) {
}
if (!this.receive_queue.length > 0) {
- throw new Error('unexpected data received');
+ throw new Error('unexpected data received');
}
var item = this.receive_queue.shift();
@@ -86,41 +81,41 @@ p0f_client.prototype.decode_response = function (data) {
// Response magic dword (0x50304602), native endian.
if (data.readUInt32LE(0) !== 0x50304602) {
- return item.cb(new Error('bad response magic!'));
+ return item.cb(new Error('bad response magic!'));
}
// Status dword: 0x00 for 'bad query', 0x10 for 'OK', and 0x20 for 'no match'
var st = data.readUInt32LE(4);
switch (st) {
- case (0x00):
- return item.cb(new Error('bad query'));
- break;
- case (0x10):
- var p0f = {
+ case (0x00):
+ return item.cb(new Error('bad query'));
+ break;
+ case (0x10):
+ var p0f = {
query: item.ip,
- first_seen: data.readUInt32LE(8),
- last_seen: data.readUInt32LE(12),
- total_conn: data.readUInt32LE(16),
- uptime_min: data.readUInt32LE(20),
- up_mod_days: data.readUInt32LE(24),
- last_nat: data.readUInt32LE(28),
- last_chg: data.readUInt32LE(32),
- distance: data.readInt16LE(36),
- bad_sw: data.readUInt8(38),
- os_match_q: data.readUInt8(39),
- os_name: decode_string(data, 40, 72),
- os_flavor: decode_string(data, 72, 104),
- http_name: decode_string(data, 104, 136),
- http_flavor: decode_string(data, 136, 168),
- link_type: decode_string(data, 168, 200),
- language: decode_string(data, 200, 232),
- }
- return item.cb(null, p0f);
- break;
- case (0x20):
- return item.cb(null, null);
- break;
- default:
- throw new Error('unknown status: ' + st);
+ first_seen: data.readUInt32LE(8),
+ last_seen: data.readUInt32LE(12),
+ total_conn: data.readUInt32LE(16),
+ uptime_min: data.readUInt32LE(20),
+ up_mod_days: data.readUInt32LE(24),
+ last_nat: data.readUInt32LE(28),
+ last_chg: data.readUInt32LE(32),
+ distance: data.readInt16LE(36),
+ bad_sw: data.readUInt8(38),
+ os_match_q: data.readUInt8(39),
+ os_name: decode_string(data, 40, 72),
+ os_flavor: decode_string(data, 72, 104),
+ http_name: decode_string(data, 104, 136),
+ http_flavor: decode_string(data, 136, 168),
+ link_type: decode_string(data, 168, 200),
+ language: decode_string(data, 200, 232),
+ }
+ return item.cb(null, p0f);
+ break;
+ case (0x20):
+ return item.cb(null, null);
+ break;
+ default:
+ throw new Error('unknown status: ' + st);
}
}
@@ -149,88 +144,100 @@ p0f_client.prototype.query = function (ip, cb) {
}
p0f_client.prototype.process_send_queue = function () {
- if (this.send_queue.length > 0) {
- for (var i=0; i<this.send_queue.length; i++) {
- if (this.socket_has_error) {
- var item = this.send_queue.shift();
- item.cb(this.socket_has_error);
- continue;
- }
- if (!this.ready) break;
+ if (this.send_queue.length === 0) { return; };
+
+ for (var i=0; i<this.send_queue.length; i++) {
+ if (this.socket_has_error) {
var item = this.send_queue.shift();
- this.receive_queue.push({ip: item.ip, cb: item.cb});
- if (!this.sock.write(item.buf)) {
- this.ready = false;
- }
+ item.cb(this.socket_has_error);
+ continue;
+ }
+ if (!this.ready) break;
+ var item = this.send_queue.shift();
+ this.receive_queue.push({ip: item.ip, cb: item.cb});
+ if (!this.sock.write(item.buf)) {
+ this.ready = false;
}
}
}
exports.p0f_client = p0f_client;
exports.hook_init_master = function (next) {
- var cfg = this.config.get('p0f.ini');
+ var cfg = this.config.get('connect.p0f.ini');
// Start p0f process?
server.notes.p0f_client = new p0f_client(cfg.main.socket_path);
return next();
}
exports.hook_init_child = function (next) {
- var cfg = this.config.get('p0f.ini');
+ var cfg = this.config.get('connect.p0f.ini');
server.notes.p0f_client = new p0f_client(cfg.main.socket_path);
return next();
}
-exports.hook_lookup_rdns = function (next, connection) {
+exports.hook_lookup_rdns = function onLookup(next, connection) {
if (!server.notes.p0f_client) return next();
- var self = this;
+ var plugin = this;
var p0f_client = server.notes.p0f_client;
- p0f_client.query(connection.remote_ip, function (err, result) {
+ p0f_client.query(connection.remote_ip, function onResults(err, result) {
if (err) {
- connection.logerror(self, 'error: ' + err.message);
- }
- else {
- if (result) {
- connection.loginfo(self, [
- 'os="' + result.os_name + ' ' + result.os_flavor + '"',
- 'link_type="' + result.link_type + '"',
- 'distance=' + result.distance,
- 'total_conn=' + result.total_conn,
- 'shared_ip=' + ((result.last_nat === 0) ? 'N' : 'Y'),
- ].join(' '));
- }
- // Store p0f results for other plugins
- connection.notes.p0f = result;
- }
+ connection.logerror(plugin, 'error: ' + err.message);
+ return next();
+ };
+
+ if (!result) {
+ connection.logdebug(plugin, 'error, no p0f results' );
+ return next();
+ };
+
+ connection.loginfo(plugin, format_results(result));
+
+ // Store p0f results for other plugins
+ connection.notes.p0f = result;
return next();
- });
+ });
}
-exports.hook_data_post = function (next, connection) {
- var txn = connection.transaction;
- txn.remove_header('X-Haraka-p0f');
- if (connection.notes.p0f) {
- var result = connection.notes.p0f;
- txn.add_header('X-Haraka-p0f', [
- 'os="' + result.os_name + ' ' + result.os_flavor + '"',
- 'link_type="' + result.link_type + '"',
- 'distance=' + result.distance,
- 'shared_ip=' + ((result.last_nat === 0) ? 'N' : 'Y'),
- ].join(' '));
- }
+function format_results(result) {
+ return [
+ 'os="' + result.os_name + ' ' + result.os_flavor + '"',
+ 'link_type="' + result.link_type + '"',
+ 'distance=' + result.distance,
+ 'total_conn=' + result.total_conn,
+ 'shared_ip=' + ((result.last_nat === 0) ? 'N' : 'Y'),
+ ].join(' ');
+};
+
+exports.hook_data_post = function onDataPostP0F(next, connection) {
+ var plugin = this;
+ var cfg = this.config.get('connect.p0f.ini');
+ var header_name = cfg.main.add_header;
+ if (!header_name) {
+ connection.logdebug(plugin, 'header disabled in ini' );
+ return next();
+ };
+
+ connection.transaction.remove_header(header_name);
+ var result = connection.notes.p0f;
+ if (!result) {
+ connection.logdebug(plugin, 'no p0f note' );
+ return next();
+ };
+
+ connection.logdebug(plugin, 'adding header' );
+ connection.transaction.add_header(header_name, format_results(result));
+
return next();
}
+/*
+// Redundant, was already logged in the query callback
exports.hook_disconnect = function (next, connection) {
- if (connection.notes.p0f) {
- var result = connection.notes.p0f;
- connection.loginfo(this, [
- 'os="' + result.os_name + ' ' + result.os_flavor + '"',
- 'link_type="' + result.link_type + '"',
- 'distance=' + result.distance,
- 'total_conn=' + result.total_conn,
- 'shared_ip=' + ((result.last_nat === 0) ? 'N' : 'Y'),
- ].join(' '));
- }
+ if (!connection.notes.p0f) { return next(); };
+ var plugin = this;
+ var result = connection.notes.p0f;
+ connection.loginfo(plugin, format_results(result));
return next();
}
+*/

0 comments on commit 10a19f4

Please sign in to comment.