Using socket.io from inside and outside LAN - node.js

I am using socket.io for a project that requires me to stream live data to a browser. Now the server part is fine, but on socket.io client io.connect() call, I have had a few problems in the past, without proper resolution. I have tried the usual suspects, but I am not getting through.
My client call to looks like this:
var socket = io.connect('http://10.95.xx.xx:5002');
where 10.95. .. is the IP address of the server on the LAN. Using this IP I can access the server from inside the LAN, on any machine in the LAN, but this doesn't work from outside the LAN. Problem being, most probably, that this IP is for LAN only and not the public IP. As per university's network settings, the public IP is 128.95 ...and if I change it to this IP, then it will be accessible from outside, but not from inside.
One way to resolve this, is that I try to read the client's IP address and based on that decide the parameter IP. But this doesn't feel robust. Also, I tried using io.connect("/") . It had worked for me in previous circumstance, but it did not this time. Also, the empty call did not work io.connect() did not work.

As you said you can use the clients IP address, this here always worked for me:
var port = window.location.port,
host = window.location.hostname,
protocol = window.location.protocol,
path = '/',
url,
options = { };
if( protocol.indexOf( 'https' ) > -1 ) {
protocol = 'wss:';
} else {
protocol = 'ws:'
}
url = protocol + "//" + host + ":" + port + path;
options = { };
/*
// If you wanted to add an access token, "Session" is where I store this
if( Session.token ) {
options.query = 'access_token=' + Session.token;
}
*/
socket = io( url, options );

Related

net module "socket.remoteAddress" value is wrong

