tscl.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
       ---
       tscl.lisp (10164B)
       ---
            1 ;;;; See LICENSE for licensing information.
            2 
            3 (in-package :usocket)
            4 
            5 (defparameter +scl-error-map+
            6   (append +unix-errno-condition-map+
            7           +unix-errno-error-map+))
            8 
            9 (defun scl-map-socket-error (err &key condition socket)
           10   (let ((usock-err (cdr (assoc err +scl-error-map+ :test #'member))))
           11     (cond (usock-err
           12        (if (subtypep usock-err 'error)
           13            (error usock-err :socket socket)
           14            (signal usock-err :socket socket)))
           15       (t
           16        (error 'unknown-error
           17           :socket socket
           18           :real-error condition)))))
           19 
           20 (defun handle-condition (condition &optional (socket nil))
           21   "Dispatch correct usocket condition."
           22   (typecase condition
           23     (ext::socket-error
           24      (scl-map-socket-error (ext::socket-errno condition)
           25                            :socket socket
           26                            :condition condition))))
           27 
           28 (defun socket-connect (host port &key (protocol :stream) (element-type 'character)
           29                        timeout deadline (nodelay t nodelay-specified)
           30                        (local-host nil local-host-p)
           31                        (local-port nil local-port-p)
           32                        &aux
           33                        (patch-udp-p (fboundp 'ext::inet-socket-send-to)))
           34   (when (and nodelay-specified 
           35              (not (eq nodelay :if-supported)))
           36     (unsupported 'nodelay 'socket-connect))
           37   (when deadline (unsupported 'deadline 'socket-connect))
           38   (when timeout (unsupported 'timeout 'socket-connect))
           39   (when (and local-host-p (not patch-udp-p))
           40      (unsupported 'local-host 'socket-connect :minimum "1.3.9"))
           41   (when (and local-port-p (not patch-udp-p))
           42      (unsupported 'local-port 'socket-connect :minimum "1.3.9"))
           43 
           44   (let ((socket))
           45     (ecase protocol
           46       (:stream
           47        (setf socket (let ((args (list (host-to-hbo host) port :kind protocol)))
           48                       (when (and patch-udp-p (or local-host-p local-port-p))
           49                         (nconc args (list :local-host (when local-host
           50                                                         (host-to-hbo local-host))
           51                                           :local-port local-port)))
           52                       (with-mapped-conditions (socket)
           53                         (apply #'ext:connect-to-inet-socket args))))
           54        (let ((stream (sys:make-fd-stream socket :input t :output t
           55                                          :element-type element-type
           56                                          :buffering :full)))
           57          (make-stream-socket :socket socket :stream stream)))
           58       (:datagram
           59        (when (not patch-udp-p)
           60          (error 'unsupported
           61                 :feature '(protocol :datagram)
           62                 :context 'socket-connect
           63                 :minumum "1.3.9"))
           64        (setf socket
           65              (if (and host port)
           66                  (let ((args (list (host-to-hbo host) port :kind protocol)))
           67                    (when (and patch-udp-p (or local-host-p local-port-p))
           68                      (nconc args (list :local-host (when local-host
           69                                                      (host-to-hbo local-host))
           70                                        :local-port local-port)))
           71                    (with-mapped-conditions (socket)
           72                      (apply #'ext:connect-to-inet-socket args)))
           73                  (if (or local-host-p local-port-p)
           74                      (with-mapped-conditions ()
           75                        (ext:create-inet-listener (or local-port 0)
           76                                                  protocol
           77                                                  :host (when local-host
           78                                                          (if (ip= local-host *wildcard-host*)
           79                                                              0
           80                                                              (host-to-hbo local-host)))))
           81                      (with-mapped-conditions ()
           82                        (ext:create-inet-socket protocol)))))
           83        (let ((usocket (make-datagram-socket socket :connected-p (and host port t))))
           84          (ext:finalize usocket #'(lambda ()
           85                                    (when (%open-p usocket)
           86                                      (ext:close-socket socket))))
           87          usocket)))))
           88 
           89 (defun socket-listen (host port
           90                            &key reuseaddress
           91                            (reuse-address nil reuse-address-supplied-p)
           92                            (backlog 5)
           93                            (element-type 'character))
           94   (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
           95          (host (if (ip= host *wildcard-host*)
           96                    0
           97                  (host-to-hbo host)))
           98          (server-sock
           99           (with-mapped-conditions ()
          100             (ext:create-inet-listener port :stream
          101                                       :host host
          102                                       :reuse-address reuseaddress
          103                                       :backlog backlog))))
          104    (make-stream-server-socket server-sock :element-type element-type)))
          105 
          106 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
          107   (with-mapped-conditions (usocket)
          108     (let* ((sock (ext:accept-tcp-connection (socket usocket)))
          109            (stream (sys:make-fd-stream sock :input t :output t
          110                                       :element-type (or element-type
          111                                                         (element-type usocket))
          112                                       :buffering :full)))
          113       (make-stream-socket :socket sock :stream stream))))
          114 
          115 ;; Sockets and their associated streams are modelled as
          116 ;; different objects. Be sure to close the socket stream
          117 ;; when closing stream-sockets; it makes sure buffers
          118 ;; are flushed and the socket is closed correctly afterwards.
          119 (defmethod socket-close ((usocket usocket))
          120   "Close socket."
          121   (when (wait-list usocket)
          122      (remove-waiter (wait-list usocket) usocket))
          123   (with-mapped-conditions (usocket)
          124     (ext:close-socket (socket usocket))))
          125 
          126 (defmethod socket-close ((usocket stream-usocket))
          127   "Close socket."
          128   (when (wait-list usocket)
          129      (remove-waiter (wait-list usocket) usocket))
          130   (with-mapped-conditions (usocket)
          131     (close (socket-stream usocket))))
          132 
          133 (defmethod socket-close :after ((socket datagram-usocket))
          134   (setf (%open-p socket) nil))
          135 
          136 (defmethod socket-shutdown ((usocket usocket) direction)
          137   (declare (ignore usocket direction))
          138   (unsupported "shutdown" 'socket-shutdown))
          139 
          140 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port)
          141   (let ((s (socket usocket))
          142         (host (if host (host-to-hbo host)))
          143         (real-buffer (if (zerop offset)
          144                          buffer
          145                          (subseq buffer offset (+ offset size)))))
          146     (multiple-value-bind (result errno)
          147         (ext:inet-socket-send-to s real-buffer size
          148                                  :remote-host host :remote-port port)
          149       (or result
          150           (scl-map-socket-error errno :socket usocket)))))
          151 
          152 (defmethod socket-receive ((socket datagram-usocket) buffer length &key)
          153   (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
          154                    (integer 0)                          ; size
          155                    (unsigned-byte 32)                   ; host
          156                    (unsigned-byte 16)))                 ; port
          157   (let ((s (socket socket)))
          158     (let ((real-buffer (or buffer
          159                            (make-array length :element-type '(unsigned-byte 8))))
          160           (real-length (or length
          161                            (length buffer))))
          162       (multiple-value-bind (result errno remote-host remote-port)
          163           (ext:inet-socket-receive-from s real-buffer real-length)
          164         (if result
          165             (values real-buffer result remote-host remote-port)
          166             (scl-map-socket-error errno :socket socket))))))
          167 
          168 (defmethod get-local-name ((usocket usocket))
          169   (multiple-value-bind (address port)
          170       (with-mapped-conditions (usocket)
          171         (ext:get-socket-host-and-port (socket usocket)))
          172     (values (hbo-to-vector-quad address) port)))
          173 
          174 (defmethod get-peer-name ((usocket stream-usocket))
          175   (multiple-value-bind (address port)
          176       (with-mapped-conditions (usocket)
          177         (ext:get-peer-host-and-port (socket usocket)))
          178     (values (hbo-to-vector-quad address) port)))
          179 
          180 (defmethod get-local-address ((usocket usocket))
          181   (nth-value 0 (get-local-name usocket)))
          182 
          183 (defmethod get-peer-address ((usocket stream-usocket))
          184   (nth-value 0 (get-peer-name usocket)))
          185 
          186 (defmethod get-local-port ((usocket usocket))
          187   (nth-value 1 (get-local-name usocket)))
          188 
          189 (defmethod get-peer-port ((usocket stream-usocket))
          190   (nth-value 1 (get-peer-name usocket)))
          191 
          192 
          193 (defun get-host-by-address (address)
          194   (multiple-value-bind (host errno)
          195       (ext:lookup-host-entry (host-byte-order address))
          196     (cond (host
          197            (ext:host-entry-name host))
          198           (t
          199            (let ((condition (cdr (assoc errno +unix-ns-error-map+))))
          200              (cond (condition
          201                     (error condition :host-or-ip address))
          202                    (t
          203                     (error 'ns-unknown-error :host-or-ip address
          204                            :real-error errno))))))))
          205 
          206 (defun get-hosts-by-name (name)
          207   (multiple-value-bind (host errno)
          208       (ext:lookup-host-entry name)
          209     (cond (host
          210            (mapcar #'hbo-to-vector-quad
          211                    (ext:host-entry-addr-list host)))
          212           (t
          213            (let ((condition (cdr (assoc errno +unix-ns-error-map+))))
          214              (cond (condition
          215                     (error condition :host-or-ip name))
          216                    (t
          217                     (error 'ns-unknown-error :host-or-ip name
          218                            :real-error errno))))))))
          219 
          220 (defun get-host-name ()
          221   (unix:unix-gethostname))
          222 
          223 
          224 ;;
          225 ;;
          226 ;;  WAIT-LIST part
          227 ;;
          228 
          229 
          230 (defun %add-waiter (wl waiter)
          231   (declare (ignore wl waiter)))
          232 
          233 (defun %remove-waiter (wl waiter)
          234   (declare (ignore wl waiter)))
          235 
          236 (defun %setup-wait-list (wl)
          237   (declare (ignore wl)))
          238 
          239 (defun wait-for-input-internal (wait-list &key timeout)
          240   (let* ((sockets (wait-list-waiters wait-list))
          241          (pollfd-size (alien:alien-size (alien:struct unix::pollfd) :bytes))
          242          (nfds (length sockets))
          243          (bytes (* nfds pollfd-size)))
          244     (alien:with-bytes (fds-sap bytes)
          245       (do ((sockets sockets (rest sockets))
          246           (base 0 (+ base 8)))
          247          ((endp sockets))
          248        (let ((fd (socket (first sockets))))
          249          (setf (sys:sap-ref-32 fds-sap base) fd)
          250          (setf (sys:sap-ref-16 fds-sap (+ base 4)) unix::pollin)))
          251       (multiple-value-bind (result errno)
          252          (let ((thread:*thread-whostate* "Poll wait")
          253                (timeout (if timeout
          254                             (truncate (* timeout 1000))
          255                             -1)))
          256            (declare (inline unix:unix-poll))
          257            (unix:unix-poll (alien:sap-alien fds-sap
          258                                             (* (alien:struct unix::pollfd)))
          259                            nfds timeout))
          260        (cond ((not result)
          261               (error "~@<Polling error: ~A~:@>"
          262                      (unix:get-unix-error-msg errno)))
          263              (t
          264               (do ((sockets sockets (rest sockets))
          265                    (base 0 (+ base 8)))
          266                   ((endp sockets))
          267                 (let ((flags (sys:sap-ref-16 fds-sap (+ base 6))))
          268                   (unless (zerop (logand flags unix::pollin))
          269                     (setf (state (first sockets)) :READ))))))))))
          270