Python - set source port number with sockets - python

I'd like to send a specific UDP broadcast packet. Unfortunately, I need to send the UDP packets from a very specific port.
Let's say I broadcast via UDP "BLABLAH". The server will only answer if my incoming packet source port was 1444; if not, then the packet is discarded.
My broadcast socket setup looks like this:
s = socket(AF_INET,SOCK_DGRAM)
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
How can I then set the source port in Python?

You need to bind the socket to the specific port you want to send from. The bind method takes an address tuple, much like connect, though you can use the wildcard address. For example:
s.bind(('0.0.0.0', 1444))

Use s.bind(('', port)).

Related

How to send packets in scapy from a specific socket?

I am trying to send some packet through scapy by creating a socket. I want to send the packets through the socket which I created. How can I accomplish this
here is some code which I tried
pkt = "\x00\x1c\x7fb\xb5\xfd\x00PV\xb8\x08\x9f\x08\x00E\x00\x000/t\x00\x00\x80\x11\x00\x00\n\xe7\xa0\xc6\n\xe7\x922\xd2\xb4\x05\xdc\x00\x1cH\xf4\t\x8d\x01\x00\x01\x01\x00\x10\xff\xff\xfe\xd4\x00\x00\x00\x00\xb2\x1a=\x0f"
Socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Socket.bind(('',udp_sport))
S = Socket.connect_ex(("10.146.144.51",1803))
Socket.settimeout(10)
sendp(pkt,socket=S)
I have seen in scapy library that there is an option to use Socket in sendp. How can I use this to send? Please help.
You can use a StreamSocket as a wrapper for your socket. See https://scapy.readthedocs.io/en/latest/layers/tcp.html?highlight=streamsocket#using-the-kernel-s-tcp-stack
In your case:
ss=StreamSocket(s,Raw)
ss.send(...)
Note: in your example, you are using sendp(). This means a layer 2 packet. This won't work with the layer 3 socket that you have created: make sure the packet you are sending is L3.

Listen to a port already in use for UDP packets in python?

Essentially I have a program, A, send results (just data points) in real time to another program, B, to handle. Each data point is sent as a UDP packet, on a specific port and 127.0.0.1, containing the point as a string. When B is not running, I can just do
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1, port))
while True:
data, addr = sock.recvfrom(65565)
And then obviously when B is running, I get
[Errno 98] Address already in use
How can I see the packets sent on these ports? In the past (separate project) I had a packet sniffer using
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
which saw all incoming and outgoing UDP packets, but that seems excessive. I only need to see the packets from a specific port. I'm fairly new to this lower level socket programming. Any help is appreciated
You can use scapy:
This is a small example:
from scapy.all import *
def callback(pkt):
pkt.show()
sniff(prn=callback, filter="tcp and ( port 25 or port 110)
No matter the packet sniffer you are using (whether it be wireshark or tcpdump), you can set packet filters to select a specific port. i.e. (tcpdump port port #) or (udp.port == port #).

Python: UDP socket's recvfrom returning localhost or external IP

I have a client program that uses two UDP sockets, one for listening and the other for writing.
OUT_SOCKET = socket(AF_INET, SOCK_DGRAM) # write-only UDP
IN_SOCKET = socket(AF_INET, SOCK_DGRAM) # read-only UDP
I run the program on localhost on two different ports and send messages to each other.
OUT_SOCKET.sendto(some_msg, (ip, port))
And I read the message like this.
msg, addr = IN_SOCKET.recvfrom(MAX_MSG_SIZE)
The problem is, when I specify 127.0.0.1 for the ip (for sending some_msg), on the receiving client, the addr is set to 127.0.0.1. But when I specify ip to 192.168.X.X (my external IP address) from sending client, the receiving client's addr is set to 192.168.X.X.
I expect addr to be consistent no matter how ip is set. Does recvfrom behave differently depending on how the sender specifies the ip address?
How does recvfrom extract remote address? What's causing recvfrom to behave differently?
The address returned from recvfrom is specifically the source IP address in the packet received. When you send a UDP packet using sendto, the IP stack will choose the appropriate interface to send the packet from. recvfrom is not behaving differently, sendto choose the source network interface based on IP routing rules.
In your case, you have two network interfaces -- one that is listening for packets with a destination address of 127.0.0.1 and another that is listening on 192.168.x.y. When you send a packet using sendto, it has to originate from a network interface that can route to the destination address.
When you send a packet to 127.0.0.1, it will originate from the loopback address (e.g., 127.0.0.1). When you send a packet to 192.168.x.y, it will originate from the second network interface. In both cases, the peer IP address returned from recvfrom identifies the sending network interface.

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.

Categories