UDP Hole punching in python - python

I am trying to establish a peer to peer communication using UDP hole punching. I am first establishing a connection with the server and then trying to make communication between 2 clients, but I am not able to communicate between 2 computers that are behind 2 different NATs as I am not understanding what IP address and port must I enter for the establishment of communication.
Please tell me what changes must I make in the code below so that 2 computers are able to communicate.
P.S : External IP doesn't seem to work and I am not supposed to use any additional tool like ngrok
Server.py
import socket
import struct
import sys
server_listening_port = 12345
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sockfd.bind(("", server_listening_port))
print("Listening on the port " + str(server_listening_port))
client_requests = []
while True:
data, addr = sockfd.recvfrom(32)
client_requests.append(addr)
print("Connection from: " + str(addr))
if len(client_requests) == 2:
break
client_a_ip = client_requests[0][0]
client_a_port = client_requests[0][1]
client_b_ip = client_requests[1][0]
client_b_port = client_requests[1][1]
message = ": "
sockfd.sendto(str(client_a_ip).encode("utf-8") + message.encode("utf-8") + str(client_a_port).encode("utf-8"), client_requests[1])
sockfd.sendto(str(client_b_ip).encode("utf-8") + message.encode("utf-8") + str(client_b_port).encode("utf-8"), client_requests[0])
sockfd.close()
Above is the rendezvous server
ClientA.py
import socket
import struct
import sys
import time
master = ("Server_IP", Port)
#Create dgram udp socket
try:
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = "Hello"
sockfd.bind(('', 0))
sockfd.sendto(message.encode("utf-8"), master)
except socket.error:
print("Failed to create socket")
sys.exit()
# #Receiving peer info from server
peer_data, addr = sockfd.recvfrom(1024)
print (peer_data)
print("trying to communicate with peer")
peer_ip = peer_data.decode("utf-8").split(":")[0]
peer_port = int(peer_data.decode("utf-8").split(":")[1])
peer = (peer_ip, peer_port)
while 1:
message1 = input(str("You:>>"))
message = message.encode("utf-8")
sockfd.sendto(str(message).encode("utf-8"), peer)
incoming_msg, sendaddr = sockfd.recvfrom(1024)
incoming_msg = incoming_msg.decode("utf-8")
print("ClientB:>>",incoming_msg)
Above code is Client A
ClientB.py
import socket #For sockets
import struct
import sys #For exit
import time
master = ("Server_IP", port)
me = ("ClientB_IP", port)
#Create dgram udp socket
try:
sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = "Hello"
sockfd.bind(('', 0))
sockfd.sendto(message.encode("utf-8"), master)
except socket.error:
print("Failed to create socket")
sys.exit()
# #Receiving peer info from server
peer_data, addr = sockfd.recvfrom(1024)
print (peer_data)
print("trying to communicate with peer")
peer_ip = peer_data.decode("utf-8").split(":")[0]
peer_port = int(peer_data.decode("utf-8").split(":")[1])
peer = (peer_ip, peer_port)
while 1:
incoming_msg, sendaddr = sockfd.recvfrom(1024)
incoming_msg = incoming_msg.decode("utf-8")
print("ClientA:>>", incoming_msg)
message = input(str("You :>>"))
message = message.encode("utf-8")
sockfd.sendto(str(message).encode("utf-8"), peer)
Above is client B
I am facing problem only in the IP address and port. So , please do help me with it to establish communication between 2 computers behind 2 different NATs

I'm currently on the same problem and I want to program it for myself. With some research I found a really good paper explaining the process in detail.
I let you know if I succeed.
https://bford.info/pub/net/p2pnat/

Related

Python sockets and commands

