9.15. Imena i DNS

Svaka dosadašnja stranica koristila je numeričke IP adrese – 192.168.1.20 i slične. Stvarne aplikacije to gotovo nikada ne rade. Poslužitelji se zovu example.com ili api.example.com, a aplikacija to ime traži tijekom izvođenja kako bi pronašla IP na koji će slati pakete. To traženje je Domain Name System, odnosno DNS.

9.15.1. U što se ime razrješava

Ime je samo oznaka. example.com sam po sebi ne nosi nikakvu IP informaciju – mora se potražiti, na isti način na koji se telefonski broj traži u telefonskom imeniku. DNS infrastruktura je distribuirani telefonski imenik interneta, a rezultat traženja je jedna ili više IP adresa na koje se kamera može povezati.

example.com  ->  93.184.216.34

Jedno ime često se razrješava u nekoliko adresa (radi raspodjele opterećenja, geografske redundancije, IPv4 i IPv6 verzija iste usluge). Bilo koja od njih radi; aplikacija odabire jednu i pokušava s njom, vraćajući se na sljedeću ako ta zakaže.

9.15.2. Kako se traženje odvija

Kada kamera zatraži example.com:

  1. Kamera šalje mali UDP datagram (da, UDP – vidi UDP – pošalji paket, nadaj se najboljem) svom konfiguriranom DNS poslužitelju. Adresa DNS poslužitelja došla je iz iste DHCP razmjene koja je kameri dodijelila vlastiti IP.

  2. DNS poslužitelj možda već ima odgovor u predmemoriji (nedavno je upitan). Ako je tako, odgovara odmah.

  3. Ako nema, DNS poslužitelj prolazi kroz globalnu DNS hijerarhiju: pita korijenske poslužitelje o .com, pita te poslužitelje o example.com, pita te poslužitelje o imenu. Cijeli obilazak stabla skriven je od kamere; kamera vidi jedan upit i jedan odgovor.

  4. DNS poslužitelj sprema rezultat u predmemoriju za sljedeći put i šalje odgovor natrag kameri kao još jedan UDP datagram.

Cijela razmjena obično traje nekoliko milisekundi pri toploj predmemoriji, do stotinjak pri hladnoj.

9.15.3. Python sučelje

Funkcija getaddrinfo() obavlja traženje i vraća adresu spremnu za predaju konstruktoru utičnice:

import socket

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

Potpis je getaddrinfo(host, port). Povratna vrijednost je lista 5-torki (jedna po razriješenoj adresi); [0] odabire prvu, a [-1] odabire posljednje polje, što je (ip, port) torka koja se može izravno predati utičnici:

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

Većina kamera-koda koja komunicira s udaljenim poslužiteljem počinje s tom jednom linijom. asyncio pomoćnici (asyncio.open_connection()) obavljaju traženje interno ako se proslijedi ime umjesto numeričkog IP-a, pa asinkroni kod obično ne poziva getaddrinfo() izravno.

9.15.4. Što može poći po zlu

  • Nijedan DNS poslužitelj nije dostupan. Ako se Wi-Fi tek podigao, a veza je nestabilna, prvi poziv getaddrinfo() može isteći. Izaziva se OSError; pokušajte ponovno kada je veza stabilna.

  • Ime ne postoji. Tipfeler ili zastarjelo ime izaziva OSError nakon što DNS poslužitelj vrati „nema takvog imena”. Kod pogreške razlikuje to od „DNS poslužitelj nedostupan”, ali za većinu kamera-aplikacija politika ponavljanja/odustajanja je ista.

  • Vraćena adresa ne radi. DNS može vratiti adresu koja više ne poslužuje uslugu. Rješenje je vratiti se na sljedeći unos u rezultatskoj listi funkcije getaddrinfo() ili potražiti ime ponovno kasnije (DNS predmemorija će se do tada vjerojatno ažurirati).

  • Zarobljavajući portali. Neke Wi-Fi mreže presreću DNS i vraćaju IP stranice zarobljavajućeg portala za sve. Činit će se da se kamera povezala, ali podaci koje dobiva natrag neće odgovarati onome što bi stvarna usluga poslala. Nije uobičajeno u proizvodnim okruženjima, ali se događa na konferencijskom Wi-Fi-ju i sličnim mrežama.

