Find OS generated port before using socket.connect() in Python - python

I would like to ask if there any way to to create a socket in Python, using:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
And then, extract the port which going to be generated by the OS, before using:
sock.connect((SERVER_IP, SERVER_PORT)
i.e. my goal is to find the source port of the first SYN packet which will be sent to the server, as part of the 3-way handshake, before actually executing the 3-way handshake.
Note: I figured out that finding out the generated port is possible using:
generated_port = sock.getsockname()[1]
But, unfortunately, the value of generated_port is set just after using sock.connect().
Any help will be much appreciated!

The local port is only available after the socket is bound to a local IP and port. If not explicitly done by calling bind this will be implicitly done doing connect. Therefore to get the local port before calling connect one has to explicitly call bind before that:
import socket
s = socket.socket()
s.bind(('0.0.0.0',0))
print(s.getsockname()[1])
s.connect((host,port))

Related

(Python/TFTP-Server) how to listen to an non (yet) existing IP-address (RNDIS)?

Hy folks,
my problem: I want to start a (tftp) server for an non-existing IP-address.
the server is meant for USB/RNDIS where its IP-address by nature only is existing when there is actual network-traffic going on -- but I want to start the server 'early' (e.g. when Windows starts).
idea was to bind() the socket to 0.0.0.0 - and then to check each request for "valid" addresses.
problem with that approach: recfrom() only returns the source-address (client), but not the destination (server)!
how do I get the IP-address this client has talked to?
(I could of course check for the same subnet at least, but I need the real server-address this client was talking to!)
or, are there by chance any options to let bind() use a non-existing IP-address?
cheers.
p.s.
this is for the Python tftpy server...
-- at the moment I need to ping from client side when starting the server, which is quite meh...
There's no way to get the local address directly but there's a trick that will usually work for you.
Let's say that you just obtained a buffer and client address from recvfrom. Now you create a new auxiliary UDP socket, connect it to the client's address, and then use getsockname to obtain the local address on this new socket. (With UDP, connect doesn't actually send anything to the peer, it just does address resolution.)
So in this way, you can discover the IP address that the server system would use as source were it to send a datagram back to the client system. That's usually the same address that the client used to target the server.
>>> cbytes, caddr = server_sock.recvfrom(2048)
>>> print(caddr) # Client IP and port
('192.168.0.11', 38869)
>>> aux_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> aux_socket.connect((caddr[0], 9999)) # Connect back to client (port doesn't matter)
>>> saddr = aux_socket.getsockname() # Get local IP and port (ignore port here too)
>>> print(saddr)
('192.168.0.12', 39753)
This works on linux. I'm not 100% sure it would work the same way on Windows but it seems reasonable to expect that it would.

Python socket NAT port address

I have a client socket behind a NAT and I want to get the local port number used by the process.
To illustrate my question, here's a quick example.
Let's say I create a server using the following code:
welcome_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
welcome_socket.bind(("", 1500))
welcome_socket.listen(5)
I then listen for incoming connections:
(client_socket, address) = self.welcome_socket.accept()
I connect from a client (behind a NAT) using the following code:
sock = socket.create_connection(("server_address", 1500))
Here is where I'm a little confused.
The address I get on the server side has the public address of the WiFi network the client is connected to (which I expect) and some port number, which based on my understanding of NATs, should be different from the actual port number used by the client and is used for address translation.
However, if I used the getsockname() function on the client, I get the same port number as the one given by the server.
Returning to the example in code.
On the server:
client_socket.getpeername()
>>> ('WiFi_address', 4551)
On the client:
sock.getsockname()
>>> ('local_address', 4551)
So, both port numbers are the same, even though the client is behind a NAT. How is this the case? Am I misunderstanding how the NAT works? Or is there another command to get the physical address that the client socket is bound to?
Any insight would be appreciated.
It is likely that the Router is using Port Address Translation (or one-to-many NAT). The wiki link further quotes
PAT attempts to preserve the original source port. If this source port
is already used, PAT assigns the first available port number starting
from the beginning of the appropriate port group 0-511, 512-1023, or
1024-65535. When there are no more ports available and there is more
than one external IP address configured, PAT moves to the next IP
address to try to allocate the original source port again. This
process continues until it runs out of available ports and external IP
addresses.
And that should be the reason why you are seeing port 4551 on the server.
(This link should also help to clarify the difference between NAT and PAT)

Finding source IP-address when binding to 0.0.0.0

When binding a UDP socket to ("", 1234) or ("0.0.0.0", 1234), is it possible to find out what IP-address it will actually send from?
As you can see in the code below, getsockname only tells me what I bound to. But when I send a packet, I can see that the IP-address is, in my case, 10.0.0.2.
Do I have to infer this address myself by looking at my network interfaces? If so, that is fine, but is there a robust way of doing so?
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(("", 1234))
print(s.getsockname()) # prints ("0.0.0.0", 1234)
s.sendto("hello", ("10.0.0.3", 1234)) # sends from 10.0.0.2
I've tried doing
import socket
print(socket.gethostbyname(socket.gethostname()))
but that doesn't seem to be very reliable (in the case where I expected 10.0.0.2, I got 127.0.1.1).
I realize that by binding to 0.0.0.0, I bind to all local network interfaces. Does that mean that my source IP-address will be determined by the routing tables when I try to send something? If so, can I still get that address in a robust way from Python?
The IP address used when sending will be determined by the routing table as the packet is sent.
There might be platform specific ways of querying that routing table, but a fairly portable way is to
connect() the socket first.
You can use another socket just for querying this information too. e.g.
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(("", 1234))
print(s.getsockname()) # prints ("0.0.0.0", 1234)
sq = socket(AF_INET, SOCK_DGRAM)
sq.connect(("10.0.0.3", 1234))
print(sq.getsockname()[0])
sq.close()
s.sendto("hello", ("10.0.0.3", 1234)) # sends from 10.0.0.2
This is more a usually-you-do-not-need-it answer. It may not correspond to your usecase.
Have alook at socket.gethostbyname_ex(socket.gethostname()). It shows all possible IP-addresses and the host name. You can receive from all of those since you did not bind to any specific one of those. They will be your source ip addresses.
It is not necessairy for you to know the exact address you send from. The receiver may see another one if it goes behind a NAT, into the internet or through a VPN.
The receiver will then know where the packet came from and can send answers.
#Joachim_Pileborg is also right. It is not usually done.
If you need a specific interface, bind to it. If not, you probaply do not need it.

Sending a UDP packet with source port, but without binding

I would like to send a UDP packet in Python and specify the source port but WITHOUT binding.
An equivalent with hping3:
hping3 -s $sourceport -p $remoteport --udp --file message.bin -d 1024 -c 1 $remoteaddr
I have tried to do something like this:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((SHOST, SPORT))
But of course, Python tries to bind, and it does not work. Now if I don't bind, I can do:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
s.sendto("message", (RHOST, RPORT))
The message gets sent, but now the source port is not defined.
Does anyone have an idea?
EDIT: extended description: my python script complements another application which is an UDP server bound to a defined port (above 1024). My script only needs to send UDP packets to a remote server, but with the same source port as my local UDP server so that the remote UDP server believes the local UDP server is the author of the packet, and will continue the transmission with it.
I think I also have to say that this is a completely legal application and is not related at all with any hacking (in fact, it already works with hping3, but I would like to remove this dependency).
EDIT 2: the solution is in the comments below Nos's answer:
Use the pyip python package and create a raw socket. Don't forget to be root, because only root can send raw packets (this is NOT a limitation of Python but an OS limitation, this is to prevent security issues, so to send raw packets as a user you need to tweak your OS config).
There's no API for sending an UDP packet with a defined source port in python , nor on most (all?) the operating systems python runs on without binding the socket to a local port first.
So you'll have to bind() your socket if you want to control the source port.
If bind() "does not work", then you're either binding to a port that another process owns, or a port number < 1024 which only the root user can bind to, or you're giving some other wrong parameters to bind() - but we'd need more info to help you, e.g. the error message you get, the actual parameters you pass to bind, etc.

Python: asynchronous tcp socketserver

I'm looking http://docs.python.org/library/socketserver.html to try and handle asynchronous requests with the socketserver in python. At the very bottom there is an example, but it doesn't make sense. It says you use port 0 which assigns an arbitrary unused port. But how do you know what port to use for the client if they are not in the same program? I do not quite understand how to make this useful.
Since the client is implemented in the same script as the server, the port is known. In a real-world scenario, you should specify a port for your daemon. Besides letting your clients know on which port to connect, you may also need to know so that you can open firewalls between your clients and your server.
In the example you linked, they are fetching the port:
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
ip, port = server.server_address
However, you should really be looking at www.twistedmatrix.com if you are serious about writing async handling :)
You need to retrieve the port that was assigned to the socketserver once the bind is done: in this case, this will probably be through ip, port = server.server_address.
The arbitrary port is just if you want to create a server without specifying a port: the OS will assign an available port.
Of course there must also be a way to specify which port to bind to.
server = ThreadedTCPServer((HOST, 0), ThreadedTCPRequestHandler)
ip, port = server.server_address
...
client(ip, port, "Hello World 1")
The PORT value 0 says "I don't care what port number it is," so the server_address's port value is assigned by the ThreadedTCPServer() call. It is not zero. Later, you pass that port value to the client, who uses it.

Categories