9.15. Nimet ja DNS

Jokainen tähänastinen sivu on käyttänyt numeerisia IP-osoitteita – 192.168.1.20 ja vastaavia. Todelliset sovellukset eivät juuri koskaan tee niin. Palvelimet on nimetty muotoon example.com tai api.example.com, ja sovellus selvittää nimen ajonaikaisesti löytääkseen IP-osoitteen, johon paketit lähetetään. Tämä selvitys on Domain Name System eli DNS.

9.15.1. Mihin nimi selviää

Nimi on vain otsake. example.com ei itse kanna mitään IP-tietoa – se on selvitettävä, samalla tavalla kuin puhelinnumero etsitään puhelinluettelosta. DNS-infrastruktuuri on internetin hajautettu puhelinluettelo, ja selvityksen tulos on yksi tai useampi IP-osoite, joihin kamera voi yhdistää.

example.com  ->  93.184.216.34

Yksittäinen nimi selviää usein useaksi osoitteeksi (kuormantasausta, maantieteellistä vikasietoisuutta sekä saman palvelun IPv4- ja IPv6-versioita varten). Mikä tahansa niistä toimii; sovellus valitsee yhden ja kokeilee sitä, siirtyen seuraavaan jos se epäonnistuu.

9.15.2. Miten selvitys tapahtuu

Kun kamera kysyy nimeä example.com:

  1. Kamera lähettää pienen UDP-datasähkeen (kyllä, UDP – katso UDP – lähetä paketti ja toivo parasta) määritetylle DNS-palvelimelleen. DNS-palvelimen osoite tuli samasta DHCP-vaihdosta, joka antoi kameralle sen oman IP-osoitteen.

  2. DNS-palvelimella saattaa jo olla vastaus välimuistissa (sitä on kysytty äskettäin). Jos näin on, se vastaa heti.

  3. Jos ei, DNS-palvelin kulkee läpi globaalin DNS-hierarkian: kysyy juuripalvelimilta osasta .com, kysyy niiltä palvelimilta osasta example.com, kysyy niiltä palvelimilta itse nimestä. Koko puun läpikäynti on piilossa kameralta; kamera näkee yhden kyselyn ja yhden vastauksen.

  4. DNS-palvelin tallentaa tuloksen välimuistiin seuraavaa kertaa varten ja lähettää vastauksen takaisin kameralle toisena UDP-datasähkeenä.

Koko vaihto kestää tyypillisesti muutaman millisekunnin lämpimällä välimuistilla, jopa sata tai vähän enemmän kylmällä.

9.15.3. Python-rajapinta

Funktio getaddrinfo() tekee selvityksen ja palauttaa soketin konstruktorille valmiin osoitteen:

import socket

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

Allekirjoitus on getaddrinfo(host, port). Paluuarvo on lista 5-monikoita (yksi kutakin selvitettyä osoitetta kohden); [0] poimii ensimmäisen ja [-1] poimii viimeisen kentän, joka on (ip, port)-monikko, jonka voi antaa soketille suoraan:

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

Useimmat etäpalvelimen kanssa keskustelevat kamerakoodit alkavat tällä yhdellä rivillä. asyncio-apufunktiot (asyncio.open_connection()) tekevät selvityksen sisäisesti, jos niille annetaan nimi numeerisen IP:n sijaan, joten asynkroninen koodi ei tyypillisesti kutsu getaddrinfo()-funktiota suoraan.

9.15.4. Mikä voi mennä pieleen

  • Yhtään DNS-palvelinta ei tavoiteta. Jos Wi-Fi juuri nousi pystyyn ja yhteys on epävakaa, ensimmäinen getaddrinfo()-kutsu saattaa aikakatkaistua. Nostetaan OSError; yritä uudelleen kun yhteys on vakaa.

  • Nimeä ei ole olemassa. Kirjoitusvirhe tai vanhentunut nimi nostaa OSError-poikkeuksen sen jälkeen kun DNS-palvelin palauttaa ”ei tällaista nimeä”. Virhekoodi erottaa tämän tilanteesta ”DNS-palvelinta ei tavoiteta”, mutta useimmissa kamerasovelluksissa uudelleenyrittämis-/luovuttamiskäytäntö on sama.

  • Palautettu osoite ei toimi. DNS voi palauttaa osoitteen, joka ei enää isännöi palvelua. Korjaus on siirtyä getaddrinfo()-tuloslistan seuraavaan merkintään tai selvittää nimi uudelleen myöhemmin (DNS-välimuisti on siihen mennessä todennäköisesti päivittynyt).

  • Vangitsevat portaalit (captive portals). Jotkin Wi-Fi-verkot sieppaavat DNS-kyselyt ja palauttavat vangitsevan portaalisivun IP-osoitteen kaikelle. Kamera näyttää yhdistävän, mutta takaisin saatu data ei vastaa sitä, mitä todellinen palvelu lähettäisi. Ei yleistä käyttöönotetuissa ympäristöissä, mutta sellaista mitä tapahtuu konferenssien Wi-Fissä ja vastaavissa verkoissa.

