commit 16eda13c367b19579c12f03b76b15a662554736d
parent f3aeb18e5e8f222f14a5e667eeff192026045e07
Author: Tomas Hlavaty <tom@logand.com>
Date:   Sun,  2 Nov 2014 13:57:41 +0100
dns queries over udp work on sbcl ccl ecl
Diffstat:
| M | cl-rw.asd |  |  | 1 | + | 
| M | der.lisp |  |  | 32 | ++------------------------------ | 
| M | dns.lisp |  |  | 112 | +++++++++++++++++++++++++++++++++++++------------------------------------------ | 
| M | socket.lisp |  |  | 110 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- | 
| A | string.lisp |  |  | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
5 files changed, 192 insertions(+), 119 deletions(-)
diff --git a/cl-rw.asd b/cl-rw.asd
@@ -47,6 +47,7 @@
                (:file "ui")
                (:file "cas")
                (:file "zip")
+               (:file "string")
                (:file "der")
                (:file "wire")
                (:file "dns")
diff --git a/der.lisp b/der.lisp
@@ -40,34 +40,6 @@
 ;; http://www.herongyang.com/Cryptography/Certificate-Format-PEM-on-Certificates.html
 ;; http://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file
 
-(defun octets-to-utf8-string (x)
-  #-(or ecl ccl sbcl)
-  (error "TODO port RW.DER::OCTETS-TO-UTF8-STRING")
-  #+ecl
-  (let ((s (ext:make-sequence-input-stream x :external-format :utf-8)))
-    (coerce (rw:till (rw:peek-reader (rw:char-reader s)) nil nil nil) 'string))
-  #+ccl
-  (ccl:decode-string-from-octets x :external-format :utf-8)
-  #+sbcl
-  (sb-ext:octets-to-string x :external-format :utf-8))
-
-(defun utf8-string-to-octets (x)
-  #-(or ecl ccl sbcl)
-  (error "TODO port RW.DER::UTF8-STRING-TO-OCTETS")
-  #+ecl
-  (let ((z (make-array 42
-                       :adjustable t
-                       :fill-pointer 0
-                       :element-type '(unsigned-byte 8)
-                       :initial-element 0)))
-    (write-string x
-                  (ext:make-sequence-output-stream z :external-format :utf-8))
-    z)
-  #+ccl
-  (ccl:encode-string-to-octets x :external-format :utf-8)
-  #+sbcl
-  (sb-ext:string-to-octets x :external-format :utf-8))
-
 (defun decode (reader)
   (labels ((len ()
              (let ((n (rw:next-u8 reader)))
@@ -147,7 +119,7 @@
                 (z (make-array n
                                :element-type '(unsigned-byte 8)
                                :initial-element 0)))
-           (dotimes (i n (list 'utf8string (octets-to-utf8-string z)))
+           (dotimes (i n (list 'utf8string (rw.string:octets-to-string z :utf-8)))
              (setf (aref z i) (rw:next-u8 reader)))))
         (19 ;; printablestring
          (list 'printable-string (ascii)))
@@ -290,7 +262,7 @@
                do (rw:write-u8 writer x))))
          (utf8string
           (rw:write-u8 writer 12)
-          (let ((x (utf8-string-to-octets (cadr x))))
+          (let ((x (rw.string:string-to-octets (cadr x) :utf-8)))
             (len (length x))
             (loop
                for x across x
diff --git a/dns.lisp b/dns.lisp
@@ -22,7 +22,7 @@
 
 (defpackage :rw.dns
   (:use :cl)
-  (:export :dns-query))
+  (:export :query))
 
 (in-package :rw.dns)
 
@@ -101,18 +101,13 @@
   (assert (= 8 (length x)))
   (map nil (lambda (x) (rw:write-u16 writer x)) x))
 
-(defun octets-to-string (x) ;; TODO refactor
-  (sb-ext:octets-to-string x :external-format :ascii))
-
-(defun string-to-octets (x) ;; TODO refactor
-  (sb-ext:string-to-octets x :external-format :ascii))
-
 (defun next-$dns-string (reader)
-  (octets-to-string
-   (rw:next-octets reader (rw:next-u8 reader))))
+  (rw.string:octets-to-string
+   (rw:next-octets reader (rw:next-u8 reader))
+   :ascii))
 
 (defun write-$dns-string (writer x)
-  (let ((b (string-to-octets x)))
+  (let ((b (rw.string:string-to-octets x :ascii)))
     (rw:write-u8 writer (length b))
     (rw:write-octets writer b)))
 
@@ -220,54 +215,53 @@
   ($resource authority :size nauthority)
   ($resource additional :size nadditional))
 
-(defun udp-query (hostname server &key (type 'A) (class 'IN) (port 53))
-  (let ((n 512) ;; TODO minus IP/UDP headers
-        (s (rw.socket::make-udp-socket server port)))
-    (unwind-protect
-         (let ((b (rw.wire::make-octet-buffer n)))
-           ;;(sb-bsd-sockets:socket-connect s server port)
-           (write-$message
-            (rw:writer b)
-            (make-$message
-             :tid #x3141 #+nil(random 65536)
-             :flags #x100 ;; std query TODO flags
-             :nquestion 1
-             :nanswer 0
-             :nauthority 0
-             :nadditional 0
-             :question (list (make-$question :name hostname
-                                             :type type
-                                             :class class))
-             :answer nil
-             :authority nil
-             :additional nil))
-           (assert (<= (length b) n))
-           (sb-bsd-sockets:socket-send s b (length b) :address (list server port))
-           (setf (fill-pointer b) n)
-           (multiple-value-bind (buf len addr)
-               (sb-bsd-sockets:socket-receive s b n)
-             (declare (ignore addr))
-             ;;(print (list ::@@@ (subseq buf 0 len)))
-             (flet ((cb (n)
-                      (let (*name-from-position*)
-                        (next-$name
-                         (rw:skip (rw:reader buf) n)))))
-               (let ((*name-from-position* #'cb))
-                 (next-$message
-                  (rw:shorter-reader (rw:reader buf) len))))))
-      (sb-bsd-sockets:socket-close s))))
-
-;;(print (udp-query "mx1.logand.com" #(8 8 8 8)))
-;;(print (udp-query "seznam.cz" #(8 8 8 8)))
-;;(print (udp-query "seznam.cz" #(192 168 1 1)))
-;;(print (udp-query "www.google.com" #(8 8 8 8)))
-
-;;(print (udp-query "mx1.logand.com" #(8 8 8 8) :type 'NS))
-;;(print (udp-query "www.google.com" #(8 8 8 8) :type 'AAAA))
-
-;;(print (udp-query "mx1.logand.com" #(8 8 8 8) :type 'MX))
-;;(print (udp-query "seznam.cz" #(8 8 8 8) :type 'MX))
-;;(print (udp-query "www.google.com" #(8 8 8 8) :type 'MX)) ;; TODO SOA follow authoritative nameservers
+(defun udp (buf server port)
+  (rw.socket:with-socket (s (rw.socket:make-udp-socket))
+    (rw.socket:udp-send s buf (length buf)
+                        :remote-host server
+                        :remote-port port)
+    (let ((n (array-total-size buf)))
+      (setf (fill-pointer buf) n)
+      (multiple-value-bind (b len addr) (rw.socket:udp-receive s buf n)
+        (declare (ignore addr))
+        ;;(print (list :@@@ (subseq b 0 len)))
+        (flet ((cb (pos)
+                 (next-$name (rw:skip (rw:reader b) pos))))
+          (let ((*name-from-position* #'cb))
+            (next-$message (rw:shorter-reader (rw:reader b) len))))))))
+
+(defun query (hostname 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
+     (rw:writer b)
+     (make-$message
+      :tid #x3141 #+nil(random 65536)
+      :flags #x100 ;; std query TODO flags
+      :nquestion 1
+      :nanswer 0
+      :nauthority 0
+      :nadditional 0
+      :question (list (make-$question :name hostname
+                                      :type type
+                                      :class class))
+      :answer nil
+      :authority nil
+      :additional nil))
+    (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 (query "mx1.logand.com" #(8 8 8 8) :type 'NS))
+;;(print (query "www.google.com" #(8 8 8 8) :type 'AAAA))
+
+;;(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
 
 #+nil ;; TODO dns over tcp doesnt seem to work, depends on server?
 (defun tcp-query (hostname server &key (port 53))
@@ -277,5 +271,3 @@
       (rw.wire:flush w)
       (rw:next-u8 (rw:byte-reader s)))))
 
-;;(dns-query "seznam.cz" "8.8.8.8")
-;;(dns-query "seznam.cz" "192.168.1.1")
diff --git a/socket.lisp b/socket.lisp
@@ -23,21 +23,44 @@
 (defpackage :rw.socket
   (:use :cl)
   (:export :accept
+           :close-socket
            :make-tcp-server-socket
-           :make-tcp-client-socket))
+           :make-tcp-client-socket
+           :make-udp-socket
+           :udp-receive
+           :udp-send
+           :with-socket))
 
 (in-package :rw.socket)
 
-(defun make-tcp-server-socket (host port &key backlog)
+(defun close-socket (socket)
+  #-(or ccl ecl sbcl)
+  (error "TODO port RW.SOCKET::CLOSE-SOCKET")
+  #+ccl
+  (close socket)
+  #+(or ecl sbcl)
+  (sb-bsd-sockets:socket-close socket))
+
+(defun call-with-socket (socket fn)
+  (unwind-protect (funcall fn socket)
+    (close-socket socket)))
+
+(defmacro with-socket ((var socket) &body body)
+  `(call-with-socket ,socket (lambda (,var) ,@body)))
+
+(defun make-tcp-server-socket (local-host local-port &key backlog)
   #-(or sbcl ecl ccl)
   (error "TODO port RW.SOCKET:MAKE-TCP-SERVER-SOCKET")
   #+(or sbcl ecl)
-  (let ((x (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp)))
+  (let ((x (make-instance 'sb-bsd-sockets:inet-socket
+                          :type :stream
+                          :protocol :tcp)))
     (setf (sb-bsd-sockets:sockopt-reuse-address x) t)
-    (sb-bsd-sockets:socket-bind x
-                                (car (sb-bsd-sockets:host-ent-addresses
-                                      (sb-bsd-sockets:get-host-by-name host)))
-                                port)
+    (sb-bsd-sockets:socket-bind
+     x
+     (car (sb-bsd-sockets:host-ent-addresses
+           (sb-bsd-sockets:get-host-by-name local-host)))
+     local-port)
     (sb-bsd-sockets:socket-listen x (or backlog 5))
     x)
   #+ccl
@@ -45,19 +68,22 @@
                    :address-family :internet
                    :type :stream
                    :format :bivalent ;; TODO :binary
-                   :local-host host
-                   :local-port port
+                   :local-host local-host
+                   :local-port local-port
                    :reuse-address t))
 
-(defun make-tcp-client-socket (host port)
+(defun make-tcp-client-socket (remote-host remote-port)
   #-(or sbcl ecl ccl)
   (error "TODO port RW.SOCKET:MAKE-TCP-CLIENT-SOCKET")
   #+(or sbcl ecl)
-  (let ((x (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp)))
-    (sb-bsd-sockets:socket-connect x
-                                   (car (sb-bsd-sockets:host-ent-addresses
-                                         (sb-bsd-sockets:get-host-by-name host)))
-                                   port)
+  (let ((x (make-instance 'sb-bsd-sockets:inet-socket
+                          :type :stream
+                          :protocol :tcp)))
+    (sb-bsd-sockets:socket-connect
+     x
+     (car (sb-bsd-sockets:host-ent-addresses
+           (sb-bsd-sockets:get-host-by-name remote-host)))
+     remote-port)
     (sb-bsd-sockets:socket-make-stream x :input t :output t ;;:buffering :none
                                        :element-type :default))
   #+ccl
@@ -65,25 +91,32 @@
                    :address-family :internet
                    :type :stream
                    :format :bivalent ;; TODO :binary
-                   :remote-host host
-                   :remote-port port))
+                   :remote-host remote-host
+                   :remote-port remote-port))
 
-(defun make-udp-socket (host port &key remote-host remote-port) ;; TODO understand
-  #-(or ccl sbcl)
+(defun make-udp-socket (&key local-host local-port remote-host remote-port)
+  #-(or ccl ecl sbcl)
   (error "TODO port RW.SOCKET:MAKE-PASSIVE-UDP-SOCKET")
   #+ccl
-  (ccl:make-socket ;;:connect :passive
-                   :address-family :internet
+  (ccl:make-socket :address-family :internet
                    :type :datagram
-                   :local-host remote-host
-                   :local-port port
+                   :local-host local-host
+                   :local-port local-port
                    :remote-host remote-host
-                   :remote-port remote-port
-                   :reuse-address t)
-  #+sbcl
-  (make-instance 'sb-bsd-sockets:inet-socket
-                 :type :datagram
-                 :protocol :udp))
+                   :remote-port remote-port)
+  #+(or ecl sbcl)
+  (let ((x (make-instance 'sb-bsd-sockets:inet-socket
+                          :type :datagram
+                          :protocol :udp)))
+    (when (and local-host local-port)
+      (sb-bsd-sockets:socket-bind
+       x
+       (car (sb-bsd-sockets:host-ent-addresses
+             (sb-bsd-sockets:get-host-by-name local-host)))
+       local-port))
+    (when (and remote-host remote-port)
+      (sb-bsd-sockets:socket-connect x remote-host remote-port))
+    x))
 
 ;; eol
 ;; keepalive nodelay broadcast linger
@@ -104,3 +137,22 @@
                                      :auto-close t)
   #+ccl
   (ccl:accept-connection socket))
+
+(defun udp-send (socket buf len &key remote-host remote-port)
+  #-(or ccl ecl sbcl)
+  (error "TODO port RW.SOCKET:UDP-SEND")
+  #+ccl
+  (ccl:send-to socket buf len
+               :remote-host remote-host
+               :remote-port remote-port)
+  #+(or ecl sbcl)
+  (sb-bsd-sockets:socket-send socket buf len
+                              :address (list remote-host remote-port)))
+
+(defun udp-receive (socket buf len)
+  #-(or ccl ecl sbcl)
+  (error "TODO port RW.SOCKET:UDP-RECEIVE")
+  #+ccl
+  (ccl:receive-from socket len :buffer buf)
+  #+(or ecl sbcl)
+  (sb-bsd-sockets:socket-receive socket buf len))
diff --git a/string.lisp b/string.lisp
@@ -0,0 +1,56 @@
+;;; Copyright (C) 2014 Tomas Hlavaty <tom@logand.com>
+;;;
+;;; Permission is hereby granted, free of charge, to any person
+;;; obtaining a copy of this software and associated documentation
+;;; files (the "Software"), to deal in the Software without
+;;; restriction, including without limitation the rights to use, copy,
+;;; modify, merge, publish, distribute, sublicense, and/or sell copies
+;;; of the Software, and to permit persons to whom the Software is
+;;; furnished to do so, subject to the following conditions:
+;;;
+;;; The above copyright notice and this permission notice shall be
+;;; included in all copies or substantial portions of the Software.
+;;;
+;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+;;; DEALINGS IN THE SOFTWARE.
+
+(defpackage :rw.string
+  (:use :cl)
+  (:export :octets-to-string
+           :string-to-octets))
+
+(in-package :rw.string)
+
+(defun octets-to-string (x encoding)
+  #-(or ecl ccl sbcl)
+  (error "TODO port RW.STRING:OCTETS-TO-STRING")
+  #+ecl
+  (let ((s (ext:make-sequence-input-stream x :external-format encoding)))
+    (coerce (rw:till (rw:peek-reader (rw:char-reader s)) nil nil nil) 'string))
+  #+ccl
+  (ccl:decode-string-from-octets x :external-format encoding)
+  #+sbcl
+  (sb-ext:octets-to-string x :external-format encoding))
+
+(defun string-to-octets (x encoding)
+  #-(or ecl ccl sbcl)
+  (error "TODO port RW.STRING:STRING-TO-OCTETS")
+  #+ecl
+  (let ((z (make-array 42
+                       :adjustable t
+                       :fill-pointer 0
+                       :element-type '(unsigned-byte 8)
+                       :initial-element 0)))
+    (write-string x
+                  (ext:make-sequence-output-stream z :external-format encoding))
+    z)
+  #+ccl
+  (ccl:encode-string-to-octets x :external-format encoding)
+  #+sbcl
+  (sb-ext:string-to-octets x :external-format encoding))