tcmucl.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
       ---
       tcmucl.lisp (11174B)
       ---
            1 ;;;; See LICENSE for licensing information.
            2 
            3 (in-package :usocket)
            4 
            5 #+win32
            6 (defun remap-for-win32 (z)
            7   (mapcar #'(lambda (x)
            8               (cons (mapcar #'(lambda (y)
            9                                 (+ 10000 y))
           10                             (car x))
           11                     (cdr x)))
           12           z))
           13 
           14 (defparameter +cmucl-error-map+
           15   #+win32
           16   (append (remap-for-win32 +unix-errno-condition-map+)
           17           (remap-for-win32 +unix-errno-error-map+))
           18   #-win32
           19   (append +unix-errno-condition-map+
           20           +unix-errno-error-map+))
           21 
           22 (defun cmucl-map-socket-error (err &key condition socket)
           23   (let ((usock-err
           24          (cdr (assoc err +cmucl-error-map+ :test #'member))))
           25     (if usock-err
           26         (if (subtypep usock-err 'error)
           27             (error usock-err :socket socket)
           28           (signal usock-err :socket socket))
           29       (error 'unknown-error
           30              :socket socket
           31              :real-error condition))))
           32 
           33 ;; CMUCL error handling is brain-dead: it doesn't preserve any
           34 ;; information other than the OS error string from which the
           35 ;; error can be determined. The OS error string isn't good enough
           36 ;; given that it may have been localized (l10n).
           37 ;;
           38 ;; The above applies to versions pre 19b; 19d and newer are expected to
           39 ;; contain even better error reporting.
           40 ;;
           41 ;;
           42 ;; Just catch the errors and encapsulate them in an unknown-error
           43 (defun handle-condition (condition &optional (socket nil))
           44   "Dispatch correct usocket condition."
           45   (typecase condition
           46     (ext::socket-error (cmucl-map-socket-error (ext::socket-errno condition)
           47                                                :socket socket
           48                                                :condition condition))))
           49 
           50 (defun socket-connect (host port &key (protocol :stream) (element-type 'character)
           51                        timeout deadline (nodelay t nodelay-specified)
           52                        (local-host nil local-host-p)
           53                        (local-port nil local-port-p)
           54                        &aux
           55                        (local-bind-p (fboundp 'ext::bind-inet-socket)))
           56   (when timeout (unsupported 'timeout 'socket-connect))
           57   (when deadline (unsupported 'deadline 'socket-connect))
           58   (when (and nodelay-specified 
           59              (not (eq nodelay :if-supported)))
           60     (unsupported 'nodelay 'socket-connect))
           61   (when (and local-host-p (not local-bind-p))
           62      (unsupported 'local-host 'socket-connect :minimum "Snapshot 2008-08 (19E)"))
           63   (when (and local-port-p (not local-bind-p))
           64      (unsupported 'local-port 'socket-connect :minimum "Snapshot 2008-08 (19E)"))
           65 
           66   (let ((socket))
           67     (ecase protocol
           68       (:stream
           69        (setf socket
           70              (let ((args (list (host-to-hbo host) port protocol)))
           71                (when (and local-bind-p (or local-host-p local-port-p))
           72                  (nconc args (list :local-host (when local-host
           73                                                  (host-to-hbo local-host))
           74                                    :local-port local-port)))
           75                (with-mapped-conditions (socket)
           76                  (apply #'ext:connect-to-inet-socket args))))
           77        (if socket
           78            (let* ((stream (sys:make-fd-stream socket :input t :output t
           79                                               :element-type element-type
           80                                               :buffering :full))
           81                   ;;###FIXME the above line probably needs an :external-format
           82                   (usocket (make-stream-socket :socket socket
           83                                                :stream stream)))
           84              usocket)
           85            (let ((err (unix:unix-errno)))
           86              (when err (cmucl-map-socket-error err)))))
           87       (:datagram
           88        (setf socket
           89              (if (and host port)
           90                  (let ((args (list (host-to-hbo host) port protocol)))
           91                    (when (and local-bind-p (or local-host-p local-port-p))
           92                      (nconc args (list :local-host (when local-host
           93                                                      (host-to-hbo local-host))
           94                                        :local-port local-port)))
           95                    (with-mapped-conditions (socket)
           96                      (apply #'ext:connect-to-inet-socket args)))
           97                  (if (or local-host-p local-port-p)
           98                      (with-mapped-conditions (socket)
           99                        (apply #'ext:create-inet-listener
          100                               (nconc (list (or local-port 0) protocol)
          101                                      (when (and local-host-p
          102                                                 (ip/= local-host *wildcard-host*))
          103                                        (list :host (host-to-hbo local-host))))))
          104                      (with-mapped-conditions (socket)
          105                        (ext:create-inet-socket protocol)))))
          106        (if socket
          107            (let ((usocket (make-datagram-socket socket :connected-p (and host port t))))
          108              (ext:finalize usocket #'(lambda () (when (%open-p usocket)
          109                                                   (ext:close-socket socket))))
          110              usocket)
          111            (let ((err (unix:unix-errno)))
          112              (when err (cmucl-map-socket-error err))))))))
          113 
          114 (defun socket-listen (host port
          115                            &key reuseaddress
          116                            (reuse-address nil reuse-address-supplied-p)
          117                            (backlog 5)
          118                            (element-type 'character))
          119  (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
          120         (server-sock
          121          (with-mapped-conditions ()
          122            (apply #'ext:create-inet-listener
          123                   (nconc  (list port :stream
          124                                 :backlog backlog
          125                                 :reuse-address reuseaddress)
          126                           (when (ip/= host *wildcard-host*)
          127                             (list :host
          128                                   (host-to-hbo host))))))))
          129    (make-stream-server-socket server-sock :element-type element-type)))
          130 
          131 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
          132   (with-mapped-conditions (usocket)
          133     (let* ((sock (ext:accept-tcp-connection (socket usocket)))
          134            (stream (sys:make-fd-stream sock :input t :output t
          135                                        :element-type (or element-type
          136                                                          (element-type usocket))
          137                                        :buffering :full)))
          138       (make-stream-socket :socket sock :stream stream))))
          139 
          140 ;; Sockets and socket streams are represented
          141 ;; by different objects. Be sure to close the
          142 ;; socket stream when closing a stream socket.
          143 (defmethod socket-close ((usocket stream-usocket))
          144   "Close socket."
          145   (when (wait-list usocket)
          146      (remove-waiter (wait-list usocket) usocket))
          147   (with-mapped-conditions (usocket)
          148     (close (socket-stream usocket))))
          149 
          150 (defmethod socket-close ((usocket usocket))
          151   "Close socket."
          152   (when (wait-list usocket)
          153      (remove-waiter (wait-list usocket) usocket))
          154   (with-mapped-conditions (usocket)
          155     (ext:close-socket (socket usocket))))
          156 
          157 (defmethod socket-close :after ((socket datagram-usocket))
          158   (setf (%open-p socket) nil))
          159 
          160 #+unicode
          161 (defun %unix-send (fd buffer length flags)
          162   (alien:alien-funcall
          163    (alien:extern-alien "send"
          164                        (function c-call:int
          165                                  c-call:int
          166                                  system:system-area-pointer
          167                                  c-call:int
          168                                  c-call:int))
          169    fd
          170    (system:vector-sap buffer)
          171    length
          172    flags))
          173 
          174 (defmethod socket-shutdown ((usocket usocket) direction)
          175   (with-mapped-conditions (usocket)
          176     (ext:inet-shutdown (socket usocket) (ecase direction
          177                                           (:input ext:shut-rd)
          178                                           (:output ext:shut-wr)))))
          179 
          180 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)
          181                         &aux (real-buffer (if (zerop offset)
          182                                               buffer
          183                                               (subseq buffer offset (+ offset size)))))
          184   (with-mapped-conditions (usocket)
          185     (if (and host port)
          186         (ext:inet-sendto (socket usocket) real-buffer size (host-to-hbo host) port)
          187         #-unicode
          188         (unix:unix-send (socket usocket) real-buffer size 0)
          189         #+unicode
          190         (%unix-send (socket usocket) real-buffer size 0))))
          191 
          192 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
          193   (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
          194                    (integer 0)                          ; size
          195                    (unsigned-byte 32)                   ; host
          196                    (unsigned-byte 16)))                 ; port
          197   (let ((real-buffer (or buffer
          198                          (make-array length :element-type '(unsigned-byte 8))))
          199         (real-length (or length
          200                          (length buffer))))
          201     (multiple-value-bind (nbytes remote-host remote-port)
          202         (with-mapped-conditions (usocket)
          203           (ext:inet-recvfrom (socket usocket) real-buffer real-length))
          204       (values real-buffer nbytes remote-host remote-port))))
          205 
          206 (defmethod get-local-name ((usocket usocket))
          207   (multiple-value-bind
          208       (address port)
          209       (ext:get-socket-host-and-port (socket usocket))
          210     (values (hbo-to-vector-quad address) port)))
          211 
          212 (defmethod get-peer-name ((usocket stream-usocket))
          213   (multiple-value-bind
          214       (address port)
          215       (ext:get-peer-host-and-port (socket usocket))
          216     (values (hbo-to-vector-quad address) port)))
          217 
          218 (defmethod get-local-address ((usocket usocket))
          219   (nth-value 0 (get-local-name usocket)))
          220 
          221 (defmethod get-peer-address ((usocket stream-usocket))
          222   (nth-value 0 (get-peer-name usocket)))
          223 
          224 (defmethod get-local-port ((usocket usocket))
          225   (nth-value 1 (get-local-name usocket)))
          226 
          227 (defmethod get-peer-port ((usocket stream-usocket))
          228   (nth-value 1 (get-peer-name usocket)))
          229 
          230 
          231 (defun lookup-host-entry (host)
          232   (multiple-value-bind
          233       (entry errno)
          234       (ext:lookup-host-entry host)
          235     (if entry
          236         entry
          237       ;;###The constants below work on *most* OSes, but are defined as the
          238       ;; constants mentioned in C
          239       (let ((exception
          240              (second (assoc errno
          241                             '((1 ns-host-not-found-error)     ;; HOST_NOT_FOUND
          242                               (2 ns-no-recovery-error)        ;; NO_DATA
          243                               (3 ns-no-recovery-error)        ;; NO_RECOVERY
          244                               (4 ns-try-again-condition)))))) ;; TRY_AGAIN
          245         (when exception
          246           (error exception))))))
          247 
          248 
          249 (defun get-host-by-address (address)
          250   (handler-case (ext:host-entry-name
          251                  (lookup-host-entry (host-byte-order address)))
          252     (condition (condition) (handle-condition condition))))
          253 
          254 (defun get-hosts-by-name (name)
          255   (handler-case (mapcar #'hbo-to-vector-quad
          256                         (ext:host-entry-addr-list
          257                          (lookup-host-entry name)))
          258     (condition (condition) (handle-condition condition))))
          259 
          260 (defun get-host-name ()
          261   (unix:unix-gethostname))
          262 
          263 (defun %setup-wait-list (wait-list)
          264   (declare (ignore wait-list)))
          265 
          266 (defun %add-waiter (wait-list waiter)
          267   (push (socket waiter) (wait-list-%wait wait-list)))
          268 
          269 (defun %remove-waiter (wait-list waiter)
          270   (setf (wait-list-%wait wait-list)
          271         (remove (socket waiter) (wait-list-%wait wait-list))))
          272 
          273 (defun wait-for-input-internal (wait-list &key timeout)
          274   (with-mapped-conditions ()
          275     (alien:with-alien ((rfds (alien:struct unix:fd-set)))
          276        (unix:fd-zero rfds)
          277        (dolist (socket (wait-list-%wait wait-list))
          278          (unix:fd-set socket rfds))
          279        (multiple-value-bind
          280            (secs musecs)
          281            (split-timeout (or timeout 1))
          282          (multiple-value-bind (count err)
          283              (unix:unix-fast-select (1+ (reduce #'max
          284                                                 (wait-list-%wait wait-list)))
          285                                     (alien:addr rfds) nil nil
          286                                     (when timeout secs) musecs)
          287            (declare (ignore err))
          288            (if (<= 0 count)
          289                ;; process the result...
          290                (dolist (x (wait-list-waiters wait-list))
          291                  (when (unix:fd-isset (socket x) rfds)
          292                    (setf (state x) :READ)))
          293                (progn
          294                  ;;###FIXME generate an error, except for EINTR
          295                  )))))))