Node.JS proxy for FAST protocol - node.js

I'm trying to make a Node.JS proxy for FAST protocol over UDP multicast (PPTP connection).
const os = require('os');
const osInterfaceName = 'VPN-подключение';
const endpoints = {
'FUT-TRADES': {
A: {ip: '239.195.9.9', port: 42009},
B: {ip: '239.195.137.9', port: 43009},
},
'OPT-TRADES': {
A: {ip: '239.195.9.25', port: 42025},
B: {ip: '239.195.137.25', port: 43025},
}
};
const dgram = require('dgram');
const osInterface = os.networkInterfaces()[osInterfaceName] !== undefined ? os.networkInterfaces()[osInterfaceName][0] : { address: '0.0.0.0' };
const clients = {};
for (let key in endpoints) {
for (let serverId in endpoints[key]) {
const client = dgram.createSocket('udp4');
client.on('listening', function () {
const address = client.address();
console.log(`UDP Client listening on ${address.address}: ${address.port} [${serverId}]`);
client.setBroadcast(true)
client.setMulticastTTL(128);
client.addMembership(endpoints[key][serverId].ip, osInterface.address);
});
client.on('message', function (message, remote) {
console.log('Message from: ' + remote.address + ':' + remote.port +' - ' + message);
});
client.on('error', function (error) {
console.log(`UDP error: ${error.stack}`);
});
client.bind(endpoints[key][serverId].port, osInterface.address);
clients[`${key}:${serverId}`] = client;
}
}
If i test it locally (starting server and send a multicast message - it's shown on client), but i can't use it with MOEX stream. In logs nothing except of "UDP Client listening on 1.1.5.171:42009 [A]..." (for every stream in endpoints list).
But according to netsh interface ip show joins client has successfully joined multicast groups.

Looks like i have found the source of problem, so does alternate resolution.
It's not enough just to join multicast group, it's also required to enable source filtering of packets:
install and start smcroute daemon:
apt-get install smcroute
smcroute -d
join multicast group with enabled filtering (source IP required)
smcroute -j ppp0 91.203.253.235 239.195.9.9
then application starts to get multicast packets:
tcpdump -i ppp0 -s 65534 host 239.195.9.9
Additional info: while i was searching for answer i've found UDP to TCP proxy tool: https://github.com/MarkoPaul0/DatagramTunneler (which solves multicast join params shortage, as i couldn't find multicast join param for source ip filter in Node.JS)

Related

Node : how to set static port to udp client in node js

I am very new to Udp Socket programming, here i implemented echo UDP Client which connects to UDP server
var buffer = require('buffer');
var udp = require('dgram');
// creating a client socket
var client = udp.createSocket('udp4');
//buffer msg
var data = Buffer.from('Pradip Shinde');
client.on('message',function(msg,info){
console.log('Data received from server : ' + msg.toString());
console.log('Received %d bytes from %s:%d\n',msg.length, info.address, info.port);
});
//sending msg
client.send(data,9300,'192.168.1.187',function(error){
if(error){
client.close();
}else{
console.log('Data sent from client!!!');
}
});
when this client send msg to server, operating system assign the random port to this client but in my scenario i want static port which will never change, is it possible to assign static port to udp client?
As mentioned in the documentation, you can use bind method to do this,
For UDP sockets, causes the dgram.Socket to listen for datagram messages on a named port and optional address that are passed as properties of an options object passed as the first argument. If port is not specified or is 0, the operating system will attempt to bind to a random port. If address is not specified, the operating system will attempt to listen on all addresses. Once binding is complete, a 'listening' event is emitted and the optional callback function is called.
Try using
// Creating a client socket
var client = udp.createSocket('udp4');
// Bind your port here
client.bind({
address: 'localhost',
port: 8000,
exclusive: true
});
For more information follow this documentation.

GCP Compute Engine Firewall Rules for TCP Server

