diff --git a/dist/dns.js b/dist/dns.js new file mode 100644 index 0000000..64f4cf3 --- /dev/null +++ b/dist/dns.js @@ -0,0 +1,319 @@ +var EventEmitter, Server, bitSlice, bufferify, bufferifyV4, bufferifyV6, createSocket, dns, domainify, functionify, isIPv6, lookup, parse, qnameify, resolve, response, responseBuffer, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +EventEmitter = require`events`.EventEmitter; + +createSocket = require('dgram').createSocket; + +isIPv6 = require('net').isIPv6; + +dns = require('dns'); + +bitSlice = function(b, offset, length) { + return b >>> 7 - (offset + length - 1) & ~(0xff << length); +}; + +bufferify = function(ip) { + if (isIPv6(ip)) { + return bufferifyV6(ip); + } else { + return bufferifyV4(ip); + } +}; + +bufferifyV4 = function(ip) { + var base, buf, i, result; + ip = ip.split('.').map(function(n) { + return parseInt(n, 10); + }); + result = 0; + base = 1; + i = ip.length - 1; + while (i >= 0) { + result += ip[i] * base; + base *= 256; + i--; + } + buf = Buffer.alloc(4); + buf.writeUInt32BE(result); + return buf; +}; + +bufferifyV6 = function(rawIp) { + var countColons, hexIp, ip; + countColons = function(x) { + var n; + n = 0; + x.replace(/:/g, function(c) { + return n++; + }); + return n; + }; + ip = rawIp.replace(/\/\d{1,3}(?=%|$)/, '').replace(/%.*$/, ''); + hexIp = ip.replace(/::/, function(two) { + return ':' + Array(7 - countColons(ip) + 1).join(':') + ':'; + }).split(':').map(function(x) { + return Array(4 - x.length).fill('0').join('') + x; + }).join(''); + return Buffer.from(hexIp, 'hex'); +}; + +domainify = function(qname) { + var i, length, offset, parts; + parts = []; + i = 0; + while (i < qname.length && qname[i]) { + length = qname[i]; + offset = i + 1; + parts.push(qname.slice(offset, offset + length).toString()); + i = offset + length; + } + return parts.join('.'); +}; + +qnameify = function(domain) { + var i, offset, qname; + qname = Buffer.alloc(domain.length + 2); + offset = 0; + domain = domain.split('.'); + i = 0; + while (i < domain.length) { + qname[offset] = domain[i].length; + qname.write(domain[i], offset + 1, domain[i].length, 'ascii'); + offset += qname[offset] + 1; + i++; + } + qname[qname.length - 1] = 0; + return qname; +}; + +functionify = function(val) { + return function(addr, callback) { + return callback(null, val); + }; +}; + +parse = function(buf) { + var b, header, question; + header = {}; + question = {}; + b = buf.slice(2, 3).toString('binary', 0, 1).charCodeAt(0); + header.id = buf.slice(0, 2); + header.qr = bitSlice(b, 0, 1); + header.opcode = bitSlice(b, 1, 4); + header.aa = bitSlice(b, 5, 1); + header.tc = bitSlice(b, 6, 1); + header.rd = bitSlice(b, 7, 1); + b = buf.slice(3, 4).toString('binary', 0, 1).charCodeAt(0); + header.ra = bitSlice(b, 0, 1); + header.z = bitSlice(b, 1, 3); + header.rcode = bitSlice(b, 4, 4); + header.qdcount = buf.slice(4, 6); + header.ancount = buf.slice(6, 8); + header.nscount = buf.slice(8, 10); + header.arcount = buf.slice(10, 12); + question.qname = buf.slice(12, buf.length - 4); + question.qtype = buf.slice(buf.length - 4, buf.length - 2); + question.qclass = buf.slice(buf.length - 2, buf.length); + return { + header: header, + question: question + }; +}; + +responseBuffer = function(query) { + var buf, header, i, length, offset, qname, question, rr; + question = query.question; + header = query.header; + qname = question.qname; + offset = 16 + qname.length; + length = offset; + i = 0; + while (i < query.rr.length) { + length += query.rr[i].qname.length + 10; + i++; + } + buf = Buffer.alloc(length); + header.id.copy(buf, 0, 0, 2); + buf[2] = 0x00 | header.qr << 7 | header.opcode << 3 | header.aa << 2 | header.tc << 1 | header.rd; + buf[3] = 0x00 | header.ra << 7 | header.z << 4 | header.rcode; + buf.writeUInt16BE(header.qdcount, 4); + buf.writeUInt16BE(header.ancount, 6); + buf.writeUInt16BE(header.nscount, 8); + buf.writeUInt16BE(header.arcount, 10); + qname.copy(buf, 12); + question.qtype.copy(buf, 12 + qname.length, question.qtype, 2); + question.qclass.copy(buf, 12 + qname.length + 2, question.qclass, 2); + i = 0; + while (i < query.rr.length) { + rr = query.rr[i]; + rr.qname.copy(buf, offset); + offset += rr.qname.length; + buf.writeUInt16BE(rr.qtype, offset); + buf.writeUInt16BE(rr.qclass, offset + 2); + buf.writeUInt32BE(rr.ttl, offset + 4); + buf.writeUInt16BE(rr.rdlength, offset + 8); + buf = Buffer.concat([buf, rr.rdata]); + offset += 14; + i++; + } + return buf; +}; + +response = function(query, ttl, to) { + var header, question, rrs; + response = {}; + header = response.header = {}; + question = response.question = {}; + rrs = resolve(query.question.qname, ttl, to); + header.id = query.header.id; + header.ancount = rrs.length; + header.qr = 1; + header.opcode = 0; + header.aa = 0; + header.tc = 0; + header.rd = 1; + header.ra = 0; + header.z = 0; + header.rcode = 0; + header.qdcount = 1; + header.nscount = 0; + header.arcount = 0; + question.qname = query.question.qname; + question.qtype = query.question.qtype; + question.qclass = query.question.qclass; + response.rr = rrs; + return responseBuffer(response); +}; + +resolve = function(qname, ttl, to) { + var r; + r = {}; + r.qname = qname; + r.qtype = to.length === 4 ? 1 : 28; + r.qclass = 1; + r.ttl = ttl; + r.rdlength = to.length; + r.rdata = to; + return [r]; +}; + +lookup = function(addr, callback) { + if (net.isIP(addr)) { + return callback(null, addr); + } + return dns.lookup(addr, callback); +}; + +Server = (function(superClass) { + extend(Server, superClass); + + function Server(proxy) { + var routes; + if (proxy == null) { + proxy = '8.8.8.8'; + } + Server.__super__.constructor.apply(this, arguments); + this._socket = createSocket(isIPv6(proxy) ? 'udp6' : 'udp4'); + routes = []; + this._socket.on('message', (function(_this) { + return function(message, rinfo) { + var domain, i, onerror, onproxy, query, respond, route, routeData; + query = parse(message); + domain = domainify(query.question.qname); + routeData = { + domain: domain, + rinfo: rinfo + }; + _this.emit('resolve', routeData); + respond = function(buf) { + return _this._socket.send(buf, 0, buf.length, rinfo.port, rinfo.address); + }; + onerror = function(err) { + return _this.emit('error', err); + }; + onproxy = function() { + var sock; + sock = createSocket(isIPv6(proxy) ? 'udp6' : 'udp4'); + sock.send(message, 0, message.length, 53, proxy); + sock.on('error', onerror); + return sock.on('message', function(response) { + respond(response); + return sock.close(); + }); + }; + i = 0; + while (i < routes.length) { + if (routes[i].pattern.test(domain)) { + route = routes[i].route; + break; + } + i++; + } + if (!route) { + return onproxy(); + } + return route(routeData, function(err, to) { + var toIp, ttl; + if (typeof to === 'string') { + toIp = to; + ttl = 1; + } else { + toIp = to.ip; + ttl = to.ttl; + } + if (err) { + return onerror(err); + } + if (!toIp) { + return onproxy(); + } + return lookup(toIp, function(err, addr) { + if (err) { + return onerror(err); + } + _this.emit('route', domain, addr); + return respond(response(query, ttl, bufferify(addr))); + }); + }); + }; + })(this)); + } + + Server.prototype.route = function(pattern, route) { + if (Array.isArray(pattern)) { + pattern.forEach((function(_this) { + return function(item) { + return _this.route(item, route); + }; + })(this)); + return this; + } + if (typeof pattern === 'function') { + return this.route('*', pattern); + } + if (typeof route === 'string') { + return this.route(pattern, functionify(route)); + } + pattern = pattern === '*' ? /.?/ : new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*\\\./g, '(.+)\\.') + '$', 'i'); + routes.push({ + pattern: pattern, + route: route + }); + return this; + }; + + Server.prototype.listen = function(port) { + this._socket.bind(port || 53); + return this; + }; + + Server.prototype.close = function(callback) { + this._socket.close(callback); + return this; + }; + + return Server; + +})(EventEmitter); diff --git a/dist/http/index.js b/dist/http/index.js index 96bf0fe..5617b2a 100644 --- a/dist/http/index.js +++ b/dist/http/index.js @@ -70,5 +70,17 @@ module.exports = function(ip, port, url) { }).post('/', cwmp(url)); srv = createServer(route); srv.keepAliveTimeout = 30000; - return srv.listen(port, ip); + srv.on('error', function(e) { + var ref1; + if ((ref1 = e.code) === 'EADDRINUSE' || ref1 === 'EADDRNOTAVAIL') { + console.log(e.code + ', retrying...'); + return setTimeout(function() { + srv.close(); + return srv.listen(port); + }, 1000); + } else { + return console.error(e); + } + }); + return srv.listen(port); }; diff --git a/dist/ntp/index.js b/dist/ntp/index.js new file mode 100644 index 0000000..f14f81c --- /dev/null +++ b/dist/ntp/index.js @@ -0,0 +1,88 @@ +'use strict'; +var EventEmitter, NTP, Packet, createSocket, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Packet = require('./packet'); + +createSocket = require('dgram').createSocket; + +EventEmitter = require('events').EventEmitter; + +NTP = (function(superClass) { + extend(NTP, superClass); + + function NTP(options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + Object.assign(this, { + server: 'pool.ntp.org', + port: 123 + }, options); + this.socket = new createSocket('udp4'); + if (typeof callback === 'function') { + this.time(callback); + } + } + + NTP.prototype.time = function(callback) { + var packet, port, ref, server, timeout; + ref = this, server = ref.server, port = ref.port, timeout = ref.timeout; + packet = NTP.createPacket(); + this.socket.send(packet, 0, packet.length, port, server, (function(_this) { + return function(err) { + if (err) { + return callback(err); + } + return _this.socket.once('message', function(data) { + var message; + this.socket.close(); + message = NTP.parse(data); + return callback(err, message); + }); + }; + })(this)); + return this; + }; + + NTP.time = function(options, callback) { + return new NTP(options, callback); + }; + + NTP.createPacket = function() { + var packet; + packet = new Packet; + packet.mode = Packet.MODES.CLIENT; + packet.originateTimestamp = Date.now(); + return packet.toBuffer(); + }; + + NTP.parse = function(buffer) { + var T1, T2, T3, T4, message; + message = Packet.parse(buffer); + message.destinationTimestamp = Date.now(); + message.time = new Date(message.transmitTimestamp); + T1 = message.originateTimestamp; + T2 = message.receiveTimestamp; + T3 = message.transmitTimestamp; + T4 = message.destinationTimestamp; + message.d = T4 - T1 - (T3 - T2); + message.t = (T2 - T1 + T3 - T4) / 2; + return message; + }; + + return NTP; + +})(EventEmitter); + +exports.Client = NTP; + +exports.Server = require('./server'); + +exports.createServer = function(options) { + return new exports.Server(options); +}; + +module.exports = NTP; diff --git a/dist/ntp/packet.js b/dist/ntp/packet.js new file mode 100644 index 0000000..5ea3fe2 --- /dev/null +++ b/dist/ntp/packet.js @@ -0,0 +1,153 @@ +var Packet, SEVENTY_YEARS, assert, toMsecs, writeMsecs; + +assert = require('assert'); + +SEVENTY_YEARS = 2208988800; + +toMsecs = function(buffer, offset) { + var fraction, i, seconds; + seconds = 0; + fraction = 0; + i = 0; + while (i < 4) { + seconds = seconds * 256 + buffer[offset + i]; + ++i; + } + i = 4; + while (i < 8) { + fraction = fraction * 256 + buffer[offset + i]; + ++i; + } + return (seconds - SEVENTY_YEARS + fraction / Math.pow(2, 32)) * 1000; +}; + +writeMsecs = function(buffer, offset, ts) { + var fraction, seconds; + seconds = Math.floor(ts / 1000) + SEVENTY_YEARS; + fraction = Math.round(ts % 1000 / 1000 * Math.pow(2, 32)); + buffer[offset + 0] = (seconds & 0xFF000000) >> 24; + buffer[offset + 1] = (seconds & 0x00FF0000) >> 16; + buffer[offset + 2] = (seconds & 0x0000FF00) >> 8; + buffer[offset + 3] = seconds & 0x000000FF; + buffer[offset + 4] = (fraction & 0xFF000000) >> 24; + buffer[offset + 5] = (fraction & 0x00FF0000) >> 16; + buffer[offset + 6] = (fraction & 0x0000FF00) >> 8; + buffer[offset + 7] = fraction & 0x000000FF; + return buffer; +}; + +Packet = (function() { + Packet.MODES = { + CLIENT: 3, + SERVER: 4 + }; + + function Packet() { + Object.assign(this, { + leapIndicator: 0, + version: 4, + mode: 3, + stratum: 0, + pollInterval: 6, + precision: 236, + referenceIdentifier: 0, + referenceTimestamp: 0, + originateTimestamp: 0, + receiveTimestamp: 0, + transmitTimestamp: 0 + }); + } + + Packet.parse = function(buffer) { + var packet; + assert.equal(buffer.length, 48, 'Invalid Package'); + packet = new Packet; + packet.leapIndicator = buffer[0] >> 6; + packet.version = (buffer[0] & 0x38) >> 3; + packet.mode = buffer[0] & 0x7; + packet.stratum = buffer[1]; + packet.pollInterval = buffer[2]; + packet.precision = buffer[3]; + packet.rootDelay = buffer.slice(4, 8); + packet.rootDispersion = buffer.slice(8, 12); + packet.referenceIdentifier = buffer.slice(12, 16); + packet.referenceTimestamp = toMsecs(buffer, 16); + packet.originateTimestamp = toMsecs(buffer, 24); + packet.receiveTimestamp = toMsecs(buffer, 32); + packet.transmitTimestamp = toMsecs(buffer, 40); + return packet; + }; + + Packet.prototype.toBuffer = function() { + var buffer; + buffer = Buffer.alloc(48).fill(0x00); + buffer[0] = 0; + buffer[0] += this.leapIndicator << 6; + buffer[0] += this.version << 3; + buffer[0] += this.mode << 0; + buffer[1] = this.stratum; + buffer[2] = this.pollInterval; + buffer[3] = this.precision; + buffer.writeUInt32BE(this.rootDelay, 4); + buffer.writeUInt32BE(this.rootDispersion, 8); + buffer.writeUInt32BE(this.referenceIdentifier, 12); + writeMsecs(buffer, 16, this.referenceTimestamp); + writeMsecs(buffer, 24, this.originateTimestamp); + writeMsecs(buffer, 32, this.receiveTimestamp); + writeMsecs(buffer, 40, this.transmitTimestamp); + return buffer; + }; + + Packet.prototype.toJSON = function() { + var output; + output = Object.assign({}, this); + output.version = this.version; + output.leapIndicator = { + 0: 'no-warning', + 1: 'last-minute-61', + 2: 'last-minute-59', + 3: 'alarm' + }[this.leapIndicator]; + switch (this.mode) { + case 1: + output.mode = 'symmetric-active'; + break; + case 2: + output.mode = 'symmetric-passive'; + break; + case 3: + output.mode = 'client'; + break; + case 4: + output.mode = 'server'; + break; + case 5: + output.mode = 'broadcast'; + break; + case 0: + case 6: + case 7: + output.mode = 'reserved'; + } + if (this.stratum === 0) { + output.stratum = 'death'; + } else if (this.stratum === 1) { + output.stratum = 'primary'; + } else if (this.stratum <= 15) { + output.stratum = 'secondary'; + } else { + output.stratum = 'reserved'; + } + output.referenceTimestamp = new Date(this.referenceTimestamp); + output.originateTimestamp = new Date(this.originateTimestamp); + output.receiveTimestamp = new Date(this.receiveTimestamp); + output.transmitTimestamp = new Date(this.transmitTimestamp); + output.destinationTimestamp = new Date(this.destinationTimestamp); + return output; + }; + + return Packet; + +})(); + +module.exports = Packet; diff --git a/dist/ntp/server.js b/dist/ntp/server.js new file mode 100644 index 0000000..3a2cd7c --- /dev/null +++ b/dist/ntp/server.js @@ -0,0 +1,62 @@ +'use strict'; +var EventEmitter, NTPServer, Packet, udp, + extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +udp = require('dgram'); + +EventEmitter = require('events'); + +Packet = require('./packet'); + +NTPServer = (function(superClass) { + extend(NTPServer, superClass); + + function NTPServer(options, onRequest) { + NTPServer.__super__.constructor.call(this); + if (typeof options === 'function') { + onRequest = options; + options = {}; + } + Object.assign(this, { + port: 123 + }, options); + this.socket = udp.createSocket('udp4'); + this.socket.on('message', this.parse.bind(this)); + if (onRequest) { + this.on('request', onRequest); + } + this; + } + + NTPServer.prototype.listen = function(port, address) { + this.socket.bind(port || this.port, address); + return this; + }; + + NTPServer.prototype.address = function() { + return this.socket.address(); + }; + + NTPServer.prototype.send = function(rinfo, message, callback) { + if (message instanceof Packet) { + message.mode = Packet.MODES.SERVER; + message = message.toBuffer(); + } + this.socket.send(message, rinfo.port, rinfo.server, callback); + return this; + }; + + NTPServer.prototype.parse = function(message, rinfo) { + var packet; + packet = Packet.parse(message); + packet.receiveTimestamp = Date.now(); + this.emit('request', packet, this.send.bind(this, rinfo)); + return this; + }; + + return NTPServer; + +})(EventEmitter); + +module.exports = NTPServer; diff --git a/package.json b/package.json index a1e1b93..b84fc09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tch-exploit", - "version": "2.0.0-rc1", + "version": "2.0.0-rc2", "main": "dist/index.js", "bin": "dist/index.js", "scripts": { diff --git a/src/dns.coffee b/src/dns.coffee index 21649e8..c86e88f 100644 --- a/src/dns.coffee +++ b/src/dns.coffee @@ -295,11 +295,11 @@ class Server extends EventEmitter @ listen: (port) -> - @@_socket.bind port or 53 + @_socket.bind port or 53 @ close: (callback) -> - @@_socket.close callback + @_socket.close callback @ diff --git a/src/http/index.coffee b/src/http/index.coffee index 1ee351c..367fb16 100644 --- a/src/http/index.coffee +++ b/src/http/index.coffee @@ -82,4 +82,14 @@ module.exports = (ip, port, url) -> srv = createServer route srv.keepAliveTimeout = 30000 - srv.listen port, ip + srv.on 'error', (e) -> + if e.code in [ 'EADDRINUSE', 'EADDRNOTAVAIL' ] + console.log e.code + ', retrying...' + + setTimeout -> + srv.close() + srv.listen port + , 1000 + else console.error e + + srv.listen port diff --git a/src/ntp/server.coffee b/src/ntp/server.coffee index 39948a0..9962246 100644 --- a/src/ntp/server.coffee +++ b/src/ntp/server.coffee @@ -1,48 +1,48 @@ -'use strict'; -const udp = require('dgram'); -const EventEmitter = require('events'); -const Packet = require('./packet'); -/** - * [NTPServer description] - * @param {[type]} options [description] - */ -class NTPServer extends EventEmitter { - constructor(options, onRequest) { - super(); - if (typeof options === 'function') { - onRequest = options; - options = {}; - } - Object.assign(this, { - port: 123 - }, options); - this.socket = udp.createSocket('udp4'); - this.socket.on('message', this.parse.bind(this)); - if (onRequest) this.on('request', onRequest); - return this; - } - listen(port, address) { - this.socket.bind(port || this.port, address); - return this; - } - address() { - return this.socket.address(); - } - send(rinfo, message, callback) { - if (message instanceof Packet) { - message.mode = Packet.MODES.SERVER; // mark mode as server - message = message.toBuffer(); - } - this.socket.send(message, rinfo.port, rinfo.server, callback); - return this; - } - parse(message, rinfo) { - const packet = Packet.parse(message); - packet.receiveTimestamp = Date.now(); - this.emit('request', packet, this.send.bind(this, rinfo)); - return this; - } -} - - -module.exports = NTPServer; \ No newline at end of file +'use strict' + +udp = require('dgram') +EventEmitter = require('events') +Packet = require('./packet') + +class NTPServer extends EventEmitter + + constructor: (options, onRequest) -> + super() + + if typeof options == 'function' + onRequest = options + options = {} + + Object.assign @, { port: 123 }, options + + @socket = udp.createSocket('udp4') + @socket.on 'message', @parse.bind(@) + + if onRequest + @on 'request', onRequest + + @ + + listen: (port, address) -> + @socket.bind port or @port, address + @ + + address: -> + @socket.address() + + send: (rinfo, message, callback) -> + if message instanceof Packet + message.mode = Packet.MODES.SERVER + message = message.toBuffer() + + @socket.send message, rinfo.port, rinfo.server, callback + @ + + parse: (message, rinfo) -> + packet = Packet.parse(message) + packet.receiveTimestamp = Date.now() + + @emit 'request', packet, @send.bind(@, rinfo) + @ + +module.exports = NTPServer