Mini Tor implementation (“CLI only anonymous network”)

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












The goal of my project is to build a mini "anonymous" network (like Tor is) (command-line only) using Node.js and frameworks (socket.io / axios / express). A network where I can download files (HTML etc) anonymously. I have central server and nodes (clients). At start each client connects to central server (using socket.io) and server appends it to the list hosts. Each minute client asks server for latest list of alive nodes.



I came here because I did my best and I cannot see more bugs / errors / flaws. Also, I am not good at security and only on my way to learning deep.



I have some questions and I will be grateful if you advise how to improve my code:



  1. What can be improved and how?

  2. What about security?

  3. My node-chain isn't that good now. How I can make it longer (right now I have to set 1 minute timer and start nodes at different time but it is bad; in- fact that if some node will die in the middle, my code would be ruined, so I am looking for real-time long (at least 3 nodes) chain)

  4. Right now I am receiving the answer as HTML in my console. Is it possible to do so that if node localhost:4500 sends request I can see the answer as website at localhost:4500?

Maybe my central server should create graf of clients where each client (node) will have 3 (strict) neighbor nodes, but how to implement that? (image visualisation below!)



Clients graf



My code is fully working and you can see it below with some rules that my project has.




URL (file) download request



  1. If the node initiates this query itself, it must keep in mind the id to recognize the answer later


  2. A network node initiating request sends a message to all known neighbors



  3. For the first time when receiving this message, the network node decides whether to download the file (probability x%, mine is 50% right now)



    1. If the file is not downloaded, the network node sends the message to all its neighbors, unless the neighbor IP matches the IP address of the transmitting node of the message


    2. When the file is downloaded, a file type message is initiated


    3. In both cases, the message id and the forwarding network node IP must be memorized



  4. Receiving a second message with the same id (or self-initiated) will be ignored


  5. The node initiating the request must take into consideration the possibility that the response is not guaranteed due to the unstable nature of the network


Return the contents of the file



  1. The network node initiating the request sends a message to the neighbor from whom they downloaded the same message with the same id


  2. If the previous step fails, the message will be sent to all neighbors



  3. For the first time when receiving this message, the network node decides if the file was its own (id parameter)



    1. If not, the network node sends the message to a neighbor who received the download request with the same id


    2. If the previous step failed (or no such neighbor is reported), the message will be sent to all neighbors, except if the neighbor IP matches the sender of the file in question with the IP address


    3. If the file was requested by the node, the query body is parsed from the status and, if any, the contents of the file



  4. Receiving a second message with the same id (or self-initiated) will be ignored



install socket.io, express, axios, request, body-parser, event-emitter



To test I am using POSTMAN and here is request example:



localhost:4500/download?id=763145322224615&url=https://google.com&port=4500


I am sending download request to node with port 4500, id is a random number which will be memorized by this node to recognize the answer later. url is file to be downloaded.



Client (at least two clients to get it work!)



node client.js 4500
node client.js 4501


Server



node server.js



Server



"use strict";

const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http);

var hosts = ;

app.get("/hosts", function(req, res)
res.send(hosts);
);

http.listen(3000, function()
console.log("listening on *:3000");
);

io.on("connection", function(socket)
const address = socket.handshake.address;
const ipAddress = address.substring(address.lastIndexOf(':') + 1, address.length);
const port = socket.handshake.query.port;
hosts.push(ip: ipAddress, port: port);

console.log(`A machine with $ipAddress:$port successfully connected!`);
socket.join("hosts");
console.log(`A machine successfully joined!`);
socket.emit("hosts", hosts);
// socket.broadcast.to("hosts").emit("hosts", hosts);

socket.on("disconnect", function() item.port !== port);
// socket.broadcast.to("hosts").emit("hosts", hosts);
);
);


Client



"use strict";

const io = require('socket.io-client');
const app = require('express')();
const http = require('http')
const server = http.Server(app);
const request = require('request');
const axios = require('axios');
const bodyParser = require('body-parser')
const EventEmitter = require('events');

class MyEmitter extends EventEmitter ;
const event = new MyEmitter();

app.use(bodyParser.urlencoded( extended: true));
app.use(bodyParser.json())

const states = "REQUEST": 1, "SEND": 2, "DOWNLOAD": 3, "FINISH": 4;
const errorCodes = "NOT_ACCEPTABLE": "NOT ACCEPTABLE", "OK": "OK";
const args = process.argv;

const myIpAddress = "127.0.0.1"
const myPort = args[2] || 4500;

const serverAddress = "127.0.0.1";
const serverPort = 3000;
const socket = io(`http://$serverAddress:$serverPort/`,
query: `port=$myPort`
);

const laziness = 0.5;

var hosts = ;
var requests = ;

server.listen(myPort, myIpAddress, () =>
console.log(`Listening $myIpAddress:$myPort`);
);

socket.on("hosts", data =>
//console.log("Received data: ", data)
hosts = data;
);

