openmcl.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       openmcl.lisp (10491B)
       ---
            1 ;;;; See LICENSE for licensing information.
            2 
            3 (in-package :usocket)
            4 
            5 (defun get-host-name ()
            6   (ccl::%stack-block ((resultbuf 256))
            7     (when (zerop (#_gethostname resultbuf 256))
            8       (ccl::%get-cstring resultbuf))))
            9 
           10 (defparameter +openmcl-error-map+
           11   '((:address-in-use . address-in-use-error)
           12     (:connection-aborted . connection-aborted-error)
           13     (:no-buffer-space . no-buffers-error)
           14     (:connection-timed-out . timeout-error)
           15     (:connection-refused . connection-refused-error)
           16     (:host-unreachable . host-unreachable-error)
           17     (:host-down . host-down-error)
           18     (:network-down . network-down-error)
           19     (:address-not-available . address-not-available-error)
           20     (:network-reset . network-reset-error)
           21     (:connection-reset . connection-reset-error)
           22     (:shutdown . shutdown-error)
           23     (:access-denied . operation-not-permitted-error)))
           24 
           25 (defparameter +openmcl-nameserver-error-map+
           26   '((:no-recovery . ns-no-recovery-error)
           27     (:try-again . ns-try-again-condition)
           28     (:host-not-found . ns-host-not-found-error)))
           29 
           30 ;; we need something which the openmcl implementors 'forgot' to do:
           31 ;; wait for more than one socket-or-fd
           32 
           33 (defun input-available-p (sockets &optional ticks-to-wait)
           34   (ccl::rletz ((tv :timeval))
           35     (ccl::ticks-to-timeval ticks-to-wait tv)
           36     ;;### The trickery below can be moved to the wait-list now...
           37     (ccl::%stack-block ((infds ccl::*fd-set-size*))
           38       (ccl::fd-zero infds)
           39       (let ((max-fd -1))
           40         (dolist (sock sockets)
           41           (let ((fd (openmcl-socket:socket-os-fd (socket sock))))
           42             (when fd ;; may be NIL if closed
           43               (setf max-fd (max max-fd fd))
           44               (ccl::fd-set fd infds))))
           45         (let ((res (#_select (1+ max-fd)
           46                              infds (ccl::%null-ptr) (ccl::%null-ptr)
           47                              (if ticks-to-wait tv (ccl::%null-ptr)))))
           48           (when (> res 0)
           49             (dolist (sock sockets)
           50               (let ((fd (openmcl-socket:socket-os-fd (socket sock))))
           51                 (when (and fd (ccl::fd-is-set fd infds))
           52                   (setf (state sock) :READ)))))
           53           sockets)))))
           54 
           55 (defun raise-error-from-id (condition-id socket real-condition)
           56   (let ((usock-err (cdr (assoc condition-id +openmcl-error-map+))))
           57     (if usock-err
           58         (error usock-err :socket socket)
           59       (error 'unknown-error :socket socket :real-error real-condition))))
           60 
           61 (defun handle-condition (condition &optional socket (host-or-ip nil))
           62   (typecase condition
           63     (openmcl-socket:socket-error
           64        (raise-error-from-id (openmcl-socket:socket-error-identifier condition)
           65                             socket condition))
           66     (ccl:input-timeout
           67        (error 'timeout-error :socket socket))
           68     (ccl:communication-deadline-expired
           69        (error 'deadline-timeout-error :socket socket))
           70     (ccl::socket-creation-error #| ugh! |#
           71        (let* ((condition-id (ccl::socket-creation-error-identifier condition))
           72               (nameserver-error (cdr (assoc condition-id
           73                                             +openmcl-nameserver-error-map+))))
           74          (if nameserver-error
           75              (if (typep nameserver-error 'serious-condition)
           76                  (error nameserver-error :host-or-ip host-or-ip)
           77                  (signal nameserver-error :host-or-ip host-or-ip))
           78            (raise-error-from-id condition-id socket condition))))))
           79 
           80 (defun to-format (element-type protocol)
           81   (cond ((null element-type)
           82          (ecase protocol ; default value of different protocol
           83            (:stream :text)
           84            (:datagram :binary)))
           85         ((subtypep element-type 'character)
           86          :text)
           87         (t :binary)))
           88 
           89 #-ipv6
           90 (defun socket-connect (host port &key (protocol :stream) element-type
           91                        timeout deadline nodelay
           92                        local-host local-port)
           93   (when (eq nodelay :if-supported)
           94     (setf nodelay t))
           95   (with-mapped-conditions (nil host)
           96     (ecase protocol
           97       (:stream
           98        (let ((mcl-sock
           99               (openmcl-socket:make-socket :remote-host (host-to-hostname host)
          100                                           :remote-port port
          101                                           :local-host local-host
          102                                           :local-port local-port
          103                                           :format (to-format element-type protocol)
          104                                           :external-format ccl:*default-external-format*
          105                                           :deadline deadline
          106                                           :nodelay nodelay
          107                                           :connect-timeout timeout)))
          108          (make-stream-socket :stream mcl-sock :socket mcl-sock)))
          109       (:datagram
          110        (let* ((mcl-sock
          111                (openmcl-socket:make-socket :address-family :internet
          112                                            :type :datagram
          113                                            :local-host local-host
          114                                            :local-port local-port
          115                                            :input-timeout timeout
          116                                            :format (to-format element-type protocol)
          117                                            :external-format ccl:*default-external-format*))
          118               (usocket (make-datagram-socket mcl-sock)))
          119          (when (and host port)
          120            (ccl::inet-connect (ccl::socket-device mcl-sock)
          121                               (ccl::host-as-inet-host host)
          122                               (ccl::port-as-inet-port port "udp")))
          123          (setf (connected-p usocket) t)
          124          usocket)))))
          125 
          126 #-ipv6
          127 (defun socket-listen (host port
          128                       &key reuseaddress
          129                            (reuse-address nil reuse-address-supplied-p)
          130                            (backlog 5)
          131                            (element-type 'character))
          132   (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
          133          (real-host (host-to-hostname host))
          134          (sock (with-mapped-conditions (nil host)
          135                   (apply #'openmcl-socket:make-socket
          136                          (append (list :connect :passive
          137                                        :reuse-address reuseaddress
          138                                        :local-port port
          139                                        :backlog backlog
          140                                        :format (to-format element-type :stream))
          141                                  (unless (eq host *wildcard-host*)
          142                                    (list :local-host real-host)))))))
          143     (make-stream-server-socket sock :element-type element-type)))
          144 
          145 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
          146   (declare (ignore element-type)) ;; openmcl streams are bi/multivalent
          147   (let ((sock (with-mapped-conditions (usocket)
          148                  (openmcl-socket:accept-connection (socket usocket)))))
          149     (make-stream-socket :socket sock :stream sock)))
          150 
          151 ;; One close method is sufficient because sockets
          152 ;; and their associated objects are represented
          153 ;; by the same object.
          154 (defmethod socket-close ((usocket usocket))
          155   (with-mapped-conditions (usocket)
          156     (close (socket usocket))))
          157 
          158 (defmethod socket-shutdown ((usocket usocket) direction)
          159   (with-mapped-conditions (usocket)
          160     (openmcl-socket:shutdown (socket usocket) :direction direction)))
          161 
          162 #-ipv6
          163 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
          164   (with-mapped-conditions (usocket host)
          165     (if (and host port)
          166         (openmcl-socket:send-to (socket usocket) buffer size
          167                                 :remote-host (host-to-hbo host)
          168                                 :remote-port port
          169                                 :offset offset)
          170         ;; Clozure CL's socket function SEND-TO doesn't support operations on connected UDP sockets,
          171         ;; so we have to define our own.
          172         (let* ((socket (socket usocket))
          173                (fd (ccl::socket-device socket)))
          174           (multiple-value-setq (buffer offset)
          175             (ccl::verify-socket-buffer buffer offset size))
          176           (ccl::%stack-block ((bufptr size))
          177             (ccl::%copy-ivector-to-ptr buffer offset bufptr 0 size)
          178             (ccl::socket-call socket "send"
          179               (ccl::with-eagain fd :output
          180                 (ccl::ignoring-eintr
          181                   (ccl::check-socket-error (#_send fd bufptr size 0))))))))))
          182 
          183 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key)
          184   (with-mapped-conditions (usocket)
          185     (openmcl-socket:receive-from (socket usocket) length :buffer buffer)))
          186 
          187 (defun usocket-host-address (address)
          188   (cond
          189     ((integerp address)
          190      (hbo-to-vector-quad address))
          191     ((and (arrayp address)
          192           (= (length address) 16)
          193           (every #'= address #(0 0 0 0 0 0 0 0 0 0 #xff #xff)))
          194      (make-array 4 :displaced-to address :displaced-index-offset 12))
          195     (t
          196      address)))
          197 
          198 (defmethod get-local-address ((usocket usocket))
          199   (usocket-host-address (openmcl-socket:local-host (socket usocket))))
          200 
          201 (defmethod get-peer-address ((usocket stream-usocket))
          202   (usocket-host-address (openmcl-socket:remote-host (socket usocket))))
          203 
          204 (defmethod get-local-port ((usocket usocket))
          205   (openmcl-socket:local-port (socket usocket)))
          206 
          207 (defmethod get-peer-port ((usocket stream-usocket))
          208   (openmcl-socket:remote-port (socket usocket)))
          209 
          210 (defmethod get-local-name ((usocket usocket))
          211   (values (get-local-address usocket)
          212           (get-local-port usocket)))
          213 
          214 (defmethod get-peer-name ((usocket stream-usocket))
          215   (values (get-peer-address usocket)
          216           (get-peer-port usocket)))
          217 
          218 (defun get-host-by-address (address)
          219   (with-mapped-conditions (nil address)
          220      (openmcl-socket:ipaddr-to-hostname (host-to-hbo address))))
          221 
          222 (defun get-hosts-by-name (name)
          223   (with-mapped-conditions (nil name)
          224      (list (hbo-to-vector-quad (openmcl-socket:lookup-hostname
          225                                 (host-to-hostname name))))))
          226 
          227 (defun %setup-wait-list (wait-list)
          228   (declare (ignore wait-list)))
          229 
          230 (defun %add-waiter (wait-list waiter)
          231   (declare (ignore wait-list waiter)))
          232 
          233 (defun %remove-waiter (wait-list waiter)
          234   (declare (ignore wait-list waiter)))
          235 
          236 (defun wait-for-input-internal (wait-list &key timeout)
          237   (with-mapped-conditions ()
          238     (let* ((ticks-timeout (truncate (* (or timeout 1)
          239                                        ccl::*ticks-per-second*))))
          240       (input-available-p (wait-list-waiters wait-list)
          241                          (when timeout ticks-timeout))
          242       wait-list)))
          243 
          244 ;;; Helper functions for option.lisp
          245 
          246 (defun get-socket-option-reuseaddr (socket)
          247   (ccl::int-getsockopt (ccl::socket-device socket)
          248                                   #$SOL_SOCKET #$SO_REUSEADDR))
          249 
          250 (defun set-socket-option-reuseaddr (socket value)
          251   (ccl::int-setsockopt (ccl::socket-device socket)
          252                                   #$SOL_SOCKET #$SO_REUSEADDR value))
          253 
          254 (defun get-socket-option-broadcast (socket)
          255   (ccl::int-getsockopt (ccl::socket-device socket)
          256                                   #$SOL_SOCKET #$SO_BROADCAST))
          257 
          258 (defun set-socket-option-broadcast (socket value)
          259   (ccl::int-setsockopt (ccl::socket-device socket)
          260                                   #$SOL_SOCKET #$SO_BROADCAST value))
          261 
          262 (defun get-socket-option-tcp-nodelay (socket)
          263   (ccl::int-getsockopt (ccl::socket-device socket)
          264                                   #$IPPROTO_TCP #$TCP_NODELAY))
          265 
          266 (defun set-socket-option-tcp-nodelay (socket value)
          267   (ccl::int-setsockopt (ccl::socket-device socket)
          268                                   #$IPPROTO_TCP #$TCP_NODELAY value))