dnsPython use IP on interface - python

I use dsnPython in a project.
I use many resolvers same as explained at Set specific DNS server using dns.resolver (pythondns).
In order to send several requests I need to dispatch my request on many IPs.
I have some IPs on my interface eth0.
Do you know a way to send a request through an specific IP ?

It's possible by using resolvers and source attribute :
import dns.resolver
my_resolver.nameservers = ['8.8.8.8']
answer = my_resolver.query(
qname = fqdn_port,
source = '1.2.3.4',
)
8.8.8.8 is the resolver IP
1.2.3.4 is an IP of server

Related

How to get the IP of the default interface with Sanic

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)

HTTPS GET without DNS lookup

I need to HTTP-GET a document over TLS without having the process let the OS resolve the domain name.
The reason for this is that I know that the IP address I want to use will be correct, but fetching for https://123.123.123.123 won't give me a valid SSL certificate. Yet I know that https://example.com will have the correct certificate and be reachable at the address 123.123.123.123. The OS's resolver will very likely yield an outdated IP address in my use case.
How can I make a request to a given IP address by explicitly specifying which domain name to use for the SSL certificate?
A possible solution would be to update the /etc/hosts file and add an entry like 123.123.123.123 example.com, but it requires root and I don't want to be editing that file only for this purpose.
As shown in my answer below, monkey-patching can also be an approach, though I'm not sure if it is really reliable in the long term.
Monkey-patching socket.getaddrinfo can be a solution when using the resuests package.
import socket
import requests
getaddrinfo_original = socket.getaddrinfo
def getaddrinfo_patched(*args, **kwargs):
if args[0] in ['example.com']:
return [(
socket.AF_INET, # family
socket.SOCK_STREAM, # type
socket.IPPROTO_TCP, # proto
'', # canonname
('123.123.123.123', 443) # sockaddr
)]
return getaddrinfo_original(*args, **kwargs)
socket.getaddrinfo = getaddrinfo_patched
r = requests.get('https://example.com/')
print(r.status_code, r.content)
But in this case maybe socket.gethostbyname and other functions would need to get patched as well.

Resolve an IP from a specific DNS server in Python

I want to resolve an IP to a hostname from a specific DNS server.
socket.gethostbyaddr() uses default DNS server. I need to resolve ip with specific DNS server.
I saw dnspython but do not know how to specify the DNS server to use for reverse lookup.
Try this:
import dns.resolver
dns.resolver.default_resolver = dns.resolver.Resolver(configure=False)
dns.resolver.default_resolver.nameservers = ['8.8.8.8']
answers = dns.resolver.query(<addr>, 'PTR')
for rdata in answers:
print(rdata)

Python Requests, how to specify port for outgoing traffic?

I'm working on a project where we want to assign a whitelist packet filters for incoming traffic on a firewall and we are using python script with requests library to make some https requests to some servers outside of that network. For now the script is using ephemeral ports to connect to the servers, but we would like to make these https requests through specific ports. This would allow us to create strict whitelist for these ports.
How can I specify the port to the requests library through which the request should be sent? Script is currently using the following type of code to send the necessary requests.
response = requests.post(data[0], data=query, headers=headers, timeout=10)
This works, but I would now need to specify the port through which the http post request should be sent to allow for more strict packet filtering on the network. How could this port declaration be achieved? I have searched for solution to this from several sources already and came up with absolutely nothing.
requests is built on urllib3, which offers the ability to set a source address for connections; when you set the source address to ('', port_number) you tell it to use the default host name but pick a specific port.
You can set these options on the pool manager, and you tell requests to use a different pool manager by creating a new transport adapter:
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.poolmanager import PoolManager
class SourcePortAdapter(HTTPAdapter):
""""Transport adapter" that allows us to set the source port."""
def __init__(self, port, *args, **kwargs):
self._source_port = port
super(SourcePortAdapter, self).__init__(*args, **kwargs)
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(
num_pools=connections, maxsize=maxsize,
block=block, source_address=('', self._source_port))
Use this adapter in a session object, the following mounts the adapter for all HTTP and HTTPS connections, using 54321 as the source port:
s = requests.Session()
s.mount('http://', SourcePortAdapter(54321))
s.mount('https://', SourcePortAdapter(54321))
You can only set the one source port, limiting you to one active connection at a time. If you need to rotate between ports, register multiple adapters (one per URL) or re-register the catch-all mounts each time.
See the create_connection() utility function documentation for the details on the source_address option:
If source_address is set it must be a tuple of (host, port) for the socket to bind as a source address before making the connection. An host of '' or port 0 tells the OS to use the default.

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