I have created a GCP compute engine instance with a static external ip address. Machine type: n1-standard-2 (2 vCPUs, 7.5 GB memory). OS is Linux/Debian.
My intention is to create a plain Node.js TCP server on the machine. The code is as follows:
var net = require('net');
var HOST = '0.0.0.0';
var PORT = 110;
net.createServer(function(sock) {
console.log('CONNECTED: ' + sock.remoteAddress +':'+ sock.remotePort);
sock.on('data', function(data) {
console.log('DATA ' + sock.remoteAddress + ': ' + data);
sock.write('You said "' + data + '"');
});
}).listen(PORT, HOST);
console.log('Server listening on ' + HOST +':'+ PORT);
The client is:
var net = require('net');
var HOST = '104.197.23.132';
var PORT = 110;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('I am Chuck Norris!');
});
client.on('data', function(data) {
console.log('DATA: ' + data);
client.destroy();
});
client.on('close', function() {
console.log('Connection closed');
});
My firewall rules are as follows:
PLEASE NOTE: I am listening on port 110, and the client is trying to connect to the static external ip address. Itt appears that I am enabling TCP traffic over 110 according to firewall rules. The error I see is
Error: connect ETIMEDOUT 104.197.23.132:110
When I ssh into the instance, and run tcp client, I connect successfully. So the final question is, why can't local tcp client (my computer) connect to compute instance? Is there something wrong with my firewall rules / source filters / IP forwarding?
I just solved this problem.
You have the wrong targets. Go to the edit page and click the select menu of "Targets", and then you can select the first option "Apply to all instance" that is the easiest way.
You need to first add firewall rule according to your host's IP, as internal traffic needs to be received from that particular host (your machine)
Then you should be able to ping to GCP Compute Instance.
You should also be able to telnet at the particular port which you configured in your program.
This should be okay.
Also - the customized rule should be added in the Network Tags of instance, to make the rule associated to that instance, after this the instance uses that particular rule.

sending OSC between machines on a LAN using Node.js and OSC.js

Has anyone created a working setup where OSC is being sent between machines on a LAN using Node.js? Ideally, using Colin Clark's osc.js package?
I have what I think should be a pretty simple example, except that it doesn't work - I get an EADDRNOTAVAIL error, which implies that the remote address isn't available. I can ping the other laptop successfully, however.
Here's the code and the error, for reference:
Sending code (laptop at 192.168.0.5):
var osc = require("osc");
var udp = new osc.UDPPort({
localAddress: "127.0.0.1", // shouldn't matter here
localPort: 5000, // not receiving, but here's a port anyway
remoteAddress: "192.168.0.7", // the other laptop
remotePort: 9999 // the port to send to
});
udp.open();
udp.on("ready", function () {
console.log("ready");
setInterval(function () {
udp.send({
address: "/sending/every/second",
args: [1, 2, 3]
})
}, 1000);
});
Receive code (on laptop at 192.168.0.7):
var osc = require("osc");
var udp = new osc.UDPPort({
localAddress: "192.168.0.7",
localPort: 9999
});
udp.open();
udp.on("ready", function () {
console.log("receiver is ready");
});
udp.on("message", function(message, timetag, info) {
console.log(message);
});
Here's the error I get when I run the sending code:
ready
events.js:141
throw er; // Unhandled 'error' event
^
Error: send EADDRNOTAVAIL 192.168.0.7:9999
at Object.exports._errnoException (util.js:907:11)
at exports._exceptionWithHostPort (util.js:930:20)
at SendWrap.afterSend [as oncomplete] (dgram.js:345:11)
The issue is that the osc.UDPPort that you are using to send OSC messages has its localAddress bound to the loopback address, which is limited to connections within your local computer. As a result, your sender can't find your receiver.
The solution is to bind your sender's localAddress to an appropriate network interface. If your 192.168.0.5 IP address is stable and you don't need to worry about it changing when you connect your laptops to another network (say, for a gig or a gallery installation), then you can just use that. Otherwise, you might want to use an mDNS name ("foo.local") or the "all interfaces" address, 0.0.0.0.
This change to your "Sender code" worked for me when I tried it on my network:
var osc = require("osc");
var udp = new osc.UDPPort({
localAddress: "0.0.0.0", // Totally does matter here :)
localPort: 5000,
remoteAddress: "192.168.0.7", // the other laptop
remotePort: 9999 // the port to send to
});
udp.open();
udp.on("ready", function () {
console.log("ready");
setInterval(function () {
udp.send({
address: "/sending/every/second",
args: [1, 2, 3]
})
}, 1000);
});
As a side note, osc.js's behaviour does differ from a regular Node.js UDP socket, since if the local address is omitted Node will default to 0.0.0.0. An osc.UDPPort, however, will always bind to 127.0.0.1 if localAddress is omitted (which seemed a little safer to me when originally implementing osc.js, but I can see how it might be confusing).
This issue is also discussed on the osc.js issue tracker, and I will update the documentation to prevent the kind of confusion you encountered here. Best of luck with your project!

Web Socket: cannot detect client connection on internet disconnect