9.15.5. Kameran oma nimi

Tähänastiset sivut ovat selvittäneet muiden laitteiden nimiä. Kameralla on myös oma nimi, jota se mainostaa paikallisverkolle pyytäessään osoitetta. Oletus on yleinen tunniste, joka vaihtelee korttikohtaisesti; network.hostname() korvaa sen jollakin, jonka muu verkko tunnistaa:

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

Aseta isäntänimi ennen rajapinnan nostamista pystyyn, jotta nimi lähtee osana kameran ensimmäistä osoitepyyntöä eikä vasta sen jälkeen.

Kaksi asiaa tapahtuu nyt, kun kamera on liittynyt verkkoon. Ensinnäkin useimmat kotireitittimet rekisteröivät jakamiaan osoitteita vastaavat isäntänimet omaan paikalliseen selvitykseensä, joten muut laitteet voivat tavoittaa kameran nimellä kitchen-cam – ilman että niiden tarvitsee tietää numeerista osoitetta, jonka reititin sattui jakamaan. (Yritysverkot saattavat tai eivät saata noudattaa tätä; käyttäytyminen riippuu reitittimestä.) Toiseksi kamera itse ajaa mDNS-vastaajaa heti laatikosta, joten sama nimi on tavoitettavissa myös muodossa kitchen-cam.local missä tahansa verkossa, jonka asiakkaat ymmärtävät mDNS:ää – mitä useimmat nykyaikaiset työpöytäkäyttöjärjestelmät tekevät.

Muista

Anna paljas isäntänimi funktiolle network.hostname() – vain "kitchen-cam", ei .local-päätettä. .local-muoto on se, minkä mDNS lisää selvityshetkellä; sen leipominen isäntänimeen saa kameran mainostamaan nimeä kitchen-cam.local tavallisena isäntänimenä, mitä kumpikaan selvityksen puoli ei odota.

9.15.6. Kun nimet eivät riitä

Muutamassa tilanteessa DNS ei auta:

  • Löytäminen paikallisverkossa. Standardi-DNS vastaa kysymyksiin globaaliin hakemistoon rekisteröidyistä nimistä; se ei tiedä mitään paikallissegmentin laitteista. Multicast DNS (mDNS) on järjestelmä, joka täyttää tuon aukon. Jokainen osallistuva laite liittyy paikallisverkon erityiseen monilähetysryhmään ja kuuntelee kyselyitä; kun laite kysyy .local-päätteistä nimeä, kyseisen nimen omistava laite vastaa suoraan. Ei keskuspalvelinta, ei DNS-määrityksiä. Bonjour Applen laitteissa, Avahi Linuxissa ja Windows 10+ puhuvat kaikki samaa protokollaa – minkä vuoksi edellisessä osiossa asetettu kitchen-cam.local-nimi selviää kotiverkossa ilman mitään ylimääräistä määritystä.

    Kameran puoli tuosta vaihdosta on vastaaja, ja se on jo käynnissä. Mitä kameralla ei ole, on selvittäjä – toinen puolisko, joka antaisi skriptin kysyä verkolta ”missä on printer.local?” ja saada vastauksen takaisin. Mukana toimitettu mDNS-koodi on vain vastaajatoiminnolla varustettu (sulautetut laitteet ovat tyypillisesti se löydettävä asia, eivät se asia, joka tekee löytämisen). Kun löytämisen on virrattava toiseen suuntaan, UDP-yleislähetys (katso UDP – lähetä paketti ja toivo parasta) on yksinkertaisempi vastaus paikallissegmentin tapaukseen, tai puhtaasti Pythonilla toteutettu moduuli kuten cbrand/micropython-mdns lisää täyden selvittäjän.

  • IPv6-nimet. getaddrinfo() palauttaa sekä IPv4- että IPv6-tulokset, jos molemmat ovat saatavilla. Valitse perhe, jota sovelluksen soketti voi käyttää.

Useimmalle kameranpuoliselle koodille getaddrinfo on yksirivinen verkkoyhteyden avaavan funktion alussa. Tämän osion muut esimerkit, jotka ajettiin osoitetta "192.168.1.20" vastaan, toimisivat kaikki samalla tavalla julkista nimeä kuten "api.example.com" vastaan – selvitä vain ensin.