How to get the IP of the default interface with Sanic - python

Is it possible to get the IP of the default interface with Sanic?
Here is how I do it with Socket. The idea is to do the same thing with Sanic.
import socket
hostname = socket.gethostname()
IP_address = socket.gethostbyname(hostname)
print(IP_address) # 192.168.1.239

It depends upon what information you want and how the app is being served (reverse proxy, etc).
Check out these values:
request.ip (connected interface)
request.remote_addr (likely what you want https://sanic.readthedocs.io/en/stable/sanic/api/core.html#sanic.request.Request.remote_addr)
request.conn_info (object with a bunch of details you may want)

Related

How to make Python HTTP server have custom url instead of localhost?

I'm using the HTTP server module in Python 3 and I want to make my local HTTP server have a custom url instead of "localhost:8080". Is there any way for me to do this in Python alone without changing default OS settings like the settings in the host file?
When you can setup your router, you must make a port-forwarding. (You usually can setup it on http://192.168.0.1/)
Then you can use that url: "http:// Your Global IP:8080/" for example, when my global IP address is 4.4.0.0, than "http://4.4.0.0:8080/".
When you use the port 80 instead of 8080 (you can setup in port forwarding), it is the professional port of http server, your url will be (with the last example ip): "http://4.4.0.0/"
You can see your global IP address for example on this page: https://whatismyipaddress.com/
or from python code:
gloval_IP = get('https://api.ipify.org').text

Get Twisted server's IP address

If I have a Twisted server, how can I find its public-facing IP address?
Take this trivial echo server example:
from twisted.internet import protocol, reactor, endpoints
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
server_endpoint = endpoints.serverFromString(reactor, "tcp:1234")
listening_port_deferred = server_endpoint.listen(EchoFactory())
reactor.run()
I was expecting something like server_endpoint.getHost(), but I can't see that TCP4ServerEndpoint offers anything useful.
By adding the following lines before reactor.run(), we can see that the server is listening on all interfaces (0.0.0.0):
def print_host(listening_port):
print("listening_port.getHost():", listening_port.getHost())
listening_port_deferred.addCallback(print_host)
It outputs listening_port.getHost(): IPv4Address(type='TCP', host='0.0.0.0', port=1234). But that doesn't help us with the IP address of the network interface of the server.
We can get the IP address of the client by adding the following as the first line of buildProtocol():
print("Client's address:", addr.host)
But that only gives us the client's address.
How should I get the server's IP address?
Twisted will tell you the address you've bound the server to using just the method you found, getHost on the listening port. Unfortunately, it has the big limitation that you found which is that when the server is listening on all local addresses (INADDR_ANY) it gives you 0.0.0.0 (the canonical IPv4 dotted-quad representation of INADDR_ANY).
When this happens, you have to go outside of Twisted. I've found the netifaces package to be pretty good for this. From the docs:
>>> netifaces.interfaces()
['lo0', 'gif0', 'stf0', 'en0', 'en1', 'fw0']
>>> >>> addrs = netifaces.ifaddresses('lo0')
>>> addrs[netifaces.AF_INET]
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]
By combining this information with the observation that 0.0.0.0 means "all local addresses" you can figure out what local addresses the server will accept connections on.
Thanks to notorious's comment, I realised that the server's IP address is available only once a client connects. However, as Jean-Paul points out, this IP address isn't necessarily public-facing and may well be behind a NATing router.
To obtain the server's IP address for a given connection, we can use the getHost() method from the transport attribute of the Protocol class. This is documented in the ITransport interface.
For example, if we add the following method into the Echo protocol class in the original question, each time a client connects, the server will print out the IP address that was used.
def connectionMade(self):
print("IP address of host given connection:", self.transport.getHost())
So, for example, if you connect from the same machine on which the server is running, you will see:
IP address of host given connection: IPv4Address(type='TCP', host='127.0.0.1', port=1234)
However, if you connect from another machine on the same network, you might see:
IP address of host given connection: IPv4Address(type='TCP', host='192.168.5.103', port=1234)

Accessing device on local network through server hosted webhook

I have a python script that acts as a webhook. A part of it is as follows:
import json
import os
import urllib
import socket
import _thread
from flask import Flask
from flask import request
from flask import make_response
app=Flask(__name__)
ip = ('192.168.1.75', 9050)
#app.route('/webhook',methods=['GET','POST'])
def webhook():
_thread.start_new_thread(sendDataToDevice,(ip))
req = request.get_json(silent=True,force=True)
print("Request:")
print(json.dumps(req,indent=4))
res=makeWebHookResult(req)
res=json.dumps(res,indent=4)
r=make_response(res)
r.headers['Content-Type']='application/json'
return r
if __name__ == '__main__':
app.run(port=8080,host='localhost')
The function of the script is to send some data to a device connected to the local network.
It works flawlessly when I open my web browser and type the following on the url bar:
http://localhost:8080/webhook
I want to host the script on a server, eg. Heroku. How can I access the local device in that case?
Note: I know I can run the script on my local machine and make it visible to the internet using ngrok, but I want to keep it accessible even when my computer is switched off. Also, want a fixed link, and the links given by ngrok change on every run.
I've faced a similar issue before with IoT. Unfortunately there is no simple way to make a device be visible online. Here's a simple solution I've used. It might not be the best, but it works.
DDNS + Port Forwarding + Static IP
If you have access to your local WiFi router, then you can setup something called as DDNS (Dynamic Domain Name System). Your router will then connect to a DDNS service provider like no-ip (www.noip.com) and it will be visible on the internet. You can give a custom URL like susmit-home.noip.com.
However susmit-home.noip.com will now point only to your WiFi router and not your WiFi network. So if you want to access the local device_ip and device_port such as "192.168.1.75", 9050. Then you can setup Port Forwarding on your router for that local IP-Port combination. Usually the setup looks like this:
Local IP: device_ip (e.g. 192.168.1.75)
Local Port: device_port (e.g. 9050)
Outbound Port: any_port (e.g. 9050)
Make sure that your device_ip is a static IP on your WiFi router so that it doesn't change.
Finally in your code you can just replace the line ip = ('192.168.1.75', 9050) with ip = ('susmit-home.noip.com', 9050).
Other solutions:
A slightly more complicated solution is setting up a VPN, such that your local network and your remote server (e.g. Heroku) will all be available to each other as if they were within the same local network.
If your device is a computer or a Raspberry Pi, then you can use SSH Remote Port Forwarding to have access to your local device from the remote server.

Python sockets: How do I get the address a client is connected to?

I have some code that hosts a local server and when a user connects it will send them some html code, which works fine.
But I want it so if they connect to http://localhost:90/abc it will show something different. How can I get the exact url they connected to?
Here is my code:
import socket
sock = socket.socket()
sock.bind(('', 90))
sock.listen(5)
print("Listening...")
while True:
client, address = sock.accept()
print("Connection recieved: ", address)
print(The exact url they connected to.)
print()
client.send(b'HTTP/1.0 200 OK\r\n')
client.send(b"Content-Type: text/html\r\n\r\n")
client.send(b'<html><body><h1>Hello, User!</body></html>')
client.close()
sock.close()
I tried print(client.getpeername()[1]), but that gets the client ip, and if there is a similar way to get the ip they connected to it probably wont get the 'abc' part of the url.
Thanks in advance.
Socket's don't have a notion of URL, that's specific to the HTTP protocol which runs on top of a socket. For this reason, only part of the HTTP URL is even used in the creation of a socket.
|--1---|----2----|-3-|--4-|
http:// localhost :90 /abc
Specifies which protocol inside of TCP the URL uses
Specifies the remote host, either by IP address or hostname
Specifies the remote port and is optional
Specifies the path of the URL
Only parts 2 and 3 are actually known to a TCP socket though! This is because TCP is a very basic form of communication, HTTP adds a bunch of functionality on top of it like requests and responses and paths and so on.
Basically if you're implementing an HTTP server, knowing the /abc part is your job. Take a look at this example. The client actually sends the /abc part to the server, otherwise it has no way of knowing which path the request is for.
When the client connects to your server, it will send:
GET /abc HTTP/1.1
Host: localhost
more headers...
<blank line>
Your server needs to parse the GET line and extract /abc from that.

How to get client IP address using python bottle framework

I need client IP address using python. I have tried below code but its not working in server:
from socket import gethostname, gethostbyname
ip = gethostbyname(gethostname())
print ip
On the server, I get '127.0.0.1' every time. Is there any way to find IP address of the client?
You're getting the IP address of your server, not of your server's clients.
You want to look at the request's REMOTE_ADDR, like this:
from bottle import Bottle, request
app = Bottle()
#app.route('/hello')
def hello():
client_ip = request.environ.get('REMOTE_ADDR')
return ['Your IP is: {}\n'.format(client_ip)]
app.run(host='0.0.0.0', port=8080)
EDIT #1: Some folks have observed that, for them, the value of REMOTE_ADDR is always the same IP address (usually 127.0.0.1). This is because they're behind a proxy (or load balancer). In this case, the client's original IP address is typically stored in header HTTP_X_FORWARDED_FOR. The following code will work in either case:
#app.route('/hello')
def hello():
client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
return ['Your IP is: {}\n'.format(client_ip)]
EDIT #2: Thanks to #ArtOfWarfare's comment, I learned that REMOTE_ADDR is not required per PEP-333. Couple of observations:
The CGI spec does require REMOTE_ADDR:
The REMOTE_ADDR variable MUST be set to the network address of the client sending the request to the server.
However, PEP-333 does not explicitly require HTTP_REMOTE_ADDR, only going as far as this (emphasis mine):
A server or gateway SHOULD attempt to provide as many other CGI variables as are applicable.
All of the (admittedly few) web frameworks that I'm familiar with set HTTP_REMOTE_ADDR. AFAICT, it's a de facto "standard." But technically, YMMV.
Server might be behind a proxy. Use this for proxy and forward support:
request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
If you're trying to get the external IP, you will need to get it from an external source, i.e whatismyip.com or somewhere that offers an api. If that's what you're looking for, take a look at the Requests module http://docs.python-requests.org/

Categories