I want to detect the client connection if client turn off the internet using web socket. My code is:
//Include util library
var util = require('util');
// Include underscore library
var _ = require('underscore')._;
//For websocket
var webSocketServer = new (require('ws')).Server({port: (process.env.PORT || 5000)}),
webSockets = {} // userID: webSocket
// CONNECT /:userID
// wscat -c ws://localhost:5000/1
webSocketServer.on('connection', function (webSocket)
{
var userID = webSocket.upgradeReq.url.substr(1);
//console.log('User_id is ',userID);
webSockets[userID] = webSocket
util.log('User_id: [' + userID + '] enter in connected users list of [ ' + Object.getOwnPropertyNames(webSockets)+' ]')
// Call function which check id exist in letswalkee DB table
check_userid(userID);
// Send msg like: [fromUserID, text] [1, "Hello, World!"]
webSocket.on('message', function(message) {
util.log('Received from [' + userID + ']: ' + message)
var messageArray = JSON.parse(message)
var toUserWebSocket = webSockets[messageArray[0]]
if (toUserWebSocket) {
util.log('Sent to [' + messageArray[0] + ']: ' + JSON.stringify(messageArray))
messageArray[0] = userID
toUserWebSocket.send(JSON.stringify(messageArray))
}
})
webSocket.on('close', function ()
{
delete webSockets[userID]
util.log('User_id Deleted from connected users: [ ' + userID+' ]');
})
webSocket.on('disconnect',function()
{
console.log('hello i am disconnected');
});
})
I used that code (webSocket.on('disconnect',function()) but did not worked.
WebSocket is based on TCP and TCP uses FIN packet to close the connection. In the case of sudden loss of Internet connection, both the WebSocket server and the phone are unaware of the already dead TCP connection because no FIN packet was sent.
To address the issue, TCP has a mechanism called keepalive.
What I did to solve the issue is adjusting the TCP keepalive settings in Linux kernel, and invoke ws._socket.setKeepAlive(true).
Reference:
https://github.com/websockets/ws/issues/353
For TCP-based protocols (e.g. Websockets) generally what is done is heartbeat/ping packets are sent back and forth at the application layer so that each side can easily/quickly determine if the connection is gone for one reason or another.

Can not send back UDP to internal PC behide NAT

I setup two PC,
one is client in local network behide NAT,
another is server on public network.
The test steps are --
1) client listen udp on port 33333
2) server listen udp on port 22222
1) client send udp to server
2) server received the data and send back
When I test the code on my test network, it's OK.
If put the server on real internet, server can get the message from client,
client can not get response from server.
What's wrong?
Here's testing code with nodejs.
server
var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.on('message', function (message, remote) {
console.log('client ip:' + remote.address + ', port:' + remote.port +', message:' + message);
//send response to client
var message = new Buffer("hello, client!");
socket.send(message, 0, message.length, remote.port, remote.address);
});
//listening port
socket.bind(22222);
client
var dgram = require('dgram');
var socket = dgram.createSocket('udp4');
socket.on('message', function (message, remote) {
//display message from server
console.log('server ip:' + remote.address + ', port:' + remote.port +', message:' + message);
});
//listening port
socket.bind(33333);
//send message to server
function send(server){
var message = new Buffer("hello, server!");
socket.send(message, 0, message.length, 22222, server, function(){
//send again after 1 seconds
setTimeout(function(){
send(server);
}, 1000);
});
};
//suppose that server address is public.server.com
send("public.server.com");
NATed computers cannot be reached from outside and this is particularly painful for peer-to-peer or friend-to-friend software. Basically because your PC has not a public IP address but you NAT device has. So, the NAT is visible, your PC isn't.
The server gets the package from the NAT device and send the response to it. Yes, the NAT receives the response and it has to relay it to your PC, that's the trick. To do so you have to configure a port forwarding in the NAT.
The NAT has a table like the following:
+----------+---------------------+---------------+
| NAT PORT | INTERNAL IP ADDRESS | INTERNAL PORT |
+----------+---------------------+---------------+
| 33333 | 198.162.0.3 (pc ip) | 33333 |
It can be read as: when NAT receives a package in its port #33333 it has to redirected to the internal IP 198.162.0.3 (your PC IP address) and port# 33333.
If your PC uses a fixed IP, you can do this mapping by hand in your NAT. However, if you use a DHCP server, your PC's IP can change after each reboot so you need to do this mapping by software in you project. Most of the NATs support Universal Plug & Play, Port Mapping Protocol or Port Control Protocol to achieve this mapping and you can do it with nodejs given that all you need are the appropiated HTTP request to the NAT.
Yes, you can do it by yourself but it is not so easy. In fact, the discovery process requires you broadcast udp messages in the LAN in specific port. I strongly recommend you to use a third-party component to do it.
I hope this helps you.

Resources