12.15. Names and DNS

Every page so far has used numeric IP addresses – 192.168.1.20 and the like. Real applications almost never do that. Servers are named example.com or api.example.com, and the application looks the name up at runtime to find an IP to send packets to. That lookup is the Domain Name System, or DNS.

12.15.1. What a name resolves to

A name is just a label. example.com does not carry any IP information itself – it has to be looked up, the same way a phone number is looked up in a phone book. The DNS infrastructure is the distributed phone book of the internet, and the result of a lookup is one or more IP addresses the camera can connect to.

example.com  ->  93.184.216.34

A single name often resolves to several addresses (for load balancing, geographic redundancy, IPv4 and IPv6 versions of the same service). Any of them works; the application picks one and tries it, falling back to the next if that fails.

12.15.2. How the lookup happens

When the camera asks for example.com:

  1. The camera sends a small UDP datagram (yes, UDP – see UDP – send a packet, hope for the best) to its configured DNS server. The DNS server’s address came from the same DHCP exchange that handed the camera its own IP.

  2. The DNS server may already have the answer cached (it has been asked recently). If so, it replies immediately.

  3. If not, the DNS server walks the global DNS hierarchy: ask the root servers about .com, ask those servers about example.com, ask those servers about the name. The whole tree-walk is hidden from the camera; the camera sees one query and one reply.

  4. The DNS server caches the result for next time and sends the reply back to the camera as another UDP datagram.

The whole exchange typically takes a few milliseconds on a warm cache, up to a hundred or so on a cold one.

12.15.3. The Python interface

The getaddrinfo() function does the lookup and returns an address ready to hand to a socket constructor:

import socket

addr = socket.getaddrinfo("example.com", 80)[0][-1]
print(addr)
# ('93.184.216.34', 80)

The signature is getaddrinfo(host, port). The return value is a list of 5-tuples (one per resolved address); the [0] picks the first one and the [-1] picks the last field, which is the (ip, port) tuple a socket can be handed directly:

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(socket.getaddrinfo("example.com", 80)[0][-1])
    s.send(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
    ...

Most camera code that talks to a remote server starts with that one line. The asyncio helpers (asyncio.open_connection()) do the lookup internally if a name is passed instead of a numeric IP, so async code typically does not call getaddrinfo() directly.

12.15.4. What can go wrong

  • No DNS server reachable. If Wi-Fi just came up and the link is flaky, the first getaddrinfo() call may time out. OSError is raised; retry once the link is stable.

  • Name does not exist. A typo or a stale name raises OSError after the DNS server returns “no such name”. The error code distinguishes this from “DNS server unreachable”, but for most camera applications the retry/give-up policy is the same.

  • Returned address does not work. DNS may return an address that no longer hosts the service. The fix is to fall back to the next entry in getaddrinfo()’s result list, or look the name up again later (the DNS cache will likely have updated by then).

  • Captive portals. Some Wi-Fi networks intercept DNS and return the captive-portal page’s IP for everything. The camera will appear to connect but the data it gets back will not match what the actual service would send. Not common in deployed environments, but a thing that happens on conference Wi-Fi and similar networks.

12.15.5. The camera’s own name

The pages so far have looked up other devices’ names. The camera also has a name of its own that it advertises to the local network when it asks for an address. The default is a generic identifier that varies by board; network.hostname() overrides it with something the rest of the network will recognise:

import network
network.hostname("kitchen-cam")
# ... then bring the link up as usual ...

Set the hostname before bringing the interface up, so the name goes out as part of the camera’s initial address request rather than after.

Two things now happen once the camera has joined the network. First, most home routers register the hostnames they hand addresses to into their own local lookup, so other devices can reach the cam as kitchen-cam – without having to know the numeric address the router happened to assign. (Enterprise networks may or may not honour this; the behaviour is up to the router.) Second, the cam itself runs an mDNS responder out of the box, so the same name is also reachable as kitchen-cam.local on any network whose clients understand mDNS – which most modern desktop operating systems do.

Note

Pass the bare hostname to network.hostname() – just "kitchen-cam", no .local suffix. The .local form is what mDNS adds at lookup time; baking it into the hostname makes the cam advertise kitchen-cam.local as a plain hostname, which is not what either side of the lookup expects.

12.15.6. When names are not enough

A few situations DNS does not help with:

  • Discovery on the local network. Standard DNS answers questions about names registered in the global directory; it knows nothing about devices on the local segment. Multicast DNS (mDNS) is the system that fills that gap. Every participating device joins a special multicast group on the local network and listens for queries; when a device asks for a name ending in .local, whichever device owns that name answers directly. No central server, no DNS configuration. Bonjour on Apple devices, Avahi on Linux, and Windows 10+ all speak the same protocol – which is why the kitchen-cam.local name the previous section set up resolves on a home network with nothing extra configured.

    The cam’s side of that exchange is the responder, and it is already running. What the cam does not have is a resolver – the other half, which would let a script ask the network “where is printer.local?” and get an answer back. The bundled mDNS code is responder-only (embedded devices are typically the thing being found, not the thing doing the finding). When discovery has to flow the other direction, UDP broadcast (see UDP – send a packet, hope for the best) is the simpler answer for the local-segment case, or a pure-Python module such as cbrand/micropython-mdns adds a full resolver.

  • IPv6 names. getaddrinfo() returns both IPv4 and IPv6 results if both are available. Pick the family the application’s socket can use.

For most camera-side code, getaddrinfo is a one-liner at the top of any function that opens a network connection. The examples elsewhere in this section that ran against "192.168.1.20" would all work the same way against a public name like "api.example.com" – just resolve first.