I'm trying to send console commands from one machine to another using Python sockets. I want the server to send back the results of the command to the client. If the client types "ls" I want the server to send back the results of running that command. Instead of the expected result, the server just says "action completed: ls". How can I fix this so the server will run the expect commands and return the result?
Server:
import socket
from subprocess import call
def main():
host = '127.0.0.1'
port = 5000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.listen(1)
c, addr = s.accept()
print('Connection established: ' + str(addr))
while True:
try:
data = c.recv(1024).decode('utf-8')
print('sending data: ' + data)
c.send(data.encode('utf-8'))
if data == 'q':
break
except NameError:
error = 'Command does not exist'
c.send(error.encode('utf-8'))
continue
except SyntaxError:
error = 'Command does not exist'
c.send(error.encode('utf-8'))
continue
c.close()
Client:
import socket
from subprocess import call
def main():
host = '127.0.0.1'
port = 5000
s = socket.socket()
s.connect((host, port))
message = str(input('> '))
while message != 'q':
try:
s.send(message.encode('utf-8'))
data = s.recv(1024).decode('utf-8')
print('Action completed: %s' % data)
message = str(input('> '))
except NameError:
print("Command not recognized.")
continue
except SyntaxError:
print("Command not recognized")
continue
I recently built a socket connection in order to communicate with an android device.
I decided to use UDP instead of TCP (which is what you did). For UDP as well as TCP you need a sender and a receiver on both sides of the communication.
The port number that is received in the "addr" variable changes with every connection, so you cannot use it.
What I did, I assigned two different ports one for sending from A to B and the other port to send from B to A.
Here is my server code:
import socket # socket connection
import threading # Multithreading
import time # Timeing
# ----------------------------------------------
# Variables
# ----------------------------------------------
UDPListen2Port = 12345
UDPSend2Port = 123456
Listen2IP = '' # input your local IP here
# ----------------------------------------------
# Threading class
# ----------------------------------------------
class signalProcessingThread(threading.Thread):
def __init__(self, iP, cmdIn):
threading.Thread.__init__(self)
self.iP = iP
self.cmdIn = cmdIn
def run(self):
print("Recv--", self.iP ,"--", self.cmdIn) # Display Thread Info
cmdOut = self.EvalMessage() # Actual signal processing
byteOut = bytes(cmdOut.encode("utf-8")) # Convert Server reply to bytes
sock.sendto(byteOut,(self.iP,UDPSend2Port)) # Send Server Reply to Socket
# ----------------------------------------------
# Initialize Socket
# ----------------------------------------------
sock = socket(AF_INET, SOCK_DGRAM) # -- UDP -- connection
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # in case the port was not properly closed before
sock.bind((Listen2IP,UDPListen2Port)) # bind to the port
# ----------------------------------------------
# Listen to Socket
# ----------------------------------------------
while True:
try: # wait for a connection
data,addr = sock.recvfrom(66507) # number of bytes in the message
msg = data.decode('utf-8')
newThread = signalProcessingThread(addr[0],msg)
newThread.start()
except KeyboardInterrupt:
print('Connection failed')
sock.close()
sock.close()
The client code is quite similar, with the difference that it doesn't necessarily need to run in a thread. Hope I could help.

Simple Python 3 Chat; Sending and receiving at the same time

I am a total beginner in Python and today I tried to create a simple chat-program. So far it doesn't work too bad, but I am unable to communicate between the server and the client. I can only send from the server to the client but not in the other direction. I tried it with multithreading and these are the results:
Server:
import socket
import threading
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "127.0.0.1"
port = 4444
s.bind((host, port))
s.listen(3)
conn, addr = s.accept()
print("Connection from: "+str(addr[0])+":"+str(addr[1]))
def recv_data():
while True:
data = s.recv(2048).decode('utf-8')
print(data)
def send_data():
while True:
msg = input(str(socket.gethostname())+"> ")
msg = str(host + "> ").encode('utf-8') + msg.encode('utf-8')
conn.send(msg)
#t1 = threading.Thread(target=recv_data)
t2 = threading.Thread(target=send_data)
#t1.start()
t2.start()
Client:
import socket
import threading
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "127.0.0.1"
port = 4444
s.connect((host, port))
print("Connected to: "+ host)
def recv_data():
while True:
data = s.recv(2048)
data = data.decode('utf-8')
print(data)
def send_data():
while True:
msg = input(str(host)+"> ").encode('utf-8')
s.send(msg)
t1 = threading.Thread(target=recv_data)
#t2 = threading.Thread(target=send_data)
t1.start()
#t2.start()
This code works; the server can send, the client receive, but whenever I uncomment the second thread, so that it can do both I get an error:
OSError: [WinError 10057] A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
I can't seem to find a solution, so please help, what am I doing wrong? :D
conn, addr = s.accept()
def recv_data():
while True:
data = s.recv(2048).decode('utf-8')
print(data)
conn is actually the socket you want to send or recv. The error occurs because you are trying to recv from the server socket, which is illegal action. Therefore you need to change s to conn if you want to make it work.

