Send UDP to local server, but not via loopback interface - python

I'm ultimately trying to test a UDP client, and want to make sure that it works when sending data not through the loopback interface, to avoid any subtle issues this introduces, such as differences in checksum validation (Bad UDP checksum has no effect: why?).
However, even when sending data to the result of socket.gethostbyname(socket.gethostname()), which is not 127.0.0.1, then according to Wireshark, the data seems to go via the loopback interface.
The below program sends and receives b'somedata' successfully, and has the below capture from Wireshark.
import asyncio
import socket
async def server():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.setblocking(False)
sock.bind(('', 4567))
data = await loop.sock_recv(sock, 512)
print('Received', data)
async def main():
local_ip = socket.gethostbyname(socket.gethostname())
print('Local IP', local_ip) # Outputs 192.168.0.34
asyncio.ensure_future(server())
await asyncio.sleep(0)
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.setblocking(False)
sock.connect((local_ip, 4567))
await loop.sock_sendall(sock, b'somedata')
await asyncio.sleep(1)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
How can I send data from a client running locally, to a server running locally, but avoiding the loopback interface and actually sending data out into the network?
Ideally answers would be applicable to both Linux and macOS.

To 'convince' the networking stack to physically transmit the frame using the Ethernet (or WiFi) card rather than the loopback, use a broadcast address.
I have successfully sent and received an UDP packet this way on my Linux system. I verified it with tcpdump. It shows one packet on Ethernet interface and no activity on loopback.
I have used literal broadcast address. The socket module documentation mentions also the string '<broadcast>' as a special case address. I did not tried it.
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.setblocking(False)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.connect(('192.168.0.255', 4567))
await loop.sock_sendall(sock, b'somedata')
await asyncio.sleep(1)
Notes:
other hosts on the same network will receive the UDP packet as well.
make sure the firewall/packet filter (e.g. Linux iptables/nftables) will not block the packet.
regarding the setsockopt: Python socket.error: [Errno 13] Permission denied

That's probably because your hostname is pointing to the loopback address hence socket.gethostbyname(socket.gethostname()) will yield 127.0.0.1
What you need to do is cancel that pointing from the hostname to the loopback address:
in Linux edit the /etc/hosts and comment out the line 127.0.0.1 YOUR_HOSTNAME
in Windows you should have c:\windows\system32\drivers\etc\hosts which looks similar to the Linux one
After this if you call the socket.gethostbyname(socket.gethostname()) it will yield your DHCP assigned IP.
Though even in this case, calling from yourip to yourip might result in the network driver to route the package through the loopback interface. Alternative would be to use the public IP, outside your network router. You could use an external service as described in this answer

Related

binding a UDP port and listening for connections

