This repository has been archived by the owner on Aug 1, 2020. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
206 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"whitelist": [ | ||
"asyncToGenerator", | ||
"es6.classes", | ||
"es6.destructuring", | ||
"es6.modules", | ||
"es6.parameters", | ||
"es6.properties.shorthand", | ||
"es6.spread", | ||
"es7.asyncFunctions", | ||
"es7.classProperties", | ||
"es7.trailingFunctionCommas", | ||
"flow", | ||
"runtime", | ||
"strict", | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import gulp from 'gulp'; | ||
import babel from 'gulp-babel'; | ||
import changed from 'gulp-changed'; | ||
import rimraf from 'rimraf'; | ||
|
||
const paths = { | ||
source: 'src/**/*.js', | ||
build: 'build', | ||
}; | ||
|
||
function build() { | ||
return gulp.src(paths.source) | ||
.pipe(changed(paths.build)) | ||
.pipe(babel()) | ||
.pipe(gulp.dest(paths.build)); | ||
} | ||
|
||
function watch(done) { | ||
gulp.watch(paths.source, build); | ||
done(); | ||
} | ||
|
||
gulp.task(build); | ||
gulp.task('watch', gulp.parallel(build, watch)); | ||
gulp.task('clean', done => { | ||
rimraf(paths.build, done); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import os from 'os'; | ||
|
||
export default class NetworkInterfaces { | ||
static getDefault() { | ||
let interfaces = os.networkInterfaces(); | ||
let names = Object.keys(interfaces); | ||
for (let name of names) { | ||
if (interfaces[name].every(iface => !iface.internal)) { | ||
return name; | ||
} | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,100 @@ | ||
import assert from 'assert'; | ||
import pcap from 'pcap'; | ||
|
||
import NetworkInterfaces from './NetworkInterfaces'; | ||
|
||
type Options = { | ||
networkInterface?: string, | ||
}; | ||
|
||
let pcapSession; | ||
|
||
function getPcapSession(interfaceName: string) { | ||
if (!pcapSession) { | ||
pcapSession = pcap.createSession(interfaceName); | ||
} else { | ||
assert.equal( | ||
interfaceName, pcapSession.device_name, | ||
'The existing pcap session must be listening on the specified interface', | ||
); | ||
} | ||
return pcapSession; | ||
} | ||
|
||
export default class PlasmaDash { | ||
constructor(macAddress: string, options?: Options = {}) { | ||
this._macAddress = macAddress; | ||
this._networkInterface = options.networkInterface || | ||
NetworkInterfaces.getDefault(); | ||
this._packetListener = this._handlePacket.bind(this); | ||
this._dashListeners = new Set(); | ||
this._isResponding = false; | ||
} | ||
|
||
addListener(listener): Subscription { | ||
if (!this._dashListeners.size) { | ||
let session = getPcapSession(); | ||
session.addListener('packet', this._packetListener); | ||
} | ||
|
||
// We run the listeners with Promise.all, which rejects early as soon as | ||
// any of its promises are rejected. Since we want to wait for all of the | ||
// listeners to finish we need to catch any errors they may throw. | ||
let guardedListener = this._createGuardedListener(listener); | ||
this._dashListeners.add(guardedListener); | ||
|
||
return new Subscription(() => { | ||
this._dashListeners.remove(guardedListener); | ||
if (!this._dashListeners.size) { | ||
let session = getPcapSession(); | ||
session.removeListener('packet', this._packetListener); | ||
if (!session.listenerCount('packet')) { | ||
session.close(); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
_createGuardedListener(listener) { | ||
return async function(...args) { | ||
try { | ||
return await listener(...args); | ||
} catch (error) { | ||
return error; | ||
} | ||
}; | ||
} | ||
|
||
async _handlePacket(rawPacket) { | ||
if (this._isResponding) { | ||
return; | ||
} | ||
|
||
let packet = pcap.decode(rawPacket); | ||
|
||
this._isResponding = true; | ||
try { | ||
// The listeners are guarded so this should never throw, but wrap it in | ||
// try-catch to be defensive | ||
await Promise.all(Array.from(this._dashListeners).map( | ||
listener => listener(packet) | ||
)); | ||
} finally { | ||
this._isResponding = false; | ||
} | ||
} | ||
} | ||
|
||
class Subscription { | ||
constructor(onRemove) { | ||
this._remove = onRemove; | ||
} | ||
|
||
remove() { | ||
if (this._remove) { | ||
return; | ||
} | ||
this._remove(); | ||
delete this._remove; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import pcap from 'pcap'; | ||
import yargs from 'yargs'; | ||
|
||
import NetworkInterfaces from './NetworkInterfaces'; | ||
|
||
if (require.main === module) { | ||
let argv = yargs | ||
.usage('Usage: $0 <command> [options]') | ||
.command('scan', 'Scan for ARP probes') | ||
.example( | ||
'$0 scan -i wlan0', | ||
'Scan for ARP probes on the given network interface' | ||
) | ||
.alias('i', 'interface') | ||
.nargs('i', 1) | ||
.default('i', NetworkInterfaces.getDefault()) | ||
.describe('i', 'The network interface on which to listen') | ||
.help('h') | ||
.alias('h', 'help') | ||
.argv; | ||
let commands = new Set(argv._); | ||
if (commands.has('scan')) { | ||
let pcapSession = pcap.createSession(argv.interface, 'arp'); | ||
pcapSession.addListener('packet', rawPacket => { | ||
console.log(rawPacket); | ||
}); | ||
} | ||
} |