Python socket not sending host name - python

I am using an asyncore.dispatcher client on python to connect to a server developed in LabWindows running on a PC. Here's the code snippet on the client that connects to the server:
class DETClient(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host,port))
On the server side, my Labwindows code is looking for two parameters, TCPPeerName and TCPPeerAddr:
GetTCPPeerName (handle, peerName, sizeof (peerName));
GetTCPPeerAddr (handle, peerAddress, sizeof (peerAddress));
It seems that the python code is not passing the hostname at all, because my server gets a NULL for PeerName.
Do I have to do anything to specifically make the asyncore client to send the PeerName when establishing a connection?

Do I have to do anything to specifically make the asyncore client to send the PeerName when establishing a connection?
No, you don't, because TCP clients don't send names when establishing a connection. They send addresses.
GetTCPPeerName is almost certainly calling gethostbyaddr(X), where X is the address returned by GetTCPPeerAddr. In your case gethostbyaddr() is failing because the information is not available.
This means that your hostname resolution database is missing some data -- you might need to update your DNS, your hosts file, your WINS data, or wherever your host name data lives.

IS that DETClient the actual implementation? Because it's missing instantiations of both host and port for that instance?
in self.connect you're using self.host and self.port, which both evaulate to None since you havent set them yet.
class DETClient(asyncore.dispatcher):
def __init__(self, host, port):
self.host = host #<---- this is missing
self.port = port #<---- this is missing
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((self.host,self.port))

Related

Python: only accept socket with authentication

I am trying to learn how to use sockets to send files between pcs on my local network.
I am using ubuntu on my 'server' and my original plan was to create a ufw rule that allows all LAN connections but ask for a password when accepting the socket connection. That way only devices that are really supposed to communicate with the server would be accepted.
I do realise that creating ufw rules for static IPs would be an option but unfortunately I am dealing with dynamic IPs.
I have a text file of allowed keys on my 'server' and a text file containing one authentication key on the 'client'.
The server script looks like this:
#!/usr/bin/env python3
import socket
with open('/path/to/allowedKeys') as f:
allowedKeys = []
for line in f:
allowedKeys.append(line.rstrip('\n'))
HOST = '127.0.0.1' #standard loopback interface address (localhost)
PORT = 9999
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind((HOST, PORT))
serversocket.listen()
(clientsocket, address) = serversocket.accept()
with clientsocket:
print('Connected by', address)
while True:
data = clientsocket.recv(1024)
data = data.decode(encoding="utf-8")
print('Received', repr(data))
if data in allowedKeys:
clientsocket.sendall(b'Thank you for logging in.')
clientsocket.shutdown(socket.SHUT_RDWR)
break
else:
clientsocket.sendall(b'Error: Failed authentication')
clientsocket.shutdown(socket.SHUT_RDWR)
break
and the client script looks like this:
#!/usr/bin/env python3
import socket
with open('/path/to/authenticationKey', 'r') as f: #read authenticationKey from textfile
authenticationKey = f.readline().rstrip('\n')
authenticationKey = bytes(authenticationKey, encoding="utf-8") #convert authenticationKey to bytes
#authenticationKey = bytes('wrongKey', encoding="utf-8") #wrong Key for testing
HOST = '127.0.0.1' #server hostname or IP address
PORT = 9999 #port used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(authenticationKey)
data = s.recv(1024)
s.shutdown(socket.SHUT_RDWR)
s.close()
print('Received', repr(data))
When I execute the scripts it looks like everything works as expected.
I get either
Received 'nhjp9NIin987BUHBlkuULK98zJKn98JH'
or
Received 'wrongKey'
and the server shuts down successfully.
I have looked at these two related questions:
socket accept only specific addresses?
Server socket - accept connections only from IP addresses in the whitelist
While I cannot filter by IP it seems as if one must first accept the connection in order to authenticate the client.
I only want devices that possess an allowed key to be able to communicate with the server. All other connections should be shut down.
Since I still have very limited knowledge I would like to know whether this is the way to go or whether this leaves me open to any vulnerabilities.
While I cannot filter by IP it seems as if one must first accept the connection in order to authenticate the client. I only want devices that possess an allowed key ..
Since you want to authenticate a client based on actual data (the key or proof of possession of the key) then you must first have a connection able to transfer the data from the client. With TCP this means that you have to accept the connection.
... whether this leaves me open to any vulnerabilities.
This is the usual way to go but it leaves you open to denial of service attacks. Creating a new connection takes resources, so by opening lots of connections an attacker can exhaust server resources. If all resources are exhausted this way even valid users can no longer access the server. See for example the SYN flood attack.
Depending on the setup of the client additional protections can be added. But these are outside of the python application and mostly out of scope of this question. But to get some ideas see the section about countermeasures in the Wikipedia article.

Get Twisted server's IP address

If I have a Twisted server, how can I find its public-facing IP address?
Take this trivial echo server example:
from twisted.internet import protocol, reactor, endpoints
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
server_endpoint = endpoints.serverFromString(reactor, "tcp:1234")
listening_port_deferred = server_endpoint.listen(EchoFactory())
reactor.run()
I was expecting something like server_endpoint.getHost(), but I can't see that TCP4ServerEndpoint offers anything useful.
By adding the following lines before reactor.run(), we can see that the server is listening on all interfaces (0.0.0.0):
def print_host(listening_port):
print("listening_port.getHost():", listening_port.getHost())
listening_port_deferred.addCallback(print_host)
It outputs listening_port.getHost(): IPv4Address(type='TCP', host='0.0.0.0', port=1234). But that doesn't help us with the IP address of the network interface of the server.
We can get the IP address of the client by adding the following as the first line of buildProtocol():
print("Client's address:", addr.host)
But that only gives us the client's address.
How should I get the server's IP address?
Twisted will tell you the address you've bound the server to using just the method you found, getHost on the listening port. Unfortunately, it has the big limitation that you found which is that when the server is listening on all local addresses (INADDR_ANY) it gives you 0.0.0.0 (the canonical IPv4 dotted-quad representation of INADDR_ANY).
When this happens, you have to go outside of Twisted. I've found the netifaces package to be pretty good for this. From the docs:
>>> netifaces.interfaces()
['lo0', 'gif0', 'stf0', 'en0', 'en1', 'fw0']
>>> >>> addrs = netifaces.ifaddresses('lo0')
>>> addrs[netifaces.AF_INET]
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]
By combining this information with the observation that 0.0.0.0 means "all local addresses" you can figure out what local addresses the server will accept connections on.
Thanks to notorious's comment, I realised that the server's IP address is available only once a client connects. However, as Jean-Paul points out, this IP address isn't necessarily public-facing and may well be behind a NATing router.
To obtain the server's IP address for a given connection, we can use the getHost() method from the transport attribute of the Protocol class. This is documented in the ITransport interface.
For example, if we add the following method into the Echo protocol class in the original question, each time a client connects, the server will print out the IP address that was used.
def connectionMade(self):
print("IP address of host given connection:", self.transport.getHost())
So, for example, if you connect from the same machine on which the server is running, you will see:
IP address of host given connection: IPv4Address(type='TCP', host='127.0.0.1', port=1234)
However, if you connect from another machine on the same network, you might see:
IP address of host given connection: IPv4Address(type='TCP', host='192.168.5.103', port=1234)

Python on GCE: connection failed because connected host has failed to respond

I've been working on a project that requires a bit of networking between a server (hosted on GCE) and multiple clients. I created a Compute Engine Instance to run a Python script as shown in this video: https://www.youtube.com/watch?v=5OL7fu2R4M8.
Here is my server-side script:
server = socket.gethostbyname(socket.gethostname()) # 10.128.X.XXX which is the Internal IP
print(server)
port = 5555
clients = 0
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((server, port))
s.listen(2)
print("Waiting for connection...")
while True:
conn, addr = s.accept()
print("Connected to: ", addr)
conn.send(str.encode(f"{clients}"))
clients += 1
and here is my client side-script:
class Network:
def __init__(self):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = "10.128.0.2"
self.port = 5555
self.addr = (self.server, self.port)
self.id = int(self.connect())
def connect(self):
self.client.connect(self.addr)
return self.client.recv(2048).decode()
network = Network()
print(f"Connected as client {network.id}")
I know this script works because I have tested it with my computer being the server and 1 client, and another computer being the 2nd client. But when I use the GCE as the server, I get this error in the client script:
TimeoutError: [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
Could this be because I am using the internal IP address and not the external?
After this, I tried changing the firewall settings (added 'python-socket') of the GCE and this is what they look like:
But the error still persists...
As answered by W_B, I tried to run these commands on my VM and got the following outputs:
From your description it's evident it's the connection problem.
First of all you have to check if the firewall rule you created is still there. If it's "too broad" and allows for very wide access then it might be removed automatically even without you knowing it. It's on you'r screenshot but check it again just to be sure.
If it's there select the protocol you're goint to be using (I assume it's TCP) - some protocols are always blocked by default by GCP (you can't change this) so creating a rule with "any protocol" allowed is risky. Also - put one or two target IP's (not all inside this VPC) - this is not a must but improves security of your network.
Second - make sure port 5555 you're trying to connect to is accessible from other computers. You can scan the target host with nmap -p 5554 put.server.ip.here
You can scan it from the Internet or other VM's in the same VPC network.
You should get something like this:
root#localhost:~$ nmap -p 443 192.168.1.6
Starting Nmap 7.70 ( https://nmap.org ) at 2020-06-25 17:12 UTC
Nmap scan report for 192.168.1.6
Host is up (0.00091s latency).
PORT STATE SERVICE
443/tcp open https
Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds
If you see 5555/tcp filtered freeciv this means that something blocks the port.
Run nmap on the server (I assume you run some version of Linux) and if you don't want to install any non-essencial software you can use sudo netstat -tulpn | grep LISTEN to get a list of open ports (5555 should be on the list).
Also make sure firewall on your server doesn't block this port. You can use iptables for that.

"Connection Refused" running a server externally , although working locally

Trying to get into sockets and networking.
I wrote some simple server and client scripts and ran them,
which was working when connected locally and client-server communicated just fine, yet when my client script tries to connect to my_external_ip:open_port it gets a "Connection Refused" [WinError 10061]
I've opened a port (5234 in this case) and checked it using those port-scanning sites, what the server seems to react to and even accept connections.
Yet when I run my client script, it throws an exception, and the server doesn't seem to respond or even be aware of the connection attempt.
I've shut down my firewall temporarily and made sure I'm listening on 0.0.0.0:5234 (which to my understanding should be what I'm doing).
Am I missing something? doesn't make sense to me that the script runs locally, the server takes incoming external connections, yet this doesn't work.
Maybe the problem is that the client's outbound connection attempt is somehow blocked?
I cleaned up some unrelated code, but that's about it:
SERVER:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server = "" #Also tried socket.gethostname() and 0.0.0.0
port = 5234
s.bind((server,port))
s.listen()
connection, address = s.accept()
CLIENT:
def __init__(self):
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server = my_public_ip
self.port = 5234
self.client.connect((self.server,self.port)

UDP- server and client ip for for far connection

I built a network game with blender game engine with udp socket, I have server.blend and client.blend. In my home, the game works great and the connection so on.. but when I sent the client.blend to a friend he couldn't connect to my server..
This is my server constructor:
class Server:
def __init__(self, host="", port= 9017):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.setblocking(False)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((host, port))
this is my client's constructor:
class Client:
def __init__(self, server_ip="192.168.1.12", server_port= 9017):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.setblocking(False)
self.serv_addr = (server_ip, server_port)
I believe it's not working on my friend's computer because of the ip addresses.
Does anyone know what's the problem?
The hard-coded IP you're using is an RFC 1918 private IP address, which is not routable over the internet. If your friend was connected to the same local area network as you he would likely be able to connect to your server. Fundamentally, this boils down to a network issue.
Possible solutions could be to define a host name in your application and use a DynDNS provider to register that host name with your router's public IP. Then set up port forwarding on your home router to allow connections from the Internet into your server on the udp ports you need. There's a thousand other ways to go about something like this but really this falls into the realm of networking than programming.

Categories