iolib.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
HTML 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))))