Add a feature flag for snode proxy

pull/709/head
Maxim Shishmarev 5 years ago
parent 5710c410c7
commit a195c98061

@ -0,0 +1,12 @@
{
"storageProfile": "swarm-testing",
"seedNodeList": [
{
"ip": "localhost",
"port": "22129"
}
],
"openDevTools": true,
"defaultPublicChatServer": "https://team-chat.lokinet.org/"
}

@ -0,0 +1,12 @@
{
"storageProfile": "swarm-testing2",
"seedNodeList": [
{
"ip": "localhost",
"port": "22129"
}
],
"openDevTools": true,
"defaultPublicChatServer": "https://team-chat.lokinet.org/"
}

@ -291,6 +291,26 @@ class LokiAppDotNetServerAPI {
}
}
async _sendToProxy(fetchOptions, endpoint, method) {
const rand_snode = await lokiSnodeAPI.getRandomSnodeAddress();
const url = `https://${rand_snode.ip}:${rand_snode.port}/file_proxy`;
const body = fetchOptions.body;
const firstHopOptions = {
method: 'POST',
body,
headers: {
"X-Loki-File-Server-Target": `/${endpoint}`,
"X-Loki-File-Server-Verb": method,
"X-Loki-File-Server-Headers": JSON.stringify(fetchOptions.headers),
}
}
return await nodeFetch(url, firstHopOptions);
}
// make a request to the server
async serverRequest(endpoint, options = {}) {
const {
@ -324,7 +344,13 @@ class LokiAppDotNetServerAPI {
fetchOptions.body = rawBody;
}
fetchOptions.headers = new Headers(headers);
if (window.lokiFeatureFlags.useSnodeProxy && this.baseServerUrl === "https://file.lokinet.org") {
log.info("Sending a proxy request to https://file.lokinet.org");
result = await this._sendToProxy({...fetchOptions, headers}, endpoint, method);
} else {
result = await nodeFetch(url, fetchOptions || undefined);
}
} catch (e) {
log.info(`e ${e}`);
return {

@ -3,7 +3,7 @@
/* global log, dcodeIO, window, callWorker, lokiP2pAPI, lokiSnodeAPI, textsecure */
const _ = require('lodash');
const { rpc } = require('./loki_rpc');
const { loki_rpc } = require('./loki_rpc');
const DEFAULT_CONNECTIONS = 3;
const MAX_ACCEPTABLE_FAILURES = 1;
@ -47,7 +47,7 @@ const trySendP2p = async (pubKey, data64, isPing, messageEventData) => {
return false;
}
try {
await rpc(p2pDetails.address, p2pDetails.port, 'store', {
await loki_rpc(p2pDetails.address, p2pDetails.port, 'store', {
data: data64,
});
lokiP2pAPI.setContactOnline(pubKey);
@ -213,6 +213,7 @@ class LokiMessageAPI {
const successfulSend = await this.sendToNode(
snode.ip,
snode.port,
snode,
params
);
if (successfulSend) {
@ -237,12 +238,12 @@ class LokiMessageAPI {
return false;
}
async sendToNode(address, port, params) {
async sendToNode(address, port, targetNode, params) {
let successiveFailures = 0;
while (successiveFailures < MAX_ACCEPTABLE_FAILURES) {
await sleepFor(successiveFailures * 500);
try {
const result = await rpc(`https://${address}`, port, 'store', params);
const result = await loki_rpc(`https://${address}`, port, 'store', params, {}, '/storage_rpc/v1', targetNode);
// Make sure we aren't doing too much PoW
const currentDifficulty = window.storage.get('PoWDifficulty', null);
@ -365,12 +366,14 @@ class LokiMessageAPI {
},
};
const result = await rpc(
const result = await loki_rpc(
`https://${nodeUrl}`,
nodeData.port,
'retrieve',
params,
options
options,
'/storage_rpc/v1',
nodeData
);
return result.messages || [];
}
@ -386,10 +389,10 @@ class LokiMessageAPI {
const lastHash = await window.Signal.Data.getLastHashBySnode(
nodes[i].address
);
this.ourSwarmNodes[nodes[i].address] = {
...nodes[i],
lastHash,
ip: nodes[i].ip,
port: nodes[i].port,
};
}

@ -1,4 +1,4 @@
/* global log, libloki, textsecure, getStoragePubKey */
/* global log, libloki, textsecure, getStoragePubKey, lokiSnodeAPI */
const nodeFetch = require('node-fetch');
const { parse } = require('url');
@ -21,8 +21,63 @@ const decryptResponse = async (response, address) => {
return {};
};
// TODO: Don't allow arbitrary URLs, only snodes and loki servers
const sendToProxy = async (options = {}, targetNode) => {
const rand_snode = await lokiSnodeAPI.getRandomSnodeAddress();
const url = `https://${rand_snode.ip}:${rand_snode.port}/proxy`;
log.info(`Proxy snode reqeust to ${targetNode.pubkey_ed25519} via ${rand_snode.pubkey_ed25519}`);
const sn_pub_key_hex = StringView.hexToArrayBuffer(targetNode.pubkey_x25519);
const my_keys = window.libloki.crypto.snodeCipher._ephemeralKeyPair;
const symmetricKey = libsignal.Curve.calculateAgreement(
sn_pub_key_hex,
my_keys.privKey
);
const textEncoder = new TextEncoder();
const body = JSON.stringify(options);
const plainText = textEncoder.encode(body);
const ivAndCiphertext = await window.libloki.crypto.DHEncrypt(symmetricKey, plainText);
const firstHopOptions = {
method: 'POST',
body: ivAndCiphertext,
headers: {
"X-Sender-Public-Key": StringView.arrayBufferToHex(my_keys.pubKey),
"X-Target-Snode-Key": targetNode.pubkey_ed25519,
}
}
const response = await nodeFetch(url, firstHopOptions);
const ciphertext = await response.text();
const ciphertextBuffer = dcodeIO.ByteBuffer.wrap(
ciphertext, 'base64'
).toArrayBuffer();
const plaintextBuffer = await window.libloki.crypto.DHDecrypt(symmetricKey, ciphertextBuffer);
const textDecoder = new TextDecoder();
const plaintext = textDecoder.decode(plaintextBuffer);
const json_res = JSON.parse(plaintext);
json_res.json = () => {
return JSON.parse(json_res.body);
}
return json_res;
}
// A small wrapper around node-fetch which deserializes response
const fetch = async (url, options = {}) => {
const loki_fetch = async (url, options = {}, targetNode = null) => {
const timeout = options.timeout || 10000;
const method = options.method || 'GET';
@ -47,12 +102,18 @@ const fetch = async (url, options = {}) => {
}
}
const fetchOptions = {
...options, timeout, method,
};
try {
const response = await nodeFetch(url, {
...options,
timeout,
method,
});
if (window.lokiFeatureFlags.useSnodeProxy && targetNode) {
const result = await sendToProxy(fetchOptions, targetNode);
return result.json();
}
const response = await nodeFetch(url, fetchOptions);
let result;
// Wrong swarm
@ -107,13 +168,14 @@ const fetch = async (url, options = {}) => {
};
// Wrapper for a JSON RPC request
const rpc = (
const loki_rpc = (
address,
port,
method,
params,
options = {},
endpoint = endpointBase
endpoint = endpointBase,
targetNode,
) => {
const headers = options.headers || {};
const portString = port ? `:${port}` : '';
@ -144,9 +206,9 @@ const rpc = (
},
};
return fetch(url, fetchOptions);
return loki_fetch(url, fetchOptions, targetNode);
};
module.exports = {
rpc,
loki_rpc,
};

@ -4,7 +4,7 @@
const is = require('@sindresorhus/is');
const dns = require('dns');
const process = require('process');
const { rpc } = require('./loki_rpc');
const { loki_rpc } = require('./loki_rpc');
const natUpnp = require('nat-upnp');
const resolve4 = url =>
@ -94,6 +94,8 @@ class LokiSnodeAPI {
fields: {
public_ip: true,
storage_port: true,
pubkey_x25519: true,
pubkey_ed25519: true,
},
};
const seedNode = seedNodes.splice(
@ -101,7 +103,8 @@ class LokiSnodeAPI {
1
)[0];
try {
const result = await rpc(
const result = await loki_rpc(
`http://${seedNode.ip}`,
seedNode.port,
'get_n_service_nodes',
@ -116,6 +119,8 @@ class LokiSnodeAPI {
this.randomSnodePool = snodes.map(snode => ({
ip: snode.public_ip,
port: snode.storage_port,
pubkey_x25519: snode.pubkey_x25519,
pubkey_ed25519: snode.pubkey_ed25519,
}));
} catch (e) {
window.mixpanel.track('Seed Node Failed');
@ -191,11 +196,12 @@ class LokiSnodeAPI {
async getSwarmNodes(pubKey) {
// TODO: Hit multiple random nodes and merge lists?
const { ip, port } = await this.getRandomSnodeAddress();
const snode = await this.getRandomSnodeAddress();
try {
const result = await rpc(`https://${ip}`, port, 'get_snodes_for_pubkey', {
const result = await loki_rpc(`https://${snode.ip}`, snode.port, 'get_snodes_for_pubkey', {
pubKey,
});
}, {}, '/storage_rpc/v1', snode);
const snodes = result.snodes.filter(snode => snode.ip !== '0.0.0.0');
return snodes;
} catch (e) {

@ -17,6 +17,8 @@
"start-multi2": "NODE_APP_INSTANCE=2 electron .",
"start-prod": "NODE_ENV=production NODE_APP_INSTANCE=devprod LOKI_DEV=1 electron .",
"start-prod-multi": "NODE_ENV=production NODE_APP_INSTANCE=devprod1 LOKI_DEV=1 electron .",
"start-swarm-test": "NODE_ENV=swarm-testing NODE_APP_INSTANCE=test1 LOKI_DEV=1 electron .",
"start-swarm-test-2": "NODE_ENV=swarm-testing2 NODE_APP_INSTANCE=test2 LOKI_DEV=1 electron .",
"grunt": "grunt",
"icon-gen": "electron-icon-maker --input=images/icon_1024.png --output=./build",
"generate": "yarn icon-gen && yarn grunt",

@ -471,4 +471,5 @@ window.SMALL_GROUP_SIZE_LIMIT = 10;
window.lokiFeatureFlags = {
multiDeviceUnpairing: true,
privateGroupChats: false,
useSnodeProxy: false,
};

Loading…
Cancel
Save