diff --git a/lib/netsh.js b/lib/netsh.js index a1398a1..76d441c 100644 --- a/lib/netsh.js +++ b/lib/netsh.js @@ -1,3 +1,90 @@ /** + * Scanning WiFis on Windows * Created by kc on 04.04.16. */ + +const systemRoot = process.env.SystemRoot || 'C:\\Windows'; +const tool = systemRoot + '\\System32\\netsh.exe'; +const cmdLine = tool + ' wlan show networks mode=Bssid'; + +/** + * Parsing netnsh output. Unfortunately netsh supplies the network information + * in the language of the operating system. Translating the terms into every + * language supplied is not possible, therefore this implementation follows + * an approach of analyzing the structure of the output + */ +function parseOutput(str, callback) { + var blocks = str.split('\n\n'); + var wifis = []; + var err = null; + try { + if (!blocks || blocks.length === 1) { + // No WiFis found + return []; + } + + // Each block has the same structure, while some parts might be available and others + // not. A sample structure: + // SSID 1 : AP-Test1 + // Network type : Infrastructure + // Authentication : WPA2-Personal + // Encryption : CCMP + // BSSID 1 : 00:aa:f2:77:a5:53 + // Signal : 46% + // Radio type : 802.11n + // Channel : 6 + // Basic rates (MBit/s) : 1 2 5.5 11 + // Other rates (MBit/s) : 6 9 12 18 24 36 48 54 + for (var i = 1, l = blocks.length; i < l; i++) { + var network = {}; + var lines = blocks[i].split('\n'); + var regexChannel = /[a-zA-Z0-9()\s]+:[\s]*[0-9]+$/g; + if (!lines || lines.length < 2) { + continue; + } + + // First line is always the SSID (which can be empty) + var ssid = lines[0].substring(lines[0].indexOf(':') + 1).trim(); + + for (var t = 1, n = lines.length; t < n; t++) { + if (lines[t].split(':').length === 7) { + // This is the mac address, use this one as trigger for a new network + if (network.mac) { + wifis.push(network); + } + network = { + ssid: ssid, + mac : lines[t].substring(lines[t].indexOf(':') + 1).trim() + }; + } + else if (lines[t].indexOf('%') > 0) { + // Network signal strength, identified by '%' + var level = parseInt(lines[t].split(':')[1].split('%')[0].trim(), 10); + + network.signal_level = (level / 2) - 100; + } + else if (!network.channel) { + // A tricky one: the channel is the first one having just ONE number. Set only + // if the channel is not already set ("Basic Rates" can be a single number also) + if (regexChannel.exec(lines[t])) { + network.channel = parseInt(lines[t].split(':')[1].trim()); + } + } + } + if (network) { + wifis.push(network); + } + } + } + catch (ex) { + err = ex; + } + callback(err, wifis); +} + + +module.exports = { + parseOutput: parseOutput, + cmdLine : cmdLine, + tool : tool +}; diff --git a/package.json b/package.json index 7257647..2b68822 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,25 @@ { - "name": "",node-wifi-scanner + "name": "node-wifi-scanner", "version": "0.0.1", - "description": "detects WiFi networks", - "author": "Christian Kuster ", - "main": "index.js" + "description": "node.js module for WiFi network detection", + "main": "index.js", + "keywords": [ + "WiFi", + "Node.js", + "scanner" + ], + "author": { + "name": "Christian Kuster, CH-8342 Wernetshausen", + "email": "christian@kusti.ch", + "url": "http://www.kusti.ch/" + }, + "homepage": "http://www.ferropoly.ch/", + "repository": { + "type": "git", + "url": "https://github.com/ancasicolica/node-wifi-scanner.git" + }, + "engines": { + "node": ">= 4.4.0", + "npm": ">= 2.14.0" + } } diff --git a/tests/netsh.js b/tests/netsh.js new file mode 100644 index 0000000..354571b --- /dev/null +++ b/tests/netsh.js @@ -0,0 +1,91 @@ +/** + * Created by kc on 04.04.16. + */ + +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); + +const netsh = require('../lib/netsh'); + + +describe('netsh', function () { + + it('parses de locale output', function (done) { + netsh.parseOutput(fs.readFileSync(path.join(__dirname, 'fixtures', 'netsh', 'netsh_de_complex01.txt'), {encoding: 'utf8'}), (err, info) => { + assert.ok(info); + assert.equal(info.length, 86); + + var ap = info[0]; + assert.equal(ap.mac, '00:f2:8b:8c:a6:88'); + assert.equal(ap.ssid, ''); + assert.equal(ap.signal_level, -88.5); + assert.strictEqual(ap.channel, 1); + + ap = info[22]; + assert.equal(ap.mac, '00:35:1a:5b:46:7b'); + assert.equal(ap.ssid, ''); + assert.equal(ap.signal_level, -90); + assert.strictEqual(ap.channel, 116); + + ap = info[23]; + assert.equal(ap.mac, '10:bd:18:ab:4d:8f'); + assert.equal(ap.ssid, 'Network-1'); + assert.equal(ap.signal_level, -81); + assert.strictEqual(ap.channel, 6); + + ap = info[74]; + assert.equal(ap.mac, '00:f2:8b:8c:a6:8d'); + assert.equal(ap.ssid, 'Network-6'); + assert.equal(ap.signal_level, -87.5); + assert.strictEqual(ap.channel, 1); + + ap = info[85]; + assert.equal(ap.mac, '00:f2:8b:8c:a6:85'); + assert.equal(ap.ssid, 'Network-7'); + assert.equal(ap.signal_level, -89.5); + assert.strictEqual(ap.channel, 1); + done(err); + }); + }); + + + it('parses en locale output', function (done) { + netsh.parseOutput(fs.readFileSync(path.join(__dirname, 'fixtures', 'netsh', 'netsh_en_complex01.txt'), {encoding: 'utf8'}), (err, info) => { + assert.ok(info); + assert.equal(info.length, 86); + + var ap = info[0]; + assert.equal(ap.mac, '00:f2:8b:8c:a6:88'); + assert.equal(ap.ssid, ''); + assert.equal(ap.signal_level, -88.5); + assert.strictEqual(ap.channel, 1); + + ap = info[22]; + assert.equal(ap.mac, '00:35:1a:5b:46:7b'); + assert.equal(ap.ssid, ''); + assert.equal(ap.signal_level, -90); + assert.strictEqual(ap.channel, 116); + + ap = info[23]; + assert.equal(ap.mac, '10:bd:18:ab:4d:8f'); + assert.equal(ap.ssid, 'Network-1'); + assert.equal(ap.signal_level, -81); + assert.strictEqual(ap.channel, 6); + + ap = info[74]; + assert.equal(ap.mac, '00:f2:8b:8c:a6:8d'); + assert.equal(ap.ssid, 'Network-6'); + assert.equal(ap.signal_level, -87.5); + assert.strictEqual(ap.channel, 1); + + ap = info[85]; + assert.equal(ap.mac, '00:f2:8b:8c:a6:85'); + assert.equal(ap.ssid, 'Network-7'); + assert.equal(ap.signal_level, -89.5); + assert.strictEqual(ap.channel, 1); + done(err); + }); + }); + +});