app.get("/download", (req, res) =>
const id = req.query.id;
const url = req.query.url;
const ip = req.connection.remoteAddress;
const port = req.query.port;

if (id == null );

app.get('/check', (req, res) =>
let id = req.query.id;

res.sendStatus(requests[id] != null && requests[id].state != states.REQUEST ? 200 : 204);
);

app.post('/file', (req, res) => requests[id].state === states.FINISH)
res.status(200).send(errorCodes.NOT_ACCEPTABLE);
return;


if (requests[id].host)
requests[id].state = states.FINISH;
console.log('StatusCode: ', req.body.status);
console.log('Mime-type: ', req.body['mime-type']);
console.log("Content: ", Buffer.from(req.body.content, "base64").toString("ascii"));
else
console.log('Sending back to host');
event.emit("sendBackToHost", id, req.body);


res.sendStatus(200);
);

event.on('check', (id) =>
axios.get(`http://$requests[id].ip:$requests[id].port/check?id=$id`)
.then(res =>
if (res.status === 204)
requests[id].url = encodeUrl(requests[id].url);
requests[id].host = true;
event.emit('sendNext', id);
else if (res.status === 200)
event.emit('downloadOrSend', id);
)
.catch(err =>
console.error('Error with checking', err);
)
);

event.on('downloadOrSend', (id) =>
if (isDownloadState())
axios.get(decodeUrl(requests[id].url))
.then(res =>
const body =
content: Buffer.from(res.data).toString('base64'),
status: res.status,
"mime-type": res.headers["content-type"]
;

requests[id].state = states.DOWNLOAD;
event.emit('sendBackToHost', id, body)
)
.catch(err =>
console.error('Error while downloading file', err.response);
)
return;

event.emit('sendNext', id);
)

event.on('sendBackToHost', (id, body) =>
axios.post(`http://$requests[id].ip:$requests[id].port/file?id=$id`, body)
.catch(err =>
console.error("Error while sending back to host", err.response);
event.emit('sendFileBackToAll', id, body);
);
);

event.on('sendNext', (id) =>
requests[id].state = states.SEND;
for (let h of hosts)
if (h.ip == requests[id].ip && h.port == requests[id].port) continue;
if (h.ip == myIpAddress && h.port == myPort) continue;

axios.get(`http://$h.ip:$h.port/download?id=$id&url=$requests[id].url&port=$myPort`)
.catch(err =>
console.error('Error while sending requests to neighbours', err.response);
);

);

event.on('sendFileBackToAll', (id, body) =>
for (let h of hosts)
if (h.ip == myIpAddress && h.port == myPort) continue;

axios.post(`http://$h.ip:$h.port/file?id=$id`, body)
.catch(err =>
console.error("Error while sending back to all hosts", err.response);
);

);

function isDownloadState()
return Math.random() >= laziness;


function encodeUrl(url)
return (decodeURIComponent(url) === url) ? encodeURIComponent(url) : url;


function decodeUrl(url)
return (decodeURIComponent(url) === url) ? url : decodeURIComponent(url);


setInterval(() =>
axios.get(`http://$serverAddress:$serverPort/hosts`)
.then(res =>
hosts = res.data;
)
.catch(err =>
console.error("Error while fetching data from server", err.response);
)
, 60000)






