Last active
December 21, 2018 06:26
-
-
Save iczero/42fef451daa8b8cce72b71d303a2d646 to your computer and use it in GitHub Desktop.
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
// dependencies: pngjs raw-socket | |
// part of a filing cabinet! | |
const ping = require('./net-ping.js'); | |
const util = require('util'); | |
const PNG = require('pngjs').PNG; | |
const fs = require('fs'); | |
const cluster = require('cluster'); | |
const IMAGE = 'draw.png'; | |
const X_OFFSET = 0; | |
const Y_OFFSET = 0; | |
const INNER_DELAY = 0; | |
const LOOP_DELAY = 100; | |
const PARALLELISM = 1; | |
if (cluster.isMaster) { | |
for (let i = 0; i < PARALLELISM; i++) cluster.fork(); | |
} else { | |
let session = ping.createSession({ | |
packetSize: 12, | |
networkProtocol: ping.NetworkProtocol.IPv6 | |
}); | |
let draw = util.promisify(function draw(x, y, r, g, b, cb) { | |
session.pingHost(`2001:4c08:2028:${x}:${y}:${r.toString(16)}:${g.toString(16)}:${b.toString(16)}`, cb); | |
}); | |
let sleep = util.promisify(setTimeout); | |
(async () => { | |
let image = await new Promise((resolve, reject) => { | |
let image = fs.createReadStream(IMAGE).pipe(new PNG({ | |
filterType: -1, | |
inputColorType: 6 | |
})); | |
image.on('parsed', () => resolve(image)); | |
}); | |
let width = image.width; | |
let height = image.height; | |
for (;;) { | |
for (let x = 0; x < width; x++) { | |
if (INNER_DELAY) await sleep(INNER_DELAY); | |
for (let y = 0; y < height; y++) { | |
let idx = (image.width * y + x) << 2; | |
let red = image.data[idx]; | |
let green = image.data[idx + 1]; | |
let blue = image.data[idx + 2]; | |
let alpha = image.data[idx + 3]; | |
if (alpha === 0) continue; | |
draw(x + X_OFFSET, y + Y_OFFSET, red, green, blue); | |
} | |
} | |
console.log(cluster.worker.id, 'drawn'); | |
if (LOOP_DELAY) await sleep(LOOP_DELAY); | |
} | |
})(); | |
} |
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
// stolen and modified from npm net-ping module | |
const EventEmitter = require('events'); | |
const rawSocket = require('raw-socket'); | |
/** | |
* Add inverses to an object | |
* @param {Object} obj | |
*/ | |
function _addInverseProps(obj) { | |
for (let [key, value] of Object.entries(obj)) obj[value] = key; | |
} | |
let NetworkProtocol = { | |
1: 'IPv4', | |
2: 'IPv6' | |
}; | |
_addInverseProps(NetworkProtocol); | |
/** Represents a session */ | |
class Session extends EventEmitter { | |
/** | |
* The constructor | |
* @param {Object} options | |
*/ | |
constructor(options) { | |
super(); | |
options = options || {}; | |
this.packetSize = options.packetSize || 16; | |
if (this.packetSize < 8) this.packetSize = 8; | |
this.addressFamily = (options.networkProtocol === NetworkProtocol.IPv6) | |
? rawSocket.AddressFamily.IPv6 | |
: rawSocket.AddressFamily.IPv4; | |
this._debug = (options && options._debug) ? true : false; | |
this.defaultTTL = (options && options.ttl) ? options.ttl : 128; | |
this.socket = null; | |
this.getSocket(); | |
} | |
/** | |
* Close the session | |
* @return {Session} this | |
*/ | |
close() { | |
if (this.socket) this.socket.close(); | |
this.flush(new Error('Socket closed')); | |
this.socket = null; | |
return this; | |
} | |
/** | |
* Print debug message for request | |
* @param {String} target | |
* @param {Object} req | |
*/ | |
_debugRequest(target, req) { | |
console.log('request: addressFamily=' + this.addressFamily + ' target=' | |
+ req.target + ' buffer=' | |
+ req.buffer.toString('hex')); | |
} | |
/** | |
* Flush the session | |
* @param {Error} error Error to send to sockets | |
*/ | |
flush(error) { | |
for (let id of Object.keys(this.reqs)) { | |
let req = this.reqRemove(id); | |
let sent = req.sent ? req.sent : new Date(); | |
req.callback(error, req.target, sent); | |
} | |
} | |
/** | |
* Initialize the socket | |
* @return {Socket} The initialized socket | |
*/ | |
getSocket() { | |
if (this.socket) return this.socket; | |
let protocol = this.addressFamily == rawSocket.AddressFamily.IPv6 | |
? rawSocket.Protocol.ICMPv6 | |
: rawSocket.Protocol.ICMP; | |
let options = { | |
addressFamily: this.addressFamily, | |
protocol: protocol | |
}; | |
this.socket = rawSocket.createSocket(options); | |
this.socket.on('error', this.onSocketError.bind(this)); | |
this.socket.on('close', this.onSocketClose.bind(this)); | |
this.ttl = null; | |
this.setTTL(this.defaultTTL); | |
return this.socket; | |
} | |
/** | |
* Called before the message is sent | |
* @param {Object} req | |
*/ | |
onBeforeSocketSend(req) { | |
// this.setTTL(req.ttl ? req.ttl : this.defaultTTL); | |
} | |
/** Called on socket close event */ | |
onSocketClose() { | |
this.emit('close'); | |
this.flush(new Error('Socket closed')); | |
} | |
/** | |
* Called on socket error event | |
* @param {Error} error | |
*/ | |
onSocketError(error) { | |
this.emit('error', error); | |
} | |
/** | |
* Called when the message is sent | |
* @param {Object} req | |
* @param {Error} error | |
*/ | |
onSocketSend(req, error) { | |
if (!req.sent) req.sent = new Date(); | |
req.callback(error, req.target, req.sent); | |
} | |
/** | |
* Ping a host | |
* @param {String} target The target to ping | |
* @param {Function} callback The callback | |
* @return {Session} this | |
*/ | |
pingHost(target, callback) { | |
let req = { | |
callback, | |
target | |
}; | |
req.buffer = this.toBuffer(req); | |
if (this._debug) this._debugRequest(req.target, req); | |
this.send(req); | |
return this; | |
} | |
/** | |
* Send a request | |
* @param {Object} req | |
*/ | |
send(req) { | |
let buffer = req.buffer; | |
// Resume readable events if the raw socket is paused | |
if (this.getSocket().recvPaused) this.getSocket().resumeRecv(); | |
this.getSocket().send(buffer, 0, buffer.length, req.target, | |
this.onBeforeSocketSend.bind(this, req), | |
this.onSocketSend.bind(this, req)); | |
} | |
/** | |
* Set the TTL of the message | |
* @param {Number} ttl | |
*/ | |
setTTL(ttl) { | |
if (this.ttl && this.ttl == ttl) return; | |
let level = this.addressFamily == rawSocket.AddressFamily.IPv6 | |
? rawSocket.SocketLevel.IPPROTO_IPV6 | |
: rawSocket.SocketLevel.IPPROTO_IP; | |
this.getSocket().setOption(level, rawSocket.SocketOption.IP_TTL, ttl); | |
this.ttl = ttl; | |
} | |
/** | |
* Encode the request into a buffer | |
* @param {Object} req | |
* @return {Buffer} | |
*/ | |
toBuffer(req) { | |
let buffer = Buffer.alloc(this.packetSize); | |
let type = this.addressFamily == rawSocket.AddressFamily.IPv6 ? 128 : 8; | |
buffer.writeUInt8(type, 0); | |
buffer.writeUInt8(0, 1); | |
buffer.writeUInt16BE(0, 2); | |
rawSocket.writeChecksum(buffer, 2, rawSocket.createChecksum(buffer)); | |
return buffer; | |
} | |
} | |
exports.createSession = function(options) { | |
return new Session(options || {}); | |
}; | |
exports.NetworkProtocol = NetworkProtocol; | |
exports.Session = Session; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment