NodeJS, get client IP on TLS server - javascript

I have the following Node.JS for creating a TLS server which pipes data to another server:
var server = net.createServer(function (localsocket) {
var remotesocket = tls.connect(options, function() {
remotesocket.on('data', function(data) {
var flushed = localsocket.write(data);
if (!flushed) {
remotesocket.pause();
}
});
remotesocket.on('drain', function() {
localsocket.resume();
});
remotesocket.on('close', function(had_error) {
localsocket.end();
});
});
localsocket.on('data', function (data) {
var flushed = remotesocket.write(data);
if (!flushed) {
localsocket.pause();
}
});
localsocket.on('drain', function() {
remotesocket.resume();
});
localsocket.on('close', function(had_error) {
remotesocket.end();
});
});
Everything seems to work fine, but I would like to change options according to client IPs.
I've tried to do console.log(localsocket), but that only gives me my servers local IP, and not clients sending packets to my TLS server.

Your connectListener (the function which is given to createServer) gets the socket_object which is an object of type net.Socket. There is a property remoteAddress which has the address of the remote host.
Example:
var server = net.createServer(function (localsocket) {
console.log(localsocket.remoteAddress);
}

Related

Connect JS secure websocket to C# websocket server (Fleck)

I have an API in C# (asp.net) in which i'm running this websocket server using fleck:
SocketService.start();
SocketService.server.Start(socket =>
{
socket.OnOpen = () =>
{
SocketService.Connessione(socket);
};
socket.OnClose = () =>
{
SocketService.Disconnesione(socket);
};
socket.OnMessage = message =>
{
SocketService.Messaggio(message, socket);
};
});
This is SocketService.Start():
public static void start()
{
server = new WebSocketServer($"wss://{GetLocalIPAddress()}:{"4450"}/BNS/");
}
I have tried with a simple HTML/JS page using unsecure ws and it worked fine.
Then I have tried in my main program which i need it to be run on HTTPS so when using unsecure ws chrome told me to use wss instead.
So i change my ws server to wss but then it does nothing, it gives me timeout error.
This is the JS code:
var start = function () {
var wsImpl = window.WebSocket || window.MozWebSocket;
var form = document.getElementById('sendForm');
var input = document.getElementById('sendText');
alert("Connessione...");
// create a new websocket and connect
window.ws = new wsImpl('#Percorsi.IndirizzoSocket');
alert("conn");
// when the connection is established, this method is called
ws.onopen = function () {
alert("Connessione aperta");
var openJson = {
"Id": "#Model.accountCorrente.Id",
"type": "Identificazione"
};
alert("send");
ws.send(stringify(openJson));
};
// when the connection is closed, this method is called
ws.onclose = function () {
alert("Connessione chiusa");
}
// when data is comming from the server, this metod is called
ws.onmessage = function (val) {
if (confirm("Hai ricevuto un nuovo messaggio!\nPremi ok per visualizzarlo.")) {
window.location("/Annunci/Chat/" + val);
} else { }
};
}
I can't figured out how to make it works.
Thanks in advance for your help!
It seems like you are not setting the server certificate to be used under WS over TLS (not to be confused with HTTPS which is HTTP over TLS).
If you see the example in fleck's webpage, you will realize that you have to set the Certificate:
server.Certificate = new X509Certificate2("MyCert.pfx");

Working with multiple tabs with Socket.io

I've the following code working in my server-side, it's all ok. But, I want to keep the same connection between n tabs, because when I open a new tab, looks like I've disconnected from the first tab... So, how can I keep the same connection?
client.js
socket.emit("connected", {user: inputUser.val()};
app.js
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
users[socket.user] = socket;
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
delete users[socket.user];
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
Update:
The typing event above works fine... So I tried the typing event according to the answer:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
users[userMsg].emit("typing", {user: socket.user});
}
});
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
But it is giving the following error:
users[userMsg].emit("typing", {user: socket.user});
^
TypeError: users[userMsg].emit is not a function
Update²:
To fix the typing event error, I just changed to:
socket.on("typing", function(data) {
var userMsg = data.user;
if(userMsg in users) {
for(let userSet of users[userMsg]) {
userSet.emit("typing", {user: socket.user});
}
}
});
There is no simple way to share a single socket.io connection among multiple tabs in the same browser. The usual model for multiple tabs would be that each tab just has its own socket.io connection.
The opening of a new tab and a new socket.io connection should not, on its own, cause your server to think anything was disconnected. If your code is doing that, then that is a fault in your code and it is probably easier to fix that particular fault.
In fact, if you want to explicitly support multiple tabs and be able to recognize that multiple tabs may all be used by the same user, then you may want to change your server side code so that it can keep track of multiple sockets for a single user, rather than how it is currently coded to only keep track of one socket per user.
If your server code is really just trying to keep track of which users online, then there's probably an easier way to do that by referencing counting each user. I will post a code example in a bit.
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// increment reference count for this user
if (!users[socket.user]) {
users[socket.user] = 0;
}
++users[socket.user];
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// decrement reference count for this user
// and remove user if reference count hits zero
if (users.hasOwnProperty(socket.user)) {
--users[socket.user];
if (users[socket.user] === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
If you need the users object to have the socket object in it, then you can change what is stored in the users object to be a Set of sockets like this:
var express = require("express"),
app = express(),
http = require("http").Server(app),
io = require("socket.io")(http),
users = {};
io.on("connection", function(socket) {
socket.on("connected", function(data) {
socket.user = data.user;
// add this socket to the Set of sockets for this user
if (!users[socket.user]) {
users[socket.user] = new Set();
}
users[socket.user].add(socket);
updateUsers();
});
function updateUsers() {
io.emit("users", Object.keys(users));
}
socket.on("disconnect", function(data) {
if(!socket.user) {
return;
}
// remove socket for this user
// and remove user if socket count hits zero
if (users[socket.user]) {
users[socket.user].delete(socket);
if (users[socket.user].size === 0) {
delete users[socket.user];
}
}
updateUsers();
});
});
var port = Number(process.env.PORT || 8000);
http.listen(port, function() {
console.log("Server running on 8000!");
});
For anyone still having this issue. here is how i fixed it.
let me explain.
once the page refreshes or a new tab is opened, socket dosen't really care so it opens a new connection every time . this is more of a advantage than disadvantage. the best way to tackle the issue is on the server side, once a user logs in with his or her user name , you can send that name along with the query options on the client so it can be used as a unique identifier. in my case i used a token
this.socket = io.connect(`${environment.domain}` , {
query: {token: this.authservice.authToken}
});
then on the server side you can create an empty array to a key and an array of values. the username of the user will be used as a key and the corresponding array of socket as the value. in my own case like i said i used a token
const users = [ ]
socket.nickname = (decoded token username);
users[socket.nickname] = [socket];
then you can perform a simple logic to check if a user already exists in an array, if it does, push the new socket to the array of the user
if ( user.username in users) {
console.log('already exists')
users[user.username].push(socket);
}
if it dosent, just create a new key and add the socket as the key.(make sure its an array because a user can always refresh or open a new tab with the same account and you dont want the chat message to deliver in one tab and not deliver in another)
else {
socket.nickname = username;
users[socket.nickname] = [socket];
}
then to emit a message you simply loop through the array and emit the message accordingly. this way each tab gets the message
socket.on('chat', (data) => {
if (data.to in users) {
for(let i = 0; i < users[data.to].length; i++) {
users[data.to][i].emit('chat', data)
}
for(let i = 0; i < users[data.user].length; i++) {
users[data.user][i].emit('chat', data)
}
}
})
you can add a disconnect logic to remove the socket from the users array too to save memory, so only currently open tabs acre active and closed tabs are removed. i hope it solved your problem
My solution is joining socket to a room with specific user Id.
io.on('connection', async (socket) => {
socket.join('user:' + socket.handshake.headers.uid) // The right way is getting `uid` from cookie/token and verifying user
})
One advantage is sending data to specific user (sending to all tabs)
io.to('user:' + uid).emit('hello');
Hope it's helpful!
I belive the best way is create a channel for the user and unique it by their ID, so, when you need to receive or send something you use the channel and every socket connected to it will receive.
Another solution is to save the flag to localStorage and use eventListener to change localStorage.
Do not connect when another connection exists.
and save message in local storage for send with master tab.

fail to connect localhost:8081 using node.js

I have opened the server.js and the address:http://localhost:8081 on my browser. But then a text "Upgrade Required" appeared at the top left conern of the website.
What is the problem of that? What else do I need to upgrade?
Here is the server.js:
var serialport = require('serialport');
var WebSocketServer = require('ws').Server;
var SERVER_PORT = 8081;
var wss = new WebSocketServer({
port: SERVER_PORT
});
var connections = new Array;
SerialPort = serialport.SerialPort,
portName = process.argv[2],
serialOptions = {
baudRate: 9600,
parser: serialport.parsers.readline('\n')
};
if (typeof portName === "undefined") {
console.log("You need to specify the serial port when you launch this script, like so:\n");
console.log(" node wsServer.js <portname>");
console.log("\n Fill in the name of your serial port in place of <portname> \n");
process.exit(1);
}
var myPort = new SerialPort(portName, serialOptions);
myPort.on('open', showPortOpen);
myPort.on('data', sendSerialData);
myPort.on('close', showPortClose);
myPort.on('error', showError);
function showPortOpen() {
console.log('port open. Data rate: ' + myPort.options.baudRate);
}
function sendSerialData(data) {
if (connections.length > 0) {
broadcast(data);
}
}
function showPortClose() {
console.log('port closed.');
}
function showError(error) {
console.log('Serial port error: ' + error);
}
function sendToSerial(data) {
console.log("sending to serial: " + data);
myPort.write(data);
}
wss.on('connection', handleConnection);
function handleConnection(client) {
console.log("New Connection");
connections.push(client);
client.on('message', sendToSerial);
client.on('close', function () {
console.log("connection closed");
var position = connections.indexOf(client);
connections.splice(position, 1);
});
}
function broadcast(data) {
for (c in connections) {
connections[c].send(data);
}
}
OK, websockets...
The "upgrade required" status marks the start of a websocket handshake. Normally your client sends this first to the WS server. The server answers in a pretty similar manner (details here : https://www.rfc-editor.org/rfc/rfc6455 ), and then proceed to pipe the actual data.
Here, you're opening a connection from your client as regular http, sending a simple GET. What you see on the screen is the server dumbly proceeding with an already corrupted handshake.
That's not how you open a WS client side connection. You don't usually open WS pages from the browser. It ought to be opened from a JavaScript call, such as new WebSocket(uri). So what you want is a regular http server on another port, that serves a page containing the necessary Javascript to open the actual WS connection and do something useful with its data. You'll find a clean example here : http://www.websocket.org/echo.html

Socket.IO not using fallback methods

I have a node server that's running a socket.io server and a client to work with it. Simple story, I need to be able to transfer messages between the two. This is working as intended in browsers that support web sockets but when a fallback method needs to be used its not working.
I should mention that pages are served from an apache server and the node server is only used for a specific page. The code that I am using is below, I've tinkered on this for a while and can't figure out how to fix it.
Also worth mentioning that when the page is opened in IE9(websockets not supported),
logging connection.io.engine.transport.name would give "websocket".
Client:
connection = io(window.location.protocol + '//localhost:8888', {
'reconnect': false,
'max reconnection attempts': 0,
'transports':
[
'websocket',
'flashsocket',
'htmlfile',
'xhr-polling',
'jsonp-polling'
]
});
connection.on('connect',function () {
console.log("Socket is open");
$('#dc-status').hide();
connection.emit('message',JSON.stringify(info));
connection.on('message',function (e) {
//DO SOMETHING WITH THE DATA RECIEVED
});
});
Server:
var ioserver = require('socket.io');
var io = ioserver.listen(8888);
var http = require("http");
console.log("server started...");
io.set('transports',[
'websocket',
'flashsocket',
'htmlfile',
'xhr-polling',
'jsonp-polling'
]);
io.sockets.on('connection', function(ws) {
var req;
var order;
var courier;
var after;
var session;
var options = {};
console.log("New client connected");
// console.log("Transport: " + io.transports[ws.id].name);
ws.on('message', function(data) {
//WORK WITH THE DATA RECEIVED
//NOT RELEVANT TO EXAMPLE
console.log('received: %s', data);
parsedData = JSON.parse(data);
});
ws.on('disconnect', function () {
console.log("Connection closed");
});
});
Ok, so after much struggle with this I have found a solution for making sockets work in old browsers.
As of version 1.0 Socket.io uses Engine.io instead of fallback methods, which takes care of transports.
To get a working solution I skipped using the Socket.io layer and used just Engine.io instead.
In the client you have something like
var connection = eio.Socket('host-address');
and then you just bind the regular events(e.g message, close).
And in the server part instead of require('Socket.IO'), you call require('Engine.IO'), example:
var engineio = require('engine.io');
var wss = engineio.listen(10101);
The binding is the same.

Node.js client and server game using Telnet

I'm trying to create a basic game (only text) using Node.js, and it's 'net' library.
I'm hitting a bit of a wall. I can't seem to figure out how to prompt the user to enter some information, and wait for the user to enter said information.
Here's the code I have so far. It's fairly basic, and will print out 2 lines for you when you run the client, and then hang. It's at that point I want the user to be able to enter information. I'm unsure of a few things here:
1. How do I enable the user to type? (more specifically, is it client or server side)
2. How do I send that data to the server when enter is pressed?
I have read up on some documentation as far as Telnet goes, and it leads me to my last question: is the Telnet module in Node really the right one for this, or is there a better alternative for client/server creation and communication?
Client Code:
var connect = require('net');
var client = connect.connect('80', 'localhost');
console.log('Connection Success!\n\n');
client.on('data', function(data) {
// Log the response from the HTTP server.
console.log('' + data);
}).on('connect', function() {
// Manually write an HTTP request.
//I'm assuming I could send data at this point here, on connect?
}).on('end', function() {
console.log('Disconnected');
});
Server Code:
var net = require('net');
var sockets = [];
function cleanInput(data) {
return data.toString().replace(/(\r\n|\n|\r)/gm,"");
}
function receiveData(socket, data) {
var cleanData = cleanInput(data);
if(cleanData === "quit") {
socket.end('Goodbye!\n');
}
else {
for(var i = 0; i<sockets.length; i++) {
if (sockets[i] !== socket) {
sockets[i].write(data);
}
}
}
}
function closeSocket(socket) {
var i = sockets.indexOf(socket);
if (i != -1) {
sockets.splice(i, 1);
}
}
function newSocket(socket) {
sockets.push(socket);
socket.write('Welcome to the Battleship Server!\n\n');
socket.write('Please enter a username: ');
socket.on('data', function(data) {
receiveData(socket, data);
})
socket.on('end', function() {
closeSocket(socket);
})
}
var server = net.createServer(newSocket);
server.listen(80);
Thanks in advance!
you want to use process.stdin and process.stdout to interact with input/output from/to the terminal. Have a look here.
You can send data to the server when enter is pressed with process.stdin.once('data', function(data){ /* ... */ }). The .once method ensures that the callback is called just once when the user hits enter.
The client code should look like:
var connect = require('net');
var client = connect.connect('80', 'localhost');
client.on('data', function(data) {
console.log('' + data);
process.stdin.once('data', function (chunk) {
client.write(chunk.toString());
});
}).on('connect', function() {
client.write('Hello');
}).on('end', function() {
console.log('Disconnected');
});

Categories