1

I want to receive unicast UDP packets sent to my machine's IPv4 address only, ignoring broadcast packets. This works if I hardcode the IP address like sock.bind(('192.168.1.69', 9)) but to make this code portable, I need a way to find the machine's own IP. I tried the below code, which isn't working:

host = socket.gethostname()
socket.gethostbyname(host)

The second line fails with: socket.gaierror: [Errno 8] nodename nor servname provided, or not known

If I instead do sock.bind(('0.0.0.0', 9)) then it works, but it also receives broadcast packets sent to 255.255.255.255 which I don't want. Using SOCK_DGRAM strips out the IP headers, so I don't think it's possible to inspect the destination IP address.

Edit: On macOS (and probably linux) I can get the destination IP using sock.recvmsg() after enabling socket.IP_RECVDSTADDR, but this doesn't work on Windows, which is what I need.

Elliott B
  • 980
  • 9
  • 32
  • https://stackoverflow.com/questions/39970606/gaierror-errno-8-nodename-nor-servname-provided-or-not-known-with-macos-sie – PlsBuffMyBrain Jun 22 '22 at 17:26
  • "*Using `SOCK_DGRAM` strips out the IP headers, so I don't think it's possible to inspect the destination IP address*" - it should be possible if you receive the packets using [`recvmsg()`](https://docs.python.org/3/library/socket.html#socket.socket.recvmsg) and look at the ancillary metadata provided. – Remy Lebeau Jun 22 '22 at 19:22
  • @RemyLebeau I tried `(data, ancdata, msg_flags, address) = sock.recvmsg(1024, 1024)` but ancdata is an empty list. – Elliott B Jun 23 '22 at 02:15
  • @ElliottB you have to use `sock.setsockopt()` first to specify which ancillary data you want to receive, such as the `IP_PKTINFO` or `IP_RECVORIGDSTADDR` option of the `IPPROTO_IP` level. – Remy Lebeau Jun 23 '22 at 02:55
  • `AttributeError: module 'socket' has no attribute 'IP_PKTINFO'`. So I had to look it up in the standard library `netinet/in.h` to find the value is 26. This option gives me `b'\x06\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff'` which doesn't look like an IP address, I'm not sure how to read it. I can't find `IP_RECVORIGDSTADDR` in the std library but it is defined in the socket module with value 7 which is the same as `IP_RECVDSTADDR`. This one works! This was a long puzzle but thanks for putting me on the right track. – Elliott B Jun 23 '22 at 03:43
  • Unfortunately `socket.recvmsg` isn't available on Windows, so I'm back to square one. – Elliott B Jun 23 '22 at 04:20
  • 1
    To do this properly you'll need the `netifaces` Python module; then you can use that module to list out all the IP interfaces present on your machine, and the IP address(es) associated with each, and choose the one(s) you want. See Examples 4 and 14 here: https://programtalk.com/python-examples/netifaces.interfaces/ – Jeremy Friesner Jun 23 '22 at 04:32

2 Answers2

0

To achieve this you need someone on the internet to tell you what is the public IP they see when you send them a package.

Check this thread provides some alternatives using services such as whatsmyip.org, api.ipify.org and ident.me.

Here is one example by @serge-stroobandt in the mentioned thread which is working for me:

import urllib.request
external_ip = urllib.request.urlopen('https://ident.me').read().decode('utf8')
print(external_ip)

Good luck!

Felipe
  • 24
  • 3
0

Use the fully qualified domain name search.

import socket
hostname = socket.getfqdn()
socket.gethostbyname_ex(hostname)

Gives you information like

('F12234.mydom.com', [], ['xxx.19.xxx.1', '10.195.3.101'])

The last list of IP's are the IP's currently associated to the networks your are connected to. If you have only one network connected, it should be one.

Kris
  • 8,680
  • 4
  • 39
  • 67
  • `getfqdn()` returns `a23-45-205-250.deploy.static.akamaitechnologies.com` which then fails to get the IP with the same `gaierror` I wrote in the question. – Elliott B Jun 23 '22 at 05:05
  • Then it means that, the internal DNS server configured is not able to resolve your host name. The network you are on, may have a different DNS lookup server and you will have to find it probably. You can use the `dns.resolver` package for that. – Kris Jun 23 '22 at 05:57