Misc improvements to net/dns' and net/private/ip'.

* Some racketisms.

* Use explicit `in-list' etc in for loops.

* Remove some redundant requires from `net/dns'.

* Move all tests to `tests/net', including a new `tests/net/ip'.  In the
  future there's a plan to have things like stripped zos etc for
  distribution, but we're not there yet, and the net collection is
  already organized nicely so this also makes it more uniform.

* Include the dns tests in the main test file.

original commit: 6149134011
This commit is contained in:
Eli Barzilay 2013-04-05 01:29:05 -04:00
parent 518051a4b3
commit 92358cb553
6 changed files with 306 additions and 325 deletions

View File

@ -3,15 +3,11 @@
;; DNS query library for Racket
(require "private/ip.rkt"
racket/bool
racket/contract
racket/format
racket/list
racket/match
racket/string
racket/system
racket/udp
(only-in unstable/sequence in-slice))
racket/udp)
(provide (contract-out
[dns-get-address
@ -29,8 +25,6 @@
[dns-find-nameserver
(-> (or/c ip-address-string? #f))]))
(module+ test (require rackunit))
;; UDP retry timeout:
(define INIT-TIMEOUT 50)
@ -39,23 +33,23 @@
;; A Type is one of the following
(define types
'((a 1)
(ns 2)
(md 3)
(mf 4)
(cname 5)
(soa 6)
(mb 7)
(mg 8)
(mr 9)
(null 10)
(wks 11)
(ptr 12)
'((a 1)
(ns 2)
(md 3)
(mf 4)
(cname 5)
(soa 6)
(mb 7)
(mg 8)
(mr 9)
(null 10)
(wks 11)
(ptr 12)
(hinfo 13)
(minfo 14)
(mx 15)
(txt 16)
(aaaa 28)))
(mx 15)
(txt 16)
(aaaa 28)))
;; A Class is one of the following
(define classes
@ -67,9 +61,9 @@
;;;
(define (cossa i l)
(cond [(null? l) #f]
(cond [(null? l) #f]
[(equal? (cadar l) i) (car l)]
[else (cossa i (cdr l))]))
[else (cossa i (cdr l))]))
(define (number->octet-pair n)
(list (arithmetic-shift n -8)
@ -88,13 +82,13 @@
;; Convert the domain name into a sequence of labels, where each
;; label is a length octet and then that many octets
(define (name->octets s)
(let ([do-one (lambda (s) (cons (bytes-length s) (bytes->list s)))])
(let loop ([s s])
(let ([m (regexp-match #rx#"^([^.]*)[.](.*)" s)])
(if m
(append (do-one (cadr m)) (loop (caddr m)))
;; terminate with zero length octet
(append (do-one s) (list 0)))))))
(define (do-one s) (cons (bytes-length s) (bytes->list s)))
(let loop ([s s])
(define m (regexp-match #rx#"^([^.]*)[.](.*)" s))
(if m
(append (do-one (cadr m)) (loop (caddr m)))
;; terminate with zero length octet
(append (do-one s) (list 0)))))
;; The query header. See RFC1035 4.1.1 for details
;;
@ -134,27 +128,26 @@
(car rr))
(define (parse-name start reply)
(let ([v (car start)])
(cond
[(zero? v)
;; End of name
(values #f (cdr start))]
[(zero? (bitwise-and #xc0 v))
;; Normal label
(let loop ([len v][start (cdr start)][accum null])
(if (zero? len)
(let-values ([(s start) (parse-name start reply)])
(let ([s0 (list->bytes (reverse accum))])
(values (if s (bytes-append s0 #"." s) s0)
start)))
(loop (sub1 len) (cdr start) (cons (car start) accum))))]
[else
;; Compression offset
(let ([offset (+ (arithmetic-shift (bitwise-and #x3f v) 8)
(cadr start))])
(let-values ([(s ignore-start)
(parse-name (list-tail reply offset) reply)])
(values s (cddr start))))])))
(define v (car start))
(cond
[(zero? v)
;; End of name
(values #f (cdr start))]
[(zero? (bitwise-and #xc0 v))
;; Normal label
(let loop ([len v] [start (cdr start)] [accum null])
(if (zero? len)
(let-values ([(s start) (parse-name start reply)])
(define s0 (list->bytes (reverse accum)))
(values (if s (bytes-append s0 #"." s) s0)
start))
(loop (sub1 len) (cdr start) (cons (car start) accum))))]
[else
;; Compression offset
(define offset (+ (arithmetic-shift (bitwise-and #x3f v) 8)
(cadr start)))
(define-values [s ignore-start] (parse-name (list-tail reply offset) reply))
(values s (cddr start))]))
(define (parse-rr start reply)
(let-values ([(name start) (parse-name start reply)])
@ -191,7 +184,7 @@
(values (list name type class) start))))
(define (parse-n parse start reply n)
(let loop ([n n][start start][accum null])
(let loop ([n n] [start start] [accum null])
(if (zero? n)
(values (reverse accum) start)
(let-values ([(rr start) (parse start reply)])
@ -205,58 +198,51 @@
(raise-type-error 'dns-query "DNS query class" class))
(define nameserver (ip-address->string nameserver-ip))
(let* ([query (make-query (random 256) (string->bytes/latin-1 addr)
type class)]
[udp (udp-open-socket nameserver 53)]
[reply
(dynamic-wind
void
(lambda ()
(let ([s (make-bytes 512)])
(let retry ([timeout INIT-TIMEOUT])
(udp-send-to udp nameserver 53 (list->bytes query))
(sync (handle-evt (udp-receive!-evt udp s)
(lambda (r)
(bytes->list (subbytes s 0 (car r)))))
(handle-evt (alarm-evt (+ (current-inexact-milliseconds)
timeout))
(lambda (v)
(retry (* timeout 2))))))))
(lambda () (udp-close udp)))])
;; First two bytes must match sent message id:
(unless (and (= (car reply) (car query))
(= (cadr reply) (cadr query)))
(error 'dns-query "bad reply id from server"))
(let ([v0 (caddr reply)]
[v1 (cadddr reply)])
;; Check for error code:
(let ([rcode (bitwise-and #xf v1)])
(unless (zero? rcode)
(error 'dns-query "error from server: ~a"
(case rcode
[(1) "format error"]
[(2) "server failure"]
[(3) "name error"]
[(4) "not implemented"]
[(5) "refused"]))))
(let ([qd-count (octet-pair->number (list-ref reply 4) (list-ref reply 5))]
[an-count (octet-pair->number (list-ref reply 6) (list-ref reply 7))]
[ns-count (octet-pair->number (list-ref reply 8) (list-ref reply 9))]
[ar-count (octet-pair->number (list-ref reply 10) (list-ref reply 11))])
(let ([start (list-tail reply 12)])
(let*-values ([(qds start) (parse-n parse-ques start reply qd-count)]
[(ans start) (parse-n parse-rr start reply an-count)]
[(nss start) (parse-n parse-rr start reply ns-count)]
[(ars start) (parse-n parse-rr start reply ar-count)])
(unless (null? start)
(error 'dns-query "error parsing server reply"))
(values (positive? (bitwise-and #x4 v0))
qds ans nss ars reply)))))))
(define query (make-query (random 256) (string->bytes/latin-1 addr)
type class))
(define udp (udp-open-socket nameserver 53))
(define reply
(dynamic-wind
void
(lambda ()
(define s (make-bytes 512))
(let retry ([timeout INIT-TIMEOUT])
(udp-send-to udp nameserver 53 (list->bytes query))
(sync (handle-evt (udp-receive!-evt udp s)
(lambda (r) (bytes->list (subbytes s 0 (car r)))))
(handle-evt (alarm-evt (+ (current-inexact-milliseconds)
timeout))
(lambda (v) (retry (* timeout 2)))))))
(lambda () (udp-close udp))))
;; First two bytes must match sent message id:
(unless (and (= (car reply) (car query))
(= (cadr reply) (cadr query)))
(error 'dns-query "bad reply id from server"))
(define v0 (caddr reply))
(define v1 (cadddr reply))
;; Check for error code:
(let ([rcode (bitwise-and #xf v1)])
(unless (zero? rcode)
(error 'dns-query "error from server: ~a"
(case rcode
[(1) "format error"]
[(2) "server failure"]
[(3) "name error"]
[(4) "not implemented"]
[(5) "refused"]))))
(define qd-count (octet-pair->number (list-ref reply 4) (list-ref reply 5)))
(define an-count (octet-pair->number (list-ref reply 6) (list-ref reply 7)))
(define ns-count (octet-pair->number (list-ref reply 8) (list-ref reply 9)))
(define ar-count (octet-pair->number (list-ref reply 10) (list-ref reply 11)))
(define start (list-tail reply 12))
(let*-values ([(qds start) (parse-n parse-ques start reply qd-count)]
[(ans start) (parse-n parse-rr start reply an-count)]
[(nss start) (parse-n parse-rr start reply ns-count)]
[(ars start) (parse-n parse-rr start reply ar-count)])
(unless (null? start)
(error 'dns-query "error parsing server reply"))
(values (positive? (bitwise-and #x4 v0))
qds ans nss ars reply)))
;; A cache for DNS query data
;; Stores a (List Boolean LB LB LB LB LB)
@ -290,10 +276,10 @@
(let-values ([(v ars auth?) (k nameserver)])
(or v
(and (not auth?)
(let* ([ns (ormap (lambda (ar)
(and (eq? (rr-type ar) 'a)
(ip->string (rr-data ar))))
ars)])
(let ([ns (ormap (lambda (ar)
(and (eq? (rr-type ar) 'a)
(ip->string (rr-data ar))))
ars)])
(and ns
(not (member ns tried))
(loop ns (cons ns tried)))))))))
@ -307,41 +293,21 @@
;; Convert an IPv4 address for reverse lookup
(define (ip->in-addr.arpa ip)
(define bytes (ipv4-bytes ip))
(format "~a.~a.~a.~a.in-addr.arpa"
(bytes-ref bytes 3) (bytes-ref bytes 2)
(bytes-ref bytes 1) (bytes-ref bytes 0)))
(module+ test
(check-equal? (ip->in-addr.arpa (ipv4 (bytes 8 8 8 8)))
"8.8.8.8.in-addr.arpa")
(check-equal? (ip->in-addr.arpa (ipv4 (bytes 127 0 0 1)))
"1.0.0.127.in-addr.arpa"))
(apply format "~a.~a.~a.~a.in-addr.arpa"
(reverse (bytes->list (ipv4-bytes ip)))))
;; Convert an IPv6 address for reverse lookup
(define (ip->ip6.arpa ip)
(define nibbles
(for/fold ([nibbles '()])
([byte (ipv6-bytes ip)])
(define nib1 (arithmetic-shift (bitwise-and #xf0 byte) -4))
(define nib2 (bitwise-and #x0f byte))
(append (list nib2 nib1) nibbles)))
(string-append
(string-join
(for/list ([n nibbles]) (~r n #:base 16))
".")
".ip6.arpa"))
(module+ test
(check-equal?
(ip->ip6.arpa (make-ip-address "4321:0:1:2:3:4:567:89ab"))
"b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa")
(check-equal?
(ip->ip6.arpa (make-ip-address "2001:db8::567:89ab"))
"b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"))
(string-join
(for/fold ([nibbles '()])
([byte (in-bytes (ipv6-bytes ip))])
(define nib1 (arithmetic-shift (bitwise-and #xf0 byte) -4))
(define nib2 (bitwise-and #x0f byte))
(append (list (~r nib2 #:base 16) (~r nib1 #:base 16)) nibbles))
"." #:after-last ".ip6.arpa"))
(define (get-ptr-list-from-ans ans)
(filter (lambda (ans-entry) (eq? (list-ref ans-entry 1) 'ptr)) ans))
(filter (λ (ans-entry) (eq? (list-ref ans-entry 1) 'ptr)) ans))
(define (dns-get-name nameserver-ip-or-string ip-or-string)
(define nameserver (if (ip-address? nameserver-ip-or-string)
@ -365,7 +331,7 @@
;; Get resource records corresponding to the given type
(define (get-records-from-ans ans type)
(for/list ([ans-entry ans]
(for/list ([ans-entry (in-list ans)]
#:when (eq? (list-ref ans-entry 1) type))
ans-entry))
@ -404,12 +370,12 @@
nss)
addr))]
[else
(let ([d (rr-data (car ans))])
(let ([pref (octet-pair->number (car d) (cadr d))])
(if (< pref best-pref)
(let-values ([(name start) (parse-name (cddr d) reply)])
(loop (cdr ans) pref name))
(loop (cdr ans) best-pref exchanger))))]))
(define d (rr-data (car ans)))
(define pref (octet-pair->number (car d) (cadr d)))
(if (< pref best-pref)
(let-values ([(name start) (parse-name (cddr d) reply)])
(loop (cdr ans) pref name))
(loop (cdr ans) best-pref exchanger))]))
ars auth?)))
nameserver)
(error 'dns-get-mail-exchanger "bad address")))
@ -421,34 +387,34 @@
(with-input-from-file "/etc/resolv.conf"
(lambda ()
(let loop ()
(let ([l (read-line)])
(or (and (string? l)
(let ([m (regexp-match
#rx"nameserver[ \t]+([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)"
l)])
(and m (cadr m))))
(and (not (eof-object? l))
(loop))))))))]
(define line (read-line))
(or (and (string? line)
(let ([m (regexp-match
#rx"nameserver[ \t]+([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)"
line)])
(and m (cadr m))))
(and (not (eof-object? line))
(loop)))))))]
[(windows)
(let ([nslookup (find-executable-path "nslookup.exe" #f)])
(and nslookup
(let-values ([(pin pout pid perr proc)
(apply
values
(process/ports
#f (open-input-file "NUL") (current-error-port)
nslookup))])
(let loop ([name #f] [ip #f] [try-ip? #f])
(let ([line (read-line pin 'any)])
(cond [(eof-object? line)
(close-input-port pin)
(proc 'wait)
(or ip name)]
[(and (not name)
(regexp-match #rx"^Default Server: +(.*)$" line))
=> (lambda (m) (loop (cadr m) #f #t))]
[(and try-ip?
(regexp-match #rx"^Address: +(.*)$" line))
=> (lambda (m) (loop name (cadr m) #f))]
[else (loop name ip #f)]))))))]
(define nslookup (find-executable-path "nslookup.exe" #f))
(and nslookup
(let-values ([(pin pout pid perr proc)
(apply
values
(process/ports
#f (open-input-file "NUL") (current-error-port)
nslookup))])
(let loop ([name #f] [ip #f] [try-ip? #f])
(define line (read-line pin 'any))
(cond [(eof-object? line)
(close-input-port pin)
(proc 'wait)
(or ip name)]
[(and (not name)
(regexp-match #rx"^Default Server: +(.*)$" line))
=> (lambda (m) (loop (cadr m) #f #t))]
[(and try-ip?
(regexp-match #rx"^Address: +(.*)$" line))
=> (lambda (m) (loop name (cadr m) #f))]
[else (loop name ip #f)]))))]
[else #f]))

View File

@ -34,8 +34,6 @@
(struct ipv4 ([bytes (bytes-of-length 4)]))
(struct ipv6 ([bytes (bytes-of-length 16)]))))
(module+ test (require rackunit))
;; data definitions
;; An IPAddress is one of
@ -73,26 +71,6 @@
[(? (bytes-of-length 4)) (ipv4 input)]
[(? (bytes-of-length 16)) (ipv6 input)]))
(module+ test
(check-equal? (make-ip-address "127.0.0.1")
(ipv4 (bytes 127 0 0 1)))
(check-equal? (make-ip-address (bytes 127 0 0 1))
(ipv4 (bytes 127 0 0 1)))
(check-equal? (make-ip-address "2607:f8b0:4009:800::100e")
(ipv6 (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14)))
(check-equal? (make-ip-address (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
(ipv6 (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14)))
(check-equal? (make-ip-address "0.0.0.1")
(ipv4 (bytes 0 0 0 1)))
(check-not-equal? (make-ip-address "128.0.0.1")
(make-ip-address "255.3.255.0"))
(let ([ip-bytes (bytes 127 0 0 1)])
(define ip (make-ip-address ip-bytes))
(bytes-set! ip-bytes 0 255)
(check-equal? ip (make-ip-address "127.0.0.1")
"IP addresses should be immutable")))
(define (ip-address-string? val)
(and (string? val)
(or (ipv4-string? val)
@ -147,39 +125,6 @@
;; this is the +1 octet pair
(regexp-match? re-end str))]))))
(module+ test
(check-true (ip-address-string? "0.0.0.0"))
(check-true (ip-address-string? "0.1.0.2"))
(check-true (ip-address-string? "8.8.8.8"))
(check-true (ip-address-string? "12.81.255.109"))
(check-true (ip-address-string? "192.168.0.1"))
(check-true (ip-address-string? "2001:0db8:85a3:0000:0000:8a2e:0370:7334"))
(check-true (ip-address-string? "2001:200:dff:fff1:216:3eff:feb1:44d7"))
(check-true (ip-address-string? "2001:db8:85a3:0:0:8a2e:370:7334"))
(check-true (ip-address-string? "2001:db8:85a3::8a2e:370:7334"))
(check-true (ip-address-string? "0:0:0:0:0:0:0:1"))
(check-true (ip-address-string? "0:0:0:0:0:0:0:0"))
(check-true (ip-address-string? "::"))
(check-true (ip-address-string? "::0"))
(check-true (ip-address-string? "::ffff:c000:0280"))
(check-true (ip-address-string? "2001:db8::2:1"))
(check-true (ip-address-string? "2001:db8:0:0:1::1"))
(check-false (ip-address-string? ""))
(check-false (ip-address-string? ":::"))
(check-false (ip-address-string? "::0::"))
(check-false (ip-address-string? "2001::db8::2:1"))
(check-false (ip-address-string? "2001:::db8:2:1"))
(check-false (ip-address-string? "52001:db8::2:1"))
(check-false (ip-address-string? "80.8.800.8"))
(check-false (ip-address-string? "80.8.800.0"))
(check-false (ip-address-string? "080.8.800.8"))
(check-false (ip-address-string? "vas8.8.800.8"))
(check-false (ip-address-string? "80.8.128.8dd"))
(check-false (ip-address-string? "0.8.800.008"))
(check-false (ip-address-string? "0.8.800.a8"))
(check-false (ip-address-string? "potatoes"))
(check-false (ip-address-string? "127.0.0")))
;; String -> Bytes
;; converts a string representating an IPv4 address to bytes
(define (ipv4-string->bytes ip)
@ -190,16 +135,6 @@
(string->number (list-ref result 3))
(string->number (list-ref result 4)))))
(module+ test
(check-equal? (ipv4-string->bytes "0.8.255.0")
(bytes 0 8 255 0))
(check-equal? (ipv4-string->bytes "8.8.8.8")
(bytes 8 8 8 8))
(check-equal? (ipv4-string->bytes "12.81.255.109")
(bytes 12 81 255 109))
(check-equal? (ipv4-string->bytes "192.168.0.1")
(bytes 192 168 0 1)))
;; String -> Bytes
;; converts a string representing an IPv6 address to bytes
(define (ipv6-string->bytes ip)
@ -224,22 +159,6 @@
(loop (bytes-append result (octet-pair-string->bytes (car splitted)))
(cdr splitted))])))
(module+ test
(check-equal? (ipv6-string->bytes "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
(bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52))
(check-equal? (ipv6-string->bytes "2001:200:dff:fff1:216:3eff:feb1:44d7")
(bytes 32 1 2 0 13 255 255 241 2 22 62 255 254 177 68 215))
(check-equal? (ipv6-string->bytes "2001:db8:85a3:0:0:8a2e:370:7334")
(bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52))
(check-equal? (ipv6-string->bytes "2001:db8:85a3::8a2e:370:7334")
(bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52))
(check-equal? (ipv6-string->bytes "2607:f8b0:4009:800::100e")
(bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
(check-equal? (ipv6-string->bytes "::1")
(bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1))
(check-equal? (ipv6-string->bytes "::ffff")
(bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255)))
;; IPAddress -> Bytestring
;; Turn an ip struct into a byte string
(define (ip-address->bytes ip)
@ -247,12 +166,6 @@
[(? ipv4?) (ipv4-bytes ip)]
[(? ipv6?) (ipv6-bytes ip)]))
(module+ test
(check-equal? (ip-address->bytes (make-ip-address "8.8.8.8"))
(bytes 8 8 8 8))
(check-equal? (ip-address->bytes (make-ip-address "::1"))
(bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1)))
;; IPAddress -> String
;; Convert an IP address to a string
(define (ip-address->string ip)
@ -260,25 +173,10 @@
[(? ipv4?) (ipv4->string (ipv4-bytes ip))]
[(? ipv6?) (ipv6->string (ipv6-bytes ip))]))
(module+ test
(check-equal? (ip-address->string (make-ip-address "8.8.8.8"))
"8.8.8.8")
(check-equal? (ip-address->string (make-ip-address "::1"))
"::1"))
;; Bytes -> String
;; Convert a bytestring for an IPv4 address to a string
(define (ipv4->string bytes)
(string-join (for/list ([b bytes]) (~r b)) "."))
(module+ test
(check-equal? (ipv4->string (bytes 0 0 0 0)) "0.0.0.0")
(check-equal? (ipv4->string (bytes 255 255 0 1))
"255.255.0.1")
(check-equal? (ipv4->string (bytes 127 0 0 1))
"127.0.0.1")
(check-equal? (ipv4->string (bytes 8 8 8 8))
"8.8.8.8"))
(string-join (for/list ([b (in-bytes bytes)]) (~r b)) "."))
;; Bytes -> String
;; Convert a bytestring representing an IPv6 address to a string
@ -291,7 +189,7 @@
(define compressed (compress two-octets))
;; add an extra "" if :: is at the start
(define compressed-strs
(for/list ([elem compressed])
(for/list ([elem (in-list compressed)])
(if (eq? elem '::)
"" ; string-join will turn this into ::
(~r elem #:base 16))))
@ -301,41 +199,21 @@
compressed-strs))
(string-join compressed-strs* ":"))
(module+ test
(check-equal? (ipv6->string (bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52))
"2001:db8:85a3::8a2e:370:7334")
(check-equal? (ipv6->string (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
"2607:f8b0:4009:800::100e")
(check-equal? (ipv6->string (bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255))
"::ffff")
(check-equal? (ipv6->string (bytes 255 255 0 0 0 0 0 0 0 0 0 0 0 0 255 255))
"ffff::ffff"))
;; (Listof Number) -> (Listof (U Number '::))
;; Compress an IPv6 address to its shortest representation
(define (compress lon)
(let loop ([acc '()] [lon lon])
(cond [(empty? lon) (reverse acc)]
[else
(define zeroes (for/list ([n lon] #:break (not (zero? n))) n))
(define zeroes
(for/list ([n (in-list lon)] #:break (not (zero? n))) n))
(define num-zs (length zeroes))
(if (<= num-zs 1)
(loop (cons (car lon) acc) (cdr lon))
(append (reverse acc) '(::) (drop lon num-zs)))])))
(module+ test
(check-equal? (compress '(0 0 0 5 5)) '(:: 5 5))
(check-equal? (compress '(0 5 5)) '(0 5 5))
(check-equal? (compress '(0 0 5 0 0 5)) '(:: 5 0 0 5))
(check-equal? (compress '(0 5 0 0 0 5)) '(0 5 :: 5)))
;; contract helper
(define (bytes-of-length n)
(flat-named-contract
`(bytes-of-length ,n)
(λ (bs) (and (bytes? bs) (= (bytes-length bs) n)))))
(module+ test
(check-true ((bytes-of-length 5) (bytes 1 2 3 4 5)))
(check-false ((bytes-of-length 5) "moogle")))

View File

@ -2,33 +2,43 @@
(require net/dns tests/eli-tester)
;; Run internal unit tests
(require (submod net/dns test))
;; internal tests
(require (only-in rackunit require/expose) net/private/ip)
(require/expose net/dns (ip->in-addr.arpa ip->ip6.arpa))
(define (internal-tests)
(test (ip->in-addr.arpa (ipv4 (bytes 8 8 8 8)))
=> "8.8.8.8.in-addr.arpa"
(ip->in-addr.arpa (ipv4 (bytes 127 0 0 1)))
=> "1.0.0.127.in-addr.arpa"
(ip->ip6.arpa (make-ip-address "4321:0:1:2:3:4:567:89ab"))
=> (string-append "b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0"
".2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa")
(ip->ip6.arpa (make-ip-address "2001:db8::567:89ab"))
=> (string-append "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0"
".0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa")))
;; Constants for testing. These can go out of sync
;; when server setups change, so keep them up-to-date.
(define *google-dns* "8.8.8.8")
(define *google-dns* "8.8.8.8")
(define *google-dns-2* "8.8.4.4")
(define *racket-url* "racket-lang.org")
(define *racket-host* "champlain.ccs.neu.edu")
(define *racket-ip* "129.10.115.116")
(define *racket-mx* "aspmx.l.google.com")
(define *kame-url* "www.kame.net")
(define *kame-ip* "2001:200:dff:fff1:216:3eff:feb1:44d7")
(define *racket-url* "racket-lang.org")
(define *racket-host* "champlain.ccs.neu.edu")
(define *racket-ip* "129.10.115.116")
(define *racket-mx* "aspmx.l.google.com")
(define *kame-url* "www.kame.net")
(define *kame-ip* "2001:200:dff:fff1:216:3eff:feb1:44d7")
(module+ main (tests))
(define (dns-test/nameserver nameserver)
(define (nameserver-tests nameserver)
(test (dns-get-address nameserver *racket-url*) => *racket-ip*
(dns-get-address nameserver *racket-host*) => *racket-ip*
(dns-get-address nameserver *kame-url* #:ipv6? #t) => *kame-ip*
(dns-get-name nameserver *racket-ip*) => *racket-host*
(dns-get-mail-exchanger nameserver *racket-url*) => *racket-mx*))
(provide tests)
(module+ main (tests))
(define (tests)
(dns-test/nameserver *google-dns*)
(dns-test/nameserver *google-dns-2*)
(define ns (dns-find-nameserver))
(when ns
(dns-test/nameserver ns)))
(test do (internal-tests)
(nameserver-tests *google-dns*)
(nameserver-tests *google-dns-2*)
(let ([ns (dns-find-nameserver)]) (when ns (nameserver-tests ns)))))

125
collects/tests/net/ip.rkt Normal file
View File

@ -0,0 +1,125 @@
#lang racket/base
(require net/private/ip tests/eli-tester)
(require (only-in rackunit require/expose))
(require/expose net/private/ip
(ipv4-string->bytes ipv6-string->bytes ipv4->string ipv6->string
compress bytes-of-length))
(provide tests)
(module+ main (tests))
(define (tests)
(test
;; ----------------------------------------
(make-ip-address "127.0.0.1")
=> (ipv4 (bytes 127 0 0 1))
(make-ip-address (bytes 127 0 0 1))
=> (ipv4 (bytes 127 0 0 1))
(make-ip-address "2607:f8b0:4009:800::100e")
=> (ipv6 (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
(make-ip-address (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
=> (ipv6 (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
(make-ip-address "0.0.0.1")
=> (ipv4 (bytes 0 0 0 1))
(not (equal? (make-ip-address "128.0.0.1")
(make-ip-address "255.3.255.0")))
do (let ([ip-bytes (bytes 127 0 0 1)])
(define ip (make-ip-address ip-bytes))
(bytes-set! ip-bytes 0 255)
(test #:failure-message "IP addresses should be immutable"
ip => (make-ip-address "127.0.0.1")))
;; ----------------------------------------
(ip-address-string? "0.0.0.0")
(ip-address-string? "0.1.0.2")
(ip-address-string? "8.8.8.8")
(ip-address-string? "12.81.255.109")
(ip-address-string? "192.168.0.1")
(ip-address-string? "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
(ip-address-string? "2001:200:dff:fff1:216:3eff:feb1:44d7")
(ip-address-string? "2001:db8:85a3:0:0:8a2e:370:7334")
(ip-address-string? "2001:db8:85a3::8a2e:370:7334")
(ip-address-string? "0:0:0:0:0:0:0:1")
(ip-address-string? "0:0:0:0:0:0:0:0")
(ip-address-string? "::")
(ip-address-string? "::0")
(ip-address-string? "::ffff:c000:0280")
(ip-address-string? "2001:db8::2:1")
(ip-address-string? "2001:db8:0:0:1::1")
(not (ip-address-string? ""))
(not (ip-address-string? ":::"))
(not (ip-address-string? "::0::"))
(not (ip-address-string? "2001::db8::2:1"))
(not (ip-address-string? "2001:::db8:2:1"))
(not (ip-address-string? "52001:db8::2:1"))
(not (ip-address-string? "80.8.800.8"))
(not (ip-address-string? "80.8.800.0"))
(not (ip-address-string? "080.8.800.8"))
(not (ip-address-string? "vas8.8.800.8"))
(not (ip-address-string? "80.8.128.8dd"))
(not (ip-address-string? "0.8.800.008"))
(not (ip-address-string? "0.8.800.a8"))
(not (ip-address-string? "potatoes"))
(not (ip-address-string? "127.0.0"))
;; ----------------------------------------
(ipv4-string->bytes "0.8.255.0")
=> (bytes 0 8 255 0)
(ipv4-string->bytes "8.8.8.8")
=> (bytes 8 8 8 8)
(ipv4-string->bytes "12.81.255.109")
=> (bytes 12 81 255 109)
(ipv4-string->bytes "192.168.0.1")
=> (bytes 192 168 0 1)
;; ----------------------------------------
(ipv6-string->bytes "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
=> (bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52)
(ipv6-string->bytes "2001:200:dff:fff1:216:3eff:feb1:44d7")
=> (bytes 32 1 2 0 13 255 255 241 2 22 62 255 254 177 68 215)
(ipv6-string->bytes "2001:db8:85a3:0:0:8a2e:370:7334")
=> (bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52)
(ipv6-string->bytes "2001:db8:85a3::8a2e:370:7334")
=> (bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52)
(ipv6-string->bytes "2607:f8b0:4009:800::100e")
=> (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14)
(ipv6-string->bytes "::1")
=> (bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1)
(ipv6-string->bytes "::ffff")
=> (bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255)
;; ----------------------------------------
(ip-address->bytes (make-ip-address "8.8.8.8"))
=> (bytes 8 8 8 8)
(ip-address->bytes (make-ip-address "::1"))
=> (bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1)
;; ----------------------------------------
(ip-address->string (make-ip-address "8.8.8.8"))
=> "8.8.8.8"
(ip-address->string (make-ip-address "::1"))
=> "::1"
;; ----------------------------------------
(ipv4->string (bytes 0 0 0 0))
=> "0.0.0.0"
(ipv4->string (bytes 255 255 0 1))
=> "255.255.0.1"
(ipv4->string (bytes 127 0 0 1))
=> "127.0.0.1"
(ipv4->string (bytes 8 8 8 8))
=> "8.8.8.8"
;; ----------------------------------------
(ipv6->string (bytes 32 1 13 184 133 163 0 0 0 0 138 46 3 112 115 52))
=> "2001:db8:85a3::8a2e:370:7334"
(ipv6->string (bytes 38 7 248 176 64 9 8 0 0 0 0 0 0 0 16 14))
=> "2607:f8b0:4009:800::100e"
(ipv6->string (bytes 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 255))
=> "::ffff"
(ipv6->string (bytes 255 255 0 0 0 0 0 0 0 0 0 0 0 0 255 255))
=> "ffff::ffff"
;; ----------------------------------------
(compress '(0 0 0 5 5)) => '(:: 5 5)
(compress '(0 5 5)) => '(0 5 5)
(compress '(0 0 5 0 0 5)) => '(:: 5 0 0 5)
(compress '(0 5 0 0 0 5)) => '(0 5 :: 5)
;; ----------------------------------------
((bytes-of-length 5) (bytes 1 2 3 4 5))
(not ((bytes-of-length 5) "moogle"))
;; ----------------------------------------
))

View File

@ -1,6 +1,8 @@
#lang racket/base
(require tests/eli-tester
(prefix-in ip: "ip.rkt")
(prefix-in dns: "dns.rkt")
(prefix-in ucodec: "uri-codec.rkt")
(prefix-in url: "url.rkt")
(prefix-in cgi: "cgi.rkt")
@ -13,7 +15,9 @@
(prefix-in websocket: "websocket.rkt"))
(define (tests)
(test do (url:tests)
(test do (ip:tests)
do (dns:tests)
do (url:tests)
do (ucodec:tests)
do (ucodec:noels-tests)
do (cgi:tests)

View File

@ -43,7 +43,9 @@
(test (combine-url/relative-vec (->vec base) relative)
=> (->vec expected)))
(define (run-tests)
(provide tests)
(module+ main (tests))
(define (tests)
(test
;; Test the current-proxy-servers parameter can be set
(parameterize ([current-proxy-servers '(("http" "proxy.com" 3128))])
@ -373,7 +375,3 @@
))
)
(provide tests)
(module+ main (tests))
(define (tests) (test do (run-tests)))