share|improve this question

























    up vote
    3
    down vote

    favorite












    The goal of my project is to build a mini "anonymous" network (like Tor is) (command-line only) using Node.js and frameworks (socket.io / axios / express). A network where I can download files (HTML etc) anonymously. I have central server and nodes (clients). At start each client connects to central server (using socket.io) and server appends it to the list hosts. Each minute client asks server for latest list of alive nodes.



    I came here because I did my best and I cannot see more bugs / errors / flaws. Also, I am not good at security and only on my way to learning deep.



    I have some questions and I will be grateful if you advise how to improve my code:



    1. What can be improved and how?

    2. What about security?

    3. My node-chain isn't that good now. How I can make it longer (right now I have to set 1 minute timer and start nodes at different time but it is bad; in- fact that if some node will die in the middle, my code would be ruined, so I am looking for real-time long (at least 3 nodes) chain)

    4. Right now I am receiving the answer as HTML in my console. Is it possible to do so that if node localhost:4500 sends request I can see the answer as website at localhost:4500?

    Maybe my central server should create graf of clients where each client (node) will have 3 (strict) neighbor nodes, but how to implement that? (image visualisation below!)



    Clients graf



    My code is fully working and you can see it below with some rules that my project has.




    URL (file) download request



    1. If the node initiates this query itself, it must keep in mind the id to recognize the answer later


    2. A network node initiating request sends a message to all known neighbors



    3. For the first time when receiving this message, the network node decides whether to download the file (probability x%, mine is 50% right now)



      1. If the file is not downloaded, the network node sends the message to all its neighbors, unless the neighbor IP matches the IP address of the transmitting node of the message


      2. When the file is downloaded, a file type message is initiated


      3. In both cases, the message id and the forwarding network node IP must be memorized



    4. Receiving a second message with the same id (or self-initiated) will be ignored


    5. The node initiating the request must take into consideration the possibility that the response is not guaranteed due to the unstable nature of the network


    Return the contents of the file



    1. The network node initiating the request sends a message to the neighbor from whom they downloaded the same message with the same id


    2. If the previous step fails, the message will be sent to all neighbors



    3. For the first time when receiving this message, the network node decides if the file was its own (id parameter)



      1. If not, the network node sends the message to a neighbor who received the download request with the same id


      2. If the previous step failed (or no such neighbor is reported), the message will be sent to all neighbors, except if the neighbor IP matches the sender of the file in question with the IP address


      3. If the file was requested by the node, the query body is parsed from the status and, if any, the contents of the file



    4. Receiving a second message with the same id (or self-initiated) will be ignored



    install socket.io, express, axios, request, body-parser, event-emitter



    To test I am using POSTMAN and here is request example:



    localhost:4500/download?id=763145322224615&url=https://google.com&port=4500


    I am sending download request to node with port 4500, id is a random number which will be memorized by this node to recognize the answer later. url is file to be downloaded.



    Client (at least two clients to get it work!)



    node client.js 4500
    node client.js 4501


    Server



    node server.js



    Server



    "use strict";

    const app = require("express")();
    const http = require("http").Server(app);
    const io = require("socket.io")(http);

    var hosts = ;

    app.get("/hosts", function(req, res)
    res.send(hosts);
    );

    http.listen(3000, function()
    console.log("listening on *:3000");
    );

    io.on("connection", function(socket)
    const address = socket.handshake.address;
    const ipAddress = address.substring(address.lastIndexOf(':') + 1, address.length);
    const port = socket.handshake.query.port;
    hosts.push(ip: ipAddress, port: port);

    console.log(`A machine with $ipAddress:$port successfully connected!`);
    socket.join("hosts");
    console.log(`A machine successfully joined!`);
    socket.emit("hosts", hosts);
    // socket.broadcast.to("hosts").emit("hosts", hosts);

    socket.on("disconnect", function() item.port !== port);
    // socket.broadcast.to("hosts").emit("hosts", hosts);
    );
    );


    Client



    "use strict";

    const io = require('socket.io-client');
    const app = require('express')();
    const http = require('http')
    const server = http.Server(app);
    const request = require('request');
    const axios = require('axios');
    const bodyParser = require('body-parser')
    const EventEmitter = require('events');

    class MyEmitter extends EventEmitter ;
    const event = new MyEmitter();

    app.use(bodyParser.urlencoded( extended: true));
    app.use(bodyParser.json())

    const states = "REQUEST": 1, "SEND": 2, "DOWNLOAD": 3, "FINISH": 4;
    const errorCodes = "NOT_ACCEPTABLE": "NOT ACCEPTABLE", "OK": "OK";
    const args = process.argv;

    const myIpAddress = "127.0.0.1"
    const myPort = args[2] || 4500;

    const serverAddress = "127.0.0.1";
    const serverPort = 3000;
    const socket = io(`http://$serverAddress:$serverPort/`,
    query: `port=$myPort`
    );

    const laziness = 0.5;

    var hosts = ;
    var requests = ;

    server.listen(myPort, myIpAddress, () =>
    console.log(`Listening $myIpAddress:$myPort`);
    );

    socket.on("hosts", data =>
    //console.log("Received data: ", data)
    hosts = data;
    );

    app.get("/download", (req, res) =>
    const id = req.query.id;
    const url = req.query.url;
    const ip = req.connection.remoteAddress;
    const port = req.query.port;

    if (id == null );

    app.get('/check', (req, res) =>
    let id = req.query.id;

    res.sendStatus(requests[id] != null && requests[id].state != states.REQUEST ? 200 : 204);
    );

    app.post('/file', (req, res) => requests[id].state === states.FINISH)
    res.status(200).send(errorCodes.NOT_ACCEPTABLE);
    return;


    if (requests[id].host)
    requests[id].state = states.FINISH;
    console.log('StatusCode: ', req.body.status);
    console.log('Mime-type: ', req.body['mime-type']);
    console.log("Content: ", Buffer.from(req.body.content, "base64").toString("ascii"));
    else
    console.log('Sending back to host');
    event.emit("sendBackToHost", id, req.body);


    res.sendStatus(200);
    );

    event.on('check', (id) =>
    axios.get(`http://$requests[id].ip:$requests[id].port/check?id=$id`)
    .then(res =>
    if (res.status === 204)
    requests[id].url = encodeUrl(requests[id].url);
    requests[id].host = true;
    event.emit('sendNext', id);
    else if (res.status === 200)
    event.emit('downloadOrSend', id);
    )
    .catch(err =>
    console.error('Error with checking', err);
    )
    );

    event.on('downloadOrSend', (id) =>
    if (isDownloadState())
    axios.get(decodeUrl(requests[id].url))
    .then(res =>
    const body =
    content: Buffer.from(res.data).toString('base64'),
    status: res.status,
    "mime-type": res.headers["content-type"]
    ;

    requests[id].state = states.DOWNLOAD;
    event.emit('sendBackToHost', id, body)
    )
    .catch(err =>
    console.error('Error while downloading file', err.response);
    )
    return;

    event.emit('sendNext', id);
    )

    event.on('sendBackToHost', (id, body) =>
    axios.post(`http://$requests[id].ip:$requests[id].port/file?id=$id`, body)
    .catch(err =>
    console.error("Error while sending back to host", err.response);
    event.emit('sendFileBackToAll', id, body);
    );
    );

    event.on('sendNext', (id) =>
    requests[id].state = states.SEND;
    for (let h of hosts)
    if (h.ip == requests[id].ip && h.port == requests[id].port) continue;
    if (h.ip == myIpAddress && h.port == myPort) continue;

    axios.get(`http://$h.ip:$h.port/download?id=$id&url=$requests[id].url&port=$myPort`)
    .catch(err =>
    console.error('Error while sending requests to neighbours', err.response);
    );

    );

    event.on('sendFileBackToAll', (id, body) =>
    for (let h of hosts)
    if (h.ip == myIpAddress && h.port == myPort) continue;

    axios.post(`http://$h.ip:$h.port/file?id=$id`, body)
    .catch(err =>
    console.error("Error while sending back to all hosts", err.response);
    );

    );

    function isDownloadState()
    return Math.random() >= laziness;


    function encodeUrl(url)
    return (decodeURIComponent(url) === url) ? encodeURIComponent(url) : url;


    function decodeUrl(url)
    return (decodeURIComponent(url) === url) ? url : decodeURIComponent(url);


    setInterval(() =>
    axios.get(`http://$serverAddress:$serverPort/hosts`)
    .then(res =>
    hosts = res.data;
    )
    .catch(err =>
    console.error("Error while fetching data from server", err.response);
    )
    , 60000)






    share|improve this question





















      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      The goal of my project is to build a mini "anonymous" network (like Tor is) (command-line only) using Node.js and frameworks (socket.io / axios / express). A network where I can download files (HTML etc) anonymously. I have central server and nodes (clients). At start each client connects to central server (using socket.io) and server appends it to the list hosts. Each minute client asks server for latest list of alive nodes.



      I came here because I did my best and I cannot see more bugs / errors / flaws. Also, I am not good at security and only on my way to learning deep.



      I have some questions and I will be grateful if you advise how to improve my code:



      1. What can be improved and how?

      2. What about security?

      3. My node-chain isn't that good now. How I can make it longer (right now I have to set 1 minute timer and start nodes at different time but it is bad; in- fact that if some node will die in the middle, my code would be ruined, so I am looking for real-time long (at least 3 nodes) chain)

      4. Right now I am receiving the answer as HTML in my console. Is it possible to do so that if node localhost:4500 sends request I can see the answer as website at localhost:4500?

      Maybe my central server should create graf of clients where each client (node) will have 3 (strict) neighbor nodes, but how to implement that? (image visualisation below!)



      Clients graf



      My code is fully working and you can see it below with some rules that my project has.




      URL (file) download request



      1. If the node initiates this query itself, it must keep in mind the id to recognize the answer later


      2. A network node initiating request sends a message to all known neighbors



      3. For the first time when receiving this message, the network node decides whether to download the file (probability x%, mine is 50% right now)



        1. If the file is not downloaded, the network node sends the message to all its neighbors, unless the neighbor IP matches the IP address of the transmitting node of the message


        2. When the file is downloaded, a file type message is initiated


        3. In both cases, the message id and the forwarding network node IP must be memorized



      4. Receiving a second message with the same id (or self-initiated) will be ignored


      5. The node initiating the request must take into consideration the possibility that the response is not guaranteed due to the unstable nature of the network


      Return the contents of the file



      1. The network node initiating the request sends a message to the neighbor from whom they downloaded the same message with the same id


      2. If the previous step fails, the message will be sent to all neighbors



      3. For the first time when receiving this message, the network node decides if the file was its own (id parameter)



        1. If not, the network node sends the message to a neighbor who received the download request with the same id


        2. If the previous step failed (or no such neighbor is reported), the message will be sent to all neighbors, except if the neighbor IP matches the sender of the file in question with the IP address


        3. If the file was requested by the node, the query body is parsed from the status and, if any, the contents of the file



      4. Receiving a second message with the same id (or self-initiated) will be ignored



      install socket.io, express, axios, request, body-parser, event-emitter



      To test I am using POSTMAN and here is request example:



      localhost:4500/download?id=763145322224615&url=https://google.com&port=4500


      I am sending download request to node with port 4500, id is a random number which will be memorized by this node to recognize the answer later. url is file to be downloaded.



      Client (at least two clients to get it work!)



      node client.js 4500
      node client.js 4501


      Server



      node server.js



      Server



      "use strict";

      const app = require("express")();
      const http = require("http").Server(app);
      const io = require("socket.io")(http);

      var hosts = ;

      app.get("/hosts", function(req, res)
      res.send(hosts);
      );

      http.listen(3000, function()
      console.log("listening on *:3000");
      );

      io.on("connection", function(socket)
      const address = socket.handshake.address;
      const ipAddress = address.substring(address.lastIndexOf(':') + 1, address.length);
      const port = socket.handshake.query.port;
      hosts.push(ip: ipAddress, port: port);

      console.log(`A machine with $ipAddress:$port successfully connected!`);
      socket.join("hosts");
      console.log(`A machine successfully joined!`);
      socket.emit("hosts", hosts);
      // socket.broadcast.to("hosts").emit("hosts", hosts);

      socket.on("disconnect", function() item.port !== port);
      // socket.broadcast.to("hosts").emit("hosts", hosts);
      );
      );


      Client



      "use strict";

      const io = require('socket.io-client');
      const app = require('express')();
      const http = require('http')
      const server = http.Server(app);
      const request = require('request');
      const axios = require('axios');
      const bodyParser = require('body-parser')
      const EventEmitter = require('events');

      class MyEmitter extends EventEmitter ;
      const event = new MyEmitter();

      app.use(bodyParser.urlencoded( extended: true));
      app.use(bodyParser.json())

      const states = "REQUEST": 1, "SEND": 2, "DOWNLOAD": 3, "FINISH": 4;
      const errorCodes = "NOT_ACCEPTABLE": "NOT ACCEPTABLE", "OK": "OK";
      const args = process.argv;

      const myIpAddress = "127.0.0.1"
      const myPort = args[2] || 4500;

      const serverAddress = "127.0.0.1";
      const serverPort = 3000;
      const socket = io(`http://$serverAddress:$serverPort/`,
      query: `port=$myPort`
      );

      const laziness = 0.5;

      var hosts = ;
      var requests = ;

      server.listen(myPort, myIpAddress, () =>
      console.log(`Listening $myIpAddress:$myPort`);
      );

      socket.on("hosts", data =>
      //console.log("Received data: ", data)
      hosts = data;
      );

      app.get("/download", (req, res) =>
      const id = req.query.id;
      const url = req.query.url;
      const ip = req.connection.remoteAddress;
      const port = req.query.port;

      if (id == null );

      app.get('/check', (req, res) =>
      let id = req.query.id;

      res.sendStatus(requests[id] != null && requests[id].state != states.REQUEST ? 200 : 204);
      );

      app.post('/file', (req, res) => requests[id].state === states.FINISH)
      res.status(200).send(errorCodes.NOT_ACCEPTABLE);
      return;


      if (requests[id].host)
      requests[id].state = states.FINISH;
      console.log('StatusCode: ', req.body.status);
      console.log('Mime-type: ', req.body['mime-type']);
      console.log("Content: ", Buffer.from(req.body.content, "base64").toString("ascii"));
      else
      console.log('Sending back to host');
      event.emit("sendBackToHost", id, req.body);


      res.sendStatus(200);
      );

      event.on('check', (id) =>
      axios.get(`http://$requests[id].ip:$requests[id].port/check?id=$id`)
      .then(res =>
      if (res.status === 204)
      requests[id].url = encodeUrl(requests[id].url);
      requests[id].host = true;
      event.emit('sendNext', id);
      else if (res.status === 200)
      event.emit('downloadOrSend', id);
      )
      .catch(err =>
      console.error('Error with checking', err);
      )
      );

      event.on('downloadOrSend', (id) =>
      if (isDownloadState())
      axios.get(decodeUrl(requests[id].url))
      .then(res =>
      const body =
      content: Buffer.from(res.data).toString('base64'),
      status: res.status,
      "mime-type": res.headers["content-type"]
      ;

      requests[id].state = states.DOWNLOAD;
      event.emit('sendBackToHost', id, body)
      )
      .catch(err =>
      console.error('Error while downloading file', err.response);
      )
      return;

      event.emit('sendNext', id);
      )

      event.on('sendBackToHost', (id, body) =>
      axios.post(`http://$requests[id].ip:$requests[id].port/file?id=$id`, body)
      .catch(err =>
      console.error("Error while sending back to host", err.response);
      event.emit('sendFileBackToAll', id, body);
      );
      );

      event.on('sendNext', (id) =>
      requests[id].state = states.SEND;
      for (let h of hosts)
      if (h.ip == requests[id].ip && h.port == requests[id].port) continue;
      if (h.ip == myIpAddress && h.port == myPort) continue;

      axios.get(`http://$h.ip:$h.port/download?id=$id&url=$requests[id].url&port=$myPort`)
      .catch(err =>
      console.error('Error while sending requests to neighbours', err.response);
      );

      );

      event.on('sendFileBackToAll', (id, body) =>
      for (let h of hosts)
      if (h.ip == myIpAddress && h.port == myPort) continue;

      axios.post(`http://$h.ip:$h.port/file?id=$id`, body)
      .catch(err =>
      console.error("Error while sending back to all hosts", err.response);
      );

      );

      function isDownloadState()
      return Math.random() >= laziness;


      function encodeUrl(url)
      return (decodeURIComponent(url) === url) ? encodeURIComponent(url) : url;


      function decodeUrl(url)
      return (decodeURIComponent(url) === url) ? url : decodeURIComponent(url);


      setInterval(() =>
      axios.get(`http://$serverAddress:$serverPort/hosts`)
      .then(res =>
      hosts = res.data;
      )
      .catch(err =>
      console.error("Error while fetching data from server", err.response);
      )
      , 60000)






      share|improve this question











      The goal of my project is to build a mini "anonymous" network (like Tor is) (command-line only) using Node.js and frameworks (socket.io / axios / express). A network where I can download files (HTML etc) anonymously. I have central server and nodes (clients). At start each client connects to central server (using socket.io) and server appends it to the list hosts. Each minute client asks server for latest list of alive nodes.



      I came here because I did my best and I cannot see more bugs / errors / flaws. Also, I am not good at security and only on my way to learning deep.



      I have some questions and I will be grateful if you advise how to improve my code:



      1. What can be improved and how?

      2. What about security?

      3. My node-chain isn't that good now. How I can make it longer (right now I have to set 1 minute timer and start nodes at different time but it is bad; in- fact that if some node will die in the middle, my code would be ruined, so I am looking for real-time long (at least 3 nodes) chain)

      4. Right now I am receiving the answer as HTML in my console. Is it possible to do so that if node localhost:4500 sends request I can see the answer as website at localhost:4500?

      Maybe my central server should create graf of clients where each client (node) will have 3 (strict) neighbor nodes, but how to implement that? (image visualisation below!)



      Clients graf



      My code is fully working and you can see it below with some rules that my project has.




      URL (file) download request



      1. If the node initiates this query itself, it must keep in mind the id to recognize the answer later


      2. A network node initiating request sends a message to all known neighbors



      3. For the first time when receiving this message, the network node decides whether to download the file (probability x%, mine is 50% right now)



        1. If the file is not downloaded, the network node sends the message to all its neighbors, unless the neighbor IP matches the IP address of the transmitting node of the message


        2. When the file is downloaded, a file type message is initiated


        3. In both cases, the message id and the forwarding network node IP must be memorized



      4. Receiving a second message with the same id (or self-initiated) will be ignored


      5. The node initiating the request must take into consideration the possibility that the response is not guaranteed due to the unstable nature of the network


      Return the contents of the file



      1. The network node initiating the request sends a message to the neighbor from whom they downloaded the same message with the same id


      2. If the previous step fails, the message will be sent to all neighbors



      3. For the first time when receiving this message, the network node decides if the file was its own (id parameter)



        1. If not, the network node sends the message to a neighbor who received the download request with the same id


        2. If the previous step failed (or no such neighbor is reported), the message will be sent to all neighbors, except if the neighbor IP matches the sender of the file in question with the IP address


        3. If the file was requested by the node, the query body is parsed from the status and, if any, the contents of the file



      4. Receiving a second message with the same id (or self-initiated) will be ignored



      install socket.io, express, axios, request, body-parser, event-emitter



      To test I am using POSTMAN and here is request example:



      localhost:4500/download?id=763145322224615&url=https://google.com&port=4500


      I am sending download request to node with port 4500, id is a random number which will be memorized by this node to recognize the answer later. url is file to be downloaded.



      Client (at least two clients to get it work!)



      node client.js 4500
      node client.js 4501


      Server



      node server.js



      Server



      "use strict";

      const app = require("express")();
      const http = require("http").Server(app);
      const io = require("socket.io")(http);

      var hosts = ;

      app.get("/hosts", function(req, res)
      res.send(hosts);
      );

      http.listen(3000, function()
      console.log("listening on *:3000");
      );

      io.on("connection", function(socket)
      const address = socket.handshake.address;
      const ipAddress = address.substring(address.lastIndexOf(':') + 1, address.length);
      const port = socket.handshake.query.port;
      hosts.push(ip: ipAddress, port: port);

      console.log(`A machine with $ipAddress:$port successfully connected!`);
      socket.join("hosts");
      console.log(`A machine successfully joined!`);
      socket.emit("hosts", hosts);
      // socket.broadcast.to("hosts").emit("hosts", hosts);

      socket.on("disconnect", function() item.port !== port);
      // socket.broadcast.to("hosts").emit("hosts", hosts);
      );
      );


      Client



      "use strict";

      const io = require('socket.io-client');
      const app = require('express')();
      const http = require('http')
      const server = http.Server(app);
      const request = require('request');
      const axios = require('axios');
      const bodyParser = require('body-parser')
      const EventEmitter = require('events');

      class MyEmitter extends EventEmitter ;
      const event = new MyEmitter();

      app.use(bodyParser.urlencoded( extended: true));
      app.use(bodyParser.json())

      const states = "REQUEST": 1, "SEND": 2, "DOWNLOAD": 3, "FINISH": 4;
      const errorCodes = "NOT_ACCEPTABLE": "NOT ACCEPTABLE", "OK": "OK";
      const args = process.argv;

      const myIpAddress = "127.0.0.1"
      const myPort = args[2] || 4500;

      const serverAddress = "127.0.0.1";
      const serverPort = 3000;
      const socket = io(`http://$serverAddress:$serverPort/`,
      query: `port=$myPort`
      );

      const laziness = 0.5;

      var hosts = ;
      var requests = ;

      server.listen(myPort, myIpAddress, () =>
      console.log(`Listening $myIpAddress:$myPort`);
      );

      socket.on("hosts", data =>
      //console.log("Received data: ", data)
      hosts = data;
      );

      app.get("/download", (req, res) =>
      const id = req.query.id;
      const url = req.query.url;
      const ip = req.connection.remoteAddress;
      const port = req.query.port;

      if (id == null );

      app.get('/check', (req, res) =>
      let id = req.query.id;

      res.sendStatus(requests[id] != null && requests[id].state != states.REQUEST ? 200 : 204);
      );

      app.post('/file', (req, res) => requests[id].state === states.FINISH)
      res.status(200).send(errorCodes.NOT_ACCEPTABLE);
      return;


      if (requests[id].host)
      requests[id].state = states.FINISH;
      console.log('StatusCode: ', req.body.status);
      console.log('Mime-type: ', req.body['mime-type']);
      console.log("Content: ", Buffer.from(req.body.content, "base64").toString("ascii"));
      else
      console.log('Sending back to host');
      event.emit("sendBackToHost", id, req.body);


      res.sendStatus(200);
      );

      event.on('check', (id) =>
      axios.get(`http://$requests[id].ip:$requests[id].port/check?id=$id`)
      .then(res =>
      if (res.status === 204)
      requests[id].url = encodeUrl(requests[id].url);
      requests[id].host = true;
      event.emit('sendNext', id);
      else if (res.status === 200)
      event.emit('downloadOrSend', id);
      )
      .catch(err =>
      console.error('Error with checking', err);
      )
      );

      event.on('downloadOrSend', (id) =>
      if (isDownloadState())
      axios.get(decodeUrl(requests[id].url))
      .then(res =>
      const body =
      content: Buffer.from(res.data).toString('base64'),
      status: res.status,
      "mime-type": res.headers["content-type"]
      ;

      requests[id].state = states.DOWNLOAD;
      event.emit('sendBackToHost', id, body)
      )
      .catch(err =>
      console.error('Error while downloading file', err.response);
      )
      return;

      event.emit('sendNext', id);
      )

      event.on('sendBackToHost', (id, body) =>
      axios.post(`http://$requests[id].ip:$requests[id].port/file?id=$id`, body)
      .catch(err =>
      console.error("Error while sending back to host", err.response);
      event.emit('sendFileBackToAll', id, body);
      );
      );

      event.on('sendNext', (id) =>
      requests[id].state = states.SEND;
      for (let h of hosts)
      if (h.ip == requests[id].ip && h.port == requests[id].port) continue;
      if (h.ip == myIpAddress && h.port == myPort) continue;

      axios.get(`http://$h.ip:$h.port/download?id=$id&url=$requests[id].url&port=$myPort`)
      .catch(err =>
      console.error('Error while sending requests to neighbours', err.response);
      );

      );

      event.on('sendFileBackToAll', (id, body) =>
      for (let h of hosts)
      if (h.ip == myIpAddress && h.port == myPort) continue;

      axios.post(`http://$h.ip:$h.port/file?id=$id`, body)
      .catch(err =>
      console.error("Error while sending back to all hosts", err.response);
      );

      );

      function isDownloadState()
      return Math.random() >= laziness;


      function encodeUrl(url)
      return (decodeURIComponent(url) === url) ? encodeURIComponent(url) : url;


      function decodeUrl(url)
      return (decodeURIComponent(url) === url) ? url : decodeURIComponent(url);


      setInterval(() =>
      axios.get(`http://$serverAddress:$serverPort/hosts`)
      .then(res =>
      hosts = res.data;
      )
      .catch(err =>
      console.error("Error while fetching data from server", err.response);
      )
      , 60000)








      share|improve this question










      share|improve this question




      share|improve this question









      asked Apr 3 at 13:13









      spyrox

      163




      163




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          I don't know javascript but some things got me wondering. I apologize in advance if I overlooked the obvious. Most of the things I point out here you can read about on the OWASP site. It's a great resource.



          • I couldn't tell if you are validating input, this is definitely a good idea. For example the urls that are being passed.

          • You should not be accepting raw HTML, this would be good to sanitize first.

          • The central server maintains a list of known hosts, but how do the clients connecting to the central server know they are not talking to a 'malicious' one? You can counter this by using public/private-keys(take a look at libsodium), so you could for example have the central server sign the hosts filelist. Then have the central hosts public key hard-coded into the clients, so that they can verify the file is 'trusted'.

          • All data is transmitted in the cleartext as I can tell, so an obvious improvement would be expanding on the public-key approach and have all nodes generate a keypair, where the public keys belonging to each node, are listed in the hosts file/list. Then whenever a node sends data to another node, that data can also be encrypted (This is much like an approach of TOR if I remember correctly).





          share|improve this answer























            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191161%2fmini-tor-implementation-cli-only-anonymous-network%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote













            I don't know javascript but some things got me wondering. I apologize in advance if I overlooked the obvious. Most of the things I point out here you can read about on the OWASP site. It's a great resource.



            • I couldn't tell if you are validating input, this is definitely a good idea. For example the urls that are being passed.

            • You should not be accepting raw HTML, this would be good to sanitize first.

            • The central server maintains a list of known hosts, but how do the clients connecting to the central server know they are not talking to a 'malicious' one? You can counter this by using public/private-keys(take a look at libsodium), so you could for example have the central server sign the hosts filelist. Then have the central hosts public key hard-coded into the clients, so that they can verify the file is 'trusted'.

            • All data is transmitted in the cleartext as I can tell, so an obvious improvement would be expanding on the public-key approach and have all nodes generate a keypair, where the public keys belonging to each node, are listed in the hosts file/list. Then whenever a node sends data to another node, that data can also be encrypted (This is much like an approach of TOR if I remember correctly).





            share|improve this answer



























              up vote
              1
              down vote













              I don't know javascript but some things got me wondering. I apologize in advance if I overlooked the obvious. Most of the things I point out here you can read about on the OWASP site. It's a great resource.



              • I couldn't tell if you are validating input, this is definitely a good idea. For example the urls that are being passed.

              • You should not be accepting raw HTML, this would be good to sanitize first.

              • The central server maintains a list of known hosts, but how do the clients connecting to the central server know they are not talking to a 'malicious' one? You can counter this by using public/private-keys(take a look at libsodium), so you could for example have the central server sign the hosts filelist. Then have the central hosts public key hard-coded into the clients, so that they can verify the file is 'trusted'.

              • All data is transmitted in the cleartext as I can tell, so an obvious improvement would be expanding on the public-key approach and have all nodes generate a keypair, where the public keys belonging to each node, are listed in the hosts file/list. Then whenever a node sends data to another node, that data can also be encrypted (This is much like an approach of TOR if I remember correctly).





              share|improve this answer

























                up vote
                1
                down vote










                up vote
                1
                down vote









                I don't know javascript but some things got me wondering. I apologize in advance if I overlooked the obvious. Most of the things I point out here you can read about on the OWASP site. It's a great resource.



                • I couldn't tell if you are validating input, this is definitely a good idea. For example the urls that are being passed.

                • You should not be accepting raw HTML, this would be good to sanitize first.

                • The central server maintains a list of known hosts, but how do the clients connecting to the central server know they are not talking to a 'malicious' one? You can counter this by using public/private-keys(take a look at libsodium), so you could for example have the central server sign the hosts filelist. Then have the central hosts public key hard-coded into the clients, so that they can verify the file is 'trusted'.

                • All data is transmitted in the cleartext as I can tell, so an obvious improvement would be expanding on the public-key approach and have all nodes generate a keypair, where the public keys belonging to each node, are listed in the hosts file/list. Then whenever a node sends data to another node, that data can also be encrypted (This is much like an approach of TOR if I remember correctly).





                share|improve this answer















                I don't know javascript but some things got me wondering. I apologize in advance if I overlooked the obvious. Most of the things I point out here you can read about on the OWASP site. It's a great resource.



                • I couldn't tell if you are validating input, this is definitely a good idea. For example the urls that are being passed.

                • You should not be accepting raw HTML, this would be good to sanitize first.

                • The central server maintains a list of known hosts, but how do the clients connecting to the central server know they are not talking to a 'malicious' one? You can counter this by using public/private-keys(take a look at libsodium), so you could for example have the central server sign the hosts filelist. Then have the central hosts public key hard-coded into the clients, so that they can verify the file is 'trusted'.

                • All data is transmitted in the cleartext as I can tell, so an obvious improvement would be expanding on the public-key approach and have all nodes generate a keypair, where the public keys belonging to each node, are listed in the hosts file/list. Then whenever a node sends data to another node, that data can also be encrypted (This is much like an approach of TOR if I remember correctly).






                share|improve this answer















                share|improve this answer



                share|improve this answer








                edited Apr 4 at 7:50


























                answered Apr 4 at 7:37







                user159822





























                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191161%2fmini-tor-implementation-cli-only-anonymous-network%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Chat program with C++ and SFML

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    Will my employers contract hold up in court?