topenmcl.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 --- topenmcl.lisp (10498B) --- 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) 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 nil) 77 (signal nameserver-error :host-or-ip nil)) 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 () 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 () 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 (when (wait-list usocket) 156 (remove-waiter (wait-list usocket) usocket)) 157 (with-mapped-conditions (usocket) 158 (close (socket usocket)))) 159 160 (defmethod socket-shutdown ((usocket usocket) direction) 161 (with-mapped-conditions (usocket) 162 (openmcl-socket:shutdown (socket usocket) :direction direction))) 163 164 #-ipv6 165 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)) 166 (with-mapped-conditions (usocket) 167 (if (and host port) 168 (openmcl-socket:send-to (socket usocket) buffer size 169 :remote-host (host-to-hbo host) 170 :remote-port port 171 :offset offset) 172 ;; Clozure CL's socket function SEND-TO doesn't support operations on connected UDP sockets, 173 ;; so we have to define our own. 174 (let* ((socket (socket usocket)) 175 (fd (ccl::socket-device socket))) 176 (multiple-value-setq (buffer offset) 177 (ccl::verify-socket-buffer buffer offset size)) 178 (ccl::%stack-block ((bufptr size)) 179 (ccl::%copy-ivector-to-ptr buffer offset bufptr 0 size) 180 (ccl::socket-call socket "send" 181 (ccl::with-eagain fd :output 182 (ccl::ignoring-eintr 183 (ccl::check-socket-error (#_send fd bufptr size 0)))))))))) 184 185 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key) 186 (with-mapped-conditions (usocket) 187 (openmcl-socket:receive-from (socket usocket) length :buffer buffer))) 188 189 (defun usocket-host-address (address) 190 (cond 191 ((integerp address) 192 (hbo-to-vector-quad address)) 193 ((and (arrayp address) 194 (= (length address) 16) 195 (every #'= address #(0 0 0 0 0 0 0 0 0 0 #xff #xff))) 196 (make-array 4 :displaced-to address :displaced-index-offset 12)) 197 (t 198 address))) 199 200 (defmethod get-local-address ((usocket usocket)) 201 (usocket-host-address (openmcl-socket:local-host (socket usocket)))) 202 203 (defmethod get-peer-address ((usocket stream-usocket)) 204 (usocket-host-address (openmcl-socket:remote-host (socket usocket)))) 205 206 (defmethod get-local-port ((usocket usocket)) 207 (openmcl-socket:local-port (socket usocket))) 208 209 (defmethod get-peer-port ((usocket stream-usocket)) 210 (openmcl-socket:remote-port (socket usocket))) 211 212 (defmethod get-local-name ((usocket usocket)) 213 (values (get-local-address usocket) 214 (get-local-port usocket))) 215 216 (defmethod get-peer-name ((usocket stream-usocket)) 217 (values (get-peer-address usocket) 218 (get-peer-port usocket))) 219 220 (defun get-host-by-address (address) 221 (with-mapped-conditions () 222 (openmcl-socket:ipaddr-to-hostname (host-to-hbo address)))) 223 224 (defun get-hosts-by-name (name) 225 (with-mapped-conditions () 226 (list (hbo-to-vector-quad (openmcl-socket:lookup-hostname 227 (host-to-hostname name)))))) 228 229 (defun %setup-wait-list (wait-list) 230 (declare (ignore wait-list))) 231 232 (defun %add-waiter (wait-list waiter) 233 (declare (ignore wait-list waiter))) 234 235 (defun %remove-waiter (wait-list waiter) 236 (declare (ignore wait-list waiter))) 237 238 (defun wait-for-input-internal (wait-list &key timeout) 239 (with-mapped-conditions () 240 (let* ((ticks-timeout (truncate (* (or timeout 1) 241 ccl::*ticks-per-second*)))) 242 (input-available-p (wait-list-waiters wait-list) 243 (when timeout ticks-timeout)) 244 wait-list))) 245 246 ;;; Helper functions for option.lisp 247 248 (defun get-socket-option-reuseaddr (socket) 249 (ccl::int-getsockopt (ccl::socket-device socket) 250 #$SOL_SOCKET #$SO_REUSEADDR)) 251 252 (defun set-socket-option-reuseaddr (socket value) 253 (ccl::int-setsockopt (ccl::socket-device socket) 254 #$SOL_SOCKET #$SO_REUSEADDR value)) 255 256 (defun get-socket-option-broadcast (socket) 257 (ccl::int-getsockopt (ccl::socket-device socket) 258 #$SOL_SOCKET #$SO_BROADCAST)) 259 260 (defun set-socket-option-broadcast (socket value) 261 (ccl::int-setsockopt (ccl::socket-device socket) 262 #$SOL_SOCKET #$SO_BROADCAST value)) 263 264 (defun get-socket-option-tcp-nodelay (socket) 265 (ccl::int-getsockopt (ccl::socket-device socket) 266 #$IPPROTO_TCP #$TCP_NODELAY)) 267 268 (defun set-socket-option-tcp-nodelay (socket value) 269 (ccl::int-setsockopt (ccl::socket-device socket) 270 #$IPPROTO_TCP #$TCP_NODELAY value))