iolib.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 --- iolib.lisp (11860B) --- 1 ;;;; See LICENSE for licensing information. 2 3 (in-package :usocket) 4 5 (defparameter *backend* :iolib) 6 7 (eval-when (:load-toplevel :execute) 8 (shadowing-import 'iolib/sockets:socket-option) 9 (export 'socket-option)) 10 11 (defparameter +iolib-error-map+ 12 `((iolib/sockets:socket-address-in-use-error . address-in-use-error) 13 (iolib/sockets:socket-address-family-not-supported-error . socket-type-not-supported-error) 14 (iolib/sockets:socket-address-not-available-error . address-not-available-error) 15 (iolib/sockets:socket-network-down-error . network-down-error) 16 (iolib/sockets:socket-network-reset-error . network-reset-error) 17 (iolib/sockets:socket-network-unreachable-error . network-unreachable-error) 18 ;; (iolib/sockets:socket-no-network-error . ?) 19 (iolib/sockets:socket-connection-aborted-error . connection-aborted-error) 20 (iolib/sockets:socket-connection-reset-error . connection-reset-error) 21 (iolib/sockets:socket-connection-refused-error . connection-refused-error) 22 (iolib/sockets:socket-connection-timeout-error . timeout-error) 23 ;; (iolib/sockets:socket-connection-in-progress-error . ?) 24 (iolib/sockets:socket-endpoint-shutdown-error . network-down-error) 25 (iolib/sockets:socket-no-buffer-space-error . no-buffers-error) 26 (iolib/sockets:socket-host-down-error . host-down-error) 27 (iolib/sockets:socket-host-unreachable-error . host-unreachable-error) 28 ;; (iolib/sockets:socket-already-connected-error . ?) 29 (iolib/sockets:socket-not-connected-error . connection-refused-error) 30 (iolib/sockets:socket-option-not-supported-error . operation-not-permitted-error) 31 (iolib/syscalls:eacces . operation-not-permitted-error) 32 (iolib/sockets:socket-operation-not-supported-error . operation-not-supported-error) 33 (iolib/sockets:unknown-protocol . protocol-not-supported-error) 34 ;; (iolib/sockets:unknown-interface . ?) 35 (iolib/sockets:unknown-service . protocol-not-supported-error) 36 (iolib/sockets:socket-error . socket-error) 37 38 ;; Nameservice errors (src/sockets/dns/conditions.lisp) 39 (iolib/sockets:resolver-error . ns-error) 40 (iolib/sockets:resolver-fail-error . ns-host-not-found-error) 41 (iolib/sockets:resolver-again-error . ns-try-again-condition) 42 (iolib/sockets:resolver-no-name-error . ns-no-recovery-error) 43 (iolib/sockets:resolver-unknown-error . ns-unknown-error) 44 )) 45 46 ;; IOlib uses (SIMPLE-ARRAY (UNSIGNED-BYTE 16) (8)) to represent IPv6 addresses, 47 ;; while USOCKET shared code uses (SIMPLE-ARRAY (UNSIGNED-BYTE 8) (16)). Here we do the 48 ;; conversion. 49 (defun iolib-vector-to-vector-quad (host) 50 (etypecase host 51 ((or (vector t 4) ; IPv4 52 (array (unsigned-byte 8) (4))) 53 host) 54 ((or (vector t 8) ; IPv6 55 (array (unsigned-byte 16) (8))) 56 (loop with vector = (make-array 16 :element-type '(unsigned-byte 8)) 57 for i below 16 by 2 58 for word = (aref host (/ i 2)) 59 do (setf (aref vector i) (ldb (byte 8 8) word) 60 (aref vector (1+ i)) (ldb (byte 8 0) word)) 61 finally (return vector))))) 62 63 (defun handle-condition (condition &optional (socket nil) (host-or-ip nil)) 64 "Dispatch correct usocket condition." 65 (let* ((usock-error (cdr (assoc (type-of condition) +iolib-error-map+))) 66 (usock-error (if (functionp usock-error) 67 (funcall usock-error condition) 68 usock-error))) 69 (if usock-error 70 (if (typep usock-error 'socket-error) 71 (cond ((subtypep usock-error 'ns-error) 72 (error usock-error :socket socket :host-or-ip host-or-ip)) 73 (t 74 (error usock-error :socket socket))) 75 (cond ((subtypep usock-error 'ns-condition) 76 (signal usock-error :socket socket :host-or-ip host-or-ip)) 77 (t 78 (signal usock-error :socket socket)))) 79 (error 'unknown-error 80 :real-error condition 81 :socket socket)))) 82 83 (defun ipv6-address-p (host) 84 (iolib/sockets:ipv6-address-p (iolib/sockets:ensure-hostname host))) 85 86 (defun socket-connect (host port &key (protocol :stream) (element-type 'character) 87 timeout deadline 88 (nodelay t) ;; nodelay == t is the ACL default 89 local-host local-port) 90 (declare (ignore element-type deadline nodelay)) 91 (with-mapped-conditions (nil host) 92 (let* ((remote (when (and host port) (iolib/sockets:ensure-hostname host))) 93 (local (when (and local-host local-port) 94 (iolib/sockets:ensure-hostname local-host))) 95 (ipv6-p (or (and remote (ipv6-address-p remote) 96 (and local (ipv6-address-p local))))) 97 (socket (apply #'iolib/sockets:make-socket 98 `(:type ,protocol 99 :address-family :internet 100 :ipv6 ,ipv6-p 101 :connect ,(cond ((eq protocol :stream) :active) 102 ((and host port) :active) 103 (t :passive)) 104 ,@(when local 105 `(:local-host ,local :local-port ,local-port)) 106 :nodelay nodelay)))) 107 (when remote 108 (apply #'iolib/sockets:connect 109 `(,socket ,remote :port ,port ,@(when timeout `(:wait ,timeout)))) 110 (unless (iolib/sockets:socket-connected-p socket) 111 (close socket) 112 (error 'iolib/sockets:socket-error))) 113 (ecase protocol 114 (:stream 115 (make-stream-socket :stream socket :socket socket)) 116 (:datagram 117 (make-datagram-socket socket :connected-p (and remote t))))))) 118 119 (defmethod socket-close ((usocket usocket)) 120 (close (socket usocket))) 121 122 (defmethod socket-shutdown ((usocket stream-usocket) direction) 123 (with-mapped-conditions () 124 (case direction 125 (:input 126 (iolib/sockets:shutdown (socket usocket) :read t)) 127 (:output 128 (iolib/sockets:shutdown (socket usocket) :write t)) 129 (t ; :io by default 130 (iolib/sockets:shutdown (socket usocket) :read t :write t))))) 131 132 (defun socket-listen (host port 133 &key reuseaddress reuse-address 134 (backlog 5) 135 (element-type 'character)) 136 (declare (ignore element-type)) 137 (with-mapped-conditions (nil host) 138 (make-stream-server-socket 139 (iolib/sockets:make-socket :connect :passive 140 :address-family :internet 141 :local-host (iolib/sockets:ensure-hostname host) 142 :local-port port 143 :backlog backlog 144 :reuse-address (or reuse-address reuseaddress))))) 145 146 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type) 147 (declare (ignore element-type)) 148 (with-mapped-conditions (usocket) 149 (let ((socket (iolib/sockets:accept-connection (socket usocket)))) 150 (make-stream-socket :socket socket :stream socket)))) 151 152 (defmethod get-local-address ((usocket usocket)) 153 (iolib-vector-to-vector-quad 154 (iolib/sockets:address-to-vector (iolib/sockets:local-host (socket usocket))))) 155 156 (defmethod get-peer-address ((usocket stream-usocket)) 157 (iolib-vector-to-vector-quad 158 (iolib/sockets:address-to-vector (iolib/sockets:remote-host (socket usocket))))) 159 160 (defmethod get-local-port ((usocket usocket)) 161 (iolib/sockets:local-port (socket usocket))) 162 163 (defmethod get-peer-port ((usocket stream-usocket)) 164 (iolib/sockets:remote-port (socket usocket))) 165 166 (defmethod get-local-name ((usocket usocket)) 167 (values (get-local-address usocket) 168 (get-local-port usocket))) 169 170 (defmethod get-peer-name ((usocket stream-usocket)) 171 (values (get-peer-address usocket) 172 (get-peer-port usocket))) 173 174 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)) 175 (apply #'iolib/sockets:send-to 176 `(,(socket usocket) ,buffer :start ,offset :end ,(+ offset size) 177 ,@(when (and host port) 178 `(:remote-host ,(iolib/sockets:ensure-hostname host) 179 :remote-port ,port))))) 180 181 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key start end) 182 (multiple-value-bind (buffer size host port) 183 (iolib/sockets:receive-from (socket usocket) 184 :buffer buffer :size length :start start :end end) 185 (values buffer size (iolib-vector-to-vector-quad host) port))) 186 187 (defun get-hosts-by-name (name) 188 (with-mapped-conditions (nil name) 189 (multiple-value-bind (address more-addresses) 190 (iolib/sockets:lookup-hostname name :ipv6 iolib/sockets:*ipv6*) 191 (mapcar #'(lambda (x) (iolib-vector-to-vector-quad 192 (iolib/sockets:address-name x))) 193 (cons address more-addresses))))) 194 195 (defun get-host-by-address (address) 196 (with-mapped-conditions (nil address) 197 nil)) ;; TODO 198 199 (defvar *event-base* 200 (make-instance 'iolib/multiplex:event-base)) 201 202 (defun %setup-wait-list (wait-list) 203 (setf (wait-list-%wait wait-list) 204 (or *event-base* 205 ;; iolib/multiplex:*default-multiplexer* is used here 206 (make-instance 'iolib/multiplex:event-base)))) 207 208 (defun make-usocket-read-handler (usocket disconnector) 209 (lambda (fd event exception) 210 (declare (ignore fd event exception)) 211 (handler-case 212 (if (eq (state usocket) :write) 213 (setf (state usocket) :read-write) 214 (setf (state usocket) :read)) 215 (end-of-file () 216 (funcall disconnector :close))))) 217 218 (defun make-usocket-write-handler (usocket disconnector) 219 (lambda (fd event exception) 220 (declare (ignore fd event exception)) 221 (handler-case 222 (if (eq (state usocket) :read) 223 (setf (state usocket) :read-write) 224 (setf (state usocket) :write)) 225 (end-of-file () 226 (funcall disconnector :close)) 227 (iolib/streams:hangup () 228 (funcall disconnector :close))))) 229 230 (defun make-usocket-error-handler (usocket disconnector) 231 (lambda (fd event exception) 232 (declare (ignore fd event exception)) 233 (handler-case 234 (setf (state usocket) nil) 235 (end-of-file () 236 (funcall disconnector :close)) 237 (iolib/streams:hangup () 238 (funcall disconnector :close))))) 239 240 (defun make-usocket-disconnector (event-base usocket) 241 (declare (ignore event-base)) 242 (lambda (&rest events) 243 (let ((socket (socket usocket))) 244 ;; if were asked to close the socket, we do so here 245 (when (member :close events) 246 (close socket :abort t))))) 247 248 (defun %add-waiter (wait-list waiter) 249 (let ((event-base (wait-list-%wait wait-list)) 250 (fd (iolib/sockets:socket-os-fd (socket waiter)))) 251 ;; reset socket state 252 (setf (state waiter) nil) 253 ;; set read handler 254 (unless (iolib/multiplex::fd-monitored-p event-base fd :read) 255 (iolib/multiplex:set-io-handler 256 event-base fd :read 257 (make-usocket-read-handler waiter 258 (make-usocket-disconnector event-base waiter)))) 259 ;; set write handler 260 #+ignore 261 (unless (iolib/multiplex::fd-monitored-p event-base fd :write) 262 (iolib/multiplex:set-io-handler 263 event-base fd :write 264 (make-usocket-write-handler waiter 265 (make-usocket-disconnector event-base waiter)))) 266 ;; set error handler 267 (unless (iolib/multiplex::fd-has-error-handler-p event-base fd) 268 (iolib/multiplex:set-error-handler 269 event-base fd 270 (make-usocket-error-handler waiter 271 (make-usocket-disconnector event-base waiter)))))) 272 273 (defun %remove-waiter (wait-list waiter) 274 (let ((event-base (wait-list-%wait wait-list))) 275 (iolib/multiplex:remove-fd-handlers event-base 276 (iolib/sockets:socket-os-fd (socket waiter)) 277 :read t 278 :write nil 279 :error t))) 280 281 ;; NOTE: `wait-list-waiters` returns all usockets 282 (defun wait-for-input-internal (wait-list &key timeout) 283 (let ((event-base (wait-list-%wait wait-list))) 284 (handler-case 285 (iolib/multiplex:event-dispatch event-base :timeout timeout) 286 (iolib/streams:hangup ()) 287 (end-of-file ())) 288 ;; close the event-base after use 289 (unless (eq event-base *event-base*) 290 (close event-base))))