diff --git a/packages/driver-mdns/package.json b/packages/driver-mdns/package.json index 0e5f450..fb09491 100644 --- a/packages/driver-mdns/package.json +++ b/packages/driver-mdns/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@syndicate-lang/core": "^0.0.19", - "@syndicate-lang/driver-streams-node": "^0.0.1" + "@syndicate-lang/driver-streams-node": "^0.0.1", + "@syndicate-lang/driver-timer": "^0.0.20" } } diff --git a/packages/driver-mdns/src/index.js b/packages/driver-mdns/src/index.js index 5582ed1..c618a27 100644 --- a/packages/driver-mdns/src/index.js +++ b/packages/driver-mdns/src/index.js @@ -21,6 +21,8 @@ const { Observe, currentFacet, genUuid } = require("@syndicate-lang/core"); const S = activate require("@syndicate-lang/driver-streams-node"); +Object.assign(module.exports, activate require('./routes.js')); + assertion type Service(name, serviceType) = Symbol.for("mdns-service"); assertion type Publish(svc, hostName, port, txtDataRecords) = Symbol.for("mdns-publish"); assertion type Published(svc, hostName, port, txtDataRecords) = Symbol.for("mdns-published"); diff --git a/packages/driver-mdns/src/routes.js b/packages/driver-mdns/src/routes.js new file mode 100644 index 0000000..bda3be6 --- /dev/null +++ b/packages/driver-mdns/src/routes.js @@ -0,0 +1,119 @@ +//--------------------------------------------------------------------------- +// @syndicate-lang/driver-mdns, mDNS support for Syndicate. +// Copyright (C) 2016-2018 Tony Garnock-Jones +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +//--------------------------------------------------------------------------- + +const { Observe } = require("@syndicate-lang/core"); +const { PeriodicTick } = activate require("@syndicate-lang/driver-timer"); +const fs = require('fs'); + +assertion type Route(ifName, destIP, destMaskBits, gateway, flags) = Symbol.for('network-route'); +assertion type DefaultGateway(ifName, gateway) = Symbol.for('network-default-gateway'); + +assertion type ScanRoutingTable() = Symbol.for('-@syndicate-lang/driver-mdns/scan-routing-table-'); + +export { + Route, DefaultGateway, +}; + +const flags = { + // From linux/route.h + UP: 0x0001, /* route usable */ + GATEWAY: 0x0002, /* destination is a gateway */ + HOST: 0x0004, /* host entry (net otherwise) */ + REINSTATE: 0x0008, /* reinstate route after tmout */ + DYNAMIC: 0x0010, /* created dyn. (by redirect) */ + MODIFIED: 0x0020, /* modified dyn. (by redirect) */ + MTU: 0x0040, /* specific MTU for this route */ + WINDOW: 0x0080, /* per route window clamping */ + IRTT: 0x0100, /* Initial round trip time */ + REJECT: 0x0200, /* Reject route */ +}; + +spawn named 'IPRouteDatabase' { + during Observe(Route(_, _, _, _, _)) assert ScanRoutingTable(); + during Observe(DefaultGateway(_, _)) assert ScanRoutingTable(); + + during ScanRoutingTable() { + const refresh = () => { + const rows = fs.readFileSync('/proc/net/route').toString('utf-8') + .split('\n') + .filter((x) => x) + .map((x) => x.split('\t').map((f) => f.trim())); + const headings = rows.shift(); + const data = rows.map((row) => { + const rec = {}; + headings.forEach((heading, i) => { rec[heading] = row[i] || ''; }); + return rec; + }); + react { + stop on message PeriodicTick(5000) refresh(); + data.forEach((rec) => { + const destnet = littleEndianHex(rec.Destination); + const destmask = 32 - countZeroBits(littleEndianHex(rec.Mask)); + const gateway = littleEndianHex(rec.Gateway); + assert Route(rec.Iface, + intToIPv4(destnet), + destmask, + intToIPv4(gateway), + decodeFlags(Number.parseInt(rec.Flags, 16))); + if (destnet === 0 && destmask === 0) { + assert DefaultGateway(rec.Iface, intToIPv4(gateway)); + } + }); + } + }; + + on start refresh(); + } +} + +function littleEndianHex(s) { + let byteCount = s.length / 2; + let v = Number.parseInt(s, 16); + let w = 0; + while (byteCount--) { + w *= 256; + w += (v & 255); + v >>= 8; + } + return w; +} + +function decodeFlags(fs) { + let result = {}; + for (let flagName in flags) { + if (fs & flags[flagName]) result[flagName] = true; + } + return result; +} + +function intToIPv4(n) { + return '' + ((n >> 24) & 255) + + '.' + ((n >> 16) & 255) + + '.' + ((n >> 8) & 255) + + '.' + (n & 255); +} + +function countZeroBits(n) { + if (n === 0) return 32; + let i = 0; + while ((n & 1) === 0) { + i++; + n >>= 1; + } + return i; +}