diff --git a/network-query.rkt b/network-query.rkt index b8b4bb3..ed47158 100644 --- a/network-query.rkt +++ b/network-query.rkt @@ -71,12 +71,12 @@ (define first-timeout 3) ;; seconds ;; A NetworkRequest is a (network-request UdpAddress Question -;; DomainName NEListOf UniqueID) representing the +;; DomainName NEListOf UniqueID) representing the ;; parameters used to start and process a network query. (struct network-request (client-socket question zone-origin - server-ips + server-names unique-id) #:transparent) @@ -85,19 +85,15 @@ (struct network-reply (unique-id answer) #:transparent) ;; A NetworkQueryState is a (network-query-state NetworkRequest -;; Integer ListOf), representing an in-progress DNS -;; network query. +;; Integer Map> ListOf +;; ListOf), representing an in-progress DNS network query. (struct network-query-state (request timeout - remaining-addresses) + known-addresses + remaining-addresses + remaining-names) #:transparent) -;; A ResolvingNameservers is a (resolving-nameservers NetworkRequest -;; Integer), representing an in-progress nameserver resolution -;; operation that must be completed before the actual query can -;; continue. -(struct resolving-nameservers (request remaining-count) #:transparent) - ;; seconds -> Maybe (define (next-timeout timeout) (case timeout @@ -149,50 +145,60 @@ ;; network-query : UdpAddress Question DomainName NEListOf UniqueId -> OsProcess (define (network-query s q zone-origin server-names unique-id) - (os-big-bang (resolving-nameservers (network-request s q zone-origin '() unique-id) - (length server-names)) - (map (lambda (nameserver-name) - (define subscription-id (list 'nameserver-name-resolution nameserver-name)) - (define subq (question nameserver-name 'a 'in)) ;; TODO: 'aaaa ? - (list (send-message subq) - (subscribe subscription-id - (message-handlers (resolving-nameservers req remaining) - [(answered-question (== subq) ans) - (define new-ips - (append (network-request-server-ips req) - (map make-dns-address - (set->list (extract-addresses nameserver-name ans))))) - (define new-req (struct-copy network-request req [server-ips new-ips])) - (define new-remaining (- remaining 1)) - (extend-transition - (if (zero? new-remaining) - (try-next-server (network-query-state new-req - first-timeout - new-ips)) - (transition (resolving-nameservers new-req new-remaining))) - (unsubscribe subscription-id))])))) - server-names))) + (os-big-bang/transition + (try-next-server (network-query-state (network-request s q zone-origin server-names unique-id) + first-timeout + (hash) + '() + server-names)))) (define (try-next-server w) (match w - [(network-query-state req timeout '()) + [(network-query-state req #f _ _ _) + ;; No more timeouts to try, so give up. + (on-answer w (empty-complete-answer))] + [(network-query-state req timeout _ '() '()) ;; No more addresses to try with this timeout. Refill the list ;; and bump the timeout and retry. ;; TODO: randomize ordering of servers in list. (Don't forget the initial fill.) (try-next-server (struct-copy network-query-state w [timeout (next-timeout timeout)] - [remaining-addresses (network-request-server-ips req)]))] - [(network-query-state req #f _) - ;; No more timeouts to try, so give up. - (on-answer w (empty-complete-answer))] - [(network-query-state req timeout (cons current-address remaining-addresses)) + [remaining-addresses '()] + [remaining-names (network-request-server-names req)]))] + [(network-query-state _ _ known-addresses '() (cons current-name remaining-names)) + (if (hash-has-key? known-addresses current-name) + (try-next-server (struct-copy network-query-state w + [remaining-addresses (hash-ref known-addresses + current-name)] + [remaining-names remaining-names])) + (let ((subscription-id (list 'nameserver-name-resolution current-name)) + (subq (question current-name 'a 'in))) ;; TODO: 'aaaa ? + (transition (struct-copy network-query-state w [remaining-names remaining-names]) + (send-message subq) + (subscribe subscription-id + (message-handlers w + [(answered-question (== subq) ans) + (define ips + (map make-dns-address (set->list (extract-addresses current-name ans)))) + (extend-transition + (try-next-server (struct-copy network-query-state w + [known-addresses (hash-set known-addresses + current-name + ips)] + [remaining-addresses ips])) + (unsubscribe subscription-id))])))))] + [(network-query-state req timeout _ (cons current-ip remaining-ips) _) (define rpc-id (gensym 'network-query/allocate-query-id)) (transition w (send-message `(request ,rpc-id allocate-query-id)) (subscribe rpc-id (message-handlers w [`(reply ,(== rpc-id) ,id) - (extend-transition (send-request req id timeout current-address remaining-addresses) + (extend-transition (send-request (struct-copy network-query-state w + [remaining-addresses remaining-ips]) + id + timeout + current-ip) (unsubscribe rpc-id))])))])) (define (on-answer w ans) @@ -200,12 +206,12 @@ (send-message (network-reply (network-request-unique-id (network-query-state-request w)) ans)))) -(define (send-request req query-id timeout server-ip remaining-ips) - (match-define (network-request s q zone-origin _ _) req) +(define (send-request w query-id timeout server-ip) + (match-define (network-request s q zone-origin _ _) (network-query-state-request w)) (define query (make-dns-query-message q query-id)) (define subscription-id (list s query-id)) (define start-time (current-inexact-milliseconds)) - (transition (network-query-state req timeout remaining-ips) + (transition w (send-message (dns-request query s server-ip)) (send-message (set-timer subscription-id (* timeout 1000) #t)) (subscribe subscription-id