Get remote client IP address in Deno - http

How to I get the IP address of the client in Deno?
I have created a test server using the standard http library but I can't figure out a way to extract client's IP.
I need that as a security feature for preventing multiple submissions.
In NodeJS/Express there is an ip property of the request object that does the same.
req.ip gives the thing I want in Express but what is it's equivalent in Deno?
My code is:
import { serve } from "https://deno.land/std#0.125.0/http/server.ts";
serve(
(req) => {
console.log(/* the client IP */);
return new Response("hello");
},
{ port: 8080 }
);
Is there any other work-around to prevent multiple access from the same device?
Thanks

To do this in a type-safe way is a little complicated because of the way that serve is typed. First, I'll show you an example of how to do it, then I'll explain the types afterward.
Example
example.ts:
import {
serve,
type ConnInfo,
type Handler,
type ServeInit,
} from 'https://deno.land/std#0.125.0/http/server.ts';
function assertIsNetAddr (addr: Deno.Addr): asserts addr is Deno.NetAddr {
if (!['tcp', 'udp'].includes(addr.transport)) {
throw new Error('Not a network address');
}
}
function getRemoteAddress (connInfo: ConnInfo): Deno.NetAddr {
assertIsNetAddr(connInfo.remoteAddr);
return connInfo.remoteAddr;
}
const handler: Handler = (request, connInfo) => {
const {hostname, port} = getRemoteAddress(connInfo);
const message = `You connected from the following address: ${hostname}`;
return new Response(message);
};
const init: ServeInit = {port: 8080};
serve(handler, init);
console.log(`Listening on port ${init.port}...\nUse ctrl+c to stop`);
Types
Looking at the documentation for the serve function, you can see that it accepts two parameters: a callback of type Handler, and some options of type ServeInit:
async function serve(handler: Handler, options?: ServeInit): Promise<void>;
The Handler callback accepts two parameters: a Request, and an object of type ConnInfo:
type Handler = (request: Request, connInfo: ConnInfo) => Response | Promise<Response>;
ConnInfo looks like this:
interface ConnInfo {
readonly localAddr: Deno.Addr;
readonly remoteAddr: Deno.Addr;
}
The part that should have the remote IP address (technically, it's the remote hostname, but it's very likely to be an IP address unless you have configured custom DNS settings in your server environment) is the object at connInfo.remoteAddr, which (you can see above) is of type Deno.Addr, which looks like this:
// in the Deno namespace
type Addr = NetAddr | UnixAddr;
This is where it becomes complicated. Deno.Addr is a discriminated union of Deno.NetAddr and Deno.UnixAddr (which means that it could be either one), and the property transport is used to discriminate between the two.
// in the Deno namespace
interface NetAddr {
hostname: string;
port: number;
transport: "tcp" | "udp";
}
interface UnixAddr {
path: string;
transport: "unix" | "unixpacket";
}
A net address has hostname property (the value of which would be the IP address) and a port property, while a unix address has a path property.
The listener which is created internally to support the server is actually only listening on TCP, so I think it's safe to assume that the remote address will be a net address. However, because the type signature of the Handler callback parameter in the serve function doesn't make this explicit (although it should!), TypeScript doesn't know that.
So, it's left up to you as the programmer to make sure that the address is actually a net address before you can access properties that would be on a net address (instead of a unix address) in a type-safe way. That's where the type assertion function assertIsNetAddr comes into play. (A type assertion performs a runtime test which results in a "guarantee" of a condition to the compiler: by throwing an exception if the condition can't be guaranteed.) Because you as the programmer already know more than the TypeScript compiler (that the address is on TCP and will be a net address), you can assert that the address is indeed a net address. Then the compiler will allow you use the address as a net address.
If you want to do something besides throwing an Error in the case that the address is not a net address: instead of an assertion function, you can use a type predicate as a condition in your code.
Here is a link to the TypeScript Playground where I've created a playground with the types used in my example, so you can explore/experiment.
Finally, (this is not type-safe) if you just want to use the value without the checks (because you've done your research and are confident that you will never handle a non-TCP connection, you can simply use a type assertion:
const handler: Handler = (request, connInfo) => {
const {hostname, port} = connInfo.remoteAddr as Deno.NetAddr;
const message = `You connected from the following address: ${hostname}`;
return new Response(message);
};

Related

Your request is prohibited because the request is on loopback from external IP

Getting this error when trying to save entity in local dynamodb ,i am running my application also locally .My all request are routed through a proxy which i can't disable due to organisation policy .Is there any way to avoid this error ?
#RequestMapping(value = "/dynamoPost", method = {RequestMethod.POST}, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody
public TxnClass testPost(#RequestBody TxnClass input) {
TxnClass response = txnInfoRepository.save(input);
System.out.println("saved successfully");
return response;
}
error :-
om.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
at [Source: (byte[])"Request on loopback from external IP
Request on loopback from external IP
Description: Your request is prohibited because the request is on loopback from external IP.

How to get the client IP adress using HTTP.jl

I'm trying to get the both the client request and IP address from http requests to my HTTP.jl server (based on the basic server example in the docs).
using HTTP
using Sockets
const APP = HTTP.Router()
# My request handler function can see the request's method
# and target but not the IP address it came from
HTTP.#register(APP,"GET","/",req::HTTP.Request -> begin
println("$(req.method) request to $(req.target)")
"Hello, world!"
end)
HTTP.serve(
APP,
Sockets.localhost,
8081;
# My tcpisvalid function can see the client's
# IP address but not the HTTP request
tcpisvalid=sock::Sockets.TCPSocket -> begin
host, port = Sockets.getpeername(sock)
println("Request from $host:$port")
true
end
)
My best guess would be that there's a way to parse the TCPSocket.buffer into an HTTP request but I can't find any methods to do it.
Can you suggest a way to get an HTTP.Request from a TCPSocket or a different way to approach this problem?
Thanks in advance!
The router (APP) is a (collection of) "request handler(s)" which can only access the HTTP.Request -- you can not get the stream from it. Instead you can define a "stream handler", which is passed the stream. From the stream you can get the client's IP adress using Sockets.getpeername (requires HTTP.jl version 0.9.7 when called on a HTTP.Stream as in the examples below).
using HTTP, Sockets
const APP = HTTP.Router()
function request_handler(req::HTTP.Request)
println("$(req.method) request to $(req.target)")
return "Hello, world!"
end
HTTP.#register APP "GET" "/" request_handler
function stream_handler(http::HTTP.Stream)
host, port = Sockets.getpeername(http)
println("Request from $host:$port")
return HTTP.handle(APP, http) # regular handling
end
# HTTP.serve with stream=true to specify that stream_handler is a function
# that expects a HTTP.Stream as input (and not a HTTP.Request)
HTTP.serve(stream_handler, Sockets.localhost, 8081; stream=true) # <-- Note stream=true
# or HTTP.listen
HTTP.listen(stream_handler, Sockets.localhost, 8081)

Firebase Function - Client IP always Internal to Google

I am writing a Firebase Function, specifically for Dialogflow chatbot fulfillment. I am having trouble getting an accurate client IP address regardless of how I am testing it.
I've seen on various posts the various ways to read client IP, but they are either undefined or an internal Google IP from one of their data centers.
I've tried reading:
"x-forwarded-for" header
req.connection.remoteAddress
req.ip
req.ips (collection of all of them, there is only ever 1 in the collection)
Any help would be much appreciated. I am trying to log analaytics around user interactions, and right now the IPs are all incorrect.
I've tried the following code which is provided here:
const functions = require('firebase-functions');
const util = require('util');
exports.helloWorld = functions.https.onRequest((req, res) => {
// For Firebase Hosting URIs, use req.headers['fastly-client-ip']
// For callable functions, use rawRequest
// Some users have better success with req.headers['x-appengine-user-ip']
const ipAddress = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const headers = JSON.stringify(req.headers, null, 2);
const message = util.format("<pre>Hello world!\n\nYour IP address: %s\n\nRequest headers: %s</pre>", ipAddress, headers);
res.send(message);
});
When tested (even with mobile data), it returned the public IP of the caller and not a Google Internal IP.
If try this, do you continue getting internal IPs?

SignalR connect error

I use SignalR 2.0.0 Win2012 iis8 with two environment with two different ips.
one environment service is up and second is down(purposely)
use websocket protocol.
i have the following scenario:
When i connect to first environment and want to connect to the second.
i disconnected from first environment and try connect to second environment i get error(its correct behavior)
i try to reconnect back to the first environment but I get still the same error.
the error is "Error during negotiation request."
after refresh the browser i can connect success again to first environment.
What am i doing wrong?
this is part of my code:
function connect(host)
{
var hubConnection = $.hubConnection.('');
hubConnection.url = host;
hubConnection.start()
.done(open)
.fail(error);
}
function open()
{
console.log('login success')
}
function disconnect()
{
var self = this,
hubConnection = $.hubConnection("");
console.log('disconnect ')
hubConnection.stop(true, true);
}
function error(error)
{
var self = this,
hubConnection = $.hubConnection("");
console.log('connection error ')
if(error && hubConnection.state !== $.connection.connectionState.connected)
{
.....
.....
//logic detemninate wich environment ip was previous
connect(environment ip)
}
}
//occured when button disconnect clicked
function disconnectFromFirstEnvironmentAndConnectToSecond()
{
disconect();
connect(second environment ip);
}
.....
.....
connect(first environment ip);
You're not retaining your first connection reference.
Aka you create a HubConnection and then never capture it in a scope that can be used later; therefore when you disconnect later the connection.stop does nothing because it's not calling stop on the HubConnection that was originally started.
This could ultimately lead to you having too many concurrently open requests which will then not allow you to negotiate with a server hence your error.
I'd recommend fixing how you stop/start connections. Next if the issue still occurs I'd inspect the network traffic to ensure that valid requests are being made.

How to get the user IP address in Meteor server?

I would like to get the user IP address in my meteor application, on the server side, so that I can log the IP address with a bunch of things (for example: non-registered users subscribing to a mailing list, or just doing anything important).
I know that the IP address 'seen' by the server can be different than the real source address when there are reverse proxies involved. In such situations, X-Forwarded-For header should be parsed to get the real public IP address of the user. Note that parsing X-Forwarded-For should not be automatic (see http://www.openinfo.co.uk/apache/index.html for a discussion of potential security issues).
External reference: This question came up on the meteor-talk mailing list in august 2012 (no solution offered).
1 - Without a http request, in the functions you should be able to get the clientIP with:
clientIP = this.connection.clientAddress;
//EX: you declare a submitForm function with Meteor.methods and
//you call it from the client with Meteor.call().
//In submitForm function you will have access to the client address as above
2 - With a http request and using iron-router and its Router.map function:
In the action function of the targeted route use:
clientIp = this.request.connection.remoteAddress;
3 - using Meteor.onConnection function:
Meteor.onConnection(function(conn) {
console.log(conn.clientAddress);
});
Similar to the TimDog answer but works with newer versions of Meteor:
var Fiber = Npm.require('fibers');
__meteor_bootstrap__.app
.use(function(req, res, next) {
Fiber(function () {
console.info(req.connection.remoteAddress);
next();
}).run();
});
This needs to be in your top-level server code (not in Meteor.startup)
This answer https://stackoverflow.com/a/22657421/2845061 already does a good job on showing how to get the client IP address.
I just want to note that if your app is served behind proxy servers (usually happens), you will need to set the HTTP_FORWARDED_COUNT environment variable to the number of proxies you are using.
Ref: https://docs.meteor.com/api/connections.html#Meteor-onConnection
You could do this in your server code:
Meteor.userIPMap = [];
__meteor_bootstrap__.app.on("request", function(req, res) {
var uid = Meteor.userId();
if (!uid) uid = "anonymous";
if (!_.any(Meteor.userIPMap, function(m) { m.userid === uid; })) {
Meteor.userIPMap.push({userid: uid, ip: req.connection.remoteAddress });
}
});
You'll then have a Meteor.userIPMap with a map of userids to ip addresses (to accommodate the x-forwarded-for header, use this function inside the above).
Three notes: (1) this will fire whenever there is a request in your app, so I'm not sure what kind of performance hit this will cause; (2) the __meteor_bootstrap__ object is going away soon I think with a forthcoming revamped package system; and (3) the anonymous user needs better handling here..you'll need a way to attach an anonymous user to an IP by a unique, persistent constraint in their request object.
You have to hook into the server sessions and grab the ip of the current user:
Meteor.userIP = function(uid) {
var k, ret, s, ss, _ref, _ref1, _ref2, _ref3;
ret = {};
if (uid != null) {
_ref = Meteor.default_server.sessions;
for (k in _ref) {
ss = _ref[k];
if (ss.userId === uid) {
s = ss;
}
}
if (s) {
ret.forwardedFor = ( _ref1 = s.socket) != null ?
( _ref2 = _ref1.headers) != null ?
_ref2['x-forwarded-for'] : void 0 : void 0;
ret.remoteAddress = ( _ref3 = s.socket) != null ?
_ref3.remoteAddress : void 0;
}
}
return ret.forwardedFor ? ret.forwardedFor : ret.remoteAddress;
};
Of course you will need the current user to be logged in. If you need it for anonymous users as well follow this post I wrote.
P.S. I know it's an old thread but it lacked a full answer or had code that no longer works.
Here's a way that has worked for me to get a client's IP address from anywhere on the server, without using additional packages. Working in Meteor 0.7 and should work in earlier versions as well.
On the client, get the socket URL (unique) and send it to the server. You can view the socket URL in the web console (under Network in Chrome and Safari).
socket_url = Meteor.default_connection._stream.socket._transport.url
Meteor.call('clientIP', socket_url)
Then, on the server, use the client's socket URL to find their IP in Meteor.server.sessions.
sr = socket_url.split('/')
socket_path = "/"+sr[sr.length-4]+"/"+sr[sr.length-3]+"/"+sr[sr.length-2]+"/"+sr[sr.length-1]
_.each(_.values(Meteor.server.sessions), (session) ->
if session.socket.url == socket_path
user_ip = session.socket.remoteAddress
)
user_ip now contains the connected client's IP address.

Resources