;;; 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
(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))
(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))))
(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))
(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))))
(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)))
(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))
(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)))))
(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))))
(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))
(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)))))
;;;--------------------------------------------------------------------------
;;; 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.
(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)
(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
(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)
(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))
(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)))
(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)))