9.15. Jména a DNS

Každá dosavadní stránka používala číselné IP adresy – 192.168.1.20 a podobné. Reálné aplikace to téměř nikdy nedělají. Servery se jmenují example.com nebo api.example.com a aplikace si jméno vyhledá za běhu, aby zjistila IP, na kterou má posílat pakety. Toto vyhledávání je Domain Name System, neboli DNS.

9.15.1. Na co se jméno přeloží

Jméno je jen štítek. example.com samo o sobě nenese žádnou informaci o IP – musí se vyhledat, stejně jako se telefonní číslo vyhledává v telefonním seznamu. Infrastruktura DNS je distribuovaný telefonní seznam internetu a výsledkem vyhledávání je jedna nebo více IP adres, na které se kamera může připojit.

example.com  ->  93.184.216.34

Jediné jméno se často přeloží na několik adres (kvůli vyrovnávání zátěže, geografické redundanci, verzím IPv4 a IPv6 téže služby). Kterákoli z nich funguje; aplikace si jednu vybere a zkusí ji, přičemž v případě selhání přejde na další.

9.15.2. Jak vyhledávání probíhá

Když kamera žádá o example.com:

  1. Kamera odešle malý UDP datagram (ano, UDP – viz UDP – odešli paket a doufej v nejlepší) na svůj nakonfigurovaný DNS server. Adresa DNS serveru přišla z téže výměny DHCP, která kameře předala její vlastní IP.

  2. DNS server může mít odpověď již uloženou v cache (byl o ni nedávno požádán). Pokud ano, odpoví okamžitě.

  3. Pokud ne, DNS server projde globální hierarchii DNS: zeptá se kořenových serverů na .com, zeptá se těchto serverů na example.com, zeptá se těchto serverů na jméno. Celé procházení stromu je před kamerou skryto; kamera vidí jeden dotaz a jednu odpověď.

  4. DNS server uloží výsledek do cache pro příště a pošle odpověď zpět kameře jako další UDP datagram.

Celá výměna obvykle trvá několik milisekund při teplé cache, až zhruba sto milisekund při studené.

9.15.3. Rozhraní v Pythonu

Funkce getaddrinfo() provede vyhledávání a vrátí adresu připravenou k předání konstruktoru socketu:

import socket

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

Signatura je getaddrinfo(host, port). Návratovou hodnotou je seznam 5tic (jedna na každou přeloženou adresu); [0] vybere první z nich a [-1] vybere poslední pole, kterým je n-tice (ip, port), již lze socketu předat přímo:

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")
    ...

Většina kódu kamery, který komunikuje se vzdáleným serverem, začíná tímto jediným řádkem. Pomocné funkce asyncio (asyncio.open_connection()) provádějí vyhledávání interně, pokud je místo číselné IP předáno jméno, takže asynchronní kód obvykle nevolá getaddrinfo() přímo.

9.15.4. Co se může pokazit

  • Žádný dosažitelný DNS server. Pokud Wi-Fi právě naběhlo a spoj je nestabilní, první volání getaddrinfo() může vypršet. Je vyvolána výjimka OSError; zkuste to znovu, jakmile je spoj stabilní.

  • Jméno neexistuje. Překlep nebo zastaralé jméno vyvolá OSError poté, co DNS server vrátí „žádné takové jméno“. Chybový kód toto odlišuje od „DNS server nedosažitelný“, ale pro většinu aplikací s kamerou je politika opakování/vzdání se stejná.

  • Vrácená adresa nefunguje. DNS může vrátit adresu, která již službu nehostí. Náprava spočívá v přechodu na další položku ve výsledném seznamu getaddrinfo() nebo v opětovném vyhledání jména později (DNS cache se do té doby pravděpodobně aktualizuje).

  • Captive portály. Některé Wi-Fi sítě zachytávají DNS a vracejí IP stránky captive portálu pro cokoli. Kamera se bude zdánlivě připojovat, ale data, která dostane zpět, nebudou odpovídat tomu, co by odeslala skutečná služba. V nasazených prostředích to není běžné, ale stává se to na konferenčních Wi-Fi a podobných sítích.

