Mise à jour de GitLab prévue ce samedi 23 octobre 2021 à partir de 9h00 CET

Unverified Commit 4aa876be authored by Éloïs's avatar Éloïs Committed by GitHub
Browse files

Merge pull request #1178 from duniter/proxy

Add WS2PTOR features
parents 07c76637 b3100e4b
......@@ -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
......@@ -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')
......
......@@ -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),
......
......@@ -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,
......
......@@ -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)
......
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
......@@ -136,6 +139,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
......@@ -156,7 +161,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() {
......
......@@ -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]
}
}
}
}
......
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
......@@ -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) {
......
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,
......
......@@ -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',
......
......@@ -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([
......
......@@ -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()
}
......
"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"
......@@ -146,7 +147,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)
}
}
......@@ -248,22 +249,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 ''
......
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
......
......@@ -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')
......@@ -291,10 +292,11 @@ 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()
const preferedNodes = (this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes) ? this.server.conf.ws2p.preferedNodes:[]
return this.acceptPubkey(expectedPub, connectedPubkeys, () => this.clientsCount(), this.maxLevel1Size, preferedNodes, (this.server.conf.ws2p && this.server.conf.ws2p.preferedOnly) || false, ws2pEndpointUUID)
......@@ -302,7 +304,7 @@ export class WS2PCluster {
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: {
......@@ -313,7 +315,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 }
......@@ -321,7 +323,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
}
}
......@@ -332,21 +334,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
......@@ -373,7 +399,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()
......@@ -440,13 +466,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 }
}
......@@ -628,7 +655,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);
......
......@@ -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)) {