When I try to ping a Minecraft server via LAN, the documents say the following:
In Singeplayer there is a function called "Open to LAN". Minecraft (in the serverlist) binds a UDP port and listens for connections to 224.0.2.60:4445 (Yes, that is the actual IP, no matter in what network you are or what your local IP Address is)" ....... client side, bind a UDP socket and listen for connections. You can use a MulticastSocket for that.
I tried to implement this in Python in the following way:
import socket
UDP_IP = "224.0.2.60"
UDP_PORT = 4445
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print("received message: %s" % data)
Which gave me an OSError: [WinError 10049] error.
Please help :( I have no idea what's wrong with my code.
By the way, sending packets to that port works, and the fake server shows up on the Minecraft app.
You cannot bind to a multicast address like that. It is somewhat more involved.
I recommend to read this article that explains all the details of using multicast with Python.

Python Multicast between client and server will only work with router between them [duplicate]

I have a Windows machine where I have two scripts that send and receive messages via UDP multicast (on the same machine). I have a C and Python3 implementation of this. The Python3 one looks like this:
sender.py
import socket
MCAST_GRP = '239.1.1.1'
MCAST_PORT = 1234
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
print("Sending")
sock.sendto(bytearray("str()", "utf-8"), (MCAST_GRP, MCAST_PORT))
data, address = sock.recvfrom(1024)
print('received %s bytes from %s' % (len(data), address))
print(data)
receiver.py
import socket
import struct
import sys
multicast_group = '239.1.1.1'
server_address = ('', 1234)
# Create the socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Bind to the server address
sock.bind(server_address)
# Tell the operating system to add the socket to the multicast group
# on all interfaces.
group = socket.inet_aton(multicast_group)
mreq = struct.pack('4sL', group, socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# Receive/respond loop
while True:
print('\nwaiting to receive message')
data, address = sock.recvfrom(1024)
print('received %s bytes from %s' % (len(data), address))
print(data)
print('sending acknowledgement to', address)
sock.sendto(bytearray("ack", "utf-8"), address)
I have another physical device hooked up to the same machine but I cannot receive any messages from it with my programs. I can see that Wireshark is seeing the messages from the other physical device, these messages are coming over the eth0 interface to the same IP and Port. I know that the traffic generated by my scripts is on the VirtualBox Host-Only Network. I am not sure if that could cause the issue of me not seeing the UDP multicast messages from the external device.
I have tested it on a Linux machine as well (latest kali version) but could not receive any messages from the external device as well.
If I am missing some information, please let me know.
EDIT 1:
My setup is as follows:
I am running a native Windows 10 machine. To this machine is a device connected that is running some OS I don't know. I am only able to send and receive messages from it. I can send Ethernet, TCP, and IPv4 packets over the physical ethernet port on my Windows 10 machine by specifying the software I am using for this to use eth0 and a defined IP(v4) address I assigned to that port in the network adapter settings (192.168.1.100)
The scripts are running on the same Windows 10 machine that is also connected to the device. They are sending on this interface VirtualBox Host-Only Network but I don't know why. I did not configure anything like this. I assume that the interface should not be a problem because that is how UDP Multicast works (I am not sure of that so if I am mistaken please let me know!)
A sample output of the sender looks like this:
Sending
received 3 bytes from ('192.168.56.1', 3000)
b'ack'
Process finished with exit code 0
and the receiver:
waiting to receive message
received 5 bytes from ('192.168.56.1', 55132)
b'robot'
sending acknowledgement to ('192.168.56.1', 55132)
waiting to receive message
I hope that clarifies the setup. If there is still information missing please let me know!
As covered in https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html, the sockets API requires that you identify the interface as well as the multicast address/port you want to use.
By not specifying this in your sample code, you have left this down to the OS to pick and it has picked a VirtualBox Host-Only Network. Unfortunately this type of network is limited to VMs running on the Windows machines.
To fix it, you need to identify the interface that you want to use for the multicast and pass that in to your sending and receving code. For example:
sender.py
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(MCAST_IF_IP))
receiver.py
mreq = struct.pack('4s4s', group, socket.inet_aton(MCAST_IF_IP))
where MCAST_IF_IP is the IP address of the interface that you want to use.

How to send socket message when Server has a preset initial response?

I'm making python binds for Blackmagic's Ethernet Control Protocol ( as documented in https://documents.blackmagicdesign.com/UserManuals/HyperDeckManual.pdf?_v=1528269592000 , page 60). Simple socket connection seems to fail however, because every commands gets rejected with the server's greeting.
This protocol documents how software can communicate with certain blackmagic devices, in this case, Blackmagic's hyperdeck, the device runs a TCP server constantly listening on port 9993, on cmd I can simply telnet to it and issue commands, you'd it expect it to be as straightforward in python, however every command gets ignored for the server's greeting message, the device's information. I have been doing socket's for at least 3 months now and i've tried several methods of code, and all seem to fail.
For the most trivial test i've used:
import socket
HOST = "device's ip"
PORT = 9993
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'play')
data = s.recv(1024)
print(data)
and a modified version to try to repeat the command:
import socket
import time
HOST = "device's ip"
PORT = 9993
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'play')
data = s.recv(1024)
time.sleep(2)
s.sendall(b'play')
It should start video playback, as documented, and as occurs when I issue the command thru telnet, however the command is completely ignored and data always equals to: b'500 connection info:\r\nprotocol version: 1.9\r\nmodel: HyperDeck Studio Mini\r\n\r\n' , the server's greeting message in byte form, it should instead be 200 ok or some sort of error / acknowledged message, as documented.
This is incredibly annoying and i've thought of using subprocess and issuing commands thru cmd as an alternative, but something tells me there's an easier workaround.

Python connected datagram socket

I'm building a simple client-server multiplayer game and I want to have connected UDP sockets. However, when I call the listen() method it produces Operation not supported exception.
try:
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind((host, port))
server.listen(5)
except socket.error, (value, message):
print "E: " + message # E: Operation not supported
Is there a way to have connected datagram sockets?
UDP protocol is connectionless and thus you really cannot create connections between 2 sockets in the same manner as with the TCP client and server; thus you also cannot do the listen system call on an UDP socket, as it concerns only the TCP server sockets. Instead you use socket.recvfrom to receive datagrams from any address:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((host, port))
data, addr = sock.recvfrom(65536) # this will fit the maximum datagram
You can respond to the client (if they still have a socket open), by destructuring the addr which is a host, port tuple for socket.AF_INET, socket.SOCK_DGRAM.
client_host, client_port = addr
You can send data back there with sendto
sock.sendto(data, (client_host, client_port))
If you want to use a well-known port, you will bind the socket; if not, the system will assign a port for you.
It is possible to do a connect system call with datagram sockets on many socket implementations; this serves as a sort of filter, that drops the packets coming from unwanted sources, and for setting the default outgoing address for sock.send (it is still OK to use sock.sendto on such socket to another address, though the response might be dropped because of the "connection"). This is very useable on a client or between 2 nodes if they agree to use 2 well known ports with each other.
However if you do connect on server, it cannot serve any other requests on this socket. Also, listen with its queues only relates to SOCK_STREAM sockets.
Thus in case of many clients to 1 server, you could have the server listen to socket 12345, and when a client contacts the server, the server could respond from server:12345 with a message that contains another port number that the client should use with the server.

UDP packets with python

I have a mobile router that can be configured by using different Python script. What I need to do is read all the packets arriving to the router in a concrete UDP port to copy this information in a .txt file afterwards.
Anyone can give me some tips about how can I do this using Python? How can I detect every time a packet arrives in to the router?
Thank you.
Here's a quick example of how to bind to a UDP port and do some action whenever a datagram is received:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', 9800))
try:
while True:
result, who = s.recvfrom(256)
print result, who
finally:
s.close()

Categories