9.15.5. Vlastní jméno kamery

Dosavadní stránky vyhledávaly jména jiných zařízení. Kamera má také vlastní jméno, které ohlašuje lokální síti, když žádá o adresu. Výchozí hodnotou je obecný identifikátor, který se liší podle desky; network.hostname() jej přepíše něčím, co zbytek sítě rozpozná:

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

Nastavte hostname předtím, než uvedete rozhraní do provozu, aby jméno vyšlo jako součást počátečního požadavku kamery na adresu, nikoli až poté.

Jakmile se kamera připojí k síti, nastanou nyní dvě věci. Za prvé, většina domácích routerů zaregistruje hostnamy, kterým přiděluje adresy, do svého vlastního lokálního vyhledávání, takže ostatní zařízení mohou kameru dosáhnout jako kitchen-cam – aniž by musela znát číselnou adresu, kterou jí router náhodou přidělil. (Podnikové sítě to mohou, ale nemusí respektovat; chování závisí na routeru.) Za druhé, kamera sama hned po vybalení provozuje mDNS respondér, takže totéž jméno je dosažitelné také jako kitchen-cam.local na libovolné síti, jejíž klienti rozumí mDNS – což většina moderních desktopových operačních systémů umí.

Poznámka

Předávejte funkci network.hostname() holý hostname – pouze "kitchen-cam", žádnou příponu .local. Tvar .local je to, co mDNS přidává v době vyhledávání; zapečení do hostname způsobí, že kamera ohlašuje kitchen-cam.local jako prostý hostname, což není to, co očekává ani jedna ze stran vyhledávání.

9.15.6. Když jména nestačí

Několik situací, s nimiž DNS nepomůže:

  • Vyhledávání na lokální síti. Standardní DNS odpovídá na otázky o jménech registrovaných v globálním adresáři; o zařízeních na lokálním segmentu neví nic. Multicast DNS (mDNS) je systém, který tuto mezeru zaplňuje. Každé zúčastněné zařízení se připojí ke speciální multicastové skupině na lokální síti a naslouchá dotazům; když se zařízení zeptá na jméno končící na .local, odpoví přímo to zařízení, které toto jméno vlastní. Žádný centrální server, žádná konfigurace DNS. Bonjour na zařízeních Apple, Avahi na Linuxu i Windows 10+ hovoří týmž protokolem – a proto se jméno kitchen-cam.local, které nastavila předchozí sekce, přeloží na domácí síti bez čehokoli dalšího nakonfigurovaného.

    Stranou kamery v této výměně je respondér, a ten už běží. Co kamera nemá, je resolver – druhá polovina, která by skriptu umožnila zeptat se sítě „kde je printer.local?“ a dostat odpověď zpět. Přibalený mDNS kód je pouze respondér (vestavěná zařízení jsou obvykle tím, co je nalézáno, nikoli tím, co nalézá). Když musí vyhledávání proudit opačným směrem, jednodušší odpovědí pro případ lokálního segmentu je UDP broadcast (viz UDP – odešli paket a doufej v nejlepší), nebo plný resolver přidává čistě pythonovský modul jako cbrand/micropython-mdns.

  • Jména IPv6. getaddrinfo() vrací výsledky IPv4 i IPv6, pokud jsou oba k dispozici. Vyberte rodinu, kterou socket aplikace umí použít.

Pro většinu kódu na straně kamery je getaddrinfo jednořádkovkou na začátku každé funkce, která otevírá síťové spojení. Příklady jinde v této sekci, které běžely proti "192.168.1.20", by všechny fungovaly stejně proti veřejnému jménu jako "api.example.com" – jen je třeba nejdříve přeložit jméno.