9.15.5. Vlastito ime kamere

Dosadašnje stranice tražile su imena drugih uređaja. Kamera također ima vlastito ime koje oglašava lokalnoj mreži kada traži adresu. Zadana vrijednost je generički identifikator koji se razlikuje ovisno o pločici; network.hostname() ga zamjenjuje nečim što će ostatak mreže prepoznati:

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

Postavite naziv računala prije podizanja sučelja, kako bi ime izašlo kao dio kamerinog početnog zahtjeva za adresom, a ne nakon njega.

Dvije se stvari sada događaju kada se kamera pridruži mreži. Prvo, većina kućnih usmjerivača registrira nazive računala kojima dodjeljuje adrese u vlastito lokalno traženje, pa drugi uređaji mogu doći do kamere kao kitchen-cam – bez potrebe za poznavanjem numeričke adrese koju je usmjerivač slučajno dodijelio. (Poslovne mreže to mogu ali i ne moraju poštovati; ponašanje ovisi o usmjerivaču.) Drugo, kamera sama odmah pokreće mDNS odgovaratelj, pa je isto ime također dostupno kao kitchen-cam.local na svakoj mreži čiji klijenti razumiju mDNS – a to čini većina modernih stolnih operacijskih sustava.

Napomena

Proslijedite goli naziv računala funkciji network.hostname() – samo "kitchen-cam", bez sufiksa .local. Oblik .local je ono što mDNS dodaje pri traženju; ugrađivanje toga u naziv računala čini da kamera oglašava kitchen-cam.local kao običan naziv računala, što nije ono što očekuje nijedna strana traženja.

9.15.6. Kada imena nisu dovoljna

Nekoliko situacija u kojima DNS ne pomaže:

  • Otkrivanje na lokalnoj mreži. Standardni DNS odgovara na pitanja o imenima registriranima u globalnom imeniku; ne zna ništa o uređajima na lokalnom segmentu. Multicast DNS (mDNS) je sustav koji popunjava tu prazninu. Svaki uređaj sudionik pridružuje se posebnoj multicast grupi na lokalnoj mreži i osluškuje upite; kada uređaj zatraži ime koje završava s .local, izravno odgovara onaj uređaj koji posjeduje to ime. Bez središnjeg poslužitelja, bez DNS konfiguracije. Bonjour na Apple uređajima, Avahi na Linuxu i Windows 10+ svi govore isti protokol – zbog čega se ime kitchen-cam.local postavljeno u prethodnom odjeljku razrješava na kućnoj mreži bez ičega dodatno konfiguriranog.

    Kamerina strana te razmjene je odgovaratelj i već je pokrenuta. Ono što kamera nema je razrješavač – druga polovica, koja bi omogućila skripti da pita mrežu „gdje je printer.local?” i dobije odgovor natrag. Priloženi mDNS kod služi samo kao odgovaratelj (ugrađeni uređaji obično su ono što se pronalazi, a ne ono što pronalazi). Kada otkrivanje mora teći u drugom smjeru, UDP emitiranje (vidi UDP – pošalji paket, nadaj se najboljem) je jednostavniji odgovor za slučaj lokalnog segmenta, ili čisto Python modul poput cbrand/micropython-mdns dodaje potpuni razrješavač.

  • IPv6 imena. getaddrinfo() vraća i IPv4 i IPv6 rezultate ako su oba dostupna. Odaberite obitelj koju aplikacijska utičnica može koristiti.

Za većinu koda na strani kamere, getaddrinfo je jedna linija na vrhu svake funkcije koja otvara mrežnu vezu. Primjeri drugdje u ovom odjeljku koji su radili protiv "192.168.1.20" svi bi radili na isti način protiv javnog imena poput "api.example.com" – samo prvo razriješite.