nodejs tls module for server-client communication without browser - node.js

I wish to securely send messages back and forth between a client and a remote server (just an IP address and a port) using nodejs. The client runs a local .js file and does not use a browser. I have tried to do so using the node TLS module (https://nodejs.org/api/tls.html).
The handshake works perfectly fine, but I can not manage to send a message from the server to the client. My current code is:
client.js
const tls = require('tls');
const fs = require('fs');
const options = {
host: <server IP address>,
port: <port number>,
key: fs.readFileSync('client.key'),
cert: fs.readFileSync('client.crt'),
ca: fs.readFileSync('ca.crt'),
checkServerIdentity: function (host, cert) {
return undefined;
}
};
const client = tls.connect(options, function(){
if (client.authorized) {
console.log("Connection authorized");
} else {
console.log("Connection not authorized: " + conn.authorizationError)
}
process.stdin.pipe(client);
process.stdin.resume();
client.emit('data','message')
});
client.setEncoding('utf8');
client.addListener('data', function(data) {
console.log(data);
});
client.on('end', () => {
server.close();
});
server.js
const tls = require('tls');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
ca: fs.readFileSync('ca.crt'),
requestCert: true,
rejectUnauthorized: true
};
const server = tls.createServer(options, function(socket){
console.log('server connected',
socket.authorized ? 'authorized' : 'unauthorized');
socket.setEncoding('utf8');
socket.pipe(socket);
socket.emit('data','I am the server')
});
server.listen(<port number>,function(){
console.log('listening')
})
server.on('connection',function(client){
console.log('client connected')
client.on('data',function(data){
console.log(data)
})
})
The server does output 'client connected' but does not do anything with the client.on() part, and I can not find another way to make the server listen for messages from the client. Is there a way to use the tls module so that I can make the client and server interact based on the messages they send and receive? Again, I do not want to use a browser.