I create a TCP IVP4 socket server in Node.js using the 'net' module, and it works great for almost all of my clients, but one client has an IP similar to (I won't disclose the real one):
180.190.154.97
but when they connect to my socket server and I console.log(socket.remoteAddress), the value is this:
180.190.193.2
Why would this be happening?
// Import dependencies
let net = require('net')
// Create socket server.
const SERVER = net.createServer(socketConnection)
function socketConnection(client) {
console.log(client.remoteAddress)
}
// Server code snippet
let SERVER_LISTEN_ADDRESS = '0.0.0.0'
if (Game.local) {
SERVER_LISTEN_ADDRESS = '127.0.0.1'
Game.port = 42480
}
SERVER.listen(Game.port, SERVER_LISTEN_ADDRESS, () => {
console.log('Listening on port ' + Game.port)
if (!Game.local)
postServer()
else
console.log('Running server locally.')
})
Expected result is that it should print their IPV4 value from a site like:
https://whatismyipaddress.com/
But instead it says something completely different.
The result shown by whatipmyaddress it is not always the public IP of the single host. It could be rather also the IP of the subnet or if there's a proxy the IP of the proxy server. The fact that the address differs by a few units suggests that those particular hosts are in a subnet where a NAT service is active, or protected by a proxy or where the router acts as a VPN to the outside, encapsulating and sending the message to the server. In this case probably the entire subnet is seen as a single host.
In case you're using Express, you may want to use request-ip package in order to retrieve the IP in a more robust way. Since it seems you're not using express you should implement something similar to the first example shown in the link. For your specific case according the code you give:
const requestIp = require('request-ip');
function socketConnection(client) {
const clientIp = requestIp.getClientIp(client);
console.log(client);
}

Connecting to Websocket in OpenShift Online Next Gen Starter

I'm in the process of trying to get an application which I'd built on the old OpenShift Online 2 free service up and running on the new OpenShift Online 3 Starter, and I'm having a bit of trouble.
The application uses websocket, and in the old system all that was required was for the client to connect to my server on port 8443 (which was automatically routed to my server). That doesn't seem to work in the new setup however - the connection just times out - and I haven't been able to find any documentation about using websocket in the new system.
My first thought was that I needed an additional rout, but 8080 is the only port option available for routing as far as I can see.
The app lives here, and the connection is made on line 21 of this script with the line:
this.socket = new WebSocket( 'wss://' + this.server + ':' + this.port, 'tabletop-protocol' );
Which becomes, in practice:
this.socket = new WebSocket( 'wss://production-instanttabletop.7e14.starter-us-west-2.openshiftapps.com:8443/', 'tabletop-protocol' );
On the back end, the server setup is unchanged from what I had on OpenShift 2, aside from updating the IP and port lookup from env as needed, and adding logging to help diagnose the issues I've been having.
For reference, here's the node.js server code (with the logic trimmed out):
var http = require( "http" );
var ws = require( "websocket" ).server;
// Trimmed some others used by the logic...
var ip = process.env.IP || process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0';
var port = process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8080;
/* FILE SERVER */
// Create a static file server for the client page
var pageHost = http.createServer( function( request, response ){
// Simple file server that seems to be working, if a bit slowly
// ...
} ).listen( port, ip );
/* WEBSOCKET */
// Create a websocket server for ongoing communications
var wsConnections = [];
wsServer = new ws( { httpServer: pageHost } );
// Start listening for events on the server
wsServer.on( 'request', function( request ){
// Server logic for the app, but nothing in here ever gets hit
// ...
} );
In another question it was suggested that nearly anything - including this -
could be related to the to the ongoing general issues with US West 2, but other related problems I was experiencing seem to have cleared, and that issue has been posted for a week with no update, so I figured I'd dig deeper into this on the assumption that it's something I'm doing wrong instead of them.
Anyone know more about this and what I need to do to make it work?

Stripping "::ffff:" prefix from request.connection.remoteAddress nodejs

I am implementing subscription/response possibility using nodejs (express). When visitor send request, beside other parameters within request (port, time interval etc...) I am going to collect ip in order to be able to send response to that ip from time to time.
I am fetching visitor ip address using folowing:
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
based on How can I get the user's IP address using Node.js?
Point is that after i get ip I have something like this : "::ffff:192.168.1.10" (explained at request.connection.remoteAddress Now Prefixed in ::ffff in node.js )
I am wondering, is it "safe" just to strip "::ffff:" prefix in order to get ip address which I will be able to use in order to reply via http response, or I am missing something else over here, and that is not what i should do?
Yes, it's safe to strip. Here's a quick way to do it.
address.replace(/^.*:/, '')
What happens is your OS is listening with a hybrid IPv4-IPv6 socket, which converts any IPv4 address to IPv6, by embedding it within the IPv4-mapped IPv6 address format. This format just prefixes the IPv4 address with :ffff:, so you can recover the original IPv4 address by just stripping the :ffff:. (Some deprecated mappings prefix with :: instead of :ffff:, so we use the regex /^.*:/ to match both forms.)
If you aren't sure that incoming IPv6 address came from an IPv4, you can check that it matches the IPv6-mapping template first:
template = /^:(ffff)?:(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/
has_ipv4_version = template.test(address)
If you want to get the ip address of IPv4, you can use:
http.createServer(callback).listen(port, '0.0.0.0');
then, you will get what you want
req.connection.remoteAddress // 192.168.1.10
Here is the relevant document of nodejs
app.post('/xyz',function(request,response)
{
var IPFromRequest=request.connection.remoteAddress;
var indexOfColon = IPFromRequest.lastIndexOf(':');
var IP = IPFromRequest.substring(indexOfColon+1,IPFromRequest.length);
console.log(IP);
});
I would split the remoteAddress ::ffff:192.168.1.10 by the : delimiter and simply take the value of the output array at index array.length - 1
like so:
const remoteAddress = '::ffff:192.168.0.3'
const array = remoteAddress.split(':')
const remoteIP = array[array.length - 1]
console.log(remoteIP)
prints out 192.168.0.3
This node code ...
returns ipv6 to ipv4 IF the ipv6 address is really a mapped ipv4 address
else it returns a normalised ipv6 address
else it just returns the ip string it originally had
var ip = (function (req) {
var ipaddr = require('ipaddr.js');
var ipString = (req.headers["X-Forwarded-For"] ||
req.headers["x-forwarded-for"] ||
'').split(',')[0] ||
req.connection.remoteAddress;
if (ipaddr.isValid(ipString)) {
try {
var addr = ipaddr.parse(ipString);
if (ipaddr.IPv6.isValid(ipString) && addr.isIPv4MappedAddress()) {
return addr.toIPv4Address().toString();
}
return addr.toNormalizedString();
} catch (e) {
return ipString;
}
}
return 'unknown';
}(req));
https://www.npmjs.com/package/ipaddr.js
https://github.com/whitequark/ipaddr.js
Just to add onto the answer provided by Michael Matthew Toomim,
If you want to test to see if the IP is an IPv4 address mapped as an IPv6, you will probably want to adjust the regex to this:
/^:{1,2}(ffff)?:(?!0)(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/
The difference being /^:{1,2} instead of /^:, which allows for both addresses which start with ::ffff: and :ffff:.
Had the same problem...im also new at javascript but i solved this with .slice
var ip = req.connection.remoteAddress;
if (ip.length < 15)
{
ip = ip;
}
else
{
var nyIP = ip.slice(7);
ip = nyIP;
}
req.connection.remoteAddress.substring(7,req.connection.remoteAddress.length)

In Node.js (running on Windows) how to get the domain name

I have requirement where my NodeJS http server (on Windows) has to listen on the hostname instead of localhost/127.0.0.1.
For this I need the full hostname (including the domain name) of my Windows machine and I am not able to get the full hostname.
I tried using
require('os').hostname()
but it does not give me the full hostname.
So I tried the below:
var dns = require('dns');
var os = require('os');
var hostname = os.hostname();
console.log("Short hostname = ", hostname);
dns.lookup(hostname, function (err, add, fam) {
if (err)
{
console.log("The error = ", JSON.stringify(err));
return;
}
console.log('addr: '+add);
console.log('family: ' + fam);
dns.reverse(add, function(err, domains){
if (err)
{
console.log("The reverse lookup error = ", JSON.stringify(err));
return;
}
console.log("The full domain name ", domains);
});
})
The above code works fine and I get the below output when I am on the ethernet of my enterprise
C:\>node getFullHostname.js
Short hostname = SPANUGANTI
addr: 16.190.58.214
family: 4
The full domain name [ 'spanuganti.abc.xyz.net' ]
But the same code does not work when I am connected to the wireless network of the enterprise
C:\>node getFullHostname.js
Short hostname = SPANUGANTI
addr: 16.183.204.47
family: 4
The reverse lookup error =
{"code":"ENOTFOUND","errno":"ENOTFOUND","syscall":"getHostByAddr"}
So need help on the below
Is there a simple way to get the full machine name (Windows) in Node.js
Or please let me know what is the problem with my code above
Servers don't listen on hostnames, they listen on IP addresses. You should never rely on the contents of the external DNS to figure out where to bind.
The easiest solution is to have it bind to the INADDR_ANY address, which is numerically equivalent to 0.0.0.0.
This would then allow queries on any interface, including the loopback.
You don't need the full hostname to listen on, you actually need your IP address. Check out the os.networkInterfaces() function.

Change port without losing data

I'm building a settings manager for my http server. I want to be able to change settings without having to kill the whole process. One of the settings I would like to be able to change is change the port number, and I've come up with a variety of solutions:
Kill the process and restart it
Call server.close() and then do the first approach
Call server.close() and initialize a new server in the same process
The problem is, I'm not sure what the repercussions of each approach is. I know that the first will work, but I'd really like to accomplish these things:
Respond to existing requests without accepting new ones
Maintain data in memory on the new server
Lose as little uptime as possible
Is there any way to get everything I want? The API for server.close() gives me hope:
server.close(): Stops the server from accepting new connections.
My server will only be accessible by clients I create and by a very limited number of clients connecting through a browser, so I will be able to notify them of a port change. I understand that changing ports is generally a bad idea, but I want to allow for the edge-case where it is convenient or possibly necessary.
P.S. I'm using connect if that changes anything.
P.P.S. Relatively unrelated, but what would change if I were to use UNIX server sockets or change the host name? This might be a more relevant use-case.
P.P.P.S. This code illustrates the problem of using server.close(). None of the previous servers are killed, but more are created with access to the same resources...
var http = require("http");
var server = false,
curPort = 8888;
function OnRequest(req,res){
res.end("You are on port " + curPort);
CreateServer(curPort + 1);
}
function CreateServer(port){
if(server){
server.close();
server = false;
}
curPort = port;
server = http.createServer(OnRequest);
server.listen(curPort);
}
CreateServer(curPort);
Resources:
http://nodejs.org/docs/v0.4.4/api/http.html#server.close
I tested the close() function. It seems to do absolute nothing. The server still accepts connections on his port. restarting the process was the only way for me.
I used the following code:
var http = require("http");
var server = false;
function OnRequest(req,res){
res.end("server now listens on port "+8889);
CreateServer(8889);
}
function CreateServer(port){
if(server){
server.close();
server = false;
}
server = http.createServer(OnRequest);
server.listen(port);
}
CreateServer(8888);
I was about to file an issue on the node github page when I decided to test my code thoroughly to see if it really is a bug (I hate filing bug reports when it's user error). I realized that the problem only manifests itself in the browser, because apparently browsers do some weird kind of HTTP request keep alive thing where it can still access dead ports because there's still a connection with the server.
What I've learned is this:
Browser caches keep ports alive unless the process on the server is killed
Utilities that do not keep caches by default (curl, wget, etc) work as expected
HTTP requests in node also don't keep the same type of cache that browsers do
For example, I used this code to prove that node http clients don't have access to old ports:
Client-side code:
var http = require('http'),
client,
request;
function createClient (port) {
client = http.createClient(port, 'localhost');
request = client.request('GET', '/create');
request.end();
request.on('response', function (response) {
response.on('end', function () {
console.log("Request ended on port " + port);
setTimeout(function () {
createClient(port);
}, 5000);
});
});
}
createClient(8888);
And server-side code:
var http = require("http");
var server,
curPort = 8888;
function CreateServer(port){
if(server){
server.close();
server = undefined;
}
curPort = port;
server = http.createServer(function (req, res) {
res.end("You are on port " + curPort);
if (req.url === "/create") {
CreateServer(curPort);
}
});
server.listen(curPort);
console.log("Server listening on port " + curPort);
}
CreateServer(curPort);
Thanks everyone for the responses.
What about using cluster?
http://learnboost.github.com/cluster/docs/reload.html
It looks interesting!

Resources