9.15. Nomi e DNS¶
Ogni pagina finora ha usato indirizzi IP numerici – 192.168.1.20 e simili. Le applicazioni reali quasi mai lo fanno. I server hanno nomi come example.com o api.example.com, e l’applicazione cerca il nome a runtime per trovare un IP a cui inviare i pacchetti. Quella ricerca è il Domain Name System, ovvero DNS.
9.15.1. A cosa si risolve un nome¶
Un nome è solo un’etichetta. example.com di per sé non contiene alcuna informazione IP – deve essere cercato, allo stesso modo in cui un numero di telefono viene cercato in un elenco telefonico. L’infrastruttura DNS è l’elenco telefonico distribuito di internet, e il risultato di una ricerca è uno o più indirizzi IP a cui la camera può connettersi.
example.com -> 93.184.216.34
Un singolo nome spesso si risolve in diversi indirizzi (per il bilanciamento del carico, la ridondanza geografica, le versioni IPv4 e IPv6 dello stesso servizio). Qualsiasi di essi funziona; l’applicazione ne sceglie uno e lo prova, ripiegando sul successivo se quello fallisce.
9.15.2. Come avviene la ricerca¶
Quando la camera richiede example.com:
La camera invia un piccolo datagramma UDP (sì, UDP – vedi UDP – invia un pacchetto e spera per il meglio) al suo server DNS configurato. L’indirizzo del server DNS è arrivato dallo stesso scambio DHCP che ha assegnato alla camera il proprio IP.
Il server DNS potrebbe già avere la risposta in cache (gli è stata chiesta di recente). In tal caso, risponde immediatamente.
In caso contrario, il server DNS percorre la gerarchia DNS globale: chiede ai server root riguardo a
.com, chiede a quei server riguardo aexample.com, chiede a quei server riguardo al nome. L’intera scansione dell’albero è nascosta alla camera; la camera vede una query e una risposta.Il server DNS mette in cache il risultato per la volta successiva e rimanda la risposta alla camera come un altro datagramma UDP.
L’intero scambio richiede in genere alcuni millisecondi con una cache calda, fino a un centinaio circa con una cache fredda.
9.15.3. L’interfaccia Python¶
La funzione getaddrinfo() esegue la ricerca e restituisce un indirizzo pronto da passare a un costruttore di socket:
import socket
addr = socket.getaddrinfo("example.com", 80)[0][-1]
print(addr)
# ('93.184.216.34', 80)
La firma è getaddrinfo(host, port). Il valore di ritorno è una lista di tuple di 5 elementi (una per indirizzo risolto); [0] seleziona la prima e [-1] seleziona l’ultimo campo, che è la tupla (ip, port) che può essere passata direttamente a un socket:
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")
...
La maggior parte del codice della camera che comunica con un server remoto inizia con quell’unica riga. Gli helper asyncio (asyncio.open_connection()) eseguono la ricerca internamente se viene passato un nome anziché un IP numerico, quindi il codice asincrono in genere non chiama direttamente getaddrinfo().
9.15.4. Cosa può andare storto¶
Nessun server DNS raggiungibile. Se il Wi-Fi è appena attivato e il collegamento è instabile, la prima chiamata a
getaddrinfo()potrebbe andare in timeout. Viene sollevatoOSError; riprova una volta che il collegamento è stabile.Il nome non esiste. Un errore di battitura o un nome obsoleto solleva
OSErrordopo che il server DNS restituisce «no such name». Il codice di errore distingue questo caso da «server DNS irraggiungibile», ma per la maggior parte delle applicazioni della camera la politica di riprova/abbandono è la stessa.L’indirizzo restituito non funziona. Il DNS può restituire un indirizzo che non ospita più il servizio. La soluzione è ripiegare sulla voce successiva nella lista dei risultati di
getaddrinfo(), oppure cercare di nuovo il nome più tardi (la cache DNS si sarà probabilmente aggiornata nel frattempo).Captive portal. Alcune reti Wi-Fi intercettano il DNS e restituiscono l’IP della pagina del captive portal per qualsiasi cosa. La camera sembrerà connettersi, ma i dati che riceve in risposta non corrisponderanno a ciò che invierebbe il servizio reale. Non è comune negli ambienti di produzione, ma è una cosa che capita sul Wi-Fi delle conferenze e su reti simili.
9.15.5. Il nome proprio della camera¶
Le pagine finora hanno cercato i nomi di altri dispositivi. Anche la camera ha un nome proprio che annuncia alla rete locale quando richiede un indirizzo. Quello predefinito è un identificatore generico che varia a seconda della scheda; network.hostname() lo sovrascrive con qualcosa che il resto della rete riconoscerà:
import network
network.hostname("kitchen-cam")
# ... then bring the link up as usual ...
Imposta l’hostname prima di attivare l’interfaccia, in modo che il nome venga inviato come parte della richiesta iniziale di indirizzo della camera anziché dopo.
Una volta che la camera si è unita alla rete, accadono due cose. Primo, la maggior parte dei router domestici registra gli hostname a cui assegna indirizzi nel proprio sistema di ricerca locale, così altri dispositivi possono raggiungere la cam come kitchen-cam – senza dover conoscere l’indirizzo numerico che il router ha assegnato. (Le reti aziendali possono o meno rispettare questo comportamento; dipende dal router.) Secondo, la cam stessa esegue un responder mDNS pronto all’uso, così lo stesso nome è raggiungibile anche come kitchen-cam.local su qualsiasi rete i cui client comprendano mDNS – cosa che la maggior parte dei sistemi operativi desktop moderni fa.
Nota
Passa l’hostname nudo a network.hostname() – semplicemente "kitchen-cam", senza il suffisso .local. La forma .local è ciò che mDNS aggiunge al momento della ricerca; incorporarla nell’hostname fa sì che la cam annunci kitchen-cam.local come hostname semplice, che non è ciò che si aspetta nessuna delle due parti della ricerca.
9.15.6. Quando i nomi non bastano¶
Alcune situazioni in cui il DNS non aiuta:
Discovery sulla rete locale. Il DNS standard risponde a domande sui nomi registrati nella directory globale; non sa nulla dei dispositivi sul segmento locale. Il Multicast DNS (mDNS) è il sistema che colma quella lacuna. Ogni dispositivo partecipante si unisce a uno speciale gruppo multicast sulla rete locale e resta in ascolto delle query; quando un dispositivo richiede un nome che termina in
.local, è il dispositivo proprietario di quel nome a rispondere direttamente. Nessun server centrale, nessuna configurazione DNS. Bonjour sui dispositivi Apple, Avahi su Linux e Windows 10+ parlano tutti lo stesso protocollo – ed è per questo che il nomekitchen-cam.localimpostato nella sezione precedente si risolve su una rete domestica senza nulla di aggiuntivo configurato.Il lato della cam in quello scambio è il responder, ed è già in esecuzione. Ciò che la cam non ha è un resolver – l’altra metà, che permetterebbe a uno script di chiedere alla rete «dov’è
printer.local?» e ottenere una risposta. Il codice mDNS incluso è solo responder (i dispositivi embedded sono in genere la cosa che viene trovata, non quella che fa la ricerca). Quando la discovery deve fluire nella direzione opposta, il broadcast UDP (vedi UDP – invia un pacchetto e spera per il meglio) è la risposta più semplice per il caso del segmento locale, oppure un modulo in puro Python come cbrand/micropython-mdns aggiunge un resolver completo.Nomi IPv6.
getaddrinfo()restituisce sia risultati IPv4 che IPv6 se entrambi sono disponibili. Scegli la famiglia che il socket dell’applicazione può usare.
Per la maggior parte del codice lato camera, getaddrinfo è una sola riga in cima a qualsiasi funzione che apre una connessione di rete. Gli esempi altrove in questa sezione che operavano su "192.168.1.20" funzionerebbero tutti allo stesso modo su un nome pubblico come "api.example.com" – basta risolverlo prima.