chiark / gitweb /
Throughout: Use shiny new macros.
[zone] / net.lisp
index 0a274235062e7f00f890fc45aeae4e2d06d9b119..40882de85e951a068ccef59f4d2f5064ce1a1b30 100644 (file)
--- a/net.lisp
+++ b/net.lisp
@@ -27,7 +27,7 @@
 ;;; Packaging.
 
 (defpackage #:net
-  (:use #:common-lisp #:mdw.base #:mdw.str #:mdw.collect)
+  (:use #:common-lisp #:mdw.base #:mdw.str #:collect)
   (:export #:ipaddr #:string-ipaddr #:ipaddr-byte #:ipaddr-string #:ipaddrp
           #:integer-netmask #:ipmask #:ipmask-cidl-slash #:make-ipnet
             #:string-ipnet #:ipnet #:ipnet-net #:ipnet-mask #:with-ipnet
@@ -71,12 +71,11 @@ (defun count-low-zero-bits (n)
 
 (defun string-ipaddr (str &key (start 0) (end nil))
   "Parse STR as an IP address in dotted-quad form and return the integer
-equivalent.  STR may be anything at all: it's converted as if by
-`stringify'.  The START and END arguments may be used to parse out a
-substring."
+   equivalent.  STR may be anything at all: it's converted as if by
+   `stringify'.  The START and END arguments may be used to parse out a
+   substring."
   (setf str (stringify str))
-  (unless end
-    (setf end (length str)))
+  (setf-default end (length str))
   (let ((addr 0) (noct 0))
     (loop
       (let* ((pos (position #\. str :start start :end end))
@@ -110,7 +109,7 @@ (defun ipaddrp (ip)
 
 (defun ipaddr (ip)
   "Convert IP to an IP address.  If it's an integer, return it unchanged;
-otherwise convert by `string-ipaddr'."
+   otherwise convert by `string-ipaddr'."
   (typecase ip
     (ipaddr ip)
     (t (string-ipaddr ip))))
@@ -124,8 +123,8 @@ (defun integer-netmask (i)
 
 (defun ipmask (ip)
   "Transform IP into a netmask.  If it's a small integer then it's converted
-by `integer-netmask'; if nil, then all-bits-set; otherwise convert using
-`ipaddr'."
+   by `integer-netmask'; if nil, then all-bits-set; otherwise convert using
+   `ipaddr'."
   (typecase ip
     (null (mask 32))
     ((integer 0 32) (integer-netmask ip))
@@ -133,7 +132,7 @@ (defun ipmask (ip)
 
 (defun ipmask-cidl-slash (mask)
   "Given a netmask MASK, return an integer N such that (integer-netmask N) =
-MASK, or nil if this is impossible."
+   MASK, or nil if this is impossible."
   (dotimes (i 33)
     (when (= mask (integer-netmask i))
       (return i))))
@@ -143,7 +142,7 @@ (defun ipmask-cidl-slash (mask)
 
 (defun make-ipnet (net mask)
   "Construct an IP-network object given the NET and MASK; these are
-transformed as though by `ipaddr' and `ipmask'."
+   transformed as though by `ipaddr' and `ipmask'."
   (let ((net (ipaddr net))
        (mask (ipmask mask)))
     (cons (logand net mask) mask)))
@@ -151,7 +150,7 @@ (defun make-ipnet (net mask)
 (defun string-ipnet (str &key (start 0) (end nil))
   "Parse an IP-network from the string STR."
   (setf str (stringify str))
-  (unless end (setf end (length str)))
+  (setf-default end (length str))
   (let ((sl (position #\/ str :start start :end end)))
     (if sl
        (make-ipnet (parse-ipaddr (subseq str start sl))
@@ -164,12 +163,13 @@ (defun string-ipnet (str &key (start 0) (end nil))
                    (integer-netmask 32)))))
 
 (defun ipnet (net)
-  "Construct an IP-network object from the given argument.  A number of
-forms are acceptable:
+  "Construct an IP-network object from the given argument.  A number of forms
+   are acceptable:
 
-  * ADDR -- a single address (equivalent to ADDR 32)
-  * (NET . MASK|nil) -- a single-object representation.
-  * IPNET -- return an equivalent (`equal', not necessarily `eql') version."
+     * ADDR -- a single address (equivalent to ADDR 32)
+     * (NET . MASK|nil) -- a single-object representation.
+     * IPNET -- return an equivalent (`equal', not necessarily `eql')
+       version."
   (cond ((or (stringp net) (symbolp net)) (string-ipnet net))
        (t (apply #'make-ipnet (pairify net 32)))))
 
@@ -183,7 +183,8 @@ (defun ipnet-mask (ipn)
 
 (defmacro with-ipnet ((net mask) ipn &body body)
   "Evaluate BODY with NET and MASK bound to the base address and netmask of
-IPN.  Either NET or MASK (or, less usefully, both) may be nil if not wanted."
+   IPN.  Either NET or MASK (or, less usefully, both) may be nil if not
+   wanted."
   (with-gensyms tmp
     `(let ((,tmp ,ipn))
        (let (,@(and net `((,net (ipnet-net ,tmp))))
@@ -214,7 +215,7 @@ (defun ipnet-hosts (ipn)
 
 (defun ipnet-host (ipn host)
   "Return the address of the given HOST in network IPN.  This works even with
-a non-contiguous netmask."
+   a non-contiguous netmask."
   (check-type host u32)
   (with-ipnet (net mask) ipn
     (let ((i 0) (m 1) (a net) (h host))
@@ -246,7 +247,7 @@ (defun ipnet-subnetp (ipn subn)
 
 (defun ipnet-changeable-bytes (mask)
   "Answers how many low-order bytes of MASK are (entirely or partially)
-changeable.  This is used when constructing reverse zones."
+   changeable.  This is used when constructing reverse zones."
   (dotimes (i 4 4)
     (when (/= (ipaddr-byte mask i) 255)
       (return (- 4 i)))))
@@ -254,19 +255,23 @@ (defun ipnet-changeable-bytes (mask)
 ;;;--------------------------------------------------------------------------
 ;;; Name resolution.
 
-#+cmu
 (defun resolve-hostname (name)
   "Resolve a hostname to an IP address using the DNS, or return nil."
-  (let ((he (ext:lookup-host-entry name)))
-    (and he
-        (ext:host-entry-addr he))))
+  #+cmu (let ((he (ext:lookup-host-entry name)))
+         (and he (ext:host-entry-addr he)))
+  #+clisp (let ((he (ext:resolve-host-ipaddr name)))
+           (and he (string-ipaddr (car (ext:hostent-addr-list he)))))
+  #+ecl (nth-value 2 (ext:lookup-host-entry name))
+  #-(or cmu clisp ecl) nil)
 
-#+cmu
 (defun canonify-hostname (name)
   "Resolve a hostname to canonical form using the DNS, or return nil."
-  (let ((he (ext:lookup-host-entry name)))
-    (and he
-        (ext:host-entry-name he))))
+  #+cmu (let ((he (ext:lookup-host-entry name)))
+         (and he (ext:host-entry-name he)))
+  #+clisp (let ((he (ext:resolve-host-ipaddr name)))
+           (and he (ext:hostent-name he)))
+  #+ecl (nth-value 0 (ext:lookup-host-entry name))
+  #-(or cmu clisp ecl) name)
 
 ;;;--------------------------------------------------------------------------
 ;;; Host names and specifiers.
@@ -274,13 +279,17 @@ (defun canonify-hostname (name)
 (defun parse-ipaddr (addr)
   "Convert the string ADDR into an IP address: tries all sorts of things:
 
-  (NET [INDEX]) -- index a network: NET is a network name defined by defnet;
-    INDEX is an index or one of the special symbols understood by net-host,
-    and defaults to :next
-  INTEGER -- an integer IP address
-  IPADDR -- an IP address in dotted-quad form
-  HOST -- a host name defined by defhost
-  DNSNAME -- a name string to look up in the DNS"
+   (NET [INDEX])       index a network: NET is a network name defined by
+                       defnet; INDEX is an index or one of the special
+                       symbols understood by net-host, and defaults to :next
+
+   INTEGER             an integer IP address
+
+   IPADDR              an IP address in dotted-quad form
+
+   HOST                        a host name defined by defhost
+
+   DNSNAME             a name string to look up in the DNS"
   (cond ((listp addr)
         (destructuring-bind
             (net host)
@@ -325,10 +334,10 @@ (defmacro defhost (name addr)
 (defstruct (net (:predicate netp))
   "A network structure.  Slots:
 
-NAME       The network's name, as a string
-IPNET      The network base address and mask
-HOSTS      Number of hosts in the network
-NEXT       Index of the next unassigned host"
+   NAME       The network's name, as a string
+   IPNET      The network base address and mask
+   HOSTS      Number of hosts in the network
+   NEXT       Index of the next unassigned host"
   name
   ipnet
   hosts
@@ -354,13 +363,13 @@ (defun net-get-as-ipnet (form)
 
 (defun process-net-form (root addr subnets)
   "Unpack a net-form.  The return value is a list of entries, each of which
-is a list of the form (NAME ADDR MASK).  The first entry is merely repeats
-the given ROOT and ADDR arguments (unpacking ADDR into separate network
-address and mask).  The SUBNETS are then processed: they are a list of items
-of the form (NAME NUM-HOSTS . SUBNETS), where NAME names the subnet,
-NUM-HOSTS is the number of hosts in it, and SUBNETS are its sub-subnets in
-the same form.  An error is signalled if a net's subnets use up more hosts
-than the net has to start with."
+   is a list of the form (NAME ADDR MASK).  The first entry is merely repeats
+   the given ROOT and ADDR arguments (unpacking ADDR into separate network
+   address and mask).  The SUBNETS are then processed: they are a list of
+   items of the form (NAME NUM-HOSTS . SUBNETS), where NAME names the subnet,
+   NUM-HOSTS is the number of hosts in it, and SUBNETS are its sub-subnets in
+   the same form.  An error is signalled if a net's subnets use up more hosts
+   than the net has to start with."
   (labels ((frob (subnets limit finger)
             (when subnets
               (destructuring-bind (name size &rest subs) (car subnets)
@@ -382,7 +391,7 @@ (defun process-net-form (root addr subnets)
 
 (defun net-create (name net)
   "Construct a new network called NAME and add it to the map.  The ARGS
-describe the new network, in a form acceptable to the ipnet function."
+   describe the new network, in a form acceptable to the ipnet function."
   (let ((ipn (ipnet net)))
     (setf (net-find name)
          (make-net :name (string-downcase (stringify name))
@@ -399,7 +408,7 @@ (defmacro defnet (name net &rest subnets)
 
 (defun net-next-host (net)
   "Given a NET, return the IP address (as integer) of the next available
-address in the network."
+   address in the network."
   (unless (< (net-next net) (net-hosts net))
     (error "No more hosts left in network ~A" (net-name net)))
   (let ((next (net-next net)))
@@ -408,10 +417,11 @@ (defun net-next-host (net)
 
 (defun net-host (net host)
   "Return the given HOST on the NEXT.  HOST may be an index (in range, of
-course), or one of the keywords:
-:NEXT       next host, as by net-next-host
-:NET        network base address
-:BROADCAST  network broadcast address"
+   course), or one of the keywords:
+
+   :NEXT       next host, as by net-next-host
+   :NET        network base address
+   :BROADCAST  network broadcast address"
   (case host
     (:next (net-next-host net))
     (:net (ipnet-net (net-ipnet net)))