Python: UDP Proxy Multithreading

I just can't manage to add Multithreading to my UDP Server.
A second client can connect, but instantly gets thrown out of the server when someone is already connected to it.
Could this be caused by something other than SingleThreading?
import sys, socket
localPort, remoteHost, remotePort = sys.argv[1].split(':')
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('', localPort))
except:
fail('Failed to bind on port ' + str(localPort))
knownClient = None
knownServer = (remoteHost, remotePort)
sys.stderr.write('Ready.\n')
while True:
data, addr = s.recvfrom(32768)
print addr
if knownClient is None:
knownClient = addr
if addr == knownClient:
try:
s.sendto(data, knownServer)
except:
pass
else:
try:
s.sendto(data, knownClient)
except:
pass
You cannot write an UDP proxy with only port. How should you know from the answer of the server to which of the two connected clients you should send your answer. You have to open for each client a new socket to the remote server.
It's not Python but "networking" and for sure not "multithreading". You need to either direct the clients to different ports or create a new outgoing socket for each new client connection.
Because you have multiple sockets a very effective approach is to sit on select and wait for the incoming calls.
In order to identify the clients, it is also needed to keep a reference of the local addresses the new sockets are using to talk to the server.
Your code reworked to open a socket on each new incoming client connection. No guarantees, because that would involve network testing against an scenario (yours) which is unknown.
For a very robust implementation you would have to add error checking, socket removal for gone connections ...
import select
import socket
import sys
localPort, remoteHost, remotePort = sys.argv[1].split(':')
try:
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('', localPort))
except:
fail('Failed to bind on port ' + str(localPort))
localaddr = s.getsockname() # (localhost, localport)
remaddr = (remoteHost, remotePort)
sys.stderr.write('Ready.\n')
allsockets = [server]
proxysocks = dict()
origins = dict()
while True:
toread, _, _ = select.select(allsockets, [], [])
s = toread[0] # 1st socket available to read
data, orig = s.recvfrom(32768) # data, (remhost,remport)
dest = s.getsockname() # (localhost, localport)
if dst == localaddr: # client -> localserver
try:
p = proxysocks[orig] # find proxy sock
except KeyError: # new client connection
proxysocks[orig] = p = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM)
proxyaddr = p.getsockname() # keep local address of new socket
origins[proxyaddr] = orig # link proxyaddr -> clientaddr
allsockets.append(p) # make it "selectable"
p.sendto(remaddr, data) # send to server
else: # server -> proxyaddr
s.sendto(origins[dstaddr])

How to send messages over internet socket

My server (listening) is as:
import os
from socket import *
host = ""
port = 1337
buf = 1024
addr = (host, port)
UDPSock = socket(AF_INET, SOCK_DGRAM)
UDPSock.bind(addr)
print("Waiting to receive messages...")
while True:
(data, addr) = UDPSock.recvfrom(buf)
data=data.decode('ascii')
print("Received message: " + data)
if data == "exit":
break
UDPSock.close()
os._exit(0)
and my client is
import os
from socket import *
host = "MY INTERNET IP"
port = 1337
addr = (host, port)
UDPSock = socket(AF_INET, SOCK_DGRAM)
while True:
data = input("Enter message to send or type 'exit': ")
data=data.encode('ascii')
UDPSock.sendto(data, addr)
if data == "exit":
break
UDPSock.close()
os._exit(0)
so I need to know, how do I transmit these messages over internet? i have all my ports open on my network yet i can not receive any messages while trying to do that. My server is listening on port 1337 and my client is transmitting to MY INTERNET IP where a server is listening on the port it is transmitting on. so how do I get this to work, what do i do wrong? thing is, it works while doing lan. it's cozy, but very pointless since i would like to transmit text over web
EDIT
Got my friend to run client side. Python just returned 4.
Nothing happened on my side.

