How to create a dual-stack (v4+v6) python UDP server? - python

By searching for 'python dual-stack', I found https://bugs.python.org/issue25667 where someone mentions that this was already resolved in https://bugs.python.org/issue17561. In the latter issue, one of the last messages helpfully mentions what the implemented solution is:
# IPv4 only
>>> socket.create_server(addr)
# IPv6 only
>>> socket.create_server(addr, family=socket.AF_INET6)
# IPv4 + IPv6
>>> socket.create_server(addr, family=socket.AF_INET6, dualstack_ipv6=True)
However, I was looking for a generic solution. Or rather, I was looking for UDP but figured that, because this is on the IP layer, any solution would be generic. It turns out that create_server() is TCP-only.
Binding to both v4 and v6 manually means I cannot just call recvfrom() and let it block until a packet comes in, because then a v6 packet might be queued while it's blocking on the v4 recvfrom call or vice versa. So I guess I need to use threading?
What is the best way to make a dual-stack UDP server in python 3.x? For me, a Linux solution would be sufficient, but of course platform-independent answers are even better.
Basically, how to write this in dual-stack:
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('0.0.0.0', 53))
while True:
message, address = server_socket.recvfrom(1400)

At least under Linux, creating an IPv6 socket listens for both IPv6 and IPv4 connections. That is, given this code:
import socket
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
s.bind(('::', 9090))
while True:
message, address = s.recvfrom(1024)
print(address, "says: ", message.decode())
I can connect using IPv4:
echo hello world | nc -u 127.0.0.1 9090
Which results in:
('::ffff:127.0.0.1', 36694, 0, 0) says: hello world
Or I can connect using IPv6:
echo hello world | nc -u ::1 9090
Which results in:
('::1', 51880, 0, 0) says: hello world

Related

UDP Socket is not receiving any message with python

I am trying to send message from Raspberry Pi (Ubuntu 20) to Laptop (Virtualbox Ubuntu 20) via UDP socket. So I am using simple code from https://wiki.python.org/moin/UdpCommunication
Sending (from Raspberry Pi)
import socket
UDP_IP = "127.0.0.1"
UDP_PORT = 5005
MESSAGE = b"Hello, World!"
print("UDP target IP: %s" % UDP_IP)
print("UDP target port: %s" % UDP_PORT)
print("message: %s" % MESSAGE)
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
Receiving (from laptop)
import socket
UDP_IP = "127.0.0.1"
UDP_PORT = 5005
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)
I tried UDP_IP = "0.0.0.0" in both ends.
I tried Ip address of my laptop at RPI end.
I tried both machine's IP address at both ends.
I tried sock.bind(("", UDP_PORT)) to bind all.
I tried adding the UDP port number in firewall settings.
I checked multiple questions from this forum related to this.
Still, I cannot get any packets at the laptop receiving side. I do not know what is wrong.
Please advise.
The problem may be in the IP address because you are using the IP '127.0.0.1' (localhost) to reach an outside device. Please find out the IPs of your devices, try using ifconfig Linux command. Also, check that nothing is blocking your connection.
Consider that for socket.bind(address) you can use '0.0.0.0' and for socket.sendto(bytes, address) you should use the IP of the device you want to send to.
I recommend you to download a program called Hercules, with this program you can create a UDP peer to figure out what is not working properly. For example, you could use python in one side and Hercules in the other to rule out mistakes in one side of the code execution, you could also try two Hercules connections and see if you can establish communication in which case the problem is most likely related to the code execution, in the other hand if you can not establish a connection between the two Hercules UDP peers the problem is most likely with the devices or the network itself.
If you are using a static IP on the RPi, you need to add a static route:
sudo ip route add 236.0.0.0/8 dev eth0
Make sure the port you are using have UDP enabled in windows 10.
To open any UDP ports, you can do the following:
Go to Control Panel> System and Security and Windows Firewall.
Advanced settings > right-click Inbound Rules and select New Rule.
Add the port(s) you want to open and click Next.
Select UDP protocol and the port(s) number(s) into the next window
and click Next.
Select Allow the connection and hit Next.
Select the network type and click Next.
Give a name for the rule and click Finish.

