-;;;--------------------------------------------------------------------------
-;;; Simple messing with IP addresses.
-
-(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."
- (setf str (stringify str))
- (unless end
- (setf end (length str)))
- (let ((addr 0) (noct 0))
- (loop
- (let* ((pos (position #\. str :start start :end end))
- (i (parse-integer str :start start :end (or pos end))))
- (unless (<= 0 i 256)
- (error "IP address octet out of range"))
- (setf addr (+ (* addr 256) i))
- (incf noct)
- (unless pos
- (return))
- (setf start (1+ pos))))
- (unless (= noct 4)
- (error "Wrong number of octets in IP address"))
- addr))
-
-(defun ipaddr-byte (ip n)
- "Return byte N (from most significant downwards) of an IP address."
- (assert (<= 0 n 3))
- (logand #xff (ash ip (* -8 (- 3 n)))))
-
-(defun ipaddr-string (ip)
- "Transform the address IP into a string in dotted-quad form."
- (check-type ip ipaddr)
- (join-strings #\. (collecting ()
- (dotimes (i 4)
- (collect (ipaddr-byte ip i))))))
-
-(defun ipaddrp (ip)
- "Answer true if IP is a valid IP address in integer form."
- (typep ip 'ipaddr))
-
-(defun ipaddr (ip)
- "Convert IP to an IP address. If it's an integer, return it unchanged;
-otherwise convert by `string-ipaddr'."
- (typecase ip
- (ipaddr ip)
- (t (string-ipaddr ip))))
-
-;;;--------------------------------------------------------------------------
-;;; Netmasks.
-
-(defun integer-netmask (i)
- "Given an integer I, return a netmask with its I top bits set."
- (- (ash 1 32) (ash 1 (- 32 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'."
- (typecase ip
- (null (mask 32))
- ((integer 0 32) (integer-netmask ip))
- (t (ipaddr 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."
- (dotimes (i 33)
- (when (= mask (integer-netmask i))
- (return i))))
-
-;;;--------------------------------------------------------------------------
-;;; Networks: pairing an address and netmask.
-
-(defun make-ipnet (net mask)
- "Construct an IP-network object given the NET and MASK; these are
-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)))
- (let ((sl (position #\/ str :start start :end end)))
- (if sl
- (make-ipnet (parse-ipaddr (subseq str start sl))
- (if (find #\. str :start (1+ sl) :end end)
- (string-ipaddr str :start (1+ sl) :end end)
- (integer-netmask (parse-integer str
- :start (1+ sl)
- :end end))))
- (make-ipnet (parse-ipaddr (subseq str start end))
- (integer-netmask 32)))))
-
-(defun ipnet (net &optional mask)
- "Construct an IP-network object from the given arguments. A number of
-forms are acceptable:
-
- * NET MASK -- as for `make-ipnet'.
- * 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 (mask (make-ipnet net mask))
- ((or (stringp net) (symbolp net)) (string-ipnet net))
- (t (apply #'make-ipnet (pairify net 32)))))
-
-(defun ipnet-net (ipn)
- "Return the base network address of IPN."
- (car ipn))
-
-(defun ipnet-mask (ipn)
- "Return the netmask of IPN."
- (cdr 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."
- (with-gensyms tmp
- `(let ((,tmp ,ipn))
- (let (,@(and net `((,net (ipnet-net ,tmp))))
- ,@(and mask `((,mask (ipnet-mask ,tmp)))))
- ,@body))))
-
-(defun ipnet-pretty (ipn)
- "Convert IPN to a pretty cons-cell form."
- (with-ipnet (net mask) ipn
- (cons (ipaddr-string net)
- (or (ipmask-cidl-slash mask) (ipaddr-string mask)))))
-
-(defun ipnet-string (ipn)
- "Convert IPN to a string."
- (with-ipnet (net mask) ipn
- (format nil "~A/~A"
- (ipaddr-string net)
- (or (ipmask-cidl-slash mask) (ipaddr-string mask)))))
-
-(defun ipnet-broadcast (ipn)
- "Return the broadcast address for the network IPN."
- (with-ipnet (net mask) ipn
- (logior net (logxor (mask 32) mask))))
-
-(defun ipnet-hosts (ipn)
- "Return the number of available addresses in network IPN."
- (ash 1 (- 32 (logcount (ipnet-mask ipn)))))
-
-(defun ipnet-host (ipn host)
- "Return the address of the given HOST in network IPN. This works even with
-a non-contiguous netmask."
- (check-type host u32)
- (with-ipnet (net mask) ipn
- (let ((i 0) (m 1) (a net) (h host))
- (loop
- (when (>= i 32)
- (error "Host index ~D out of range for network ~A"
- host (ipnet-pretty ipn)))
- (cond ((zerop h)
- (return a))
- ((logbitp i mask)
- (setf h (ash h 1)))
- (t
- (setf a (logior a (logand m h)))
- (setf h (logandc2 h m))))
- (setf m (ash m 1))
- (incf i)))))
-
-(defun ipaddr-networkp (ip ipn)
- "Returns true if address IP is within network IPN."
- (with-ipnet (net mask) ipn
- (= net (logand ip mask))))
-
-(defun ipnet-subnetp (ipn subn)
- "Returns true if SUBN is a (non-strict) subnet of IPN."
- (with-ipnet (net mask) ipn
- (with-ipnet (subnet submask) subn
- (and (= net (logand subnet mask))
- (= submask (logior mask submask))))))
-
-(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."
- (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
-(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))))