diff --git a/.gitignore b/.gitignore index 3b71ae8dbefc0c4382cb82ecdc3cfebf75628cba..8d7e9c280b5a26fdbffb7db57fc517098e3908f2 100644 --- a/.gitignore +++ b/.gitignore @@ -54,5 +54,7 @@ test/fast/modules/crawler/block_pulling.js* test/fast/modules/crawler/block_pulling.d.ts test/fast/fork*.js* test/fast/fork*.d.ts +test/fast/proxies*.js* +test/fast/proxies*.d.ts test/fast/modules/ws2p/*.js* test/fast/modules/ws2p/*.d.ts diff --git a/app/cli.ts b/app/cli.ts index cedf3a7fa3b2bf3705896010e990d2474b85cbe4..26accf24f158facb605808a72bfaec2dfc9f2abd 100644 --- a/app/cli.ts +++ b/app/cli.ts @@ -49,6 +49,12 @@ export const ExecuteCommand = () => { .option('--nostdout', 'Disable stdout printing for `export-bc` command') .option('--noshuffle', 'Disable peers shuffling for `sync` command') + .option('--socks-proxy <host:port>', 'Use Socks Proxy') + .option('--tor-proxy <host:port>', 'Use Tor Socks Proxy') + .option('--reaching-clear-ep <clear|tor|none>', 'method for reaching an clear endpoint') + .option('--force-tor', 'force duniter to contact endpoint tor (if you redirect the traffic to tor yourself)') + .option('--rm-proxies', 'Remove all proxies') + .option('--timeout <milliseconds>', 'Timeout to use when contacting peers', parseInt) .option('--httplogs', 'Enable HTTP logs') .option('--nohttplogs', 'Disable HTTP logs') diff --git a/app/lib/common-libs/constants.ts b/app/lib/common-libs/constants.ts index 14465aade0602c1525116e0c7fa7a210011373f6..89b6e7dba4952a541a4d0011ef93bdad4b48a52f 100644 --- a/app/lib/common-libs/constants.ts +++ b/app/lib/common-libs/constants.ts @@ -31,9 +31,13 @@ const UNLOCK = "(SIG\\(" + INTEGER + "\\)|XHX\\(" + XUNLOCK + "\\))" const CONDITIONS = "(&&|\\|\\|| |[()]|(SIG\\(" + PUBKEY + "\\)|(XHX\\([A-F0-9]{64}\\)|CLTV\\(" + CLTV_INTEGER + "\\)|CSV\\(" + CSV_INTEGER + "\\))))*" const BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/ +const BMATOR_REGEXP = /^BMATOR( ([a-z0-9]{16})\.onion)( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/ const WS2P_REGEXP = /^WS2P ([a-f0-9]{8}) ([a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) ([0-9]+)(?: (.+))?$/ +const WS2PTOR_REGEXP = /^WS2PTOR ([a-f0-9]{8}) ([a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+.onion) ([0-9]+)(?: (.+))?$/ +const WS_FULL_ADDRESS_ONION_REGEX = /^(?:wss?:\/\/)(?:www\.)?([0-9a-z]{16}\.onion)(:[0-9]+)?$/ const IPV4_REGEXP = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/; const IPV6_REGEXP = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(([0-9A-Fa-f]{1,4}:){0,5}:((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(::([0-9A-Fa-f]{1,4}:){0,5}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/; +const HOST_ONION_REGEX = /^(?:www\.)?([0-9a-z]{16}\.onion)$/ const MAXIMUM_LEN_OF_COMPACT_TX = 100 const MAXIMUM_LEN_OF_OUTPUT = 2000 @@ -89,9 +93,13 @@ export const CommonConstants = { SWITCH_ON_BRANCH_AHEAD_BY_X_BLOCKS: 3, BMA_REGEXP, + BMATOR_REGEXP, WS2P_REGEXP, + WS2PTOR_REGEXP, + WS_FULL_ADDRESS_ONION_REGEX, IPV4_REGEXP, IPV6_REGEXP, + HOST_ONION_REGEX, PUBLIC_KEY: exact(PUBKEY), INTEGER: /^\d+$/, BASE58: exact(BASE58), diff --git a/app/lib/constants.ts b/app/lib/constants.ts index cac2f7a07204a72e2af657ee0b36282c85f4ab59..26180e81d5fb6efe6799f0096f9a914b0091d885 100644 --- a/app/lib/constants.ts +++ b/app/lib/constants.ts @@ -109,7 +109,8 @@ module.exports = { STATUS_INTERVAL: { UPDATE: 2, // Every X blocks MAX: 20 // MAX Y blocks - } + }, + ONION_ENDPOINT_REGEX: new RegExp('(?:https?:\/\/)?(?:www)?(\S*?\.onion)(\/[-\w]*)*') }, PROOF_OF_WORK: { EVALUATION: 1000, diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index 6281d83fbb597df5703258b7ffde398550a5ba07..6002261231996b2e8b4b2538b8d8349109ef127f 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -15,6 +15,7 @@ import {DBBlock} from "../db/DBBlock" import {DBMembership} from "./sqliteDAL/MembershipDAL" import {MerkleDTO} from "../dto/MerkleDTO" import {CommonConstants} from "../common-libs/constants" +import { ProxiesConf } from '../proxy'; const fs = require('fs') const path = require('path') @@ -142,7 +143,7 @@ export class FileDAL { } async getWS2Peers() { - return this.peerDAL.getPeersWithEndpointsLike('WS2P ') + return this.peerDAL.getPeersWithEndpointsLike('WS2P') } async getBlock(number:number) { @@ -842,7 +843,11 @@ export class FileDAL { let conf = ConfDTO.complete(overrideConf || {}); if (!defaultConf) { const savedConf = await this.confDAL.loadConf(); + const savedProxyConf = _(savedConf.proxyConf).extend({}); conf = _(savedConf).extend(overrideConf || {}); + if (overrideConf.proxiesConf !== undefined) {} else { + conf.proxyConf = _(savedProxyConf).extend({}); + } } if (this.loadConfHook) { await this.loadConfHook(conf) diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts index 361eb49c9d0922b3e4fffb6b4e0c4bc9e25ce7ed..f6c57cc5eea581e96a0e188d4265941ceb79b239 100644 --- a/app/lib/dto/ConfDTO.ts +++ b/app/lib/dto/ConfDTO.ts @@ -1,4 +1,5 @@ import {CommonConstants} from "../common-libs/constants" +import { ProxiesConf } from '../proxy'; const _ = require('underscore'); const constants = require('../constants'); @@ -46,7 +47,9 @@ export interface KeypairConfDTO { } export interface NetworkConfDTO { + proxiesConf: ProxiesConf|undefined nobma: boolean + bmaWithCrawler: boolean remoteport: number remotehost: string|null remoteipv4: string|null @@ -134,6 +137,8 @@ export class ConfDTO implements CurrencyConfDTO, KeypairConfDTO, NetworkConfDTO, public homename: string, public memory: boolean, public nobma: boolean, + public bmaWithCrawler: boolean, + public proxiesConf: ProxiesConf|undefined, public ws2p?: { privateAccess: boolean publicAccess: boolean @@ -152,7 +157,7 @@ export class ConfDTO implements CurrencyConfDTO, KeypairConfDTO, NetworkConfDTO, ) {} static mock() { - return new ConfDTO("", "", [], [], 0, 0, 0.6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", 0, "", "", null, false, "", true, true) + return new ConfDTO("", "", [], [], 0, 0, 0.6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", 0, "", "", null, false, "", true, false, true, undefined) } static defaultConf() { diff --git a/app/lib/dto/PeerDTO.ts b/app/lib/dto/PeerDTO.ts index 738c540632ab7e831e6a501fde83d622a2e2d3a7..dc5754b6893349a0009b66e1cbb21c78d7b116a7 100644 --- a/app/lib/dto/PeerDTO.ts +++ b/app/lib/dto/PeerDTO.ts @@ -94,16 +94,30 @@ export class PeerDTO implements Cloneable { return bma || {}; } - getWS2P() { + getWS2P(canReachTorEp:boolean, canReachClearEp:boolean) { let api:{ uuid:string, host:string, port:number, path:string }|null = null + const endpointRegexp = (canReachTorEp) ? CommonConstants.WS2PTOR_REGEXP:CommonConstants.WS2P_REGEXP for (const ep of this.endpoints) { - const matches:any = !api && ep.match(CommonConstants.WS2P_REGEXP) - if (matches) { - api = { - uuid: matches[1], - host: matches[2] || '', - port: parseInt(matches[3]) || 0, - path: matches[4] + if (canReachTorEp) { + const matches:any = ep.match(CommonConstants.WS2PTOR_REGEXP) + if (matches) { + return { + uuid: matches[1], + host: matches[2] || '', + port: parseInt(matches[3]) || 0, + path: matches[4] + } + } + } + if (canReachClearEp) { + const matches:any = !api && ep.match(CommonConstants.WS2P_REGEXP) + if (matches) { + api = { + uuid: matches[1], + host: matches[2] || '', + port: parseInt(matches[3]) || 0, + path: matches[4] + } } } } diff --git a/app/lib/proxy.ts b/app/lib/proxy.ts new file mode 100644 index 0000000000000000000000000000000000000000..340de605c58ebb5b84fec40fd73889acbb7339cd --- /dev/null +++ b/app/lib/proxy.ts @@ -0,0 +1,52 @@ +import {CommonConstants} from "./common-libs/constants" + +const SocksProxyAgent = require('socks-proxy-agent'); + +export class ProxiesConf { + public proxySocksAddress: string|undefined + public proxyTorAddress: string|undefined + public reachingClearEp: string + public forceTor: boolean + + constructor () { + this.proxySocksAddress = undefined + this.proxyTorAddress = undefined + this.reachingClearEp = 'clear' + this.forceTor = false + } + + static canReachClearEndpoint(proxiesConf: ProxiesConf|undefined):boolean { + return (proxiesConf === undefined || proxiesConf.reachingClearEp !== 'none') + } + + static canReachTorEndpoint(proxiesConf: ProxiesConf|undefined):boolean { + return (proxiesConf !== undefined && (proxiesConf.forceTor || proxiesConf.proxyTorAddress !== undefined) ) + } + + static httpProxy(url:string, proxiesConf: ProxiesConf|undefined):string|undefined { + return ProxiesConf.chooseProxyAgent(url, proxiesConf, CommonConstants.HOST_ONION_REGEX) + } + + static wsProxy(address:string, proxiesConf: ProxiesConf|undefined):string|undefined { + return ProxiesConf.chooseProxyAgent(address, proxiesConf, CommonConstants.WS_FULL_ADDRESS_ONION_REGEX) + } + + private static chooseProxyAgent(address:string, proxiesConf: ProxiesConf|undefined, onionRegex:RegExp):string|undefined { + if (proxiesConf !== undefined) { + if (address.match(onionRegex)) { + if (ProxiesConf.canReachTorEndpoint(proxiesConf)) { + return proxiesConf.proxyTorAddress + } + } else { + if (ProxiesConf.canReachClearEndpoint(proxiesConf)) { + if (proxiesConf.reachingClearEp == 'tor') { + return proxiesConf.proxyTorAddress + } else { + return proxiesConf.proxySocksAddress + } + } + } + } + return undefined + } +} \ No newline at end of file diff --git a/app/modules/bma/index.ts b/app/modules/bma/index.ts index 914a6ecf4465032babe6e223b09273bf017ef416..d9b4c2d023f256aaa4ce520b6180e40b0257b657 100644 --- a/app/modules/bma/index.ts +++ b/app/modules/bma/index.ts @@ -29,6 +29,8 @@ export const BmaDependency = { { value: '--noupnp', desc: 'Do not use UPnP to open remote port.' }, { value: '--bma', desc: 'Enables BMA API and its crawlers.' }, { value: '--nobma', desc: 'Disables BMA API and its crawlers.' }, + { value: '--bma-with-crawler', desc: 'Enables BMA Crawler.' }, + { value: '--bma-without-crawler', desc: 'Disable BMA Crawler.' }, { value: '-p, --port <port>', desc: 'Port to listen for requests', parser: (val:string) => parseInt(val) }, { value: '--ipv4 <address>', desc: 'IPv4 interface to listen for requests' }, { value: '--ipv6 <address>', desc: 'IPv6 interface to listen for requests' }, @@ -73,6 +75,9 @@ export const BmaDependency = { } } + // If bmaWithCrawler hasn't been defined yet + if (conf.bmaWithCrawler === undefined) { conf.bmaWithCrawler = false } + if (program.port !== undefined) conf.port = parseInt(program.port) if (program.ipv4 !== undefined) conf.ipv4 = program.ipv4; if (program.ipv6 !== undefined) conf.ipv6 = program.ipv6; @@ -82,6 +87,8 @@ export const BmaDependency = { if (program.remotep !== undefined) conf.remoteport = parseInt(program.remotep) if (program.bma !== undefined) conf.nobma = false if (program.nobma !== undefined) conf.nobma = true + if (program.bmaWithCrawler !== undefined) conf.bmaWithCrawler = true + if (program.bmaWithoutCrawler !== undefined) conf.bmaWithCrawler = false if (!conf.ipv4) delete conf.ipv4; if (!conf.ipv6) delete conf.ipv6; @@ -253,6 +260,9 @@ export class BMAPI extends stream.Transform { function getEndpoint(theConf:NetworkConfDTO) { let endpoint = 'BASIC_MERKLED_API'; if (theConf.remotehost) { + if (theConf.remotehost.match(BMAConstants.HOST_ONION_REGEX)) { + endpoint = 'BMATOR'; + } endpoint += ' ' + theConf.remotehost; } if (theConf.remoteipv4) { diff --git a/app/modules/bma/lib/constants.ts b/app/modules/bma/lib/constants.ts index 6caa2204bd605ba3eadce521da6eaea61a352c22..20a76a6eb9cc64e8a6710c5667c1129a836e7694 100644 --- a/app/modules/bma/lib/constants.ts +++ b/app/modules/bma/lib/constants.ts @@ -1,3 +1,4 @@ +import {CommonConstants} from "../../../lib/common-libs/constants" export const BMAConstants = { BMA_PORTS_START: 10901, @@ -6,6 +7,7 @@ export const BMAConstants = { DEFAULT_PORT: 10901, IPV4_REGEXP: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/, IPV6_REGEXP: /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(([0-9A-Fa-f]{1,4}:){0,5}:((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(::([0-9A-Fa-f]{1,4}:){0,5}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/, + HOST_ONION_REGEX: CommonConstants.HOST_ONION_REGEX, PORT_START: 15000, UPNP_INTERVAL: 300, UPNP_TTL: 600, diff --git a/app/modules/bma/lib/network.ts b/app/modules/bma/lib/network.ts index 33b994afed703260ca4ab2a28e235195608be799..3e9e026f966695db0dd83a977605e5a26c1f2439 100644 --- a/app/modules/bma/lib/network.ts +++ b/app/modules/bma/lib/network.ts @@ -337,7 +337,9 @@ async function upnpConf (noupnp:boolean, logger:any) { const publicPort = await getAvailablePort(client) const privatePort = publicPort const conf:NetworkConfDTO = { + proxiesConf: undefined, nobma: true, + bmaWithCrawler: false, port: privatePort, ipv4: '127.0.0.1', ipv6: '::1', diff --git a/app/modules/crawler/lib/crawler.ts b/app/modules/crawler/lib/crawler.ts index 44bf5a217fa08548046b2a2e297c9dd341db41c5..9520c5e3abbae6b58d033c04f7879ef051205b56 100644 --- a/app/modules/crawler/lib/crawler.ts +++ b/app/modules/crawler/lib/crawler.ts @@ -48,7 +48,7 @@ export class Crawler extends stream.Transform implements DuniterService { } startService() { - if (this.conf.nobma) { + if (this.conf.nobma || !this.conf.bmaWithCrawler) { return Promise.resolve() } return Promise.all([ @@ -60,7 +60,7 @@ export class Crawler extends stream.Transform implements DuniterService { } stopService() { - if (this.conf.nobma) { + if (this.conf.nobma || !this.conf.bmaWithCrawler) { return Promise.resolve() } return Promise.all([ diff --git a/app/modules/router.ts b/app/modules/router.ts index 1530cc43e516b63511b385767f50390ebe88bcba..d6484f05ba8d3ef1a74e8440c44c25aae80af128 100644 --- a/app/modules/router.ts +++ b/app/modules/router.ts @@ -44,7 +44,7 @@ class Router extends stream.Transform { }; async startService() { - if (this.server.conf.nobma) { + if (this.server.conf.nobma || !this.server.conf.bmaWithCrawler) { // Disable BMA return Promise.resolve() } @@ -69,7 +69,7 @@ class Router extends stream.Transform { } async stopService() { - if (this.server.conf.nobma) { + if (this.server.conf.nobma || !this.server.conf.bmaWithCrawler) { // Disable BMA return Promise.resolve() } diff --git a/app/modules/ws2p/index.ts b/app/modules/ws2p/index.ts index 53e311b02d229b320107b38dcaddbc9ea031c009..a225e63ec314dc43e0dd77f308ec4cb7a5d2a58b 100644 --- a/app/modules/ws2p/index.ts +++ b/app/modules/ws2p/index.ts @@ -1,7 +1,8 @@ "use strict"; +import { WS2PConstants } from './lib/constants'; import {ConfDTO, WS2PConfDTO} from "../../lib/dto/ConfDTO" import {Server} from "../../../server" -import * as stream from "stream" +import * as stream from 'stream'; import {WS2PCluster} from "./lib/WS2PCluster" import {WS2PUpnp} from "./lib/ws2p-upnp" import {CommonConstants} from "../../lib/common-libs/constants" @@ -140,7 +141,7 @@ export const WS2PDependency = { const peers = await server.dal.getWS2Peers() for (const p of peers) { for (const ep of p.endpoints) { - if (ep.match(/^WS2P /)) { + if (ep.match(/^WS2P/)) { console.log(p.pubkey, ep) } } @@ -242,22 +243,29 @@ export class WS2PAPI extends stream.Transform { // If WS2P defined and enabled if (this.server.conf.ws2p !== undefined && (this.server.conf.ws2p.publicAccess || this.server.conf.ws2p.privateAccess)) { + let endpointType = "WS2P" if (this.server.conf.upnp && this.upnpAPI) { const config = this.upnpAPI.getCurrentConfig() - return !config ? '' : ['WS2P', this.server.conf.ws2p.uuid, config.remotehost, config.port].join(' ') + if (config) { + if (config.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) { endpointType += "TOR"; } + return [endpointType, this.server.conf.ws2p.uuid, config.remotehost, config.port].join(' ') + } else { + return '' + } } else if (this.server.conf.ws2p.uuid && this.server.conf.ws2p.remotehost && this.server.conf.ws2p.remoteport) { - let ep = ['WS2P', - this.server.conf.ws2p.uuid, - this.server.conf.ws2p.remotehost, - this.server.conf.ws2p.remoteport - ].join(' ') - if (this.server.conf.ws2p.remotepath) { - ep += ` ${this.server.conf.ws2p.remotepath}` - } - return ep + if (this.server.conf.ws2p.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) { endpointType += "TOR"; } + let ep = [endpointType, + this.server.conf.ws2p.uuid, + this.server.conf.ws2p.remotehost, + this.server.conf.ws2p.remoteport + ].join(' ') + if (this.server.conf.ws2p.remotepath) { + ep += ` ${this.server.conf.ws2p.remotepath}` + } + return ep } } return '' diff --git a/app/modules/ws2p/lib/WS2PClient.ts b/app/modules/ws2p/lib/WS2PClient.ts index 34710012a92988df92e4255bd6c2357c1381cebf..569609f6e1c06bbdef4b7c8c9fadfcf48569972c 100644 --- a/app/modules/ws2p/lib/WS2PClient.ts +++ b/app/modules/ws2p/lib/WS2PClient.ts @@ -1,3 +1,4 @@ +import { WS2PCluster } from './WS2PCluster'; import {Server} from "../../../../server" import {WS2PConnection, WS2PPubkeyLocalAuth, WS2PPubkeyRemoteAuth} from "./WS2PConnection" import {Key} from "../../../lib/common-libs/crypto/keyring" @@ -5,18 +6,21 @@ import {WS2PMessageHandler} from "./impl/WS2PMessageHandler" import {WS2PConstants} from "./constants" import {WS2PStreamer} from "./WS2PStreamer" import {WS2PSingleWriteStream} from "./WS2PSingleWriteStream" +import { ProxiesConf } from '../../../lib/proxy'; +import { server } from '../../../../test/integration/tools/toolbox'; export class WS2PClient { private constructor(public connection:WS2PConnection) {} - static async connectTo(server:Server, fullEndpointAddress:string, messageHandler:WS2PMessageHandler, expectedPub:string, allowKey:(pub:string)=>Promise<boolean> ) { + static async connectTo(server:Server, fullEndpointAddress:string, uuid:string, messageHandler:WS2PMessageHandler, expectedPub:string, allowKey:(pub:string)=>Promise<boolean> ) { const k2 = new Key(server.conf.pair.pub, server.conf.pair.sec) const c = WS2PConnection.newConnectionToAddress( fullEndpointAddress, messageHandler, new WS2PPubkeyLocalAuth(server.conf.currency , k2, allowKey), new WS2PPubkeyRemoteAuth(server.conf.currency, k2, allowKey), + ProxiesConf.wsProxy(fullEndpointAddress, server.conf.proxiesConf), { connectionTimeout: WS2PConstants.REQUEST_TIMEOUT, requestTimeout: WS2PConstants.REQUEST_TIMEOUT diff --git a/app/modules/ws2p/lib/WS2PCluster.ts b/app/modules/ws2p/lib/WS2PCluster.ts index 4ea5d7a21df56bf5c1def34fdab5088ecbaa36d5..70197333448da3996fb2eab262c2745616bd323c 100644 --- a/app/modules/ws2p/lib/WS2PCluster.ts +++ b/app/modules/ws2p/lib/WS2PCluster.ts @@ -13,9 +13,10 @@ import {OtherConstants} from "../../../lib/other_constants" import {Key, verify} from "../../../lib/common-libs/crypto/keyring" import {WS2PServerMessageHandler} from "./interface/WS2PServerMessageHandler" import {WS2PMessageHandler} from "./impl/WS2PMessageHandler" -import { CommonConstants } from "../../../lib/common-libs/constants"; +import { CommonConstants } from '../../../lib/common-libs/constants'; import { Package } from "../../../lib/common/package"; import { Constants } from "../../prover/lib/constants"; +import { ProxiesConf } from '../../../lib/proxy'; const es = require('event-stream') const nuuid = require('node-uuid') @@ -290,17 +291,18 @@ export class WS2PCluster { async connectToRemoteWS(host: string, port: number, path:string, messageHandler:WS2PMessageHandler, expectedPub:string, ws2pEndpointUUID:string = ""): Promise<WS2PConnection> { const uuid = nuuid.v4() - let pub = "--------" + let pub = expectedPub.slice(0, 8) + const api:string = (host.match(WS2PConstants.HOST_ONION_REGEX) !== null) ? 'WS2PTOR':'WS2P' try { const fullEndpointAddress = WS2PCluster.getFullAddress(host, port, path) - const ws2pc = await WS2PClient.connectTo(this.server, fullEndpointAddress, messageHandler, expectedPub, (pub:string) => { + const ws2pc = await WS2PClient.connectTo(this.server, fullEndpointAddress, ws2pEndpointUUID, messageHandler, expectedPub, (pub:string) => { const connectedPubkeys = this.getConnectedPubkeys() return this.acceptPubkey(expectedPub, connectedPubkeys, () => this.clientsCount(), this.maxLevel1Size, (this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes || []), ws2pEndpointUUID) }) this.ws2pClients[uuid] = ws2pc pub = ws2pc.connection.pubkey ws2pc.connection.closed.then(() => { - this.server.logger.info('WS2P: connection [%s `WS2P %s %s`] has been closed', pub.slice(0, 8), host, port) + this.server.logger.info(api+': connection [%s `'+api+' %s %s`] has been closed', pub.slice(0, 8), host, port) this.server.push({ ws2p: 'disconnected', peer: { @@ -311,7 +313,7 @@ export class WS2PCluster { delete this.ws2pClients[uuid] } }) - this.server.logger.info('WS2P: connected to peer %s using `WS2P %s %s`!', pub.slice(0, 8), host, port) + this.server.logger.info(api+': connected to peer %s using `'+api+' %s %s`!', pub.slice(0, 8), host, port) this.server.push({ ws2p: 'connected', to: { host, port, pubkey: pub } @@ -319,7 +321,7 @@ export class WS2PCluster { await this.server.dal.setPeerUP(pub) return ws2pc.connection } catch (e) { - this.server.logger.info('WS2P: Could not connect to peer %s using `WS2P %s %s: %s`', pub.slice(0, 8), host, port, (e && e.message || e)) + this.server.logger.info(api+': Could not connect to peer %s using `'+api+' %s %s: %s`', pub.slice(0, 8), host, port, (e && e.message || e)) throw e } } @@ -330,21 +332,45 @@ export class WS2PCluster { const prefered = ((this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes) || []).slice() // Copy // Our key is also a prefered one, so we connect to our siblings prefered.push(this.server.conf.pair.pub) + const canReachTorEndpoint = ProxiesConf.canReachTorEndpoint(this.server.conf.proxiesConf) peers.sort((a, b) => { const aIsPrefered = prefered.indexOf(a.pubkey) !== -1 const bIsPrefered = prefered.indexOf(b.pubkey) !== -1 - if ((aIsPrefered && bIsPrefered) || (!aIsPrefered && !bIsPrefered)) { - return 0 - } else if (aIsPrefered) { - return -1 + + if (canReachTorEndpoint) { + const aAtWs2pTorEnpoint = a.endpoints.filter(function (element) { return element.match(CommonConstants.WS2PTOR_REGEXP); }).length > 0 + const bAtWs2pTorEnpoint = b.endpoints.filter(function (element) { return element.match(CommonConstants.WS2PTOR_REGEXP); }).length > 0 + + if ( (aAtWs2pTorEnpoint && bAtWs2pTorEnpoint) || (!aAtWs2pTorEnpoint && !bAtWs2pTorEnpoint) ) { + if ((aIsPrefered && bIsPrefered) || (!aIsPrefered && !bIsPrefered)) { + return 0 + } else if (aIsPrefered) { + return -1 + } else { + return 1 + } + } else { + if (aAtWs2pTorEnpoint) { + return -1 + } else { + return 1 + } + } } else { - return 1 + if ((aIsPrefered && bIsPrefered) || (!aIsPrefered && !bIsPrefered)) { + return 0 + } else if (aIsPrefered) { + return -1 + } else { + return 1 + } } }) let i = 0 + const canReachClearEndpoint = ProxiesConf.canReachClearEndpoint(this.server.conf.proxiesConf) while (i < peers.length && this.clientsCount() < this.maxLevel1Size) { const p = peers[i] - const api = p.getWS2P() + const api = p.getWS2P(canReachTorEndpoint, canReachClearEndpoint) if (api) { try { // We do not connect to local host @@ -371,7 +397,7 @@ export class WS2PCluster { // New peer if (data.endpoints) { const peer = PeerDTO.fromJSONObject(data) - const ws2pEnpoint = peer.getWS2P() + const ws2pEnpoint = peer.getWS2P(ProxiesConf.canReachTorEndpoint(this.server.conf.proxiesConf), ProxiesConf.canReachClearEndpoint(this.server.conf.proxiesConf)) if (ws2pEnpoint) { // Check if already connected to the pubkey (in any way: server or client) const connectedPubkeys = this.getConnectedPubkeys() @@ -437,13 +463,14 @@ export class WS2PCluster { } private sayHeadChangedTo(number:number, hash:string) { + const api = (this.server.conf.ws2p && this.server.conf.ws2p.remotehost && this.server.conf.ws2p.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) ? 'WS2P':'WS2P' const key = new Key(this.server.conf.pair.pub, this.server.conf.pair.sec) const pub = key.publicKey const software = 'duniter' const softVersion = Package.getInstance().version const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '00000000' const prefix = this.server.conf.prefix || Constants.DEFAULT_PEER_ID - const message = `WS2P:HEAD:1:${pub}:${number}-${hash}:${ws2pId}:${software}:${softVersion}:${prefix}` + const message = `${api}:HEAD:1:${pub}:${number}-${hash}:${ws2pId}:${software}:${softVersion}:${prefix}` const sig = key.signSync(message) return { sig, message, pub } } @@ -624,7 +651,7 @@ export class WS2PCluster { async startCrawling(waitConnection = false) { // For connectivity - this.reconnectionInteval = setInterval(() => this.server.push({ ws2p: 'disconnected' }), 1000 * 60 * 10) + this.reconnectionInteval = setInterval(() => this.server.push({ ws2p: 'disconnected' }), 1000 * WS2PConstants.RECONNEXION_INTERVAL_IN_SEC) // For blocks if (this.syncBlockInterval) clearInterval(this.syncBlockInterval); diff --git a/app/modules/ws2p/lib/WS2PConnection.ts b/app/modules/ws2p/lib/WS2PConnection.ts index e97bde407c0420f028f2c8e95ae0c907abc8017b..0595e5626b4e307fffdd2be5d149ce6d33323bd7 100644 --- a/app/modules/ws2p/lib/WS2PConnection.ts +++ b/app/modules/ws2p/lib/WS2PConnection.ts @@ -7,7 +7,9 @@ import {MembershipDTO} from "../../../lib/dto/MembershipDTO" import {TransactionDTO} from "../../../lib/dto/TransactionDTO" import {PeerDTO} from "../../../lib/dto/PeerDTO" import {WS2PConstants} from "./constants" +import { ProxiesConf } from '../../../lib/proxy'; const ws = require('ws') +const SocksProxyAgent = require('socks-proxy-agent'); const nuuid = require('node-uuid'); const logger = require('../../../lib/logger').NewLogger('ws2p') @@ -267,15 +269,22 @@ export class WS2PConnection { messageHandler:WS2PMessageHandler, localAuth:WS2PLocalAuth, remoteAuth:WS2PRemoteAuth, + proxySocksAddress:string|undefined = undefined, options:{ - connectionTimeout:number + connectionTimeout:number, requestTimeout:number } = { connectionTimeout: REQUEST_TIMEOUT_VALUE, requestTimeout: REQUEST_TIMEOUT_VALUE }, expectedPub:string = "") { - const websocket = new ws(address) + if (address.match(WS2PConstants.FULL_ADDRESS_ONION_REGEX)) { + options = { + connectionTimeout: WS2PConstants.CONNEXION_TOR_TIMEOUT, + requestTimeout: WS2PConstants.REQUEST_TOR_TIMEOUT + } + } + const websocket = (proxySocksAddress !== undefined) ? new ws(address, { agent: SocksProxyAgent("socks://"+proxySocksAddress) }):new ws(address) const onWsOpened:Promise<void> = new Promise(res => { websocket.on('open', () => res()) }) diff --git a/app/modules/ws2p/lib/WS2PServer.ts b/app/modules/ws2p/lib/WS2PServer.ts index c9d3dc9fb025dbcc318c7ef6ede9bbedf08c44de..ae79815fe93af6f0c24ebb355e7d404681165748 100644 --- a/app/modules/ws2p/lib/WS2PServer.ts +++ b/app/modules/ws2p/lib/WS2PServer.ts @@ -63,16 +63,23 @@ export class WS2PServer extends events.EventEmitter { } return await this.shouldAcceptConnection(pub, this.getConnexions().map(c => c.pubkey)) } + let timeout = { + connectionTimeout: WS2PConstants.CONNEXION_TIMEOUT, + requestTimeout: WS2PConstants.REQUEST_TIMEOUT + } + if (this.server.conf.ws2p && this.server.conf.ws2p.remotehost && this.server.conf.ws2p.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) { + timeout = { + connectionTimeout: WS2PConstants.CONNEXION_TOR_TIMEOUT, + requestTimeout: WS2PConstants.REQUEST_TOR_TIMEOUT + } + } const c = WS2PConnection.newConnectionFromWebSocketServer( ws, messageHandler, new WS2PPubkeyLocalAuth(this.server.conf.currency, key, acceptPubkey), new WS2PPubkeyRemoteAuth(this.server.conf.currency, key, acceptPubkey), - { - connectionTimeout: WS2PConstants.CONNEXION_TIMEOUT, - requestTimeout: WS2PConstants.REQUEST_TIMEOUT - } + timeout ) try { diff --git a/app/modules/ws2p/lib/constants.ts b/app/modules/ws2p/lib/constants.ts index ace1fe96e1d5c67685ff2d36edbdd34a6002d541..51972d7f7d1dca2c5406d8f7af8fea58bfe740a0 100644 --- a/app/modules/ws2p/lib/constants.ts +++ b/app/modules/ws2p/lib/constants.ts @@ -6,8 +6,11 @@ export const WS2PConstants = { WS2P_PORTS_END: 20999, WS2P_UPNP_INTERVAL: 300, - CONNEXION_TIMEOUT: 10000, - REQUEST_TIMEOUT: 10000, + CONNEXION_TIMEOUT: 15000, + REQUEST_TIMEOUT: 15000, + CONNEXION_TOR_TIMEOUT: 30000, + REQUEST_TOR_TIMEOUT: 60000, + RECONNEXION_INTERVAL_IN_SEC: 60 * 10, // 10 minutes BLOCK_PULLING_INTERVAL: 300 * 2, // 10 minutes DOCPOOL_PULLING_INTERVAL: 3600 * 4, // 4 hours @@ -27,7 +30,7 @@ export const WS2PConstants = { + CommonConstants.FORMATS.BLOCKSTAMP + '$'), - HEAD_V1_REGEXP: new RegExp('^WS2P:HEAD:1:' + HEAD_V1_REGEXP: new RegExp('^WS2P(?:O[CT][SAM])?(?:I[CT])?:HEAD:1:' + '(' + CommonConstants.FORMATS.PUBKEY + '):' + '(' + CommonConstants.FORMATS.BLOCKSTAMP + '):' + '(' + CommonConstants.FORMATS.WS2PID + '):' @@ -36,5 +39,8 @@ export const WS2PConstants = { + '(' + CommonConstants.FORMATS.POW_PREFIX + ')' + '$'), + HOST_ONION_REGEX: CommonConstants.HOST_ONION_REGEX, + FULL_ADDRESS_ONION_REGEX: CommonConstants.WS_FULL_ADDRESS_ONION_REGEX, + HEADS_SPREAD_TIMEOUT: 100 // Wait 100ms before sending a bunch of signed heads } \ No newline at end of file diff --git a/index.ts b/index.ts index 3c6b04bc5ad36d1aae2701a97854444164bb3c76..f9e3cf445157876a33b1c8d5be9335d121b6a070 100644 --- a/index.ts +++ b/index.ts @@ -8,6 +8,7 @@ import {CrawlerDependency} from "./app/modules/crawler/index" import {BmaDependency} from "./app/modules/bma/index" import {WS2PDependency} from "./app/modules/ws2p/index" import {Constants} from "./app/modules/prover/lib/constants" +import { ProxiesConf } from './app/lib/proxy'; const path = require('path'); const _ = require('underscore'); @@ -440,7 +441,6 @@ class Stack { function commandLineConf(program:any, conf:any = {}) { conf = conf || {}; - conf.sync = conf.sync || {}; const cli = { currency: program.currency, cpu: program.cpu, @@ -448,10 +448,12 @@ function commandLineConf(program:any, conf:any = {}) { server: { port: program.port, }, - db: { - mport: program.mport, - mdb: program.mdb, - home: program.home + proxies: { + proxySocks: program.socksProxy, + proxyTor: program.torProxy, + reachingClearEp: program.reachingClearEp, + forceTor: program.forceTor, + rmProxies: program.rmProxies }, logs: { http: program.httplogs, @@ -465,19 +467,30 @@ function commandLineConf(program:any, conf:any = {}) { timeout: program.timeout }; - // Update conf - if (cli.currency) conf.currency = cli.currency; - if (cli.server.port) conf.port = cli.server.port; - if (cli.cpu) conf.cpu = Math.max(0.01, Math.min(1.0, cli.cpu)); - if (cli.prefix) conf.prefix = Math.max(Constants.MIN_PEER_ID, Math.min(Constants.MAX_PEER_ID, cli.prefix)); - if (cli.logs.http) conf.httplogs = true; - if (cli.logs.nohttp) conf.httplogs = false; - if (cli.db.mport) conf.mport = cli.db.mport; - if (cli.db.home) conf.home = cli.db.home; - if (cli.db.mdb) conf.mdb = cli.db.mdb; - if (cli.isolate) conf.isolate = cli.isolate; - if (cli.timeout) conf.timeout = cli.timeout; - if (cli.forksize != null) conf.forksize = cli.forksize; + // Declare and update proxiesConf + if (cli.proxies.proxySocks || cli.proxies.proxyTor || cli.proxies.reachingClearEp || cli.proxies.forceTor || cli.proxies.rmProxies) { + conf.proxiesConf = new ProxiesConf() + if (cli.proxies.proxySocks) conf.proxiesConf.proxySocksAddress = cli.proxies.proxySocks; + if (cli.proxies.proxyTor) conf.proxiesConf.proxyTorAddress = cli.proxies.proxyTor; + if (cli.proxies.reachingClearEp) { + switch (cli.proxies.reachingClearEp) { + case 'tor': conf.proxiesConf.reachingClearEp = 'tor'; break; + case 'none': conf.proxiesConf.reachingClearEp = 'none'; break; + } + } + if (cli.proxies.forceTor) conf.proxiesConf.forceTor = true + } + + // Update the rest of the conf + if (cli.currency) conf.currency = cli.currency; + if (cli.server.port) conf.port = cli.server.port; + if (cli.cpu) conf.cpu = Math.max(0.01, Math.min(1.0, cli.cpu)); + if (cli.prefix) conf.prefix = Math.max(Constants.MIN_PEER_ID, Math.min(Constants.MAX_PEER_ID, cli.prefix)); + if (cli.logs.http) conf.httplogs = true; + if (cli.logs.nohttp) conf.httplogs = false; + if (cli.isolate) conf.isolate = cli.isolate; + if (cli.timeout) conf.timeout = cli.timeout; + if (cli.forksize != null) conf.forksize = cli.forksize; return conf; } diff --git a/package.json b/package.json index 524a17cfa687b9b29f3e0da2b9ac01553e6d770d..97649b31477ddaf8b6c30b6912a3e0c7182098f4 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "scryptb": "6.0.4", "seedrandom": "^2.4.3", "sha1": "1.1.1", + "socks-proxy-agent": "^3.0.1", "sqlite3": "3.1.4", "tail": "^1.2.1", "tweetnacl": "0.14.3", diff --git a/server.ts b/server.ts index 2f7fbc5c166fa0bc90875727e58b2d333c25716f..fff2e8accbe0ef6874ec7218559fdd0592bf3f65 100644 --- a/server.ts +++ b/server.ts @@ -24,6 +24,7 @@ import {PeerDTO} from "./app/lib/dto/PeerDTO" import {OtherConstants} from "./app/lib/other_constants" import {WS2PCluster} from "./app/modules/ws2p/lib/WS2PCluster" import {DBBlock} from "./app/lib/db/DBBlock" +import { ProxiesConf } from './app/lib/proxy'; export interface HookableServer { generatorGetJoinData: (...args:any[]) => Promise<any> @@ -148,6 +149,7 @@ export class Server extends stream.Duplex implements HookableServer { logger.debug('Loading conf...'); this.conf = await this.dal.loadConf(this.overrideConf, useDefaultConf) // Default values + this.conf.proxiesConf = this.conf.proxiesConf === undefined ? new ProxiesConf() : this.conf.proxiesConf this.conf.remoteipv6 = this.conf.remoteipv6 === undefined ? this.conf.ipv6 : this.conf.remoteipv6 this.conf.remoteport = this.conf.remoteport === undefined ? this.conf.port : this.conf.remoteport this.conf.c = this.conf.c === undefined ? constants.CONTRACT.DEFAULT.C : this.conf.c diff --git a/test/fast/proxies.ts b/test/fast/proxies.ts new file mode 100644 index 0000000000000000000000000000000000000000..73377bf219f4d62120022e781d1dc160a9088671 --- /dev/null +++ b/test/fast/proxies.ts @@ -0,0 +1,98 @@ +import * as assert from 'assert' +import { ProxiesConf } from '../../app/lib/proxy'; + +describe("Proxies Conf", function() { + + // First conf : do not use any sock proxy + let proxiesConf1 = new ProxiesConf() + + // Second conf : use tor only to reach ".onion" endpoints + let proxiesConf2 = new ProxiesConf() + proxiesConf2.proxyTorAddress = "127.0.0.1:9050" + + // Third conf : always use tor + let proxiesConf3 = new ProxiesConf() + proxiesConf3.proxyTorAddress = "127.0.0.1:9050" + proxiesConf3.reachingClearEp = 'tor' + + // Fourth conf : use classical socks proxy + let proxiesConf4 = new ProxiesConf() + proxiesConf4.proxySocksAddress = "127.0.0.1:8888" + + // Fifth conf : use classical socks proxy + use tor proxy only to reach ".onion" endpoints + let proxiesConf5 = new ProxiesConf() + proxiesConf5.proxySocksAddress = "127.0.0.1:8888" + proxiesConf5.proxyTorAddress = "127.0.0.1:9050" + + // Sixth conf : always use tor and contact only tor endpoints + let proxiesConf6 = new ProxiesConf() + proxiesConf6.proxyTorAddress = "127.0.0.1:9050" + proxiesConf6.reachingClearEp = 'none' + + // Seventh conf : force duniter to contact endpoint tor (if user redirect the traffic to tor himself) + let proxiesConf7 = new ProxiesConf() + proxiesConf7.forceTor = true; + + it('should do not use any sock proxy', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf1), true) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf1), false) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf1), undefined) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf1), undefined) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf1), undefined) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf1), undefined) + }) + + it('should use tor proxy only to reach ".onion" endpoints', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf2), true) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf2), true) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf2), proxiesConf2.proxyTorAddress) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf2), undefined) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf2), proxiesConf2.proxyTorAddress) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf2), undefined) + }) + + it('should always use tor proxy', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf3), true) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf3), true) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf3), proxiesConf3.proxyTorAddress) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf3), proxiesConf3.proxyTorAddress) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf3), proxiesConf3.proxyTorAddress) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf3), proxiesConf3.proxyTorAddress) + }) + + it('should always use classical socks proxy', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf4), true) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf4), false) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf4), undefined) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf4), proxiesConf4.proxySocksAddress) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf4), undefined) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf4), proxiesConf4.proxySocksAddress) + }) + + it('should use tor proxy for ".onion" endpoints and classical socks proxy for everyone else', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf5), true) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf5), true) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf5), proxiesConf5.proxyTorAddress) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf5), proxiesConf5.proxySocksAddress) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf5), proxiesConf5.proxyTorAddress) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf5), proxiesConf5.proxySocksAddress) + }) + + it('should always use tor proxy and contact only tor endpoints', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf6), false) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf6), true) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf6), proxiesConf6.proxyTorAddress) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf6), undefined) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf6), proxiesConf6.proxyTorAddress) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf6), undefined) + }) + + it('should never use proxy and contact tor endpoints (user redirect the traffic to tor himself)', () => { + assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf7), true) + assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf7), true) + assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf7), undefined) + assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf7), undefined) + assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf7), undefined) + assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf7), undefined) + }) +}); diff --git a/test/integration/forwarding.js b/test/integration/forwarding.js index b550a400b3e7317832594088833bd0ed6d70f348..ba392a69af5d2b461920c09622ec5d2558d34215 100644 --- a/test/integration/forwarding.js +++ b/test/integration/forwarding.js @@ -19,7 +19,7 @@ describe("Forwarding", function() { describe("Nodes", function() { - const common = { currency: 'bb', nobma: false, ws2p: { upnp: false }, ipv4: '127.0.0.1', remoteipv4: '127.0.0.1', rootoffset: 0, sigQty: 1 }; + const common = { currency: 'bb', nobma: false, bmaWithCrawler:true, ws2p: { upnp: false }, ipv4: '127.0.0.1', remoteipv4: '127.0.0.1', rootoffset: 0, sigQty: 1 }; const node1 = node('db_1', _({ upnp: false, httplogs: false, port: 9600, remoteport: 9600, pair: { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'} }).extend(common)); const node2 = node('db_2', _({ upnp: false, httplogs: false, port: 9601, remoteport: 9601, pair: { pub: 'G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU', sec: '58LDg8QLmF5pv6Dn9h7X4yFKfMTdP8fdAiWVcyDoTRJu454fwRihCLULH4MW37zncsg4ruoTGJPZneWk22QmG1w4'} }).extend(common)); diff --git a/test/integration/network.js b/test/integration/network.js index 32c2fae9e9de7dfc8bcc710cf0d8362590c5564a..88a4afa84aaaecb944945bb20420712285d17b31 100644 --- a/test/integration/network.js +++ b/test/integration/network.js @@ -11,6 +11,7 @@ const expectAnswer = httpTest.expectAnswer; const MEMORY_MODE = true; const commonConf = { + bmaWithCrawler: true, ipv4: '127.0.0.1', remoteipv4: '127.0.0.1', currency: 'bb', diff --git a/test/integration/peerings.js b/test/integration/peerings.js index b9a7296136088877798cc6cabbfbc1394f39cc0f..da7f16c6e538c29d12a5e42e198e2f99d6e0c49c 100644 --- a/test/integration/peerings.js +++ b/test/integration/peerings.js @@ -22,6 +22,7 @@ const expectJSON = httpTest.expectJSON; const MEMORY_MODE = true; const commonConf = { + bmaWithCrawler: true, ipv4: '127.0.0.1', remoteipv4: '127.0.0.1', currency: 'bb', diff --git a/test/integration/start_generate_blocks.js b/test/integration/start_generate_blocks.js index e700d38d5f0160607678a185eb2c5b4c72955cb3..c71bd8c808a9f997afed7a64d7f4861a6ac5e3c3 100644 --- a/test/integration/start_generate_blocks.js +++ b/test/integration/start_generate_blocks.js @@ -19,6 +19,7 @@ const expectJSON = httpTest.expectJSON; const MEMORY_MODE = true; const commonConf = { + bmaWithCrawler: true, ipv4: '127.0.0.1', remoteipv4: '127.0.0.1', currency: 'bb', diff --git a/test/integration/tools/toolbox.ts b/test/integration/tools/toolbox.ts index fcf1e9670666bef7ca0b48139c49b1bbfc5d07bb..cbbe1d156eeff3f849dbfa850986bb6b96f83a88 100644 --- a/test/integration/tools/toolbox.ts +++ b/test/integration/tools/toolbox.ts @@ -170,7 +170,7 @@ export const fakeSyncServer = async (readBlocksMethod:any, readParticularBlockMe processRequest: () => { /* Does nothing */ } }; - const fakeServer = await Network.createServersAndListen("Fake Duniter Server", new Server("", true, {}), [{ + const fakeServer = await Network.createServersAndListen("Fake Duniter Server", new Server("", true, ConfDTO.mock()), [{ ip: host, port: port }], NO_HTTP_LOGS, logger, NO_STATIC_PATH, (app:any, httpMethods:any) => { @@ -226,6 +226,7 @@ export const NewTestingServer = (conf:any) => { const port = conf.port ||Â PORT++ const commonConf = { nobma: false, + bmaWithCrawler: true, port: port, ipv4: host, remoteipv4: host, @@ -696,6 +697,7 @@ export const simpleWS2PNetwork: (s1: TestingServer, s2: TestingServer) => Promis export function simpleTestingConf(now = 1500000000, pair:{ pub:string, sec:string }): any { return { + bmaWithCrawler: true, pair, nbCores: 1, udTime0: now, diff --git a/test/integration/ws2p_connection.ts b/test/integration/ws2p_connection.ts index 894c2ac29f61545f410ac88813d05dd84f208808..c84d9a42ecc109cd4dd0033d27d4229cd4e15671 100644 --- a/test/integration/ws2p_connection.ts +++ b/test/integration/ws2p_connection.ts @@ -95,7 +95,7 @@ describe('WS2P', () => { it('should refuse the connection if the server does not answer', async () => { const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP') - const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PPubkeyRemoteAuth(gtest, keypair), { + const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PPubkeyRemoteAuth(gtest, keypair), undefined, { connectionTimeout: 100, requestTimeout: 100 }) @@ -104,7 +104,7 @@ describe('WS2P', () => { it('should refuse the connection if the server answers with a wrong signature', async () => { const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP') - const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PPubkeyRemoteAuth(gtest, keypair), { + const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PPubkeyRemoteAuth(gtest, keypair), undefined, { connectionTimeout: 100, requestTimeout: 100 }) @@ -113,7 +113,7 @@ describe('WS2P', () => { it('should refuse the connection if the server refuses our signature', async () => { const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP') - const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PPubkeyRemoteAuth(gtest, keypair), { + const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PPubkeyRemoteAuth(gtest, keypair), undefined, { connectionTimeout: 100, requestTimeout: 100 }) @@ -123,7 +123,7 @@ describe('WS2P', () => { it('should accept the connection if the server answers with a good signature', async () => { const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP') - const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PNoRemoteAuth(), { + const ws2p = WS2PConnection.newConnectionToAddress('ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair), new WS2PNoRemoteAuth(), undefined, { connectionTimeout: 1000, requestTimeout: 1000 })