New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

haraka-plugin-p0f

Package Overview
Dependencies
Maintainers
0
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

haraka-plugin-p0f - npm Package Compare versions

Comparing version 1.0.9 to 1.0.10

CHANGELOG.md

419

index.js

@@ -1,262 +0,265 @@

'use strict';
'use strict'
// p0f v3 client - http://lcamtuf.coredump.cx/p0f3/
const net = require('net');
const ipaddr = require('ipaddr.js');
const net = require('net')
const ipaddr = require('ipaddr.js')
class P0FClient {
constructor (path) {
constructor(path) {
this.sock = null
this.send_queue = []
this.receive_queue = []
this.connected = false
this.ready = false
this.socket_has_error = false
this.restart_interval = false
this.sock = null;
this.send_queue = [];
this.receive_queue = [];
this.connected = false;
this.ready = false;
this.socket_has_error = false;
this.restart_interval = false;
this.connect(path)
}
this.connect(path);
}
connect(path) {
this.sock = net.createConnection(path)
this.sock.setTimeout(5 * 1000)
connect (path) {
this.sock = net.createConnection(path);
this.sock.setTimeout(5 * 1000);
this.sock.on('connect', () => {
this.sock.setTimeout(30 * 1000)
this.connected = true
this.socket_has_error = false
this.ready = true
if (this.restart_interval) clearInterval(this.restart_interval)
this.process_send_queue()
})
this.sock.on('connect', () => {
this.sock.setTimeout(30 * 1000);
this.connected = true;
this.socket_has_error = false;
this.ready = true;
if (this.restart_interval) clearInterval(this.restart_interval);
this.process_send_queue();
})
this.sock.on('data', (data) => {
for (let i = 0; i < data.length / 232; i++) {
this.decode_response(data.slice(i ? 232 * i : 0, 232 * (i + 1)))
}
})
this.sock.on('data', (data) => {
for (let i=0; i<data.length/232; i++) {
this.decode_response(data.slice(((i) ? 232*i : 0), 232*(i+1)));
}
})
this.sock.on('drain', () => {
this.ready = true
this.process_send_queue()
})
this.sock.on('drain', () => {
this.ready = true;
this.process_send_queue();
})
this.sock.on('error', (error) => {
this.connected = false
error.message = `${error.message} (socket: ${path})`
this.socket_has_error = error
this.sock.destroy()
this.sock.on('error', (error) => {
this.connected = false;
error.message = `${error.message} (socket: ${path})`;
this.socket_has_error = error;
this.sock.destroy();
// Try and reconnect
if (!this.restart_interval) {
this.restart_interval = setInterval(() => {
this.connect(path)
}, 5 * 1000)
}
// Clear the receive queue
for (let i = 0; i < this.receive_queue.length; i++) {
const item = this.receive_queue.shift()
item.cb(this.socket_has_error)
continue
}
this.process_send_queue()
})
}
// Try and reconnect
if (!this.restart_interval) {
this.restart_interval = setInterval(() => { this.connect(path); }, 5 * 1000);
}
// Clear the receive queue
for (let i=0; i<this.receive_queue.length; i++) {
const item = this.receive_queue.shift();
item.cb(this.socket_has_error);
continue;
}
this.process_send_queue();
})
shutdown() {
if (this.restart_interval) {
clearInterval(this.restart_interval)
}
}
shutdown () {
if (this.restart_interval) {
clearInterval(this.restart_interval);
}
decode_response(data) {
function decode_string(data2, start, end) {
let str = ''
for (let a = start; a < end; a++) {
const b = data2.readUInt8(a)
if (b === 0x0) break
str = str + String.fromCharCode(b)
}
return str
}
decode_response (data) {
if (this.receive_queue.length <= 0) {
throw new Error('unexpected data received')
}
const item = this.receive_queue.shift()
function decode_string (data2, start, end) {
let str = '';
for (let a=start; a<end; a++) {
const b = data2.readUInt8(a);
if (b === 0x0) break;
str = str + String.fromCharCode(b);
}
return str;
}
///////////////////
// Decode packet //
///////////////////
if (this.receive_queue.length <= 0) {
throw new Error('unexpected data received');
}
const item = this.receive_queue.shift();
// Response magic dword (0x50304602), native endian.
if (data.readUInt32LE(0) !== 0x50304602) {
return item.cb(new Error('bad response magic!'))
}
///////////////////
// Decode packet //
///////////////////
// Response magic dword (0x50304602), native endian.
if (data.readUInt32LE(0) !== 0x50304602) {
return item.cb(new Error('bad response magic!'));
// Status dword: 0x00 for 'bad query', 0x10 for 'OK', and 0x20 for 'no match'
const st = data.readUInt32LE(4)
switch (st) {
case 0x00:
return item.cb(new Error('bad query'))
case 0x10: {
const 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)
}
case 0x20:
return item.cb(null, null)
default:
throw new Error(`unknown status: ${st}`)
}
}
// Status dword: 0x00 for 'bad query', 0x10 for 'OK', and 0x20 for 'no match'
const st = data.readUInt32LE(4);
switch (st) {
case (0x00):
return item.cb(new Error('bad query'));
case (0x10): {
const 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);
}
case (0x20):
return item.cb(null, null);
default:
throw new Error(`unknown status: ${st}`);
}
query(ip, cb) {
if (this.socket_has_error) {
return cb(this.socket_has_error)
}
if (!this.connected) {
return cb(new Error('socket not connected'))
}
const addr = ipaddr.parse(ip)
const bytes = addr.toByteArray()
const buf = new Buffer(21)
buf.writeUInt32LE(0x50304601, 0) // query magic
buf.writeUInt8(addr.kind() === 'ipv6' ? 0x6 : 0x4, 4)
for (let i = 0; i < bytes.length; i++) {
buf.writeUInt8(bytes[i], 5 + i)
}
if (!this.ready) {
this.send_queue.push({ ip, cb, buf })
} else {
this.receive_queue.push({ ip, cb })
if (!this.sock.write(buf)) this.ready = false
}
}
query (ip, cb) {
if (this.socket_has_error) {
return cb(this.socket_has_error);
}
if (!this.connected) {
return cb(new Error('socket not connected'));
}
const addr = ipaddr.parse(ip);
const bytes = addr.toByteArray();
const buf = new Buffer(21);
buf.writeUInt32LE(0x50304601, 0); // query magic
buf.writeUInt8(((addr.kind() === 'ipv6') ? 0x6 : 0x4), 4);
for (let i=0; i < bytes.length; i++) {
buf.writeUInt8(bytes[i], 5 + i);
}
if (!this.ready) {
this.send_queue.push({ip, cb, buf});
}
else {
this.receive_queue.push({ip, cb});
if (!this.sock.write(buf)) this.ready = false;
}
process_send_queue() {
if (this.send_queue.length === 0) {
return
}
process_send_queue () {
if (this.send_queue.length === 0) { return; }
for (let i=0; i<this.send_queue.length; i++) {
let item;
if (this.socket_has_error) {
item = this.send_queue.shift();
item.cb(this.socket_has_error);
continue;
}
if (!this.ready) break;
item = this.send_queue.shift();
this.receive_queue.push({ip: item.ip, cb: item.cb});
if (!this.sock.write(item.buf)) {
this.ready = false;
}
}
for (let i = 0; i < this.send_queue.length; i++) {
let item
if (this.socket_has_error) {
item = this.send_queue.shift()
item.cb(this.socket_has_error)
continue
}
if (!this.ready) break
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.P0FClient = P0FClient;
exports.P0FClient = P0FClient
exports.register = function () {
this.load_p0f_ini();
this.load_p0f_ini()
this.register_hook('init_master', 'start_p0f_client')
this.register_hook('init_child', 'start_p0f_client')
this.register_hook('init_master', 'start_p0f_client')
this.register_hook('init_child', 'start_p0f_client')
this.register_hook('lookup_rdns', 'query_p0f')
this.register_hook('data_post', 'add_p0f_header')
this.register_hook('lookup_rdns', 'query_p0f')
this.register_hook('data_post', 'add_p0f_header')
}
exports.load_p0f_ini = function () {
const plugin = this;
plugin.cfg = plugin.config.get('p0f.ini', function () {
plugin.load_p0f_ini();
})
const plugin = this
plugin.cfg = plugin.config.get('p0f.ini', function () {
plugin.load_p0f_ini()
})
}
exports.start_p0f_client = function (next, server) {
if (!this.cfg.main.socket_path) {
server.logerror("main.socket_path not defined in p0f.ini!");
return next();
}
// Start p0f process
server.notes.p0f_client = new P0FClient(this.cfg.main.socket_path);
next();
if (!this.cfg.main.socket_path) {
server.logerror('main.socket_path not defined in p0f.ini!')
return next()
}
// Start p0f process
server.notes.p0f_client = new P0FClient(this.cfg.main.socket_path)
next()
}
exports.query_p0f = function onLookup (next, connection) {
const plugin = this;
if (connection.remote.is_private) return next();
exports.query_p0f = function onLookup(next, connection) {
const plugin = this
if (connection.remote.is_private) return next()
if (!connection.server.notes.p0f_client) {
connection.logerror(plugin, 'missing p0f client');
return next();
}
if (!connection.server.notes.p0f_client) {
connection.logerror(plugin, 'missing p0f client')
return next()
}
connection.server.notes.p0f_client.query(connection.remote.ip, (err, result) => {
if (err) {
connection.results.add(plugin, {err: err.message});
return next();
}
connection.server.notes.p0f_client.query(
connection.remote.ip,
(err, result) => {
if (err) {
connection.results.add(plugin, { err: err.message })
return next()
}
if (!result) {
connection.results.add(plugin, {err: 'no p0f results'});
return next();
}
if (!result) {
connection.results.add(plugin, { err: 'no p0f results' })
return next()
}
connection.loginfo(plugin, format_results(result));
connection.results.add(plugin, result);
next();
})
connection.loginfo(plugin, format_results(result))
connection.results.add(plugin, result)
next()
},
)
}
function format_results (r) {
const data = [];
if (r.os_name) data.push(`os="${r.os_name} ${r.os_flavor}"`);
if (r.link_type) data.push(`link_type="${r.link_type}"`);
if (r.distance) data.push(`distance=${r.distance}`);
if (r.total_conn) data.push(`total_conn=${r.total_conn}`);
if (r.last_nat) data.push(`shared_ip=${((r.last_nat === 0) ? 'N' : 'Y')}`);
return data.join(' ');
function format_results(r) {
const data = []
if (r.os_name) data.push(`os="${r.os_name} ${r.os_flavor}"`)
if (r.link_type) data.push(`link_type="${r.link_type}"`)
if (r.distance) data.push(`distance=${r.distance}`)
if (r.total_conn) data.push(`total_conn=${r.total_conn}`)
if (r.last_nat) data.push(`shared_ip=${r.last_nat === 0 ? 'N' : 'Y'}`)
return data.join(' ')
}
exports.add_p0f_header = function (next, connection) {
const plugin = this;
if (connection.remote.is_private) return next();
const plugin = this
if (connection.remote.is_private) return next()
const header_name = plugin.cfg.main.add_header;
if (!header_name) {
connection.logdebug(plugin, 'header disabled in ini' );
return next();
}
const header_name = plugin.cfg.main.add_header
if (!header_name) {
connection.logdebug(plugin, 'header disabled in ini')
return next()
}
connection.transaction.remove_header(header_name);
const result = connection.results.get('p0f');
if (!result || !result.os_name) {
connection.results.add(plugin, {err: 'no p0f note'});
return next();
}
connection.transaction.remove_header(header_name)
const result = connection.results.get('p0f')
if (!result || !result.os_name) {
connection.results.add(plugin, { err: 'no p0f note' })
return next()
}
connection.logdebug(plugin, 'adding header');
connection.transaction.add_header(header_name, format_results(result));
connection.logdebug(plugin, 'adding header')
connection.transaction.add_header(header_name, format_results(result))
next();
next()
}
{
"name": "haraka-plugin-p0f",
"version": "1.0.9",
"version": "1.0.10",
"description": "Haraka plugin that adds TCP fingerprinting",
"files": [
"config",
"contrib",
"CHANGELOG.md"
],
"main": "index.js",
"scripts": {
"cover": "NODE_ENV=cov npx nyc --reporter=lcovonly npm run test",
"format": "npm run prettier:fix && npm run lint:fix",
"lint": "npx eslint *.js test",
"lintfix": "npx eslint --fix *.js test",
"test": "npx mocha"
"lint:fix": "npx eslint --fix *.js test",
"prettier": "npx prettier . --check",
"prettier:fix": "npx prettier . --write --log-level=warn",
"test": "npx mocha",
"versions": "npx dependency-version-checker check",
"versions:fix": "npx dependency-version-checker update"
},

@@ -28,10 +38,9 @@ "repository": {

"devDependencies": {
"eslint": ">=8",
"eslint-plugin-haraka": "*",
"haraka-test-fixtures": "*",
"mocha": ">=9"
"@haraka/eslint-config": "^2.0.2",
"haraka-test-fixtures": "^1.3.8",
"mocha": "^11.1.0"
},
"dependencies": {
"ipaddr.js": "^2.0.1"
"ipaddr.js": "^2.2.0"
}
}
[![Build Status][ci-img]][ci-url]
[![Code Climate][clim-img]][clim-url]
[![NPM][npm-img]][npm-url]

@@ -15,12 +14,12 @@ # haraka-plugin-p0f

genre => FreeBSD
detail => 8.x (1)
uptime => 1390
link => ethernet/modem
distance => 17
genre => FreeBSD
detail => 8.x (1)
uptime => 1390
link => ethernet/modem
distance => 17
Which was parsed from this p0f fingerprint:
24.18.227.2:39435 - FreeBSD 8.x (1) (up: 1390 hrs)
-> 208.75.177.101:25 (distance 17, link: ethernet/modem)
24.18.227.2:39435 - FreeBSD 8.x (1) (up: 1390 hrs)
-> 208.75.177.101:25 (distance 17, link: ethernet/modem)

@@ -32,6 +31,4 @@ The following additional values may also be available in

## Configuration
Configuration
-----------------
1. start p0f

@@ -51,3 +48,2 @@

3. review settings in config/p0f.ini

@@ -63,4 +59,4 @@

<!-- leave these buried at the bottom of the document -->
<!-- leave these buried at the bottom of the document -->
[ci-img]: https://github.com/haraka/haraka-plugin-p0f/actions/workflows/ci.yml/badge.svg

@@ -70,3 +66,1 @@ [ci-url]: https://github.com/haraka/haraka-plugin-p0f/actions/workflows/ci.yml

[clim-url]: https://codeclimate.com/github/haraka/haraka-plugin-p0f
[npm-img]: https://nodei.co/npm/haraka-plugin-p0f.png
[npm-url]: https://www.npmjs.com/package/haraka-plugin-p0f
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc