Asyncio Sockets, Localhost vs local IP - python

I have an Asyncio script I'm writing. Everything is working but I had a question about what I'm seeing in a tuple asyncio returns as the address.
This line of code returns two different things depending on weather I connect with a client using localhost or my local IP address.
(Server code)
addr = writer.get_extra_info('peername')
print("Received %r from %r" % (message, addr))
With localhost as my connection in my client, I see this on my server
(Client code)
reader, writer = await asyncio.open_connection('localhost', 8888, loop=asyncloop)
(Server prints)
Received 'Hello World!' from ('::1', 50402, 0, 0)
And with an IP address as my connection in my client,
(Client code)
reader, writer = await asyncio.open_connection('192.168.147.200', 8888, loop=asyncloop)
(Server prints)
Received 'Hello World!' from ('192.168.147.139', 50313)
What are the meanings of the two zero's in the first tuple? And why aren't they there when I connect with an IP?

See https://docs.python.org/3.5/library/socket.html?highlight=socket#socket-families
Your localhost connection is arriving via IPv6:
For AF_INET6 address family, a four-tuple (host, port, flowinfo, scopeid) is used, where flowinfo and scopeid represent the sin6_flowinfo and sin6_scope_id members in struct sockaddr_in6 in C. For socket module methods, flowinfo and scopeid can be omitted just for backward compatibility. Note, however, omission of scopeid can cause problems in manipulating scoped IPv6 addresses.
Your explicit IP address connection is an IPv4 connection, for which:
A pair (host, port) is used for the AF_INET address family, where host is a string representing either a hostname in Internet domain notation like 'daring.cwi.nl' or an IPv4 address like '100.50.200.5', and port is an integer.
The hostname 'localhost' can be resolved to either an IPv6 or IPv4 address, whereas '192.168.147.200' is an explicit IPv4 address.

Additional fields seems to be related about the IPv6 address scoping.
See also socket.getnameinfo and socket.getpeername.

Related

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)

I need to send a UDP packet from the ethernet adapter to an FPGA with a known MAC address and ipv4 address

I need to send a UDP packet over ethernet from 169.254.xx.xx to 192.168.xx.xx. The second address is the address of the FPGA and its MAC address is known. I am using wireshark to monitor the packets, but when i have an unbound socket, and I call sock.sendto() it sends over WLAN. When I bind the socket to the WLAN interface, it sends, but when I bind the socket to the ethernet interface, I get this error when I try to send:
OSError: [WinError 10051] A socket operation was attempted to an unreachable network
When bound to the ethernet interface, and i send to an unused address in the 169.254.xx.xx subnet, it sends an ARP, but nothing is sent when the destination is in the 192.168.xx.xx subnet.
Here is the code:
import socket
import time
address = '192.168.1.239'
port = 1235
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('169.254.190.73', 0))
sock.sendto('100'.encode('utf-8'), (bytes(address, 'UTF-8'), port))
time.sleep(0.005)
sock.close()
'''
'''
Since 169.254.xx.xx and 192.168.xx.xx represent different networks, traffic in between needs to be routed. However, 169.254.0.0/16 (autoconf) isn't usually routed.
If both nodes actually reside in the same layer 2 segment, just (manually) change the autoconf client's IP address.

Python 2.7 Socket connection on 2 different networks?

