tusocket.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://hg6vgqziawt5s4dj.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) LICENSE
       ---
       tusocket.lisp (26070B)
       ---
            1 ;;;; See LICENSE for licensing information.
            2 
            3 (in-package :usocket)
            4 
            5 (defparameter *wildcard-host* #(0 0 0 0)
            6   "Hostname to pass when all interfaces in the current system are to
            7   be bound.  If this variable is passed to socket-listen, IPv6 capable
            8   systems will also listen for IPv6 connections.")
            9 
           10 (defparameter *auto-port* 0
           11   "Port number to pass when an auto-assigned port number is wanted.")
           12 
           13 (defparameter *version* #.(asdf:component-version (asdf:find-system :usocket))
           14   "usocket version string")
           15 
           16 (defconstant +max-datagram-packet-size+ 65507
           17   "The theoretical maximum amount of data in a UDP datagram.
           18 
           19 The IPv4 UDP packets have a 16-bit length constraint, and IP+UDP header has 28-byte.
           20 
           21 IP_MAXPACKET = 65535,       /* netinet/ip.h */
           22 sizeof(struct ip) = 20,     /* netinet/ip.h */
           23 sizeof(struct udphdr) = 8,  /* netinet/udp.h */
           24 
           25 65535 - 20 - 8 = 65507
           26 
           27 (But for UDP broadcast, the maximum message size is limited by the MTU size of the underlying link)")
           28 
           29 (defclass usocket ()
           30   ((socket
           31     :initarg :socket
           32     :accessor socket
           33     :documentation "Implementation specific socket object instance.'")
           34    (wait-list
           35     :initform nil
           36     :accessor wait-list
           37     :documentation "WAIT-LIST the object is associated with.")
           38    (state
           39     :initform nil
           40     :accessor state
           41     :documentation "Per-socket return value for the `wait-for-input' function.
           42 
           43 The value stored in this slot can be any of
           44  NIL          - not ready
           45  :READ        - ready to read
           46  :READ-WRITE  - ready to read and write
           47  :WRITE       - ready to write
           48 
           49 The last two remain unused in the current version.
           50 ")
           51    #+(and win32 (or sbcl ecl lispworks))
           52    (%ready-p
           53     :initform nil
           54     :accessor %ready-p
           55     :documentation "Indicates whether the socket has been signalled
           56 as ready for reading a new connection.
           57 
           58 The value will be set to T by `wait-for-input-internal' (given the
           59 right conditions) and reset to NIL by `socket-accept'.
           60 
           61 Don't modify this slot or depend on it as it is really intended
           62 to be internal only.
           63 
           64 Note: Accessed, but not used for 'stream-usocket'.
           65 "
           66    ))
           67   (:documentation
           68 "The main socket class.
           69 
           70 Sockets should be closed using the `socket-close' method."))
           71 
           72 (defgeneric socket-state (socket)
           73   (:documentation "NIL          - not ready
           74 :READ        - ready to read
           75 :READ-WRITE  - ready to read and write
           76 :WRITE       - ready to write"))
           77 
           78 (defmethod socket-state ((socket usocket))
           79   (state socket))
           80 
           81 (defclass stream-usocket (usocket)
           82    ((stream
           83      :initarg :stream
           84      :accessor socket-stream
           85      :documentation "Stream instance associated with the socket."
           86 ;;
           87 ;;Iff an external-format was passed to `socket-connect' or `socket-listen'
           88 ;;the stream is a flexi-stream. Otherwise the stream is implementation
           89 ;;specific."
           90 ))
           91    (:documentation
           92 "Stream socket class.
           93 '
           94 Contrary to other sockets, these sockets may be closed either
           95 with the `socket-close' method or by closing the associated stream
           96 (which can be retrieved with the `socket-stream' accessor)."))
           97 
           98 (defclass stream-server-usocket (usocket)
           99   ((element-type
          100     :initarg :element-type
          101     :initform #-lispworks 'character
          102               #+lispworks 'base-char
          103     :reader element-type
          104     :documentation "Default element type for streams created by
          105 `socket-accept'."))
          106   (:documentation "Socket which listens for stream connections to
          107 be initiated from remote sockets."))
          108 
          109 (defclass datagram-usocket (usocket)
          110   ((connected-p :type boolean
          111                 :accessor connected-p
          112                 :initarg :connected-p)
          113    #+(or cmu scl lispworks mcl
          114          (and clisp ffi (not rawsock)))
          115    (%open-p     :type boolean
          116                 :accessor %open-p
          117                 :initform t
          118                 :documentation "Flag to indicate if usocket is open,
          119 for GC on implementions operate on raw socket fd.")
          120    #+(or lispworks mcl
          121          (and clisp ffi (not rawsock)))
          122    (recv-buffer :documentation "Private RECV buffer.")
          123    #+(or lispworks mcl)
          124    (send-buffer :documentation "Private SEND buffer."))
          125   (:documentation "UDP (inet-datagram) socket"))
          126 
          127 (defun usocket-p (socket)
          128   (typep socket 'usocket))
          129 
          130 (defun stream-usocket-p (socket)
          131   (typep socket 'stream-usocket))
          132 
          133 (defun stream-server-usocket-p (socket)
          134   (typep socket 'stream-server-usocket))
          135 
          136 (defun datagram-usocket-p (socket)
          137   (typep socket 'datagram-usocket))
          138 
          139 (defun make-socket (&key socket)
          140   "Create a usocket socket type from implementation specific socket."
          141   (unless socket
          142     (error 'invalid-socket-error))
          143   (make-stream-socket :socket socket))
          144 
          145 (defun make-stream-socket (&key socket stream)
          146   "Create a usocket socket type from implementation specific socket
          147 and stream objects.
          148 
          149 Sockets returned should be closed using the `socket-close' method or
          150 by closing the stream associated with the socket.
          151 "
          152   (unless socket
          153     (error 'invalid-socket-error))
          154   (unless stream
          155     (error 'invalid-socket-stream-error))
          156   (make-instance 'stream-usocket
          157                  :socket socket
          158                  :stream stream))
          159 
          160 (defun make-stream-server-socket (socket &key (element-type
          161                                                #-lispworks 'character
          162                                                #+lispworks 'base-char))
          163   "Create a usocket-server socket type from an
          164 implementation-specific socket object.
          165 
          166 The returned value is a subtype of `stream-server-usocket'.
          167 "
          168   (unless socket
          169     (error 'invalid-socket-error))
          170   (make-instance 'stream-server-usocket
          171                  :socket socket
          172                  :element-type element-type))
          173 
          174 (defun make-datagram-socket (socket &key connected-p)
          175   (unless socket
          176     (error 'invalid-socket-error))
          177   (make-instance 'datagram-usocket
          178                  :socket socket
          179                  :connected-p connected-p))
          180 
          181 (defgeneric socket-accept (socket &key element-type)
          182   (:documentation
          183       "Accepts a connection from `socket', returning a `stream-socket'.
          184 
          185 The stream associated with the socket returned has `element-type' when
          186 explicitly specified, or the element-type passed to `socket-listen' otherwise."))
          187 
          188 (defgeneric socket-close (usocket)
          189   (:documentation "Close a previously opened `usocket'."))
          190 
          191 ;; also see http://stackoverflow.com/questions/4160347/close-vs-shutdown-socket
          192 (defgeneric socket-shutdown (usocket direction)
          193   (:documentation "Shutdown communication on the socket in DIRECTION.
          194 
          195 After a shutdown no input and/or output of the indicated DIRECTION
          196 can be performed on the `usocket'.
          197 
          198 DIRECTION should be either :INPUT or :OUTPUT or :IO"))
          199 
          200 (defgeneric socket-send (usocket buffer length &key host port)
          201   (:documentation "Send packets through a previously opend `usocket'."))
          202 
          203 (defgeneric socket-receive (usocket buffer length &key)
          204   (:documentation "Receive packets from a previously opend `usocket'.
          205 
          206 Returns 4 values: (values buffer size host port)"))
          207 
          208 (defgeneric get-local-address (socket)
          209   (:documentation "Returns the IP address of the socket."))
          210 
          211 (defgeneric get-peer-address (socket)
          212   (:documentation
          213    "Returns the IP address of the peer the socket is connected to."))
          214 
          215 (defgeneric get-local-port (socket)
          216   (:documentation "Returns the IP port of the socket.
          217 
          218 This function applies to both `stream-usocket' and `server-stream-usocket'
          219 type objects."))
          220 
          221 (defgeneric get-peer-port (socket)
          222   (:documentation "Returns the IP port of the peer the socket to."))
          223 
          224 (defgeneric get-local-name (socket)
          225   (:documentation "Returns the IP address and port of the socket as values.
          226 
          227 This function applies to both `stream-usocket' and `server-stream-usocket'
          228 type objects."))
          229 
          230 (defgeneric get-peer-name (socket)
          231   (:documentation
          232    "Returns the IP address and port of the peer
          233 the socket is connected to as values."))
          234 
          235 (defmacro with-connected-socket ((var socket) &body body)
          236   "Bind `socket' to `var', ensuring socket destruction on exit.
          237 
          238 `body' is only evaluated when `var' is bound to a non-null value.
          239 
          240 The `body' is an implied progn form."
          241   `(let ((,var ,socket))
          242      (unwind-protect
          243          (when ,var
          244            (with-mapped-conditions (,var)
          245              ,@body))
          246        (when ,var
          247          (socket-close ,var)))))
          248 
          249 (defmacro with-client-socket ((socket-var stream-var &rest socket-connect-args)
          250                               &body body)
          251   "Bind the socket resulting from a call to `socket-connect' with
          252 the arguments `socket-connect-args' to `socket-var' and if `stream-var' is
          253 non-nil, bind the associated socket stream to it."
          254   `(with-connected-socket (,socket-var (socket-connect ,@socket-connect-args))
          255      ,(if (null stream-var)
          256           `(progn ,@body)
          257            `(let ((,stream-var (socket-stream ,socket-var)))
          258               ,@body))))
          259 
          260 (defmacro with-server-socket ((var server-socket) &body body)
          261   "Bind `server-socket' to `var', ensuring socket destruction on exit.
          262 
          263 `body' is only evaluated when `var' is bound to a non-null value.
          264 
          265 The `body' is an implied progn form."
          266   `(with-connected-socket (,var ,server-socket)
          267      ,@body))
          268 
          269 (defmacro with-socket-listener ((socket-var &rest socket-listen-args)
          270                                 &body body)
          271   "Bind the socket resulting from a call to `socket-listen' with arguments
          272 `socket-listen-args' to `socket-var'."
          273   `(with-server-socket (,socket-var (socket-listen ,@socket-listen-args))
          274      ,@body))
          275 
          276 (defstruct (wait-list (:constructor %make-wait-list))
          277   %wait     ;; implementation specific
          278   waiters ;; the list of all usockets
          279   map)  ;; maps implementation sockets to usockets
          280 
          281 ;; Implementation specific:
          282 ;;
          283 ;;  %setup-wait-list
          284 ;;  %add-waiter
          285 ;;  %remove-waiter
          286 
          287 (defun make-wait-list (waiters)
          288   (let ((wl (%make-wait-list)))
          289     (setf (wait-list-map wl) (make-hash-table))
          290     (%setup-wait-list wl)
          291     (dolist (x waiters wl)
          292       (add-waiter wl x))))
          293 
          294 (defun add-waiter (wait-list input)
          295   (setf (gethash (socket input) (wait-list-map wait-list)) input
          296         (wait-list input) wait-list)
          297   (pushnew input (wait-list-waiters wait-list))
          298   (%add-waiter wait-list input))
          299 
          300 (defun remove-waiter (wait-list input)
          301   (%remove-waiter wait-list input)
          302   (setf (wait-list-waiters wait-list)
          303         (remove input (wait-list-waiters wait-list))
          304         (wait-list input) nil)
          305   (remhash (socket input) (wait-list-map wait-list)))
          306 
          307 (defun remove-all-waiters (wait-list)
          308   (dolist (waiter (wait-list-waiters wait-list))
          309     (%remove-waiter wait-list waiter))
          310   (setf (wait-list-waiters wait-list) nil)
          311   (clrhash (wait-list-map wait-list)))
          312 
          313 (defun wait-for-input (socket-or-sockets &key timeout ready-only)
          314   "Waits for one or more streams to become ready for reading from
          315 the socket.  When `timeout' (a non-negative real number) is
          316 specified, wait `timeout' seconds, or wait indefinitely when
          317 it isn't specified.  A `timeout' value of 0 (zero) means polling.
          318 
          319 Returns two values: the first value is the list of streams which
          320 are readable (or in case of server streams acceptable).  NIL may
          321 be returned for this value either when waiting timed out or when
          322 it was interrupted (EINTR).  The second value is a real number
          323 indicating the time remaining within the timeout period or NIL if
          324 none.
          325 
          326 Without the READY-ONLY arg, WAIT-FOR-INPUT will return all sockets in
          327 the original list you passed it. This prevents a new list from being
          328 consed up. Some users of USOCKET were reluctant to use it if it
          329 wouldn't behave that way, expecting it to cost significant performance
          330 to do the associated garbage collection.
          331 
          332 Without the READY-ONLY arg, you need to check the socket STATE slot for
          333 the values documented in usocket.lisp in the usocket class."
          334 
          335   ;; for NULL sockets, return NIL with respect of TIMEOUT.
          336   (when (null socket-or-sockets)
          337     (when timeout
          338       (sleep timeout))
          339     (return-from wait-for-input nil))
          340 
          341   (unless (wait-list-p socket-or-sockets)
          342     (let ((wl (make-wait-list (if (listp socket-or-sockets)
          343                                   socket-or-sockets (list socket-or-sockets)))))
          344       (multiple-value-bind
          345             (socks to)
          346           (wait-for-input wl :timeout timeout :ready-only ready-only)
          347         ;; NOTE: in case waiter is not created by the user, it should be removed here.
          348         (remove-all-waiters wl)
          349         (return-from wait-for-input
          350           (values (if ready-only socks socket-or-sockets) to)))))
          351   (let* ((start (get-internal-real-time))
          352          (sockets-ready 0))
          353     (dolist (x (wait-list-waiters socket-or-sockets))
          354       (when (setf (state x)
          355                   #+(and win32 (or sbcl ecl)) nil ; they cannot rely on LISTEN
          356                   #-(and win32 (or sbcl ecl))
          357                   (if (and (stream-usocket-p x)
          358                            (listen (socket-stream x)))
          359                       :read
          360                       nil))
          361         (incf sockets-ready)))
          362     ;; the internal routine is responsibe for
          363     ;; making sure the wait doesn't block on socket-streams of
          364     ;; which theready- socket isn't ready, but there's space left in the
          365     ;; buffer
          366     (wait-for-input-internal socket-or-sockets
          367                              :timeout (if (zerop sockets-ready) timeout 0))
          368     (let ((to-result (when timeout
          369                        (let ((elapsed (/ (- (get-internal-real-time) start)
          370                                          internal-time-units-per-second)))
          371                          (when (< elapsed timeout)
          372                            (- timeout elapsed))))))
          373       (values (if ready-only
          374                   (remove-if #'null (wait-list-waiters socket-or-sockets) :key #'state)
          375                   socket-or-sockets)
          376               to-result))))
          377 
          378 ;;
          379 ;; Data utility functions
          380 ;;
          381 
          382 (defun integer-to-octet-buffer (integer buffer octets &key (start 0))
          383   (do ((b start (1+ b))
          384        (i (ash (1- octets) 3) ;; * 8
          385           (- i 8)))
          386       ((> 0 i) buffer)
          387     (setf (aref buffer b)
          388           (ldb (byte 8 i) integer))))
          389 
          390 (defun octet-buffer-to-integer (buffer octets &key (start 0))
          391   (let ((integer 0))
          392     (do ((b start (1+ b))
          393          (i (ash (1- octets) 3) ;; * 8
          394             (- i 8)))
          395         ((> 0 i)
          396          integer)
          397       (setf (ldb (byte 8 i) integer)
          398             (aref buffer b)))))
          399 
          400 (defmacro port-to-octet-buffer (port buffer &key (start 0))
          401   `(integer-to-octet-buffer ,port ,buffer 2 :start ,start))
          402 
          403 (defmacro ip-to-octet-buffer (ip buffer &key (start 0))
          404   `(integer-to-octet-buffer (host-byte-order ,ip) ,buffer 4 :start ,start))
          405 
          406 (defmacro port-from-octet-buffer (buffer &key (start 0))
          407   `(octet-buffer-to-integer ,buffer 2 :start ,start))
          408 
          409 (defmacro ip-from-octet-buffer (buffer &key (start 0))
          410   `(octet-buffer-to-integer ,buffer 4 :start ,start))
          411 
          412 ;;
          413 ;; IPv4 utility functions
          414 ;;
          415 
          416 (defun list-of-strings-to-integers (list)
          417   "Take a list of strings and return a new list of integers (from
          418 parse-integer) on each of the string elements."
          419   (let ((new-list nil))
          420     (dolist (element (reverse list))
          421       (push (parse-integer element) new-list))
          422     new-list))
          423 
          424 (defun ip-address-string-p (string)
          425   "Return a true value if the given string could be an IP address."
          426   (every (lambda (char)
          427            (or (digit-char-p char)
          428                (eql char #\.)))
          429          string))
          430 
          431 (defun hbo-to-dotted-quad (integer) ; exported
          432   "Host-byte-order integer to dotted-quad string conversion utility."
          433   (let ((first (ldb (byte 8 24) integer))
          434         (second (ldb (byte 8 16) integer))
          435         (third (ldb (byte 8 8) integer))
          436         (fourth (ldb (byte 8 0) integer)))
          437     (format nil "~A.~A.~A.~A" first second third fourth)))
          438 
          439 (defun hbo-to-vector-quad (integer) ; exported
          440   "Host-byte-order integer to dotted-quad string conversion utility."
          441   (let ((first (ldb (byte 8 24) integer))
          442         (second (ldb (byte 8 16) integer))
          443         (third (ldb (byte 8 8) integer))
          444         (fourth (ldb (byte 8 0) integer)))
          445     (vector first second third fourth)))
          446 
          447 (defun vector-quad-to-dotted-quad (vector) ; exported
          448   (format nil "~A.~A.~A.~A"
          449           (aref vector 0)
          450           (aref vector 1)
          451           (aref vector 2)
          452           (aref vector 3)))
          453 
          454 (defun dotted-quad-to-vector-quad (string) ; exported
          455   (let ((list (list-of-strings-to-integers (split-sequence #\. string))))
          456     (vector (first list) (second list) (third list) (fourth list))))
          457 
          458 (defgeneric host-byte-order (address)) ; exported
          459 
          460 (defmethod host-byte-order ((string string))
          461   "Convert a string, such as 192.168.1.1, to host-byte-order,
          462 such as 3232235777."
          463   (let ((list (list-of-strings-to-integers (split-sequence #\. string))))
          464     (+ (* (first list) 256 256 256) (* (second list) 256 256)
          465        (* (third list) 256) (fourth list))))
          466 
          467 (defmethod host-byte-order ((vector vector)) ; IPv4 only
          468   "Convert a vector, such as #(192 168 1 1), to host-byte-order, such as
          469 3232235777."
          470   (+ (* (aref vector 0) 256 256 256) (* (aref vector 1) 256 256)
          471      (* (aref vector 2) 256) (aref vector 3)))
          472 
          473 (defmethod host-byte-order ((int integer))
          474   int) ; this assume input integer is already host-byte-order
          475 
          476 ;;
          477 ;; IPv6 utility functions
          478 ;;
          479 
          480 (defun vector-to-ipv6-host (vector) ; exported
          481   (with-output-to-string (*standard-output*)
          482     (loop with zeros-collapsed-p
          483           with collapsing-zeros-p
          484           for i below 16 by 2
          485           for word = (+ (ash (aref vector i) 8)
          486                         (aref vector (1+ i)))
          487           do (cond
          488                ((and (zerop word)
          489                      (not collapsing-zeros-p)
          490                      (not zeros-collapsed-p))
          491                 (setf collapsing-zeros-p t))
          492                ((or (not (zerop word))
          493                     zeros-collapsed-p)
          494                 (when collapsing-zeros-p
          495                   (write-string ":")
          496                   (setf collapsing-zeros-p nil
          497                         zeros-collapsed-p t))
          498                 (format t "~:[~;:~]~X" (plusp i) word)))
          499           finally (when collapsing-zeros-p
          500                     (write-string "::")))))
          501 
          502 (defun split-ipv6-address (string)
          503   (let ((pos 0)
          504         word
          505         double-colon-seen-p
          506         words-before-double-colon
          507         words-after-double-colon)
          508     (loop
          509       (multiple-value-setq (word pos) (parse-integer string :radix 16 :junk-allowed t :start pos))
          510       (labels ((at-end-p ()
          511                  (= pos (length string)))
          512                (looking-at-colon-p ()
          513                  (char= (char string pos) #\:))
          514                (ensure-colon ()
          515                  (unless (looking-at-colon-p)
          516                    (error "unsyntactic IPv6 address string ~S, expected a colon at position ~D"
          517                           string pos))
          518                  (incf pos)))
          519         (cond
          520           ((null word)
          521            (when double-colon-seen-p
          522              (error "unsyntactic IPv6 address string ~S, can only have one double-colon filler mark"
          523                     string))
          524            (setf double-colon-seen-p t))
          525           (double-colon-seen-p
          526            (push word words-after-double-colon))
          527           (t
          528            (push word words-before-double-colon)))
          529         (if (at-end-p)
          530             (return (list (nreverse words-before-double-colon) (nreverse words-after-double-colon)))
          531             (ensure-colon))))))
          532 
          533 (defun ipv6-host-to-vector (string) ; exported
          534   (assert (> (length string) 2) ()
          535           "Unsyntactic IPv6 address literal ~S, expected at least three characters" string)
          536   (destructuring-bind (words-before-double-colon words-after-double-colon)
          537       (split-ipv6-address (concatenate 'string
          538                                        (when (eql (char string 0) #\:)
          539                                          "0")
          540                                        string
          541                                        (when (eql (char string (1- (length string))) #\:)
          542                                          "0")))
          543     (let ((number-of-words-specified (+ (length words-before-double-colon) (length words-after-double-colon))))
          544       (assert (<= number-of-words-specified 8) ()
          545               "Unsyntactic IPv6 address literal ~S, too many colon separated address components" string)
          546       (assert (or (= number-of-words-specified 8) words-after-double-colon) ()
          547               "Unsyntactic IPv6 address literal ~S, too few address components and no double-colon filler found" string)
          548       (loop with vector = (make-array 16 :element-type '(unsigned-byte 8))
          549             for i below 16 by 2
          550             for word in (append words-before-double-colon
          551                                 (make-list (- 8 number-of-words-specified) :initial-element 0)
          552                                 words-after-double-colon)
          553             do (setf (aref vector i) (ldb (byte 8 8) word)
          554                      (aref vector (1+ i)) (ldb (byte 8 0) word))
          555             finally (return vector)))))
          556 
          557 (defun host-to-hostname (host) ; host -> string
          558   "Translate a string, vector quad or 16 byte IPv6 address to a
          559 stringified hostname."
          560   (etypecase host
          561     (string host)      ; IPv4 or IPv6
          562     ((or (vector t 4)  ; IPv4
          563          (array (unsigned-byte 8) (4)))
          564      (vector-quad-to-dotted-quad host))
          565     ((or (vector t 16) ; IPv6
          566          (array (unsigned-byte 8) (16)))
          567      (vector-to-ipv6-host host))
          568     (integer (hbo-to-dotted-quad host)) ; integer input is IPv4 only
          569     (null "0.0.0.0")))                  ; null is IPv4
          570 
          571 (defun ip= (ip1 ip2) ; exported
          572   (etypecase ip1
          573     (string (string= ip1                  ; IPv4 or IPv6
          574                      (host-to-hostname ip2)))
          575     ((or (vector t 4)                     ; IPv4
          576          (array (unsigned-byte 8) (4))    ; IPv4
          577          (vector t 16)                    ; IPv6
          578          (array (unsigned-byte 8) (16)))  ; IPv6
          579      (equalp ip1 ip2))
          580     (integer (= ip1                       ; IPv4 only
          581                 (host-byte-order ip2))))) ; convert ip2 to integer (hbo)
          582 
          583 (defun ip/= (ip1 ip2) ; exported
          584   (not (ip= ip1 ip2)))
          585 
          586 ;;
          587 ;; DNS helper functions
          588 ;;
          589 
          590 (defun get-host-by-name (name)
          591   "0.7.1+: if there're IPv4 addresses, return the first IPv4 address."
          592   (let* ((hosts (get-hosts-by-name name))
          593          (pos (position-if #'(lambda (ip) (= 4 (length ip))) hosts)))
          594     (if pos (elt hosts pos)
          595       (car hosts))))
          596 
          597 (defun get-random-host-by-name (name)
          598   "0.7.1+: if there're IPv4 addresses, only return a random IPv4 address."
          599   (let* ((hosts (get-hosts-by-name name))
          600          (ipv4-hosts (remove-if-not #'(lambda (ip) (= 4 (length ip))) hosts)))
          601     (cond (ipv4-hosts
          602            (elt ipv4-hosts (random (length ipv4-hosts))))
          603           (hosts
          604            (elt hosts (random (length hosts)))))))
          605 
          606 (defun host-to-vector-quad (host) ; internal
          607   "Translate a host specification (vector quad, dotted quad or domain name)
          608 to a vector quad."
          609   (etypecase host
          610     (string (let* ((ip (when (ip-address-string-p host)
          611                          (dotted-quad-to-vector-quad host))))
          612               (if (and ip (= 4 (length ip)))
          613                   ;; valid IP dotted quad? not sure
          614                   ip
          615                 (get-random-host-by-name host))))
          616     ((or (vector t 4)
          617          (array (unsigned-byte 8) (4)))
          618      host)
          619     (integer (hbo-to-vector-quad host))))
          620 
          621 (defun host-to-hbo (host) ; internal
          622   (etypecase host
          623     (string (let ((ip (when (ip-address-string-p host)
          624                         (dotted-quad-to-vector-quad host))))
          625               (if (and ip (= 4 (length ip)))
          626                   (host-byte-order ip)
          627                   (host-to-hbo (get-host-by-name host)))))
          628     ((or (vector t 4)
          629          (array (unsigned-byte 8) (4)))
          630      (host-byte-order host))
          631     (integer host)))
          632 
          633 ;;
          634 ;; Other utility functions
          635 ;;
          636 
          637 (defun split-timeout (timeout &optional (fractional 1000000))
          638   "Split real value timeout into seconds and microseconds.
          639 Optionally, a different fractional part can be specified."
          640   (multiple-value-bind
          641       (secs sec-frac)
          642       (truncate timeout 1)
          643     (values secs
          644             (truncate (* fractional sec-frac) 1))))
          645 
          646 ;;
          647 ;; Setting of documentation for backend defined functions
          648 ;;
          649 
          650 ;; Documentation for the function
          651 ;;
          652 ;; (defun SOCKET-CONNECT (host port &key element-type nodelay some-other-keys...) ..)
          653 ;;
          654 (setf (documentation 'socket-connect 'function)
          655       "Connect to `host' on `port'.  `host' is assumed to be a string or
          656 an IP address represented in vector notation, such as #(192 168 1 1).
          657 `port' is assumed to be an integer.
          658 
          659 `element-type' specifies the element type to use when constructing the
          660 stream associated with the socket.  The default is 'character.
          661 
          662 `nodelay' Allows to disable/enable Nagle's algorithm (http://en.wikipedia.org/wiki/Nagle%27s_algorithm).
          663 If this parameter is omitted, the behaviour is inherited from the
          664 CL implementation (in most cases, Nagle's algorithm is
          665 enabled by default, but for example in ACL it is disabled).
          666 If the parameter is specified, one of these three values is possible:
          667   T - Disable Nagle's algorithm; signals an UNSUPPORTED
          668       condition if the implementation does not support explicit
          669       manipulation with that option.
          670   NIL - Leave Nagle's algorithm enabled on the socket;
          671       signals an UNSUPPORTED condition if the implementation does
          672       not support explicit manipulation with that option.
          673   :IF-SUPPORTED - Disables Nagle's algorithm if the implementation
          674       allows this, otherwises just ignore this option.
          675 
          676 Returns a usocket object.")
          677 
          678 ;; Documentation for the function
          679 ;;
          680 ;; (defun SOCKET-LISTEN (host port &key reuseaddress backlog element-type) ..)
          681 ;;###FIXME: extend with default-element-type
          682 (setf (documentation 'socket-listen 'function)
          683       "Bind to interface `host' on `port'. `host' should be the
          684 representation of an ready-interface address.  The implementation is
          685 not required to do an address lookup, making no guarantees that
          686 hostnames will be correctly resolved.  If `*wildcard-host*' or NIL is
          687 passed for `host', the socket will be bound to all available
          688 interfaces for the system.  `port' can be selected by the IP stack by
          689 passing `*auto-port*'.
          690 
          691 Returns an object of type `stream-server-usocket'.
          692 
          693 `reuse-address' and `backlog' are advisory parameters for setting socket
          694 options at creation time. `element-type' is the element type of the
          695 streams to be created by `socket-accept'.  `reuseaddress' is supported for
          696 backward compatibility (but deprecated); when both `reuseaddress' and
          697 `reuse-address' have been specified, the latter takes precedence.
          698 ")
          699 
          700 ;;; Small utility functions mapping true/false to 1/0, moved here from option.lisp
          701 
          702 (proclaim '(inline bool->int int->bool))
          703 
          704 (defun bool->int (bool) (if bool 1 0))
          705 (defun int->bool (int) (= 1 int))