Python socket listen on all ports

Basically, I want to listen to all ports using the socket module. How do I make it so that port is = to all the open ports on the server? Any guides and or resources are highly appreciated Here is my current code.
import socket
def Main():
host = '127.0.0.1'
port = 5000
s = socket.socket()
s.bind((host, port))
s.listen(1)
c, addr = s.accept()
print('Connection from: ' + str(addr))
while True:
data = c.recv(1024)
if not data:
break
print('from connected user: ' + str(data))
data = str(data).upper()
print('sending: ' + str(data))
c.send(data)
c.close()
if __name__ == '__main__':
Main()
You may try all possible ports and store them in a list. Remember ports below 1024 are reserved and some of the ports may be in use. So, you will get some errors and you need to handle those if you cannot bind to that port. Also, you need a socket for each port since a socket can only listen at one port. Create a function create_socket which returns socket, then store them is a list. If you get error while trying to connect, just pass those errors. This may not be a good approach but it will work for you.
def create_socket(port_number):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('127.0.0.1', port_number))
server_socket.listen(1)
return server_socket
socket_list = []
for port_number in range(1025,65536):
try:
socket_list.append(create_socket(port_number))
except Exception:
pass
Do Packet Filtering and Translation
An alternative would be to setup packet filtering and translation in the host machine to direct all incoming TCP requests to your process, regardless of the destination port. sshuttle does this to tunnel all requests to an ssh server. This way, your process would not need to open thousands of files.
In freeBSD/macOS the configuration is achieved as follows. Other OSs will have their specific way of doing this (e.g. nftables' nft(8) in Debian).
Configuration File
Create a file (named rules.txt for this example) with the following contents:
# Redirect incoming traffic on interface en0 to localhost:5000
rdr pass on en0 inet proto tcp all -> 127.0.0.1 port 5000
Change en0 to the interface that you wish to intercept incoming connections on. Remove inet or replace with inet6 to accept both IP and IPv6 or just IPv6, respectively. Check pf.conf(5) for exact semantics and syntax of this file.
Enable Rules
With administrative access run the following to load up the rules contained in the previously created file.
Enable packet filtering and translation:
pfctl -e
Flush everything (be careful as this will erase existing routing and translating configurations already set):
pfctl -F a
Load the rules:
pfctl -f rules.txt
Test it out.
Also Include Outgoing Traffic
If you also want to include outgoing traffic, as sshuttle does, you should append the next line to rules.txt:
pass out route-to lo0 inet proto tcp all
You can also tweak this rule to be more selective and avoid setting yourself a networking jail (see entry 1 of notes below).
Notes
If you include outgoing traffic, you will be unable to communicate with the outside world unless you receive a connection first or have an alternative interface. This is because outgoing traffic will be routed to your catchall process (due to the route-to keyword).
Beware that this method allows processes bound to localhost to be reachable from the outside. In the setup above, anyone connecting to any port on en0 will be able to talk to the process bound to 127.0.0.1:5000.
Remember to reload the rules after changing rules.txt for them to take effect.
To disable packet filtering and translation run pfctl -d.

Can't connect to local Machine IP through TCP From Arduino Uno using SIM900 Shield

So you have a basic understanding of the parts im using, I have:
Arduino Uno
Seeed Studio GPRS Shield v2.0 (http://www.seeedstudio.com/wiki/GPRS_Shield_V2.0)
Ultimate GPS for Adafruit V3.3 (https://www.adafruit.com/products/746?gclid=Cj0KEQjw3-W5BRCymr_7r7SFt8cBEiQAsLtM8qn4SCfVWIvAwW-x9Mu-FLeB6hLmVd0PAPVU8IAXXPgaAtaC8P8HAQ)
Here is my problem:
I have tested the Arduino stacked with the GPRS shield, and it works fine with regards to accessing the internet through TCP, sending SMS, etc.. However, my application requires me to send GPS data from the adafruit GPS to a web server that I have already coded with Django and postgresql. The backend is set up.
I need to send the data from the Uno (client) to my laptop (server), which I coded in python (This is just to check whether it is creating a connection):
#!/usr/bin/env python
import socket
# import postgres database functions
TCP_IP = '192.168.1.112'
TCP_PORT = 10000
BUFFER_SIZE = 40
server_address = (TCP_IP,TCP_PORT)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created.'
# Bind socket to TCP server and port
try:
s.bind(server_address)
except socket.error as msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket Bind Complete.'
# Start Listening on socket
s.listen(1) # Puts socket into server mode
print 'Listening on port: ', TCP_PORT
# Now Keep Talking with the client
while (1):
# Wait to accept a connection
conn, addr = s.accept() # Wait for incoming connection with accept()
print 'Connection address:', addr
data = conn.recv(BUFFER_SIZE)
if not data: break
print "recieved data: data", data
conn.send(data) #echo
conn.close()
I dont think there is a problem with this. From this I will post data to my postgreSQL database. However, When I try to use AT commands on the SIM900 module to connect to the server using port 10000, I cannot connect:
AT+CIPSHUT
SHUT OK
AT+CGATT?
+CGATT: 1
OK
AT+CIPMUX=0
OK
AT+CSTT="fast.t-mobile.com","",""
OK
AT+CIICR
OK
AT+CIFSR
6.60.94.49
AT+CIPSTART="TCP","192.168.1.112,"10000"
OK
STATE: TCP CLOSED
CONNECT FAIL
I have tried connecting through TCP and replaced the AT+CIPSTART line with the below statement and it worked, so I know TCP works:
AT+CIPSTART="TCP","www.vishnusharma.com", "80"
Is the IP i'm using wrong? I'm new to this, but if it makes a difference, im using Ubuntu 16.04 partitioned on my Mac OSX. I have also checked the APN for T-mobile and it seems fine.
Any help would be greatly appreciated. Thank You!
The IP you're using is inside a NAT since it starts with 192.168. Unless you have a private apn with the mobile operator you're using, you won't be able to reach your Ubuntu from a public IP. Your ISP gives you a public IP address which ir administrated by your router, so if you want this to work, you'll have to do a port forwarding from your router to your Ubuntu.
To do the port forwarding you have to get in the router's configuration page (Typically 192.168.1.1 but depends on the model) an there you'll have to redirect the port XXX to 192.168.1.112:10000. After that you have to obtain your public IP (curl ifconfig.co) and use it to access from the SIM900.
First of all as a suggestion, you can combine the two shields by using SIM908 (unless you are getting more precision on your GPS shield). Since your TCP connection is working, I bet that the port 10000 on your ubuntu is blocked by the firewall. You can first try to turn off your firewall and see if it works. If it did not worked its something else. If it worked, turn on your firewall and then unblock the tcp port using the following command:
sudo ufw allow 10000/tcp

Python socket on Windows 7 is not accessible from another machine

I'm trying to create a Python program that will listen on a socket. I'm using Windows 7 with Python 2.7. Whatever I do, the socket seems to be accessible from the local machine but not from elsewhere on the network.
I've got the following code:
from werkzeug.wrappers import Request, Response
#Request.application
def application(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
# Using empty string or the machine's real IP address here
# gives the same problem
run_simple('0.0.0.0', 4000, application)
If I connect from the local machine I see the response fine. If I execute
$ curl 'http://192.168.1.1:4000/'
from another (linux) box on the network, the curl hangs for a long time before timing out. Wireshark shows that I receive a SYN packet to port 4000 but don't see it ACKed.
I've tried making sure packets to this port are allowed through the firewall (the fact that I see the SYNs in Wireshark suggests this is not the problem). I've tried setting Python to run as administrator (and I've checked that ctypes.windll.shell32.IsUserAnAdmin() returns true). This isn't just Werkzeug, I've tried with SocketServer from the Python standard library as well.
Running Windows Apache on the same port works fine from across the network, which suggests there's no problem with the network or firewall or with my curl request.
netstat -an shows:
TCP 0.0.0.0:4000 0.0.0.0:0 LISTENING
Edit: I've tried with the following minimal code. On the server side (Windows 7):
import socket
s = socket.socket()
s.bind(('', 8080))
s.listen(1)
remotesock, addr = s.accept()
And on the linux client:
import socket
s = socket.socket()
s.connect('192.168.1.1', 8080)
This hangs until timeout, as with the curl.
I believe the problem is due to your address binding. Python does not allow sockets bound to localhost (or 0.0.0.0) to be visible from the outside world. Change the binding to your actual IP Address.
EDIT: Showing example code
Change your code to this
import socket
s = socket.socket()
s.bind(('192.168.1.1', 8080)) # assumes your machine's IP address is 192.168.1.1
s.listen(1)
remotesock, addr = s.accept()

Receiving multicast data on specific interface

tcmpdump can view all the multicast traffic to specific group and port on eth2, but my Python program cannot. The Python program, running on Ubuntu 12.04:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Multicast port is 52122
sock.bind(('', 52122))
# Interface eth2 IP is 1.2.3.4, multicast group is 6.7.8.9
mreq = socket.inet_aton('6.7.8.9')+socket.inet_aton('1.2.3.4')
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print '\nwaiting to receive message'
data, address = sock.recvfrom(1024)
print data
When I use another program to send a multicast packet to eth2, it works and prints the packet. But it fails to see all the current multicast traffic. If I run tcpdump on eth2 on the same port and group as the above program:
sudo tcpdump -i eth2 host 6.7.8.9 and port 52122
it sees both the packets I send from another program AND all the current multicast traffic. It's output looks likes this...
# Packet sent from my other program
09:52:51.952714 IP 1.2.3.4.57940 > 6.7.8.9.52122: UDP, length 19
# Packet send from the outside world
09:52:52.143339 IP 9.9.9.9.39295 > 6.7.8.9.52122: UDP, length 62
Why can't my program see the packets from the outside world? How can I modify it (or something else) to fix this?
Edit:
I should have mentioned, the interface this going over is not eth2 but eth2.200 a VLAN. (The local IP and the tcpdump commands are all run with eth2.200, I just changed that in this question to make it simpler.) Based on this answer that could be the problem?
Edit #2:
netstat -ng when the program is running shows eth2.200 subscribed to 224.0.0.1 and 6.7.8.9`.
tshark -i eth2.200 igmp shows three repeated 1.2.3.4 -> 6.7.8.9 IGMP 46 V2 Membership Report / Join group 6.7.8.9 when the program first starts. When the program process is killed, it shows 1.2.3.4 -> 224.0.0.2 IGMP 46 V2 Leave group 6.7.8.9. There is also an infrequent 1.2.3.1 -> 224.0.0.1 IGMP 60 V2 Membership Query, general, where 1.2.3.1 is 1.2.3.4's gateway.
Not sure if it will help, but the routing table looks like:
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 1.2.5.6 0.0.0.0 UG 0 0 0 eth1
1.2.3.0 0.0.0.0 255.255.255.240 U 0 0 0 eth2.200
Thank you!
Finally! Found this question on ServerFault that addresses the same thing. Basically the kernel was not forwarding on / was filtering out the packets because it thought the sourced address was spoofed.
Changed the settings in /etc/sysctl.conf to match:
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.ip_forward = 1
Rebooted and everything works.

Categories