commit 2697b4ec6deceeea0bc3df73a8c598a2ad35c5aa
parent 33b17eb0e4d3a5d7505944c9a86410d20baace6f
Author: Tomas Hlavaty <tom@logand.com>
Date: Sun, 14 Dec 2014 17:46:43 +0100
more dns
Diffstat:
M | dns.lisp | | | 152 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
M | socket.lisp | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++------ |
2 files changed, 176 insertions(+), 27 deletions(-)
diff --git a/dns.lisp b/dns.lisp
@@ -27,6 +27,10 @@
(in-package :rw.dns)
;;https://www.ietf.org/rfc/rfc1035.txt
+;;https://en.wikipedia.org/wiki/Punycode
+;;https://www.iana.org/domains/root/files
+;;http://www.internic.net/domain/named.root
+;;http://www.internic.net/domain/root.zone
(defvar *name-from-position*)
@@ -88,14 +92,14 @@
(map nil (lambda (x) (rw:write-u8 writer x)) x))
(defun next-$ipv6-address (reader)
- (vector (rw:next-u16 reader)
- (rw:next-u16 reader)
- (rw:next-u16 reader)
- (rw:next-u16 reader)
- (rw:next-u16 reader)
- (rw:next-u16 reader)
- (rw:next-u16 reader)
- (rw:next-u16 reader)))
+ (vector (rw:next-u16be reader)
+ (rw:next-u16be reader)
+ (rw:next-u16be reader)
+ (rw:next-u16be reader)
+ (rw:next-u16be reader)
+ (rw:next-u16be reader)
+ (rw:next-u16be reader)
+ (rw:next-u16be reader)))
(defun write-$ipv6-address (writer x)
(assert (= 8 (length x)))
@@ -181,7 +185,6 @@
($type type)
($class class)
(rw:u32be ttl)
- #+nil(rw:u8 data :length rw:u16be)
((ecase type
(A $ipv4-address)
(AAAA $ipv6-address)
@@ -205,7 +208,7 @@
(rw.wire:defstruc $message ()
(rw:u16be tid)
- (rw:u16be flags)
+ (rw:u16be flags) ;; TODO decode flags
(rw:u16be nquestion)
(rw:u16be nanswer)
(rw:u16be nauthority)
@@ -230,7 +233,7 @@
(let ((*name-from-position* #'cb))
(next-$message (rw:shorter-reader (rw:reader b) len))))))))
-(defun query (name server &key (type 'A) (class 'IN) (port 53))
+(defun query1 (name server &key (type 'A) (class 'IN) (port 53))
(let* ((n 512) ;; TODO minus IP/UDP headers
(b (rw.wire::make-octet-buffer n)))
(write-$message
@@ -249,18 +252,125 @@
(assert (<= (length b) n)) ;; TODO dns over tcp
(udp b server port)))
-;;(print (query "mx1.logand.com" #+ccl "8.8.8.8" #-ccl #(8 8 8 8)))
-;;(print (query "seznam.cz" #+ccl "8.8.8.8" #-ccl #(8 8 8 8)))
-;;(print (query "seznam.cz" #(192 168 1 1)))
-;;(print (query "www.google.com" #(8 8 8 8)))
+;;(print (query1 "mx1.logand.com" #(8 8 8 8)))
+;;(print (query1 "mx1.logand.com" "8.8.8.8"))
+
+;;(print (query1 "mx1.logand.com" #(8 8 8 8)))
+;;(print (query1 "seznam.cz" #(8 8 8 8)))
+;;(print (query1 "seznam.cz" #(192 168 1 1)))
+;;(print (query1 "www.google.com" #(8 8 8 8)))
+
+;;(print (query1 "mx1.logand.com" #(8 8 8 8) :type 'NS))
+;;(print (query1 "www.google.com" #(8 8 8 8) :type 'AAAA))
+
+;;(print (query1 "mx1.logand.com" #(8 8 8 8) :type 'MX))
+;;(print (query1 "seznam.cz" #(8 8 8 8) :type 'MX))
+;;(print (query1 "www.google.com" #(8 8 8 8) :type 'MX)) ;; TODO SOA follow authoritative nameservers
+
+;;; how to resolve logand.com?
+;;; logand.com? com logand.com nserv0.domainexpress.co.uk
+;;(print (query1 "com" #(8 8 8 8) :type 'A))
+;;(print (query1 "a.gtld-servers.net" #(8 8 8 8) :type 'A))
+;;(print (query1 "logand.com" #(192 5 6 30) :type 'A))
+;;(print (query1 "uk" #(192 5 6 30) :type 'A))
+
+;;(print (query1 "www.google.com" #(8 8 8 8) :type 'MX))
+;;(print (query1 "ns1.google.com" #(8 8 8 8) :type 'A)) ;; -> #(216 239 32 10)
+;;(print (query1 "www.google.com" #(216 239 32 10) :type 'MX))
+;;(print (query1 "google.com" #(216 239 32 10) :type 'MX))
+
+;;(print (query1 "logand.com" #(8 8 8 8) :type 'MX))
+;;(print (query1 "mx1.logand.com" #(8 8 8 8) :type 'A))
+;;(print (query1 "com" #(8 8 8 8)))
+
+(defvar *cache* (make-hash-table :test #'equal)) ;; TODO locking?
+
+(defstruct cached time ttl data)
+
+(defun validp (cached)
+ (<= (get-universal-time) (+ (cached-time cached) (cached-ttl cached))))
+
+(defun lookup (name type class)
+ (let ((k (list name type class))
+ (n 0)
+ (i 0))
+ (dolist (v (gethash k *cache*))
+ (incf n)
+ (when (validp v)
+ (incf i)))
+ (cond ;; validp?
+ ((<= n i)) ;; everything
+ ((< 0 i) ;; some
+ (setf (gethash k *cache*) (delete-if-not 'validp (gethash k *cache*))))
+ (t ;; none
+ (remhash k *cache*)))
+ (mapcar 'cached-data (gethash k *cache*))))
+
+(defun remember (resource) ;; TODO preserve original ordering?
+ ;; TODO cca pushnew?
+ (push (make-cached :time (get-universal-time)
+ :ttl ($resource-ttl resource)
+ :data ($resource-data resource))
+ (gethash (list ($resource-name resource)
+ ($resource-type resource)
+ ($resource-class resource))
+ *cache*)))
+
+(defun query (name server &key (type 'A) (class 'IN) (port 53))
+ (clrhash *cache*) ;; TODO remove
+ (let ((i 0))
+ (labels
+ ((rec (name type server)
+ (or (lookup name type class)
+ (let* ((q (query1 name server :type type :class class :port port))
+ (answer ($message-answer q))
+ (authority ($message-authority q)))
+ (incf i)
+ (map nil #'remember answer)
+ (map nil #'remember authority)
+ (map nil #'remember ($message-additional q))
+ (cond
+ (answer
+ (or (lookup name type class)
+ (unless (eq 'CNAME type)
+ (loop
+ for x in (rec name 'CNAME server)
+ appending (rec x type server)))))
+ (authority
+ (dolist (a authority)
+ (ecase ($resource-type a)
+ (NS
+ (dolist (server (rec ($resource-data a) type server))
+ (let ((z (rec name type server)))
+ (when z
+ (return-from rec z)))))
+ (SOA (return-from rec nil))))))))))
+ (values (rec name type server) i))))
+
+;;(query "logand.com" #(8 8 8 8) :type 'MX)
+;;(query "mx1.logand.com" #(8 8 8 8) :type 'A)
+;;(query "mx1.logand.com" #(8 8 8 8) :type 'CNAME)
+;;(query "logand.com" #(8 8 8 8) :type 'SOA)
+
+;;(query "google.com" #(8 8 8 8) :type 'AAAA)
+;;(query "google.com" #(8 8 8 8) :type 'A)
+;;(query "google.com" #(8 8 8 8) :type 'MX)
+;;(query "google.com" #(8 8 8 8) :type 'NS)
+;;(query "google.com" #(8 8 8 8) :type 'SOA)
+
+;;(query "google.com" #(8 8 8 8) :type 'CNAME)
+;;(query "google.com" #(8 8 8 8) :type 'PTR)
+;;(query "mx1.logand.com" #(8 8 8 8) :type 'AAAA)
+;;(query "82.192.70.8" #(8 8 8 8) :type 'PTR)
+
+;;(query "google.com" #(8 8 8 8) :type 'TXT)
+;;(query "google.com" #(8 8 8 8) :type 'WKS)
-;;(print (query "mx1.logand.com" #(8 8 8 8) :type 'NS))
-;;(print (query "www.google.com" #(8 8 8 8) :type 'AAAA))
+;;(query "google.com" #(198 41 0 4) :type 'A)
+;;(query "mx1.logand.com" #(198 41 0 4) :type 'A)
-;;(print (query "mx1.logand.com" #(8 8 8 8) :type 'MX))
-;;(print (query "seznam.cz" #(8 8 8 8) :type 'MX))
-;;(print (query "www.google.com" #(8 8 8 8) :type 'MX)) ;; TODO SOA follow authoritative nameservers
-;;(print (query "com" #(8 8 8 8) :type 'A))
+;; TODO root hints http://technet.microsoft.com/en-us/library/cc758353(v=ws.10).aspx
+;; TODO reverse dns
#+nil ;; TODO dns over tcp doesnt seem to work, depends on server?
(defun tcp-query (name server &key (port 53))
diff --git a/socket.lisp b/socket.lisp
@@ -33,6 +33,44 @@
(in-package :rw.socket)
+(defun native-address (x)
+ (when x
+ (etypecase x
+ (string
+ #-ccl
+ (let ((r (rw:peek-reader (rw:reader x))))
+ (vector (rw:next-z0 r)
+ (progn
+ (assert (eql #\. (rw:next r)))
+ (rw:next-z0 r))
+ (progn
+ (assert (eql #\. (rw:next r)))
+ (rw:next-z0 r))
+ (progn
+ (assert (eql #\. (rw:next r)))
+ (rw:next-z0 r))))
+ #+ccl
+ x)
+ (vector
+ #-ccl
+ x
+ #+ccl
+ (with-output-to-string (s)
+ (loop
+ for e across x
+ for i from 0
+ do (progn
+ (assert (< i 4))
+ (assert (<= 0 e 255))
+ (when (plusp i)
+ (write-char #\. s))
+ (format s "~d" e)))))
+ (list (native-address (coerce x 'vector))))))
+
+;;(native-address #(127 0 0 1))
+;;(native-address '(127 0 0 1))
+;;(native-address "127.0.0.1")
+
(defun close-socket (socket)
#-(or ccl ecl sbcl)
(error "TODO port RW.SOCKET::CLOSE-SOCKET")
@@ -71,7 +109,7 @@
:address-family :internet
:type :stream
:format :bivalent ;; TODO :binary
- :local-host local-host
+ :local-host (native-address local-host)
:local-port local-port
:reuse-address t))
@@ -96,7 +134,7 @@
:address-family :internet
:type :stream
:format :bivalent ;; TODO :binary
- :remote-host remote-host
+ :remote-host (native-address remote-host)
:remote-port remote-port))
(defun make-udp-socket (&key local-host local-port remote-host remote-port)
@@ -107,9 +145,9 @@
#+ccl
(ccl:make-socket :address-family :internet
:type :datagram
- :local-host local-host
+ :local-host (native-address local-host)
:local-port local-port
- :remote-host remote-host
+ :remote-host (native-address remote-host)
:remote-port remote-port)
#+(or ecl sbcl)
(let ((x (make-instance 'sb-bsd-sockets:inet-socket
@@ -152,11 +190,12 @@
(error "TODO port RW.SOCKET:UDP-SEND")
#+ccl
(ccl:send-to socket buf len
- :remote-host remote-host
+ :remote-host (native-address remote-host)
:remote-port remote-port)
#+(or ecl sbcl)
(sb-bsd-sockets:socket-send socket buf len
- :address (list remote-host remote-port)))
+ :address (list (native-address remote-host)
+ remote-port)))
(defun udp-receive (socket buf len)
#-(or ccl ecl sbcl)