From 697cac711579a504ed5ce30fe925a48f7ac6ab31 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Tue, 16 Mar 2021 13:24:47 +0100 Subject: [PATCH] Add bench files --- bench/bench1/.dockerignore | 1 + bench/bench1/.gitignore | 2 + bench/bench1/Dockerfile | 11 + bench/bench1/client.js | 226 ++++++ bench/bench1/granax.patch | 19 + bench/bench1/log.js | 21 + bench/bench1/package-lock.json | 1124 +++++++++++++++++++++++++++++ bench/bench1/package.json | 21 + bench/bench1/parse.js | 462 ++++++++++++ bench/bench1/server.js | 96 +++ bench/bench1/test.js | 8 + bench/bench1/tor.js | 34 + bench/bench1/worker.js | 55 ++ bench/bench2/.gitignore | 2 + bench/bench2/Dockerfile | 25 + bench/bench2/README.md | 13 + bench/bench2/client.js | 101 +++ bench/bench2/enable-one-hop.patch | 31 + bench/bench2/package-lock.json | 27 + bench/bench2/package.json | 14 + bench/bench2/torrc | 7 + bench/bench2/torrc_simple | 1 + bench/bench3/obs.py | 51 ++ bench/bench3/requirements.txt | 1 + bench/bench3/torrc_simple | 1 + results/parse.c | 12 +- 26 files changed, 2362 insertions(+), 4 deletions(-) create mode 100644 bench/bench1/.dockerignore create mode 100644 bench/bench1/.gitignore create mode 100644 bench/bench1/Dockerfile create mode 100644 bench/bench1/client.js create mode 100644 bench/bench1/granax.patch create mode 100644 bench/bench1/log.js create mode 100644 bench/bench1/package-lock.json create mode 100644 bench/bench1/package.json create mode 100644 bench/bench1/parse.js create mode 100644 bench/bench1/server.js create mode 100644 bench/bench1/test.js create mode 100644 bench/bench1/tor.js create mode 100644 bench/bench1/worker.js create mode 100644 bench/bench2/.gitignore create mode 100644 bench/bench2/Dockerfile create mode 100644 bench/bench2/README.md create mode 100644 bench/bench2/client.js create mode 100644 bench/bench2/enable-one-hop.patch create mode 100644 bench/bench2/package-lock.json create mode 100644 bench/bench2/package.json create mode 100644 bench/bench2/torrc create mode 100644 bench/bench2/torrc_simple create mode 100644 bench/bench3/obs.py create mode 100644 bench/bench3/requirements.txt create mode 100644 bench/bench3/torrc_simple diff --git a/bench/bench1/.dockerignore b/bench/bench1/.dockerignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/bench/bench1/.dockerignore @@ -0,0 +1 @@ +node_modules/ diff --git a/bench/bench1/.gitignore b/bench/bench1/.gitignore new file mode 100644 index 0000000..eb03e3e --- /dev/null +++ b/bench/bench1/.gitignore @@ -0,0 +1,2 @@ +node_modules +*.log diff --git a/bench/bench1/Dockerfile b/bench/bench1/Dockerfile new file mode 100644 index 0000000..934785b --- /dev/null +++ b/bench/bench1/Dockerfile @@ -0,0 +1,11 @@ +FROM amd64/debian:buster + +RUN apt-get update && \ + apt-get install -y nodejs npm patch + +COPY . /srv/tor +WORKDIR /srv/tor + +RUN npm install && \ + cd node_modules && \ + patch -p1 < ../granax.patch diff --git a/bench/bench1/client.js b/bench/bench1/client.js new file mode 100644 index 0000000..08410c1 --- /dev/null +++ b/bench/bench1/client.js @@ -0,0 +1,226 @@ +'use strict' + +const request = require('request') +const socks_client = require('socks').SocksClient +const fs = require('fs') +const torm = require('./tor.js') +const logm = require('./log.js') +const worker = require('./worker.js') + +const measure_interval = 1000 +const measure_count = 600 +const parralelism = 100 +const base_url = process.env.SERVER_URL || "http://127.0.0.1:3000" + +const repeat_for = (times, delay, fn) => new Promise((resolve, reject) => { + const timer = setInterval(() => { + if (times-- <= 0) { + clearInterval(timer) + resolve() + return + } + try { + fn() + } catch(err) { + clearInterval(timer) + reject(err) + return + } + }, delay) +}) + +const doMeasure = socket => () => { + socket.write(`${Date.now().toString()}\n`) +} + +const clean = (id) => new Promise((resolve, reject) => { + request({uri: `${base_url}/hidden_service`, method: "DELETE", json: {id: id}}, (err,res,body) => { + if (err) reject(err) + if (body.err) reject(body.err) + resolve(body) + }) +}) + +const wait_for_proxy = (socks_opt, max) => new Promise((resolve, reject) => { + const loop = cur => { + socks_client.createConnection(socks_opt, (err, info) => { + if (err && max <= 0) { + console.error("No more retry") + reject("No more retry") + return + } + + if (err) { + console.error(`Proxy failed to build with error "${err.message}", ${cur} retries left...`) + setTimeout(loop, 5000, cur - 1) + return + } + + resolve(info) + }) + } + loop(max) +}) + +const create_hidden_service = base_url => new Promise((resolve, reject) => { + request.post(`${base_url}/hidden_service`, {json: {}}, (err,res,body) => { + if (err) { + reject(err) + return + } + if (!body.hs || !body.port) reject("Missing value from API endpoint") + else resolve(body) + }) +}) + +const wait_for_hs = socks_port => + create_hidden_service(base_url) + .then(body => Promise.all([ + body, + logm.get_write_fd(`logs/${Date.now()}-${body.hs}.onion-client.log`) + ])) + .then(([body, log]) => { + fs.write(log, `#info ${Date.now().toString()} Got ${body.hs}:${body.port}\n`,() => {}) + console.info(`We got this hidden service: ${body.hs} on port ${body.port}`) + const socks_opt = { + proxy: {ipaddress: '127.0.0.1', port: socks_port, type: 5}, + command: 'connect', + destination: { host: `${body.hs}.onion`, port: body.port } + } + + /* + // used to speedup tests + socks_opt.destination.host = "ysw5uau3pbjaviha.onion" + socks_opt.destination.port = 43035 + */ + return Promise.all([ + wait_for_proxy(socks_opt, 120), + {body: body, socks_opt: socks_opt, log: log} + ]) + }) + .then(([info, data]) => new Promise((resolve, reject) => { + info.socket.on('close', () => resolve(data)) + info.socket.destroy() + })) + +const do_measurement = data => + Promise.all([wait_for_proxy(data.socks_opt), data]) + .then(([info, data]) => { + const socks_opt = data.socks_opt + fs.write(data.log, `#info ${Date.now().toString()} Socks has been created, HS is up\n`, () => {}) + console.info(`SOCKS proxy to ${socks_opt.destination.host}:${socks_opt.destination.port} has been created`) + + const recv_back = new Promise((resolve, reject) => { + setTimeout(reject, measure_count * measure_interval + 2 * 60 * 1000, "Receive answer timed out") + let missing = measure_count + info.socket.on('data', recv_data => { + const tss = recv_data.split('\n') + tss.pop() // Remove last empty line + tss.forEach((ts, idx) => { + fs.write(data.log, `delta_timestamp, ${missing}, ${socks_opt.destination.host}, ${socks_opt.destination.port}, ${Date.now()}, ${ts}\n`, () => {}) + if (idx > 0) fs.write(data.log, `#info aggregation: ${missing} was aggregated with ${missing + idx} for ${socks_opt.destination.host}\n`, () => {}) + if (--missing == 0) resolve() + }) + }) + }) + + info.socket.setNoDelay(true) + info.socket.setEncoding("utf-8") + data.info = info + + return Promise.all([ + recv_back, + repeat_for(measure_count, measure_interval, doMeasure(info.socket)), + data + ]) + }) + .then(([recv_back, repeat, data]) => { + data.info.socket.destroy() + fs.write(data.log, `#info ${Date.now().toString()} Done\n`, () => fs.close(data.log, () => {})) + console.info(`Measurement terminated for ${data.socks_opt.destination.host}\n`) + return Promise.all([clean(data.body.id), data]) + }) + .then(([cl, data]) => { + console.info(`Done for ${data.socks_opt.destination.host}`) + }) + .catch(err => { + data.info.socket.destroy() + fs.write(data.log, `#info ${Date.now().toString()} ERROR occured\n`, () => fs.close(data.log, () => {})) + console.error(err) + }) + +const wait_bootstrap = tor => new Promise((resolve, reject) => { + const loop = () => { + console.info("Waiting for bootstrap, next check in 5s...") + setTimeout(() => { + tor.getInfo("status/circuit-established", (err, res) => { + if (err) reject(err) + else if (res == '0\n') loop() + else resolve() + }) + }, 5000) + } + loop() +}) + +const get_socks_port = tor => new Promise((resolve, reject) => { + tor.getInfo('net/listeners/socks', (err, result) => { + if (err) { + reject(err) + return + } + + const port = parseInt(result.match(/:(\d+)/)[1]) + console.info(`Tor daemon accepts SOCKS connections on port ${port}`) + resolve(port) + }) +}) + +logm + .init + .then(([stream_fd, circ_fd]) => torm.init(stream_fd, circ_fd)) + .then(tor => Promise.all([ + get_socks_port(tor), + wait_bootstrap(tor), + tor + ])) + //.then(([socks_port, boot, tor]) => one_req(socks_port)) + .then(([socks_port, boot, tor]) => { + const hs_spawn_worker = new worker(200, 5000) + const measurement_worker = new worker(100, 1000) + + for (let i = 0; i < 200; i++) { + hs_spawn_worker.add(() => wait_for_hs(socks_port)) + } + + hs_spawn_worker.on('success', v => { + console.debug("HS ready, forwarding it to measurement") + measurement_worker.add(() => do_measurement(v)) + }) + + hs_spawn_worker.on('error', e => { + console.error("Failed to create HS", e) + hs_spawn_worker.add(() => wait_for_hs(socks_port)) + }) + + measurement_worker.on('success', v => { + console.debug("Measurement done, readding token to HS") + hs_spawn_worker.add(() => wait_for_hs(socks_port)) + }) + + measurement_worker.on('error', e => { + console.error("Failed to do a measurement, readding token to HS") + hs_spawn_worker.add(() => wait_for_hs(socks_port)) + }) + + //let loop = () => one_req(socks_port).then(loop).catch(console.error) + //for (let i = 0; i < 100; i++) setTimeout(loop, i * 10 * 1000) + }) + .catch(console.error) + + +// status/bootstrap-phase +// status/circuit-established +// status/enough-dir-info +// status/good-server-descriptor +// status/accepted-server-descriptor diff --git a/bench/bench1/granax.patch b/bench/bench1/granax.patch new file mode 100644 index 0000000..279d674 --- /dev/null +++ b/bench/bench1/granax.patch @@ -0,0 +1,19 @@ +diff -Naur node_modules/granax/lib/controller.js node_patch/granax/lib/controller.js +--- node_modules/granax/lib/controller.js 1985-10-26 09:15:00.000000000 +0100 ++++ node_patch/granax/lib/controller.js 2018-11-18 17:29:52.470853999 +0100 +@@ -177,6 +177,7 @@ + + switch (code.toString()[0]) { + case '2': ++ if (this._stack.length == 0) { console.error("Stack is empty, bug!"); break; } + let { method, callback } = this._stack.pop(); + let parsed = replies[method] + ? replies[method](lines) +@@ -185,6 +186,7 @@ + break; + case '4': + case '5': ++ if (this._stack.length == 0) { console.error("Stack is empty, bug!"); break; } + this._stack.pop().callback(new Error(lines[0])); + break; + case '6': diff --git a/bench/bench1/log.js b/bench/bench1/log.js new file mode 100644 index 0000000..7c8acf4 --- /dev/null +++ b/bench/bench1/log.js @@ -0,0 +1,21 @@ +'use strict' + +const fs = require('fs') + +const get_write_fd = path => new Promise((resolve, reject) => { + fs.open(path, 'wx', (err, fd) => err ? reject(err) : resolve(fd)) +}) + +const init_files = new Promise((resolve, reject) => { + const prefix = Date.now().toString() + Promise.all([get_write_fd(`logs/${prefix}-stream.log`), get_write_fd(`logs/${prefix}-circuit.log`)]) + .then(values => { + console.info("Files module has been inited") + resolve(values) + }).catch(err => { + console.error("Files module failed with", err) + reject(err) + }) +}) + +module.exports = {init: init_files, get_write_fd: get_write_fd} diff --git a/bench/bench1/package-lock.json b/bench/bench1/package-lock.json new file mode 100644 index 0000000..1d12920 --- /dev/null +++ b/bench/bench1/package-lock.json @@ -0,0 +1,1124 @@ +{ + "name": "bench", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "7zip": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/7zip/-/7zip-0.0.6.tgz", + "integrity": "sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA=", + "optional": true + }, + "@types/node": { + "version": "10.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.7.tgz", + "integrity": "sha512-Zh5Z4kACfbeE8aAOYh9mqotRxaZMro8MbBQtR8vEXOMiZo2rGEh2LayJijKdlu48YnS6y2EFU/oo2NCe5P6jGw==" + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", + "integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==", + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "^4.17.10" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cheerio": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", + "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + } + }, + "cli": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", + "integrity": "sha1-ePlIXNFhtWbppsctcXDEJw6B22E=", + "requires": { + "glob": ">= 3.1.4" + } + }, + "cliff": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz", + "integrity": "sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=", + "requires": { + "colors": "~1.0.3", + "eyes": "~0.1.8", + "winston": "0.8.x" + } + }, + "colors": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "css-select": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz", + "integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ==" + }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dom-serializer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "requires": { + "domelementtype": "~1.1.1", + "entities": "~1.1.1" + }, + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" + } + } + }, + "domelementtype": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.2.1.tgz", + "integrity": "sha512-SQVCLFS2E7G5CRCMdn6K9bIhRj1bS6QBWZfF0TUPh4V/BbqrQ619IdSS3/izn0FZ+9l+uODzaZjb08fjOfablA==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "follow-redirects": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.9.tgz", + "integrity": "sha512-Bh65EZI/RU8nx0wbYF9shkFZlqLP+6WT/5FnA3cE/djNSuKNHJEinGGZgu/cQEkeeb2GdFOgenAmn8qaqYke2w==", + "requires": { + "debug": "=3.1.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "granax": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/granax/-/granax-3.1.4.tgz", + "integrity": "sha512-MhmOZs4c2KKCYqC5ORANfW535QTvkqAGOZPTBpiUsdqtgF5sOix14pDsq3Ye11kx+C6IgFPQZoTmifESDBswaA==", + "requires": { + "7zip": "0.0.6", + "async": "^2.3.0", + "latest-torbrowser-version": "^2.0.1", + "merge": "^1.2.0", + "mkdirp": "^0.5.1", + "ncp": "^2.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "htmlparser2": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", + "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==", + "requires": { + "domelementtype": "^1.3.0", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.0.6" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" + } + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "latest-torbrowser-version": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/latest-torbrowser-version/-/latest-torbrowser-version-2.0.1.tgz", + "integrity": "sha512-HtbbcKs6cl7Tz4RG+l1uulWd+tOJujn8wqyXd2zZjPRk9rO/mslg7Ajg72beEFCXwHPWZzwVR+w8mPO5yDiOWg==", + "requires": { + "async": "^2.6.0", + "cheerio": "^1.0.0-rc.2", + "follow-redirects": "^1.2.4", + "semver": "^5.4.1" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ncp": { + "version": "2.0.0", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "proxysocket": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/proxysocket/-/proxysocket-1.2.0.tgz", + "integrity": "sha1-DWVMNnVKQEIefmWXHWL+HxppOLc=" + }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz", + "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "smart-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.1.tgz", + "integrity": "sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg==" + }, + "socks": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.2.tgz", + "integrity": "sha512-g6wjBnnMOZpE0ym6e0uHSddz9p3a+WsBaaYQaBaSCJYvrC4IXykQR9MNGjLQf38e9iIIhp3b1/Zk8YZI3KGJ0Q==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.0.1" + } + }, + "socksv5": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/socksv5/-/socksv5-0.0.6.tgz", + "integrity": "sha1-EycjX/fo3iGsQ0oKV53GnD8HEGE=", + "requires": { + "ipv6": "*" + }, + "dependencies": { + "ipv6": { + "version": "3.1.1", + "bundled": true, + "requires": { + "cli": "0.4.x", + "cliff": "0.1.x", + "sprintf": "0.1.x" + }, + "dependencies": { + "sprintf": { + "version": "0.1.3", + "bundled": true + } + } + } + } + }, + "sshpk": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", + "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "winston": { + "version": "0.8.3", + "resolved": "http://registry.npmjs.org/winston/-/winston-0.8.3.tgz", + "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", + "requires": { + "async": "0.2.x", + "colors": "0.6.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "pkginfo": "0.3.x", + "stack-trace": "0.0.x" + }, + "dependencies": { + "async": { + "version": "0.2.10", + "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" + }, + "colors": { + "version": "0.6.2", + "resolved": "http://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/bench/bench1/package.json b/bench/bench1/package.json new file mode 100644 index 0000000..eea0b77 --- /dev/null +++ b/bench/bench1/package.json @@ -0,0 +1,21 @@ +{ + "name": "bench", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Quentin Dufour", + "license": "AGPL-3.0-or-later", + "dependencies": { + "bluebird": "^3.5.3", + "body-parser": "^1.18.3", + "express": "^4.16.4", + "granax": "^3.1.4", + "proxysocket": "^1.2.0", + "request": "^2.88.0", + "socks": "^2.2.2", + "socksv5": "0.0.6" + } +} diff --git a/bench/bench1/parse.js b/bench/bench1/parse.js new file mode 100644 index 0000000..5a46e9e --- /dev/null +++ b/bench/bench1/parse.js @@ -0,0 +1,462 @@ +const fs = require('fs') +const Promise = require('bluebird') + +const threshold = (rttl, thrshld) => + rttl.map(circuit => { + const too_high_dur = [] + let already_too_high = false + circuit.forEach(rtt => { + const too_high = rtt > thrshld + if (too_high) { + if (!already_too_high) { + already_too_high = true + too_high_dur.push(0) + } + too_high_dur[too_high_dur.length - 1]++ + } else { + already_too_high = false + } + }) + + return too_high_dur + }) + +const multi_threshold = rttl => { + const lim = [0,2,20,600].reverse() + const limtxt = ["0-1 s", "2-19 s", "20-559 s","600-inf s"] + const thresholdv = [100, 250, 500, 1000] + const thresholdtxt = thresholdv.map(t => `${t}ms`) + const answr = thresholdv + .map((th, idx, thtable) => + threshold(rttl, th) + .reduce((acc, e) => acc.concat(e), []) + .reduce((acc, e) => { + lim.some((l, i) => { + if (e >= l) { + acc[i]++ + return true + } + return false + }) + + return acc + }, [0,0,0,0]) + .reverse() + .map((v, i, t) => v / t.reduce((a,b) => a+b)) + .map((v, i) => [thresholdtxt[idx], limtxt[i], v]) + ) + .reduce((acc, e) => acc.concat(e), []) + + return answr +} + +const flip = data => { + const res = {} + Object + .keys(data[0]) + .forEach(k => { + res[k] = data.map(e => e[k]) + }) + + return res +} + +const split_in_perc = ar => new Object({ + min: ar[0], + _5th: ar[Math.floor((ar.length - 1) * 0.05)], + _25th: ar[Math.floor((ar.length - 1) * 0.25)], + med: ar[Math.floor((ar.length - 1) * 0.50)], + _75th: ar[Math.floor((ar.length - 1) * 0.75)], + _95th: ar[Math.floor((ar.length - 1) * 0.95)], + max: ar[ar.length - 1] +}) + +const comp_perc = (hsl, rttl) => + rttl.map((rtt, idx) => { + rtt.sort((a, b) => a - b) + const hs = hsl[idx] + const sprtt = split_in_perc(rtt) + return [ + [hs, "min", sprtt.min], + [hs, "5th", sprtt._5th], + [hs, "25th", sprtt._25th], + [hs, "med", sprtt.med], + [hs, "75th", sprtt._75th], + [hs, "95th", sprtt._95th], + [hs, "max", sprtt.max] + ] + }) + +const comp_delta = (hsl, rttl) => + comp_perc(hsl, rttl) + .map(percentiles => [ + [percentiles[0][0], "max - min", percentiles[6][2] - percentiles[0][2]], + [percentiles[0][0], "95th - 5th", percentiles[5][2] - percentiles[1][2]], + [percentiles[0][0], "75th - 25th", percentiles[4][2] - percentiles[2][2]] + ]) + +const pick_circuits = (rtt, n) => + Array(n) + .fill(0) + .map(v => Math.floor(Math.random() * rtt.length)) + .map(v => rtt[v]) + +const prepare_circuit_merge = group => + group + .reduce((acc, circuit) => + circuit.reduce((acc2, v, idx) => { + if (!acc2[idx]) acc2[idx] = [] + acc2[idx].push(v) + return acc2 + }, acc) + , Array(Math.max.apply(null, group.map(v => v.length))).fill(null)) + +const new_circuits = (group, k) => + prepare_circuit_merge(group) + .map(values => { + const c = values + .sort((a,b) => a-b) + .filter(a => a != null) + + return c[Math.min(c.length - 1, k)] + }) + +const new_circuits_with_pkt = (group, k, delay) => + prepare_circuit_merge(group) + .filter(v => v.length >= k) + .reduce((acc, v, idx) => { + if (idx % k == 0) acc.push([]) + + const pos = idx % k + acc[acc.length-1].push(new Object({ arriv: pos * delay + v[pos], idx: pos})) + if ((idx + 1) % k == 0) acc[acc.length-1] = acc[acc.length-1].concat(v.slice(k).map(rtt => new Object({ arriv: pos * delay + rtt, idx: null}))) + return acc + }, []) + .filter(v => v.length >= k) + .map(v => + v + .sort((a,b) => a.arriv - b.arriv) // Now we sort packets according to their arrival time considering that they were not sent at the seme time + .slice(0, k) // We only keep the k first received packets, as they are the only needed one to decode our stuff + .reduce((acc, v, idx, tab) => { + if (acc.length == 0) acc = Array(tab.length).fill(tab[tab.length - 1].arriv) // init accumulator with last significative received value + if (v.idx) acc[v.idx] = v.arriv + return acc + }, []) // If we receive a packet with idx, it is a clear packet and can be delivered now, however we will need to receive k packets to decode + .reduce((acc, v) => { + if (acc.length > 0) acc.push(Math.max(acc[acc.length - 1], v)) + else acc.push(v) + return acc + }, []) // Implement Head of Line blocking, as packets must be delivered in order. + .map((v, idx) => v - idx * delay) // Reset time according to packet emission and not first packet encoded + ) + .reduce((acc, v) => acc.concat(v), []) // flatten data + +const latency_per_real_time_sec = (aggreg, include_onion) => + aggreg + .reduce((acc2, v) => + v.reduce((acc, [onion, end, start, rtt]) => { + const in_sec = Math.floor(start / 1000) + if (acc.start > in_sec) acc.start = in_sec + if (!acc.latencies[in_sec]) acc.latencies[in_sec] = [] + acc.latencies[in_sec].push(include_onion ? [onion, rtt] : rtt) + return acc + }, acc2), {start: Math.floor(Date.now() / 1000), latencies: []}) + +const latency_histo = lat_per_sec => + lat_per_sec + .latencies + .slice(lat_per_sec.start) + //.forEach((v, idx) => console.log(idx+lat_per_sec.start,v)) + .map((v, idx) => new Object({ + time: idx+lat_per_sec.start, + count: v.length, + avg: split_in_perc(v.sort((a,b) => a-b)) + })) + .map(v => Object.keys(v.avg).map(p => [v.time, v.count, p, v.avg[p]])) + +const med_by_circuit = perc => { + const res = {} + perc.forEach(([min,fith,quart,[med_hs, med_name, med_rtt],...rest]) => res[med_hs] = med_rtt) + return res +} + +const med_threshold = (lat_per_sec, med_by_circ, mult) => + lat_per_sec + .latencies + .slice(lat_per_sec.start) + .map((v, idx) => new Object({ + time: idx+lat_per_sec.start, + count: v.length, + above_th: v.reduce((acc, [onion, rtt]) => { + return rtt > mult * med_by_circ[onion] ? acc + 1 : acc + }, 0) / v.length + })) + +const circuit_over_time = rttl => + Array(600) + .fill(null) + .map((_v, idx) => + rttl + .map(circuit => circuit[idx] ? circuit[idx] : null) + .filter(v => v != null) + .sort((a,b) => a - b) + ) + .map(v => split_in_perc(v)) + .map(v => Object.keys(v).map(p => [p, v[p]])) + +const passe_bas = (ar, th) => ar.filter(circ => circ[0] <= th) + +const order_circuit_time = aggreg => aggreg.sort((c1, c2) => c1[0][2] - c2[0][2]) + +const pick_circuits_ord = (aggreg_ord, n, max_difference) => { + const pivot = Math.floor(Math.random() * aggreg_ord.size()) +} + +fs.readdir(process.argv[2], (err, items) => { + if (err) { + console.error(err) + return + } + + const started = new Date(parseInt(items[0].match(/^\d+/)[0])) + const stopped = new Date(parseInt(items[items.length - 1].match(/^\d+/)[0])) + stopped.setMinutes(stopped.getMinutes() - 30) + const cut = stopped.getTime() + + const result = items.filter(e => { + if (!e.match(/onion/)) return false + const current = parseInt(e.match(/^\d+/)[0]) + return current <= cut + }) + + console.error(`${result.length} accepted results`) + console.error(`Started at ${started.toString()} and stopped at ${stopped.toString()}`) + Promise.map(result, f => new Promise((resolve, reject) => { + fs.readFile(`${process.argv[2]}/${f}`, 'utf-8', (err, data) => { + if (err) { + reject(err) + return + } + + const aggregation = [] + const rtt = [] + let res + const reg = /delta_timestamp, (\d+), ([\d\w.]+), (\d+), (\d+), (\d+)\n/g + + while (res = reg.exec(data)) { + aggregation.push([ + res[2], // hs + parseInt(res[4]), // end + parseInt(res[5]), // start + parseInt(res[4]) - parseInt(res[5]) // rtt + ]) + rtt.push(parseInt(res[4]) - parseInt(res[5])) + } + + const same_buffer = [] + const reg2 = /#info aggregation: (\d+) was aggregated with (\d+) for ([\w\d.]+)/g + while (res = reg2.exec(data)) { + same_buffer.push({ + current: parseInt(res[1]), + orig: parseInt(res[2]), + hs: res[3] + }) + } + + resolve({ + aggreg: aggregation, + rtt: rtt, + hs: aggregation[0][0], + failed_at: rtt.length, + same_buffer: same_buffer + }) + }) + }), {concurrency: 512}) + .then(data => { + ({aggreg, rtt, hs, failed_at, same_buffer} = flip(data)) + + //const [new_rtt, new_hs] = generate_new_rtt(7000, 1, 2, 500, 0, rtt) + //out_percentiles(new_hs, new_rtt) + out_ordered_median(hs,rtt,'med') + //out_raw() + }) + .catch(console.error) + +}) + +const sample = (a,n) => ((w) => + Array(n) + .fill(null) + .map(v => w.splice(Math.floor(Math.random() * w.length), 1)[0]) +)(JSON.parse(JSON.stringify(a))) + +const out_lat_real_time = aggreg => { + console.log("time,count,type,rtt") + latency_histo(latency_per_real_time_sec(aggreg)) + .reduce((acc, v) => acc.concat(v), []) + .forEach(a => console.log(`${a[0]},${a[1]},${a[2]},${a[3]}`)) +} + +const out_circ_over_measurement = rtt => { + console.log("type,rtt,time") + circuit_over_time(rtt) + .reduce((acc,v,idx) => acc.concat(v.map(w => w.concat([idx]))), []) + .forEach(v => console.log(`${v[0]}, ${v[1]}, ${v[2]} `)) +} + +const out_above_median = aggreg => { + const lprlt = latency_per_real_time_sec(aggreg, true) + const mbc = med_by_circuit(comp_perc(hs, rtt)) + console.log("time,count,above_th") + med_threshold(lprlt, mbc, 2) + .forEach(a => console.log(`${a.time},${a.count},${a.above_th}`)) +} + +const out_raw = aggreg => { + console.log("onion,end,start,delta") + aggreg + .reduce((acc, val) => acc.concat(val), []) + .forEach(e => console.log(`${e[0]}, ${e[1]}, ${e[2]}, ${e[3]}`)) +} + +const out_percentiles = (hs, rtt) => { + console.log("hs,type,delta") + comp_perc(hs, rtt) + .reduce((acc, rtt) => acc.concat(rtt), []) + .forEach(e => console.log(`${e[0]}, ${e[1]}, ${e[2]}`)) +} + +const out_delta_percentiles = (hs, rtt) => { + console.log("hs,type,delta") + comp_delta(hs,rtt) + .reduce((acc, delta) => acc.concat(delta), []) + .forEach(e => console.log(`${e[0]}, ${e[1]}, ${e[2]}`)) +} + +const out_failed_at = failed_at => { + console.log("failed_at") + failed_at.forEach(e => console.log(e)) +} + +const out_latency_spikes = rtt => { + console.log("threshold,group,count") + multi_threshold(rtt).forEach(l => console.log(`${l[0]},${l[1]},${l[2]}`)) +} + +const generate_new_rtt = (generate, k, n, seuil, delay, rtt) => { + const pre_filter = passe_bas(rtt, seuil) + const new_rtt = Array(generate) + .fill(null) + .map(v => pick_circuits(pre_filter, n)) + .map(v => new_circuits(v, k - 1)) + //.map(v => new_circuits_with_pkt(v, k, delay)) //20ms delay + .filter(v => { + if (v.length <= 597 || v.length > 600) { + console.error(`Wrong final size ${v.length}`) + return false + } + return true + }) + + const new_hs = Array(generate).fill(null).map((v, idx) => `${idx}.onion`) + return [new_rtt, new_hs] +} + +const out_pike_corel = aggreg => { + const n = 3 + const max_difference = 10 * 1000 // 10s in ms + const aggreg_ord = order_circuit_time(aggreg) + shift_to_match(pick_circuits_ord(aggreg_ord, n, max_difference)) +} + +const out_ecdf_first_rtt = rtt => rtt.map(v => v[0]).forEach(v => console.log(v)) + +const out_low_pass_pct = (rtt, max_rtt) => console.log(`${max_rtt}ms keeps`, passe_bas(rtt, max_rtt).length / rtt.length) + +const out_hol = same_buffer => { + const sbres = same_buffer.map(sb => + sb.reduce((acc, v) => { + if (acc.hs == null) acc.hs = v.hs + if (v.orig != acc.orig && v.orig != acc.last - 1) { + acc.orig = v.orig + acc.l.push(0) + } + + if (v.orig == acc.last - 1) acc.orig = v.orig + + acc.last = v.current + acc.l[acc.l.length - 1] += 1 + return acc + }, {l: [], last: null, orig: null, hs: null}) + ) + .reduce((acc, sb) => acc.concat(sb), []) + .map(v => v.l) + //.filter(v => v.length > 0) + + //console.log(sbres.length) + + //const hol = sbres.reduce((acc, sb) => acc.concat(sb), []).sort((a, b) => b - a)) + console.log("hol") + sbres.map(v => v.reduce((a,b) => a+b,0)).forEach(v => console.log(v)) +} + +const out_ordered_median = (hs, rtt, percname) => { + const pos = ["min", "_5th", "_25th", "med", "_75th", "_95th", "max"].indexOf(percname) + console.log("hs,type,rtt,rank") + comp_perc(hs, rtt) + .filter(v => Math.random() < 0.1) + .sort((a, b) => a[pos][2] - b[pos][2]) // Sort by [percname][rtt] + .map((circuit, idx) => circuit.map(entry => entry.concat([idx]))) // Add rank to each value + .reduce((acc, v) => acc.concat(v), []) // Reduce it to have a data structure similar to our future CSV + .forEach(v => console.log(`${v[0]}, ${v[1]}, ${v[2]}, ${v[3]}`)) // Output CSV line by line +} + +const predict = () => { + const split_at = 60 + const too_long = 3000 + const predict = rtt + .map(circuit => new Object({seen: circuit.slice(0,split_at), to_predict: circuit.slice(split_at) })) + .filter(({seen, to_predict}) => Math.max.apply(null, seen) <= too_long) + .map(({seen, to_predict}) => new Object({ + seen: seen, + computed: Object.assign(split_in_perc(seen.sort((a,b) => a-b)), {avg: seen.reduce((a,b) => a+b)/seen.length }), + good: to_predict.every(v => v <= too_long) + //good: Math.max.apply(null, to_predict) + })) + .map(v => { + v.computed.delta_max = v.computed.max - v.computed.min + v.computed.delta_5 = v.computed._95th - v.computed._5th + v.computed.delta_25 = v.computed._75th - v.computed._25th + + v.computed.stddev = + Math.sqrt( + v.seen + .map(rtt => Math.pow(rtt - v.computed.avg, 2)) + .reduce((a,b) => a+b) + / v.seen.length) + + return v + }) + + const predict_keys = ['good'].concat(Object.keys(predict[0].computed)) + console.log(predict_keys.join()) + predict + .forEach(({seen,computed,good}) => + console.log(([good].concat(predict_keys.slice(1).map(k => computed[k])).join()))) + + /* + console.log("good,rtt0,rtt1,rtt2,rtt3,rtt4,rtt5,rtt6,rtt7,rtt8,rtt9") + rtt + .filter(v => v.length == 600) + .map(v => [v.some(r => r > 2000) ? 'f' : 't'].concat(v.slice(0,10))) + .map(v => v.join()) + .forEach(v => console.log(v)) + */ + + /* + const new_sb = same_buffer + .filter((v, idx) => rtt[idx][0] <= 500) + out_hol(new_sb) + */ + +} diff --git a/bench/bench1/server.js b/bench/bench1/server.js new file mode 100644 index 0000000..116e952 --- /dev/null +++ b/bench/bench1/server.js @@ -0,0 +1,96 @@ +'use strict' + +const express = require('express') +const net = require('net') +const bodyParser = require('body-parser') +const fs = require('fs') +const torm = require('./tor.js') +const logm = require('./log.js') + +const timeout_sock = 120 * 60 * 1000 // 120 minute * 60 second/minute * 1000 ms/second + +const counter = function*(count) { + while (true) { + yield count + count++ + } +} +const idgen = counter(0) + +const socks = {} + +const clean_sock = (tor, sock_id) => () => { + if (!socks[sock_id]) { + console.error(`Server ${sock_id} does not exist`) + return "Unable to find the desired server" + } + + if (socks[sock_id].timer) + clearTimeout(socks[sock_id].timer) + if (socks[sock_id].hs) + tor.destroyHiddenService(socks[sock_id].hs, err => err ? console.error(err) : null) + + delete socks[sock_id] + console.info(`Server ${sock_id} has been cleaned`) + return null +} + +const register_routes = (app, echo_server, tor) => { + app.post('/hidden_service', (req, res) => { + const id = idgen.next().value + const port = echo_server.address().port + const to = setTimeout(clean_sock(tor, id), timeout_sock) + tor.createHiddenService( + [{virtualPort: port, target: `127.0.0.1:${port}`}], + (err, data) => { + if (err) { + console.error("Unable to create Tor Hidden Service", err) + res.send({err: err}) + return + } + socks[id] = {timer: to, hs: data.serviceId} + console.info(`Service online at ${data.serviceId}.onion:${port} for ${timeout_sock}ms`) + res.send({err: null, id: id, hs: data.serviceId, port: port}) + }) + }) + + app.delete('/hidden_service', (req, res) => { + const sock_id = req.body.id + const err = clean_sock(tor, sock_id)() + res.send({err: err}) + console.info(`Server ${sock_id} has been deleted with error "${err}"`) + }) +} + +const init_http = (echo_server, tor) => new Promise((resolve, reject) => { + const app = express() + app.use(bodyParser.json()) + register_routes(app, echo_server, tor) + app.listen(3000, () => { + console.info("HTTP module has been inited") + resolve(app) + }) +}) + +const init_echo = new Promise((resolve, reject) => { + const echo_server = net.createServer() + echo_server + .on('error', err => { + console.error("Unable to create socket", err) + reject(err) + }) + .on('connection', socket => socket.on('data', socket.write)) // echo socket + .on('listening', () => { + console.info("Echo module has been inited") + resolve(echo_server) + }) + .listen() +}) + +const init = () => { + Promise.all([init_echo, logm.init.then(res => torm.init(res[0], res[1]))]) + .then(values => init_http(values[0], values[1])) + .catch(console.error) +} + +init() diff --git a/bench/bench1/test.js b/bench/bench1/test.js new file mode 100644 index 0000000..6ac2065 --- /dev/null +++ b/bench/bench1/test.js @@ -0,0 +1,8 @@ +let worker = require('./worker.js') +let w = new worker(3,100) +w.on('success', console.log) +w.on('error', console.error) +for (let i = 0; i < 7; i++) { + w.add(() => new Promise((r,f) => setTimeout(() => r("yes"),2000))) +} + diff --git a/bench/bench1/tor.js b/bench/bench1/tor.js new file mode 100644 index 0000000..831864a --- /dev/null +++ b/bench/bench1/tor.js @@ -0,0 +1,34 @@ +'use strict' + +const granax = require('granax') +const fs = require('fs') + +const init = (stream_fd, circ_fd) => new Promise((resolve, reject) => { + const tor = granax(null, { + UseEntryGuards: 1, + NumEntryGuards: 100000, + NumPrimaryGuards: 100000, + NumDirectoryGuards: 100000, + SocksPort: "auto IsolateClientAddr IsolateSOCKSAuth IsolateClientProtocol IsolateDestPort IsolateDestAddr OnionTrafficOnly", + }) + + //tor.process.stdout.setEncoding("utf-8") + //tor.process.stdout.on('data', console.info) + + tor.on('error', err => { console.error("Unable to start Tor", err); reject(err)}) + tor.on('close', () => console.error("Control socket has been closed")) + tor.on('STREAM', function(data) { + fs.write(stream_fd, `${data}\n`, () => {}) + }) + tor.on('CIRC', function(data) { + fs.write(circ_fd, `${data}\n`, () => {}) + }) + + tor.on('ready', () => + tor.addEventListeners(['STREAM', 'CIRC'], () => { + console.info("Tor module has been inited") + resolve(tor) + })) +}) + +module.exports = { init: init } diff --git a/bench/bench1/worker.js b/bench/bench1/worker.js new file mode 100644 index 0000000..3b741b7 --- /dev/null +++ b/bench/bench1/worker.js @@ -0,0 +1,55 @@ +"use strict" + +const worker = function(sim_limit, start_delay) { + this.sim_limit = sim_limit + this.start_delay = start_delay + this.last_started = 0 + this.count = 0 + this.wait = [] + this.success_cb = [] + this.error_cb = [] +} + +worker.prototype.__schedule = function() { + if (this.count >= this.sim_limit) { + console.debug(`Worker is full, ${this.wait.length} jobs in the queue`) + return + } + if (this.wait.length <= 0) return + + const now = Date.now() + let will_start_in = 0 + if (this.last_started + this.start_delay > now) { + will_start_in = this.last_started + this.start_delay - now + console.debug(`Too many jobs started at once, throttle job of ${will_start_in}ms`) + } + + this.last_started = now + will_start_in + this.count++ + setTimeout(((refw, job) => () => { + console.debug("A job has been started") + job() + .then(v => { + refw.count-- + refw.__schedule() + refw.success_cb.forEach(cb => cb(v)) + }) + .catch(e => { + refw.count-- + refw.__schedule() + refw.error_cb.forEach(cb => cb(e)) + }) + })(this,this.wait.pop()), will_start_in) +} + +worker.prototype.add = function(prom) { + this.wait.push(prom) + this.__schedule() +} + +worker.prototype.on = function(evt, cb) { + if (evt == 'success') this.success_cb.push(cb) + else if (evt == 'error') this.error_cb.push(cb) +} + +module.exports = worker diff --git a/bench/bench2/.gitignore b/bench/bench2/.gitignore new file mode 100644 index 0000000..e15cdee --- /dev/null +++ b/bench/bench2/.gitignore @@ -0,0 +1,2 @@ +*.csv +node_modules diff --git a/bench/bench2/Dockerfile b/bench/bench2/Dockerfile new file mode 100644 index 0000000..b59ae3e --- /dev/null +++ b/bench/bench2/Dockerfile @@ -0,0 +1,25 @@ +FROM amd64/debian:buster + +RUN apt-get update && \ + apt-get dist-upgrade -y && \ + apt-get install -y nodejs npm build-essential libevent-dev libssl-dev patch wget tar zlib1g-dev + +COPY enable-one-hop.patch /root/enable-one-hop.patch + +RUN wget https://www.torproject.org/dist/tor-0.3.5.7.tar.gz && \ + tar xf tor-0.3.5.7.tar.gz && \ + rm tor-0.3.5.7.tar.gz && \ + cd tor-0.3.5.7 && \ + patch -p1 < /root/enable-one-hop.patch && \ + ./configure && \ + make && \ + make install + +COPY client.js /root/client.js +COPY package.json /root/package.json +COPY package-lock.json /root/package-lock.json + +WORKDIR /root +RUN npm install + +COPY torrc /root/torrc diff --git a/bench/bench2/README.md b/bench/bench2/README.md new file mode 100644 index 0000000..90a7ef6 --- /dev/null +++ b/bench/bench2/README.md @@ -0,0 +1,13 @@ +``` +sudo docker build -t superboum/am64_tor_lat:v1 . +``` + +``` +sudo docker run -d -t -i -p 443:443a --name tor_relay -v `pwd`/log:/log superboum/am64_tor_lat:v1 bash -c 'tor -f torrc > /log/tor.log 2>&1' +``` + +Wait ~2 hours + +``` +sudo docker run -d -t -i --net=container:tor_relay superboum/am64_tor_lat:v1 bash -c 'node client.js TryToDoResearch,IPredator > /log/xp-stdout.log 2> /log/xp-stderr.log' +``` diff --git a/bench/bench2/client.js b/bench/bench2/client.js new file mode 100644 index 0000000..5df898a --- /dev/null +++ b/bench/bench2/client.js @@ -0,0 +1,101 @@ +'use strict' + +const socks_client = require('socks').SocksClient +const net = require('net') + +const controlPort = net.connect(9051, "127.0.0.1") +controlPort.setEncoding('utf8'); + +const do_cmd = (cmd, content) => new Promise((resolve, reject) => { + const cb = data => { + data.includes(content) ? resolve(data) : reject(data) + controlPort.removeListener('data', cb) + } + controlPort.on('data', cb) + controlPort.write(cmd+"\n") +}) + +const sock_connect = socks_opt => + new Promise((resolve, reject) => + socks_client.createConnection(socks_opt, (err, info) => + err ? reject(err) : resolve(info))) + +const get_stream_id = url => + new Promise((resolve, reject) => { + const cb = data => { + const m = data.match(`650 STREAM (\\d+) NEW \\d+ ${url}`) + if (m) { + controlPort.removeListener('data', cb) + resolve(m[1]) + } + } + controlPort.on('data', cb) + }) + +const wait_for_circ = cid => + new Promise((resolve, reject) => { + const cb = data => { + const m = data.match(`650 CIRC ${cid} BUILT`) + if (m) { + controlPort.removeListener('data', cb) + resolve(m[1]) + } + } + controlPort.on('data', cb) + }) + +const do_one_measurement = info => + new Promise((resolve, reject) => { + const begin = Date.now().toString() + const cb = recv_data => { + const parsed = recv_data.split("\n") + .filter(e => e.includes(begin)) + + if (parsed.length > 1) { + reject("There is an error in the code", parsed) + info.socket.removeListener('data', cb) + } else if (parsed.length == 1) { + resolve(`${begin},${Date.now().toString()}`) + info.socket.removeListener('data', cb) + } + } + info.socket.on('data', cb) + info.socket.setNoDelay(true) + info.socket.setEncoding("utf-8") + info.socket.write(begin + "\n") + }) + +const do_measurements = info => new Promise((resolve, reject) => { + setInterval(() => { + do_one_measurement(info).then(r => console.log(r)) + }, 1000) + }) + +const circuitToBuild = process.argv.length > 2 ? process.argv[2] : 'IPredator,UOfMichigan,PrivacyRepublic0001' +console.log("Circuit to be built: "+circuitToBuild) + +// Init +do_cmd('authenticate ""', "250 OK") + .then(r => do_cmd('setconf __LeaveStreamsUnattached=1', "250 OK")) + .then(r => do_cmd('setconf FastFirstHopPK=0', '250 OK')) + .then(r => do_cmd('setconf EnforceDistinctSubnets=0', '250 OK')) + .then(r => do_cmd('setconf UseEntryGuards=0', '250 OK')) + .then(r => do_cmd('setconf AllowSingleHopExits=1', '250 OK')) + .then(r => do_cmd('extendcircuit 0 '+circuitToBuild, "250 EXTENDED")) + .then(r => r.match(/250 EXTENDED (\d+)/)[1]) + .then(cid => Promise.all([cid, do_cmd('setevents CIRC', '250 OK')])) + .then(([cid, garb]) => Promise.all([cid, wait_for_circ(cid)])) + .then(([cid, garb]) => Promise.all([cid, do_cmd('setevents STREAM', '250 OK')])) + .then(([cid, garb]) => Promise.all([ + sock_connect({ + proxy: {ipaddress: '127.0.0.1', port: 9050, type: 5}, + command: 'connect', + destination: { host: `lupine.machine.deuxfleurs.fr`, port: 1337 } + }), + get_stream_id("lupine.machine.deuxfleurs.fr:1337") + .then(sid => Promise.all([cid, sid, do_cmd(`attachstream ${sid} ${cid}`, "250 OK")])) + ])) + .then(([sock, ids]) => Promise.all([sock, ids, do_cmd('setevents', '250 OK')])) + .then(([sock, ids, garb]) => Promise.all([sock, ids, do_measurements(sock)])) + .then(([sock, ids, res]) => console.log(res)) + .catch(err => console.error("AN ERROR OCCURED", err)) diff --git a/bench/bench2/enable-one-hop.patch b/bench/bench2/enable-one-hop.patch new file mode 100644 index 0000000..3518aba --- /dev/null +++ b/bench/bench2/enable-one-hop.patch @@ -0,0 +1,31 @@ +diff -Naur tor-0.3.5.7/src/feature/client/circpathbias.c tor-0.3.5.7-3/src/feature/client/circpathbias.c +--- tor-0.3.5.7/src/feature/client/circpathbias.c 2018-10-17 20:44:07.000000000 +0200 ++++ tor-0.3.5.7-3/src/feature/client/circpathbias.c 2019-02-06 15:22:30.937135861 +0100 +@@ -362,8 +362,8 @@ + } + + /* Completely ignore one hop circuits */ +- if (circ->build_state->onehop_tunnel || +- circ->build_state->desired_path_len == 1) { ++ if (false && (circ->build_state->onehop_tunnel || ++ circ->build_state->desired_path_len == 1)) { + /* Check for inconsistency */ + if (circ->build_state->desired_path_len != 1 || + !circ->build_state->onehop_tunnel) { +diff -Naur tor-0.3.5.7/src/feature/control/control.c tor-0.3.5.7-3/src/feature/control/control.c +--- tor-0.3.5.7/src/feature/control/control.c 2018-12-18 14:10:11.000000000 +0100 ++++ tor-0.3.5.7-3/src/feature/control/control.c 2019-02-06 15:48:23.629998186 +0100 +@@ -3873,13 +3873,6 @@ + conn); + return 0; + } +- /* Is this a single hop circuit? */ +- if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) { +- connection_write_str_to_buf( +- "551 Can't attach stream to this one-hop circuit.\r\n", conn); +- return 0; +- } +- + if (circ && hop>0) { + /* find this hop in the circuit, and set cpath */ + cpath = circuit_get_cpath_hop(circ, hop); diff --git a/bench/bench2/package-lock.json b/bench/bench2/package-lock.json new file mode 100644 index 0000000..ca7efc9 --- /dev/null +++ b/bench/bench2/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "bench2", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "smart-buffer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==" + }, + "socks": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", + "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + } + } +} diff --git a/bench/bench2/package.json b/bench/bench2/package.json new file mode 100644 index 0000000..1cde31c --- /dev/null +++ b/bench/bench2/package.json @@ -0,0 +1,14 @@ +{ + "name": "bench2", + "version": "1.0.0", + "description": "", + "main": "client.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "socks": "^2.2.3" + } +} diff --git a/bench/bench2/torrc b/bench/bench2/torrc new file mode 100644 index 0000000..cab306d --- /dev/null +++ b/bench/bench2/torrc @@ -0,0 +1,7 @@ +ControlPort 9051 +ORPort 443 +Nickname TryToDoResearch +ContactInfo John Doe +ExitPolicy reject *:* +RelayBandwidthRate 512 KB +RelayBandwidthBurst 1024 KB diff --git a/bench/bench2/torrc_simple b/bench/bench2/torrc_simple new file mode 100644 index 0000000..aa0ef0c --- /dev/null +++ b/bench/bench2/torrc_simple @@ -0,0 +1 @@ +ControlPort 9051 diff --git a/bench/bench3/obs.py b/bench/bench3/obs.py new file mode 100644 index 0000000..5a5db88 --- /dev/null +++ b/bench/bench3/obs.py @@ -0,0 +1,51 @@ +from stem.control import Controller, EventType +from stem import CircStatus, CircEvent, HiddenServiceState +import time +import datetime +import functools + +per_circuit = {} +per_hs = {} + +hs_hr = { + HiddenServiceState.HSCR_CONNECTING: 'CLIENT - Création du point de RDV en cours...', + HiddenServiceState.HSCI_CONNECTING: 'CLIENT - Connexion au point d\'introduction...', + HiddenServiceState.HSCR_ESTABLISHED_IDLE: 'CLIENT - Création du point de RDV terminée', + HiddenServiceState.HSCI_INTRO_SENT: 'CLIENT - RDV envoyé via le point d\'introduction', + HiddenServiceState.HSCR_ESTABLISHED_WAITING: 'CLIENT - Le point de RDV attend l\'Onion Service', + HiddenServiceState.HSCI_DONE: 'CLIENT - Fermeture de la connexion avec le point d\'introduction', + HiddenServiceState.HSCR_JOINED: 'CLIENT - L\'Onion Service est connecté, le lien est prêt' +} + +def handle_circ(event): + print('.', end='', flush=True) + if event.id not in per_circuit: per_circuit[event.id] = [] + per_circuit[event.id].append((EventType.CIRC, datetime.datetime.now(), event)) + +def handle_circ_minor(event): + print('+', end='', flush=True) + if event.id not in per_circuit: per_circuit[event.id] = [] + if len(per_circuit[event.id]) > 0 and per_circuit[event.id][-1][2].hs_state == event.hs_state: return + per_circuit[event.id].append((EventType.CIRC_MINOR, datetime.datetime.now(), event)) + +with Controller.from_port(port = 9051) as controller: + controller.authenticate() + controller.add_event_listener(handle_circ, EventType.CIRC) + controller.add_event_listener(handle_circ_minor, EventType.CIRC_MINOR) + input() + print('-- results --') + + for circ, val in per_circuit.items(): + hs = functools.reduce(lambda acc, v: acc or v[2].rend_query, val, None) + if hs not in per_hs: per_hs[hs] = [] + per_hs[hs] = per_hs[hs] + val + + for hs, val in per_hs.items(): + if hs == None: hs = "Not linked with HS" + val.sort(key=lambda x: x[1]) + print(f"{hs}, delay: {val[-1][1] - val[0][1]}") + for evt, ts, circ in val: + if evt == EventType.CIRC_MINOR and circ.hs_state != None: + print(f"\t{circ.id}, {ts}, {hs_hr[circ.hs_state]}") + elif evt == EventType.CIRC and circ.status == CircStatus.LAUNCHED: + print(f"\t{circ.id}, {ts}, création d'un nouveau circuit") diff --git a/bench/bench3/requirements.txt b/bench/bench3/requirements.txt new file mode 100644 index 0000000..e63e524 --- /dev/null +++ b/bench/bench3/requirements.txt @@ -0,0 +1 @@ +stem diff --git a/bench/bench3/torrc_simple b/bench/bench3/torrc_simple new file mode 100644 index 0000000..aa0ef0c --- /dev/null +++ b/bench/bench3/torrc_simple @@ -0,0 +1 @@ +ControlPort 9051 diff --git a/results/parse.c b/results/parse.c index 6476928..e8d5c2a 100644 --- a/results/parse.c +++ b/results/parse.c @@ -17,7 +17,7 @@ int cmpuint64t(const void* u1, const void* u2) { int main(int argc, char *argv[]) { if (argc < 3) { - printf("Usage: %s +\n", argv[0]); + printf("Usage: %s +\n", argv[0]); exit(EXIT_FAILURE); } @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { sprintf(filename, "%d.csv", target); if ((w = fopen(filename, "w")) == NULL) goto iofail; - for (int i = 2; i < argc; i++) { + for (int i = 3; i < argc; i++) { printf("selected %s\n", argv[i]); if ((r = fopen(argv[i], "r")) == NULL) goto iofail; if (fread(&taglen, sizeof(taglen), 1, r) != 1) goto iofail; @@ -53,12 +53,13 @@ int main(int argc, char *argv[]) { uint64_t real_log_size = meascount; // cut beginning - while (real_log[0] == 0 && real_log_size > 0) { + int i = atoi(argv[2]); + while ((real_log[0] == 0 || i-- > 0) && real_log_size > 0) { real_log = &(real_log[1]); real_log_size--; } printf("cutted %lu values at beginning\n", meascount - real_log_size); - assert(real_log_size > target); + if(real_log_size < target) goto file_done; uint64_t missmeas = 0; for (int j = 0; j < target; j++) { @@ -91,9 +92,12 @@ int main(int argc, char *argv[]) { if (fprintf(w, "%s,%s,q25,%lu\n", tag, uuidstr, q25) < 0) goto iofail; if (fprintf(w, "%s,%s,q75,%lu\n", tag, uuidstr, q75) < 0) goto iofail; if (fprintf(w, "%s,%s,q99,%lu\n", tag, uuidstr, q99) < 0) goto iofail; + +file_done: if(fclose(r) != 0) goto iofail; } + if(fclose(w) != 0) goto iofail; printf("success\n"); return EXIT_SUCCESS;