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.
Related
I am trying to send a udp packet to a local ip address. This is my example code:
from scapy.all import *
if __name__ == "__main__":
send(IP(dst="127.0.0.1")/UDP(sport=19600,dport=39600)/"abc")
I've started netcat to catch what I am going to send:
nc -ul 39600
Then I am executing the code:
python3 example_scapy_send.py
Nothing is received by the listening netcat.
At the same time I have started wireshark and I can see the packet is sent.
If I send a packet using netcat it is ariving on the listening netcat.
usr#dev:/home/usr# nc -u 127.0.0.1 39600
test
Wireshark:
The only difference I can see is that at layer 2 - destination address is multicast/broadcast when sent with scapy and unicast when sent with netcat. But this is not something I can control.
If I sent the same packet with scapy to another ip on the network (another host) the packet is received (by netcat). So the issue applies only if I am sending to a local address. Tested with any local ip. Not only 127.0.0.1. I've also tested with sendp and sr scapy functions but the result is the same.
Something more: if I've started another scapy script to listen to UDP/39600 (instead of netcat) I can see/I am receiving the packet I've sent.
Any ideas what is wrong?
tests done under ubuntu/scapy 2.5/python 3.8
I couldn't find a way to make it work with send/sendp scapy functions, but instead I tried using standart python socket and it did the job:
someSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
someSocket.sendto(bytes('abc', "utf-8"), (127.0.0.1, 39600))
Acording to Scapy troubleshooting:
The loopback interface is a very special interface. Packets going through it are not really assembled and disassembled. The kernel routes the packet to its destination while it is still stored an internal structure. What you see with tcpdump -i lo is only a fake to make you think everything is normal. The kernel is not aware of what Scapy is doing behind his back, so what you see on the loopback interface is also a fake. Except this one did not come from a local structure. Thus the kernel will never receive it.
On Linux, in order to speak to local IPv4 applications, you need to build your packets one layer upper, using a PF_INET/SOCK_RAW socket instead of a PF_PACKET/SOCK_RAW (or its equivalent on other systems than Linux)
So you may need to add line before sending packet:
conf.L3socket = L3RawSocket
In your script. That way everything should supposed to work. At least in my environment worked out fine.
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.
Server code:
import socket
import base64
filename = open("received.xvid","ab")
TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
data = conn.recv(16)
filesize = int(data)
iter = filesize//BUFFER_SIZE
i = 0
while (i < iter):
data = conn.recv(BUFFER_SIZE)
filename.write(data.decode('base64'))
if not data:
continue
i = i + 1
data = conn.recv((filesize - (iter*BUFFER_SIZE)))
filename.write(data.decode('base64'))
filename.close()
conn.close()
Client code:
import socket
import time
import base64
TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 1024
filename = open("test.xvid","rb")
MESSAGE = base64.b64encode(filename.read())
filesize = '%16s'%len(MESSAGE)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(filesize)
time.sleep(1)
s.send(MESSAGE)
s.close()
When I run the client code, I get "socket.error: [Errno 10061] No connection could be made because the target machine actively refused it". I don't understand why. What should I put in place of TCP_IP and TCP_PORT to make the code work?
This is what I get when I enter the command netstat -tnl.
Displays protocol statistics and current TCP/IP network connections.
NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-x] [-t] [interval]
-a Displays all connections and listening ports.
-b Displays the executable involved in creating each connection or
listening port. In some cases well-known executables host
multiple independent components, and in these cases the
sequence of components involved in creating the connection
or listening port is displayed. In this case the executable
name is in [] at the bottom, on top is the component it called,
and so forth until TCP/IP was reached. Note that this option
can be time-consuming and will fail unless you have sufficient
permissions.
-e Displays Ethernet statistics. This may be combined with the -s
option.
-f Displays Fully Qualified Domain Names (FQDN) for foreign
addresses.
-n Displays addresses and port numbers in numerical form.
-o Displays the owning process ID associated with each connection.
-p proto Shows connections for the protocol specified by proto; proto
may be any of: TCP, UDP, TCPv6, or UDPv6. If used with the -s
option to display per-protocol statistics, proto may be any of:
IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, or UDPv6.
-r Displays the routing table.
-s Displays per-protocol statistics. By default, statistics are
shown for IP, IPv6, ICMP, ICMPv6, TCP, TCPv6, UDP, and UDPv6;
the -p option may be used to specify a subset of the default.
-t Displays the current connection offload state.
-x Displays NetworkDirect connections, listeners, and shared
endpoints.
-y Displays the TCP connection template for all connections.
Cannot be combined with the other options.
interval Redisplays selected statistics, pausing interval seconds
between each display. Press CTRL+C to stop redisplaying
statistics. If omitted, netstat will print the current
configuration information once.
If this happens always, it means that the machine exists but that it
has no services listening on the specified port, or there is a
firewall stopping you.
If it happens occasionally and retrying succeeds, it is likely
because the server has a full 'backlog'.
When you are waiting to be accepted on a listening socket, you are placed in a backlog. This backlog is finite and quite short - values of 1, 2 or 3 are not unusual - and so the OS might be unable to queue your request for the accept to consume.
The backlog is a parameter on the listen function - all languages and platforms have basically the same API in this regard. This parameter is often configurable if you control the server, and is likely read from some settings file or the registry. Investigate how to configure your server.
Regardless of whether you can increase the server backlog, you do need retry logic in your client code to cope with this issue as even with a long backlog the server might be receiving lots of other requests on that port at that time.
There is a rare possibility where a NAT router would give this error should it's ports for mappings be exhausted. I think we can discard this possibility as too much of a long shot though, since the router has 64K simultaneous connections to the same destination address/port before exhaustion.
Also check here for more causes:
No connection could be made because the target machine actively refused it.
When I run the server and client on the same machine, things are fine. When I run them on different machines, that is when the problems arise.
OK, I know what your problem is.
TCP_IP = '127.0.0.1'
You have hardcoded this IP address into server and client. It is a special IP address that means this machine and only this machine. When you use it in bind(), the server listens for connections only from the same machine. When you use it in connect(), the client tries to connect to a server running on the same machine.
To listen for connections from any machine, as is probably your intention for the server, you call bind with the special IP address 0.0.0.0. (A complete server for production use needs to do something slightly more complicated than this, but that is enough for a test program like this one.)
To have a client connect to a server running on another machine, it needs to take the DNS name of the other machine as a command line argument, and run that through getaddrinfo to get the IP address to connect to.
You should purchase and read UNIX Network Programming, Volume 1, 3rd Edition and Advanced Programming in the UNIX Environment. They are expensive, but they are worth it. They will flesh out the above advice and teach you many other things which you need to know if you're going to write a complete network server or client. (Make sure to get the third edition of UNIX Network Programming; earlier editions contain a bunch of obsolete junk you don't need to know about. The second volume of UNIX Network Programming is also valuable but not as applicable to your current situation. Advanced Programming... is also less immediately relevant, particularly if you are on Windows, as I suspect you are from the phrasing of the error message.)
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.
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)).