I am using a Python script to send UDP packets to a server which will register my script to receive notifications from the server. The protocol requires that I send my own IP-Address and port on which I want to receive these notifications, so this should be an address that is reachable from the server's network.
Solutions should at least work on Windows XP and above and preferably Mac OS X, as this is my development platform.
The first step is to get the IP-Address of any of my interfaces. I am currently using the commonly suggested approach of resolving the machines own host name:
def get_local_address():
return socket.gethostbyname(socket.gethostname())
This only works sometimes. Currently it returns 172.16.249.1
, which is the address of a virtual interface used by VM Ware, so this is a problem.
Much better would be a way to get the IP-Address of the default interface, which should be the right one most of the time.
Even better would be a way to get the actual IP-Address used to connect to the server by a connection-oriented protocol like TCP. I can actually get that address, but not without attempting to open the connection:
def get_address_to_connect_to(server_addr):
non_open_port = 50000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((server_addr, non_open_port))
except socket.error:
pass
else:
s.close() # just in case
return s.getsockname()[0]
This will block until some TCP timeout has been reached in the case that the server is unreachable or an evil firewall in between is blocking ICMP packets. Is there a better way to get that information?
Python socket module gethostbyname() function accepts hostname argument and returns the IP address in the string format.
ipaddr.py is a library for working with IP addresses, both IPv4 and IPv6. It has been superseded by ipaddress from the Python 3 standard library, and its Python 2 backport.
I had to solve the same problem once, and spent considerable time trying to find a better way, without success. I also started with gethostname, but discovered, as you did, that it doesn't always return the right result, and can even throw an exception in cases where there is a problem with the hosts file.
The only solution I could find that works reliably across platforms is to try the connection, as you're doing. One improvement on your code is to use UDP instead of TCP, i.e. SOCK_DGRAM instead of SOCK_STREAM. That avoids the connect timeout, and the function just completes immediately.
If you're deploying this code to client locations that might be running firewalls, make sure you document the fact that it does this check. Otherwise they may see a firewall warning for an outbound connection to port 50000, not understand its purpose, and consider it a bug.
Edit: here's roughly the code I used:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect((host, 9))
client = s.getsockname()[0]
except socket.error:
client = "Unknown IP"
finally:
del s
return client
I think you're getting 0.0.0.0 because you're closing the socket before calling getsockname. Another tip is my use of port 9; it's the RFC863 UDP discard port, and therefore slightly less weird than some random high-numbered port.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With