The comments by #robertklep helped me solve my issue. Delete the socket.pipe(socket) line on the server side, and on the client side just use socket.write() to send a message to the server. This can be received by a socket.on('data') instance on the server side. Also, to interact with multiple clients, you can write something like:
var socket = {};
const server = tls.createServer(options, function(s){
client=s.getPeerCertificate().subject.CN
socket[client] = s;
socket[client].setEncoding('utf8');
socket[client].write('welcome')
for (key in socket){
socket[key].write('new client connected')
}
socket[client].on('data',function(data){
socket[client].write('data received')
})

I think using a Set for notifying new connections of being connected makes more sense. An object being added to a set must be unique to the set if it is to be added. You can also just use socket.server._connectionKey to determine what client it is, don't have to mess around with getting the peer certificate.
var sockets = new Set();
const server = tls.createServer(options, (socket) => {
console.log('server connected',
socket.authorized ? 'authorized' : 'unauthorized');
var sock_set_len = sockets.size; //get size of sockets set
var key = socket.server._connectionKey;
sockets.add(key); //attempt to add socket to connection list
if(sock_set_len < sockets.size) { //if the socket _connectionKey is unique, it will be added, and will be treated as a new connection here.
socket.write("you are connected...");
}
socket.on('data',function(data){
socket.write(data);
})
socket.on('end', socket.end);
});
On a side note, I created a package to handle sockets with Obj-C iOS, taking into account the latest (iOS 13) TLS restrictions imposed by Apple:
https://github.com/eamonwhiter73/IOSObjCWebSockets
I used a Node JS TLS server to receive connections, a variation of the echo server example in the Node JS docs

Related

socketio failed: WebSocket is closed before the connection is established on production

I created an node app for websocket type project. There are no issues in my local machine, everything is working, e.g. connecting, sending and receiving but when upload to server, client is not connecting at all, it returns
WebSocket is closed before the connection is established.
below is my server code
require('dotenv').config();
const path = require('path');
const fs = require('fs');
const options = {
key: fs.readFileSync(path.join(__dirname,'../'+process.env.SSL_KEY)),
cert: fs.readFileSync(path.join(__dirname,'../'+process.env.SSL_CRT)),
ca: process.env.SSL_CA
};
const http = require('https').createServer(options);
const io = require('socket.io')(http,{
cors : {
origin : '*'
}
});
// middleware
io.use((socket, next) => {
let token = socket.handshake.query.token;
if( token == process.env.TOKEN ){
return next();
}
return next(new Error('authentication error'));
});
http.listen(3800, () => {
console.log('listening on *:3700');
});
and below is the client
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3n3ZTxJ30zuH39rimUggmTwmh2u7wvQsDTHESnmfQ==" crossorigin="anonymous"></script>
<script>
var socket = io('mydomain.com:3800?token=myapp2021',{
transports : ['websocket'],
secure : true
});
socket.on('connect', function() {
console.log( socket.connected );
});
</script>
Any help, ideas, suggestions, are greatly appreciated.
Did you allow incoming TCP connections at port 3800 on your server? Seems like firewall is blocking connections.

Socket.IO server.origins always returns '*' as the origin value (Nodejs)

I am trying to build a two way socket.io server/client connection. The server will remain behind one IP/domain and the client will behind a different IP. The point is to notify me when the server goes offline, in case of power outage or server failure. The issue I am having, is I am trying to secure the socket so not just anyone can connect to the socket. Socket.IO has a server.origins function that will return the origin of socket trying to connect. Their API documentation explains it like this.
io.origins((origin, callback) => {
if (origin !== 'https://foo.example.com') {
return callback('origin not allowed', false);
}
callback(null, true);
});
The issue I am having is whenever I connect to the socket.io server with socket.io-client the origin is always '*'.
Under potential drawbacks in there API is says:
"in some situations, when it is not possible to determine origin it may have value of *"
How do I get socket.io it see the IP where the socket connection request is coming from?
Once the connection is established I can use the socket information and see the IP where the socket lives, but the connection is already made. I am trying to stop rouge connections.
# Server
const express = require('express');
const app = express();
const chalk = require('chalk')
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const cors = require('cors');
const port = 4424;
app.use(cors());
io.origins((origin, callback) => {
console.log(origin);
if (origin !== '*') {
return callback('origin not allowed', false);
}
callback(null, true);
});
io.on('connection', (socket) => {
console.log('Client connected...');
socket.on('join', (data) => {
console.log(data);
socket.emit('messages', 'Hello from server');
});
})
server.listen(port, () => console.log(chalk.blue(`Express started on port ${port}!`)));
Client:
# Client
const io = require('socket.io-client');
const socket = io('https://"MY DOMAIN THAT THE SERVER IS BEHIND"', { reconnect: true })
socket.on('connect', (data) => {
console.log("Connection successful");
socket.emit('join', 'Hello World from client');
});
socket.on('connect_error', (error) => {
console.log("Connection error");
});
socket.on('disconnect', (timeout) => {
console.log("Connection disconnected");
})
socket.on('messages', (data) => {
console.log(data);
});
I have the server behind a NGINX server using SSL, and connected to the server with the client on a different IP and it goes through and creates the connection, but the Origin is always "*".
Actually I found out you can use middleware with Socket.io with the io.use() function. I just wrote a simple middleware that checks the incoming socket ip with a list of approved ips.
io.use((socket, next) => {
const ip = socket.handshake.headers['x-forwarded-for']
if (firewall(ip))
{
return next();
}
})
And firewall is a function that checks if the ip is in the array of approved ips.

How do you connect to an EPP server (domain registry) in Node JS?

I'm building a project to manage my own domain names (just for learning purposes). The standard for interfacing with the registry API is to use their EPP server on port 700 with your username, password, and an SSL cert on the client side.
How do I connect to this in node js? Should I open a TLS connection? Their documentation is vague, at best, so if anyone has had any experience doing this, that would help out.
It's also hard testing because I'm not sure if my ip was properly whitelisted. Would like to see a sample code snippet connecting to an EPP server with username, password, and SSL cert, or perhaps just point me in the right direction as I'm most likely overthinking it :p.
Here's where I've started found from the only example online I can find in node.
var fs = require('fs')
var tls = require('tls')
var options = {
cert: fs.readFileSync('cert.pem'),
passphrase: 'passphrase',
username: 'username', // ?
}
var stream = tls.connect(700, 'example.com', options);
stream.setEncoding('utf8')
stream.on('secureConnect', function() {
console.log('client connected')
})
stream.on('data', function(data) {
console.log(data)
})
But that doesn't do anything and won't make the connection.
If I understand it right RFC EPP can be connected by TCP.
I would use Node.JS API net to create client.
And by EPP documentation after connect you need send command in this format for example login.
var host = 'example.com';
var port = 700;
var command = '{{xml_command}}';
var client = new net.Socket();
client.connect(port, host, function() {
console.log('Connected');
client.write(command);
});
client.on('data', function(data) {
console.log('Received: ' + data);
client.destroy();
});
client.on('close', function() {
console.log('Connection closed');
});
As an alternative I'd try node-epp and epp-reg.
Maybe I helped where to go next.

NodeJS external connection error?

I have a problem w my Node Server + Socket Client.
Well, my localhost server works perfect.
But... when i connect my socket.io client (node) w my external server (ex: www.myserver.com:3000) doesnt works.
I dont recieve any data. (message var)
This is my sample code:
var io = require("socket.io-client");
var requestHTTP = require("request");
var socket = io('http://myserver.com:3000');
var room = "game881";
console.log('test simple socket '+room+'');
socket.on('news', function (data) {
console.log(data);
socket.emit('conectado', { my: 'Conectado!' });
});
socket.on('connect', function () {
socket.emit('room', room);
console.log('Conectando a ' + room);
});
socket.on('message', function (data) {
var json = data.message;
json = JSON.parse(json);
console.log(json);
});+
Why dont recieve any data?
CORS? I've already tried.
Port used?
:) thanks!
Well... i solved my problem.
I use this.
Setup Server-Server SSL communication using socket.io in node.js
The previous answers didn't do it for me. require('https').globalAgent is always >undefined.
Did some seaching and found the rejectUnauthorized parameter in the docs (https://nodejs.org/api/tls.html). Not sure if it's related to SocketIO, but it somehow seems to work with self-signed certificates:
var socket = io.connect('//yourhost:8000', {secure: true, rejectUnauthorized: false})
secure: true might be optional, but I like to enforce it anyhow.

Node JS Server to Server Connection

Is it possible to connect to a NodeJS Server from another server? Two NodeJS servers communicating with each other?
//Server Code
var io = require('socket.io').listen(8090);
io.sockets.on('connection', function (socket) {
io.sockets.emit('this', { will: 'be received by everyone'});
socket.on('private message', function (from, msg) {
console.log('I received a private message by ', from, ' saying ', msg);
});
socket.on('disconnect', function () {
io.sockets.emit('user disconnected');
});
});
//Client Code in Server Code. Connecting to another server.
io.connect( "http://192.168.0.104:8091" ); //Connect to another server from this one.
//ETC...
Here's a simple example that creates a server and a client that connects to that server. Remember that what you send has to be a buffer (strings are automatically converted to buffers). The client and server works independently of eachother, so can be put in the same app or on totally different computers.
Server (server.js):
const net = require("net");
// Create a simple server
var server = net.createServer(function (conn) {
console.log("Server: Client connected");
// If connection is closed
conn.on("end", function() {
console.log('Server: Client disconnected');
// Close the server
server.close();
// End the process
process.exit(0);
});
// Handle data from client
conn.on("data", function(data) {
data = JSON.parse(data);
console.log("Response from client: %s", data.response);
});
// Let's response with a hello message
conn.write(
JSON.stringify(
{ response: "Hey there client!" }
)
);
});
// Listen for connections
server.listen(61337, "localhost", function () {
console.log("Server: Listening");
});
Client (client.js):
const net = require("net");
// Create a socket (client) that connects to the server
var socket = new net.Socket();
socket.connect(61337, "localhost", function () {
console.log("Client: Connected to server");
});
// Let's handle the data we get from the server
socket.on("data", function (data) {
data = JSON.parse(data);
console.log("Response from server: %s", data.response);
// Respond back
socket.write(JSON.stringify({ response: "Hey there server!" }));
// Close the connection
socket.end();
});
The conn and socket objects both implement the Stream interface.
Check Substrack's dnode. It auto maps literal objects from the 1st env to the 2nd one. You gain a kind of RPC out of the box. And it works in the browser too...

Resources