Title says all. I have a client and a server setup, but they only work with localhost. How can I connect to the socket from a different network?
Client
# Echo client program
import socket
print "Client"
HOST = "localhost" # Symbolic name meaning all available interfaces
PORT = 5001
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST,PORT))
while True:
data2 = raw_input()
s.sendall(data2)
Server
# Echo server program
import socket
print "Server"
HOST = "" # Symbolic name meaning all available interfaces
PORT = 5001 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
data = conn.recv(1024)
print data
if data == "Ping":
print "Pong!"
conn.close()
Check your public ip address using online tools like http://whatismyipaddress.com/
Check your local ip address using ipconfig -a in windows ifconfig in linux.
Now if you are behind a dsl router (generally, you are) these addresses are completely different. So you have to tell your router to send whenever a connection attemp received to a TCP port XXXX , forward it to "My Machine" (called Port Forwarding ). Although port forwarding settings are similar in most routers, there is no standard (Generally under NAT / Port Forwarding menu items on your router web configuration interface ). You may have to search instructions for your specific model.It's a good idea to set your computer to use a static ip address before port forwarding.Otherwise, the settings will be invalid if your computer is assigned another IP adress via DHCP.
If port forwarding is successful, now you only have to set your client application to connect to your public ip address. In your specific situation it's HOST = "X.X.X.X" in your client source code. Check if port forwarding works with a socket tester application you downloaded from somewhere. ( Don't test it with your experimental code, use an application you are sure that it's working). All did not work, check out the note below. It's the last resort ,though.
Note : Some ISP's put their clients behind an extra firewall for security. A simple method to detect if this is the situation is , your Wan ip address you see in your router web interface will be different from what you see in online tools like whatsmyip. In this situation no matter what you do , you will not be able to connect. You have to call your ISP and tell them to disable firewall for your connection . You may have some difficulties to explain them what you are talking about :).

Python sockets - connecting using the same src IP/Port doesn't work

I have the server bind to a particular port:
serv_sock = socket(AF_INET, SOCK_STREAM)
serv_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
serv_sock.bind((gethostname(), MAGIC_FLOW_PORT))
serv_sock.listen(MAX_MAGIC_FLOWS)
while True:
(client_sock, address) = serv_sock.accept()
print "Accepted a flow"
And the client does this:
client_sock = socket(AF_INET, SOCK_STREAM)
client_sock.bind((gethostname(), MAGIC_FLOW_PORT+1))
client_sock.connect((server_ip, MAGIC_FLOW_PORT))
while True:
client_socket.send("ABCDEF")
time.sleep(5)
So the expectation is client sends a TCP/IP packet with src port MAGIC_FLOW_PORT+1 and dst port MAGIC_FLOW_PORT every 5 seconds.
I enabled tcpdump in the server and I can see the packet as expected every 5s. However, the server prints the "Accepted a flow" only once, and nothing after that.
However if I comment this line in the client socket:
client_sock.bind((gethostname(), MAGIC_FLOW_PORT+1))
client then generates a packet with a different src port every time and the server accepts that. I also tried the server with
serv_sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
I still don't the second packet picked up the server.
Can I connect with the same src IP/port multiple times to the same server?
Thanks
Binding to a certain port should always work if you have the permissions to do so (<1024 you need to be root) and no other socket is inhabiting the same port on the same interface.
Except, if you use SO_REUSEPORT, then you can bind multiple listening sockets to the same port.
Just think about it, how would the server side know where to send the reply? (Assuming no anycast routing magic here.)
If you comment out that client_sock.bind() then the kernel picks a free port as the source of your connection, so that explains why the port is different after every connect().
The counterpart of the server's accept() is the client's connect(), not send(). So you should get an accept() call to return every time some other socket connects to that listening socket. If you want to react to received messages you should use client_sock.recv(). (Docs).

Python server only working on local wifi

The code:
import socket, threading
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("my ipv4 from ipconfig", 12))
server.listen(5)
def client_handler(client_socket):
request = client_socket.recv(100)
print "[*] Received: " + request
client_socket.close()
while True:
client, addr = server.accept()
print "[*] Accepted connection from: %s:%d" % (addr[0], addr[1])
servert = threading.Thread(target=client_handler, args=(client,))
servert.start()
So the server seems to work fine locally but if i ask my friend on a different network to connect, it doesn't connect. I tried port forwarding from the router
http://i.stack.imgur.com/wSUir.png ( cant post img apparently cause my_reputation < 10)
I also tried using the ip i get from the whatismyip website, but i get the error:
error: [Errno 10049] The requested address is not valid in its context
Any ideas on what i could do so other people can connect? Thanks.
When you're trying to access your server from outside your LAN, you ought to use 0.0.0.0 as your IP address while creating your socket object. 0.0.0.0 usually means the default route (the route to "the rest of" the internet, aside from routes in your local network etc.). If you use the IP address allotted by DCHP (in your case, the router), the devices connected to the network (router) is only aware of the fact that your IP address is what you get in $ifconfig command, a private IP address which the client is unaware

Categories