UDP Client/Server Socket in Python

I'm new to python and sockets and am trying to write an echoing client/server socket. I have written the server so that 30% of the packets are lost. I programmed my client to timeout after one second since the packet could be lost. However, whenever I run my client socket, my output is 100% REQUEST TIMED OUT. I'm assuming I'm getting this output because my server is never receiving the message. I've looked over my code multiple times and cannot figure out why I am constantly getting this output. Below is my code for my server and client sockets. Any help would be appreciated.
Server Socket:
# We will need the following module to generate randomized lost packets
import random
from socket import *
# Create a UDP socket
# Notice the use of SOCK_DGRAM for UDP packets
serverSocket = socket(AF_INET, SOCK_DGRAM)
# Assign IP address and port number to socket
serverSocket.bind(('', 12000))
while True:
# Generate random number in the range of 0 to 10
rand = random.randint(0, 10)
# Receive the client packet along with the address it is coming from
message, address = serverSocket.recvfrom(1024)
# Capitalize the message from the client
message = message.upper()
# If rand is less is than 4, we consider the packet lost and do notrespond
if rand < 4:
continue
# Otherwise, the server responds
serverSocket.sendto(message, address)
Client Socket:
import time
from socket import *
pings = 1
#Send ping 10 times
while pings < 11:
#Create a UDP socket
clientSocket = socket(AF_INET, SOCK_DGRAM)
#Set a timeout value of 1 second
clientSocket.settimeout(1)
#Ping to server
message = 'test'
addr = ("127.0.0.1", 12000)
#Send ping
start = time.time()
clientSocket.sendto(message, addr)
#If data is received back from server, print
try:
data, server = clientSocket.recvfrom(1024)
end = time.time()
elapsed = end - start
print data + " " + pings + " "+ elapsed
#If data is not received back from server, print it has timed out
except timeout:
print 'REQUEST TIMED OUT'
pings = pings - 1
I tested your code, and it works as expected on my machine. Your issue might not be your code. It could be a firewall or something else blocking all the packets on the loopback interface (127.0.0.1). Depending on your operating system, try testing with a packet monitor like Wireshark.
Also, here are a few suggestions on how to improve your code to be more Pythonic:
Server
import random
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('', 12000))
while True:
rand = random.randint(0, 10)
message, address = server_socket.recvfrom(1024)
message = message.upper()
if rand >= 4:
server_socket.sendto(message, address)
Client
import time
import socket
for pings in range(10):
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.settimeout(1.0)
message = b'test'
addr = ("127.0.0.1", 12000)
start = time.time()
client_socket.sendto(message, addr)
try:
data, server = client_socket.recvfrom(1024)
end = time.time()
elapsed = end - start
print(f'{data} {pings} {elapsed}')
except socket.timeout:
print('REQUEST TIMED OUT')
Here is an alternative with asyncio.
import asyncio
import random
class EchoServerProtocol:
def connection_made(self, transport):
self.transport = transport
def datagram_received(self, data, addr):
message = data.decode()
print('Received %r from %s' % (message, addr))
rand = random.randint(0, 10)
if rand >= 4:
print('Send %r to %s' % (message, addr))
self.transport.sendto(data, addr)
else:
print('Send %r to %s' % (message, addr))
self.transport.sendto(data, addr)
loop = asyncio.get_event_loop()
print("Starting UDP server")
# One protocol instance will be created to serve all client requests
listen = loop.create_datagram_endpoint(
EchoServerProtocol, local_addr=('127.0.0.1', 12000))
transport, protocol = loop.run_until_complete(listen)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
transport.close()
loop.close()

Categories