clisp.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 --- clisp.lisp (26836B) --- 1 ;;;; See LICENSE for licensing information. 2 3 (in-package :usocket) 4 5 (eval-when (:compile-toplevel :load-toplevel :execute) 6 #-ffi 7 (warn "This image doesn't contain FFI package, GET-HOST-NAME won't work.") 8 #-(or ffi rawsock) 9 (warn "This image doesn't contain either FFI or RAWSOCK package, no UDP support.")) 10 11 ;; utility routine for looking up the current host name 12 #+ffi 13 (ffi:def-call-out get-host-name-internal 14 (:name "gethostname") 15 (:arguments (name (FFI:C-PTR (FFI:C-ARRAY-MAX ffi:character 256)) 16 :OUT :ALLOCA) 17 (len ffi:int)) 18 #+win32 (:library "WS2_32") 19 #-win32 (:library :default) 20 (:language #-win32 :stdc 21 #+win32 :stdc-stdcall) 22 (:return-type ffi:int)) 23 24 (defun get-host-name () 25 #+ffi 26 (multiple-value-bind (retcode name) 27 (get-host-name-internal 256) 28 (when (= retcode 0) 29 name)) 30 #-ffi 31 "localhost") 32 33 (defun get-host-by-address (address) 34 (with-mapped-conditions (nil address) 35 (let ((hostent (posix:resolve-host-ipaddr (host-to-hostname address)))) 36 (posix:hostent-name hostent)))) 37 38 (defun get-hosts-by-name (name) 39 (with-mapped-conditions (nil name) 40 (let ((hostent (posix:resolve-host-ipaddr name))) 41 (mapcar #'host-to-vector-quad 42 (posix:hostent-addr-list hostent))))) 43 44 ;; Format: ((UNIX Windows) . CONDITION) 45 (defparameter +clisp-error-map+ 46 #-win32 47 `((:EADDRINUSE . address-in-use-error) 48 (:EADDRNOTAVAIL . address-not-available-error) 49 (:EBADF . bad-file-descriptor-error) 50 (:ECONNREFUSED . connection-refused-error) 51 (:ECONNRESET . connection-reset-error) 52 (:ECONNABORTED . connection-aborted-error) 53 (:EINVAL . invalid-argument-error) 54 (:ENOBUFS . no-buffers-error) 55 (:ENOMEM . out-of-memory-error) 56 (:ENOTSUP . operation-not-supported-error) 57 (:EPERM . operation-not-permitted-error) 58 (:EPROTONOSUPPORT . protocol-not-supported-error) 59 (:ESOCKTNOSUPPORT . socket-type-not-supported-error) 60 (:ENETUNREACH . network-unreachable-error) 61 (:ENETDOWN . network-down-error) 62 (:ENETRESET . network-reset-error) 63 (:ESHUTDOWN . already-shutdown-error) 64 (:ETIMEDOUT . timeout-error) 65 (:EHOSTDOWN . host-down-error) 66 (:EHOSTUNREACH . host-unreachable-error) 67 ;; when blocked reading, and we close our socket due to a timeout. 68 ;; POSIX.1 says that EAGAIN and EWOULDBLOCK may have the same values. 69 (:EAGAIN . timeout-error) 70 (:EWOULDBLOCK . timeout-error)) ;linux 71 #+win32 72 `((:WSAEADDRINUSE . address-in-use-error) 73 (:WSAEADDRNOTAVAIL . address-not-available-error) 74 (:WSAEBADF . bad-file-descriptor-error) 75 (:WSAECONNREFUSED . connection-refused-error) 76 (:WSAECONNRESET . connection-reset-error) 77 (:WSAECONNABORTED . connection-aborted-error) 78 (:WSAEINVAL . invalid-argument-error) 79 (:WSAENOBUFS . no-buffers-error) 80 (:WSAENOMEM . out-of-memory-error) 81 (:WSAENOTSUP . operation-not-supported-error) 82 (:WSAEPERM . operation-not-permitted-error) 83 (:WSAEPROTONOSUPPORT . protocol-not-supported-error) 84 (:WSAESOCKTNOSUPPORT . socket-type-not-supported-error) 85 (:WSAENETUNREACH . network-unreachable-error) 86 (:WSAENETDOWN . network-down-error) 87 (:WSAENETRESET . network-reset-error) 88 (:WSAESHUTDOWN . already-shutdown-error) 89 (:WSAETIMEDOUT . timeout-error) 90 (:WSAEHOSTDOWN . host-down-error) 91 (:WSAEHOSTUNREACH . host-unreachable-error))) 92 93 (defun parse-errno (condition) 94 "Returns a number or keyword if it can parse what is within parens, else NIL" 95 (let ((s (princ-to-string condition))) 96 (let ((pos1 (position #\( s)) 97 (pos2 (position #\) s))) 98 ;mac: number, linux: keyword 99 (ignore-errors 100 (if (digit-char-p (char s (1+ pos1))) 101 (parse-integer s :start (1+ pos1) :end pos2) 102 (let ((*package* (find-package "KEYWORD"))) 103 (car (read-from-string s t nil :start pos1 :end (1+ pos2))))))))) 104 105 (defun handle-condition (condition &optional (socket nil) (host-or-ip nil)) 106 "Dispatch a usocket condition instead of a CLISP specific one, if we can." 107 (let ((errno 108 (cond 109 ;clisp 2.49+ 110 ((typep condition (find-symbol "OS-STREAM-ERROR" "EXT")) 111 (parse-errno condition)) 112 ;clisp 2.49 113 ((typep condition (find-symbol "SIMPLE-STREAM-ERROR" "SYSTEM")) 114 (car (simple-condition-format-arguments condition)))))) 115 (when errno 116 (let ((error-keyword (if (keywordp errno) errno #+ffi(os:errno errno)))) 117 (let ((usock-error (cdr (assoc error-keyword +clisp-error-map+)))) 118 (when usock-error 119 (if (subtypep usock-error 'error) 120 (cond ((subtypep usock-error 'ns-error) 121 (error usock-error :socket socket :host-or-ip host-or-ip)) 122 (t 123 (error usock-error :socket socket))) 124 (cond ((subtypep usock-error 'ns-condition) 125 (signal usock-error :socket socket :host-or-ip host-or-ip)) 126 (t 127 (signal usock-error :socket socket)))))))))) 128 129 (defun socket-connect (host port &key (protocol :stream) (element-type 'character) 130 timeout deadline (nodelay t nodelay-specified) 131 local-host local-port) 132 (declare (ignorable timeout local-host local-port)) 133 (when deadline (unsupported 'deadline 'socket-connect)) 134 (when (and nodelay-specified 135 (not (eq nodelay :if-supported))) 136 (unsupported 'nodelay 'socket-connect)) 137 (case protocol 138 (:stream 139 (let ((socket) 140 (hostname (host-to-hostname host))) 141 (with-mapped-conditions (socket host) 142 (setf socket 143 (if timeout 144 (socket:socket-connect port hostname 145 :element-type element-type 146 :buffered t 147 :timeout timeout) 148 (socket:socket-connect port hostname 149 :element-type element-type 150 :buffered t)))) 151 (make-stream-socket :socket socket 152 :stream socket))) ;; the socket is a stream too 153 (:datagram 154 #+(or rawsock ffi) 155 (with-mapped-conditions (nil (or host local-host)) 156 (socket-create-datagram (or local-port *auto-port*) 157 :local-host (or local-host *wildcard-host*) 158 :remote-host (and host (host-to-vector-quad host)) 159 :remote-port port)) 160 #-(or rawsock ffi) 161 (unsupported '(protocol :datagram) 'socket-connect)))) 162 163 (defun socket-listen (host port 164 &key reuseaddress 165 (reuse-address nil reuse-address-supplied-p) 166 (backlog 5) 167 (element-type 'character)) 168 ;; clisp 2.39 sets SO_REUSEADDRESS to 1 by default; no need to 169 ;; to explicitly turn it on; unfortunately, there's no way to turn it off... 170 (declare (ignore reuseaddress reuse-address reuse-address-supplied-p)) 171 (let ((sock (apply #'socket:socket-server 172 (append (list port 173 :backlog backlog) 174 (when (ip/= host *wildcard-host*) 175 (list :interface host)))))) 176 (with-mapped-conditions (nil host) 177 (make-stream-server-socket sock :element-type element-type)))) 178 179 (defmethod socket-accept ((socket stream-server-usocket) &key element-type) 180 (let ((stream 181 (with-mapped-conditions (socket) 182 (socket:socket-accept (socket socket) 183 :element-type (or element-type 184 (element-type socket)))))) 185 (make-stream-socket :socket stream 186 :stream stream))) 187 188 ;; Only one close method required: 189 ;; sockets and their associated streams 190 ;; are the same object 191 (defmethod socket-close ((usocket usocket)) 192 "Close socket." 193 (with-mapped-conditions (usocket) 194 (close (socket usocket)))) 195 196 (defmethod socket-close ((usocket stream-server-usocket)) 197 (socket:socket-server-close (socket usocket))) 198 199 (defmethod socket-shutdown ((usocket stream-usocket) direction) 200 (with-mapped-conditions (usocket) 201 (socket:socket-stream-shutdown (socket usocket) direction))) 202 203 (defmethod get-local-name ((usocket stream-usocket)) 204 (multiple-value-bind 205 (address port) 206 (socket:socket-stream-local (socket usocket) t) 207 (values (dotted-quad-to-vector-quad address) port))) 208 209 (defmethod get-local-name ((usocket stream-server-usocket)) 210 (values (get-local-address usocket) 211 (get-local-port usocket))) 212 213 (defmethod get-peer-name ((usocket stream-usocket)) 214 (multiple-value-bind 215 (address port) 216 (socket:socket-stream-peer (socket usocket) t) 217 (values (dotted-quad-to-vector-quad address) port))) 218 219 (defmethod get-local-address ((usocket usocket)) 220 (nth-value 0 (get-local-name usocket))) 221 222 (defmethod get-local-address ((usocket stream-server-usocket)) 223 (dotted-quad-to-vector-quad 224 (socket:socket-server-host (socket usocket)))) 225 226 (defmethod get-peer-address ((usocket usocket)) 227 (nth-value 0 (get-peer-name usocket))) 228 229 (defmethod get-local-port ((usocket usocket)) 230 (nth-value 1 (get-local-name usocket))) 231 232 (defmethod get-local-port ((usocket stream-server-usocket)) 233 (socket:socket-server-port (socket usocket))) 234 235 (defmethod get-peer-port ((usocket usocket)) 236 (nth-value 1 (get-peer-name usocket))) 237 238 (defun %setup-wait-list (wait-list) 239 (declare (ignore wait-list))) 240 241 (defun %add-waiter (wait-list waiter) 242 ;; clisp's #'socket-status takes a list whose elts look either like, 243 ;; (socket-stream direction . x) or like, 244 ;; (socket-server . x) 245 ;; and it replaces the x's. 246 (push (cons (socket waiter) 247 (cond ((stream-usocket-p waiter) (cons NIL NIL)) 248 (t NIL))) 249 (wait-list-%wait wait-list))) 250 251 (defun %remove-waiter (wait-list waiter) 252 (setf (wait-list-%wait wait-list) 253 (remove (socket waiter) (wait-list-%wait wait-list) :key #'car))) 254 255 (defmethod wait-for-input-internal (wait-list &key timeout) 256 (with-mapped-conditions () 257 (multiple-value-bind 258 (secs musecs) 259 (split-timeout (or timeout 1)) 260 (dolist (x (wait-list-%wait wait-list)) 261 (when (consp (cdr x)) ;it's a socket-stream not socket-server 262 (setf (cadr x) :INPUT))) 263 (let* ((request-list (wait-list-%wait wait-list)) 264 (status-list (if timeout 265 (socket:socket-status request-list secs musecs) 266 (socket:socket-status request-list))) 267 (sockets (wait-list-waiters wait-list))) 268 (do* ((x (pop sockets) (pop sockets)) 269 (y (cdr (last (pop status-list))) (cdr (last (pop status-list))))) 270 ((null x)) 271 (when (member y '(T :INPUT :EOF)) 272 (setf (state x) :READ))) 273 wait-list)))) 274 275 ;;; 276 ;;; UDP/Datagram sockets (RAWSOCK version) 277 ;;; 278 279 #+rawsock 280 (progn 281 (defun make-sockaddr_in () 282 (make-array 16 :element-type '(unsigned-byte 8) :initial-element 0)) 283 284 (declaim (inline fill-sockaddr_in)) 285 (defun fill-sockaddr_in (sockaddr_in ip port) 286 (port-to-octet-buffer port sockaddr_in) 287 (ip-to-octet-buffer ip sockaddr_in :start 2) 288 sockaddr_in) 289 290 (defun socket-create-datagram (local-port 291 &key (local-host *wildcard-host*) 292 remote-host 293 remote-port) 294 (let ((sock (rawsock:socket :inet :dgram 0)) 295 (lsock_addr (fill-sockaddr_in (make-sockaddr_in) 296 local-host local-port)) 297 (rsock_addr (when remote-host 298 (fill-sockaddr_in (make-sockaddr_in) 299 remote-host (or remote-port 300 local-port))))) 301 (rawsock:bind sock (rawsock:make-sockaddr :inet lsock_addr)) 302 (when rsock_addr 303 (rawsock:connect sock (rawsock:make-sockaddr :inet rsock_addr))) 304 (make-datagram-socket sock :connected-p (if rsock_addr t nil)))) 305 306 (defmethod socket-receive ((socket datagram-usocket) buffer length &key) 307 "Returns the buffer, the number of octets copied into the buffer (received) 308 and the address of the sender as values." 309 (let* ((sock (socket socket)) 310 (sockaddr (rawsock:make-sockaddr :inet)) 311 (real-length (or length +max-datagram-packet-size+)) 312 (real-buffer (or buffer 313 (make-array real-length 314 :element-type '(unsigned-byte 8))))) 315 (let ((rv (rawsock:recvfrom sock real-buffer sockaddr 316 :start 0 :end real-length)) 317 (host 0) (port 0)) 318 (unless (connected-p socket) 319 (let ((data (rawsock:sockaddr-data sockaddr))) 320 (setq host (ip-from-octet-buffer data :start 4) 321 port (port-from-octet-buffer data :start 2)))) 322 (values (if buffer real-buffer (subseq real-buffer 0 rv)) 323 rv 324 host 325 port)))) 326 327 (defmethod socket-send ((socket datagram-usocket) buffer size &key host port (offset 0)) 328 "Returns the number of octets sent." 329 (let* ((sock (socket socket)) 330 (sockaddr (when (and host port) 331 (rawsock:make-sockaddr :inet 332 (fill-sockaddr_in 333 (make-sockaddr_in) 334 (host-byte-order host) 335 port)))) 336 (real-size (min size +max-datagram-packet-size+)) 337 (real-buffer (if (typep buffer '(simple-array (unsigned-byte 8) (*))) 338 buffer 339 (make-array real-size 340 :element-type '(unsigned-byte 8) 341 :initial-contents (subseq buffer 0 real-size)))) 342 (rv (if (and host port) 343 (rawsock:sendto sock real-buffer sockaddr 344 :start offset 345 :end (+ offset real-size)) 346 (rawsock:send sock real-buffer 347 :start offset 348 :end (+ offset real-size))))) 349 rv)) 350 351 (defmethod socket-close ((usocket datagram-usocket)) 352 (rawsock:sock-close (socket usocket))) 353 354 (declaim (inline get-socket-name)) 355 (defun get-socket-name (socket function) 356 (let ((sockaddr (rawsock:make-sockaddr :inet (make-sockaddr_in)))) 357 (funcall function socket sockaddr) 358 (let ((data (rawsock:sockaddr-data sockaddr))) 359 (values (hbo-to-vector-quad (ip-from-octet-buffer data :start 2)) 360 (port-from-octet-buffer data :start 0))))) 361 362 (defmethod get-local-name ((usocket datagram-usocket)) 363 (get-socket-name (socket usocket) 'rawsock:getsockname)) 364 365 (defmethod get-peer-name ((usocket datagram-usocket)) 366 (get-socket-name (socket usocket) 'rawsock:getpeername)) 367 368 ) ; progn 369 370 ;;; 371 ;;; UDP/Datagram sockets (FFI version) 372 ;;; 373 374 #+(and ffi (not rawsock)) 375 (progn 376 ;; C primitive types 377 (ffi:def-c-type socklen_t ffi:uint32) 378 379 ;; C structures 380 (ffi:def-c-struct sockaddr 381 #+macos (sa_len ffi:uint8) 382 (sa_family #-macos ffi:ushort 383 #+macos ffi:uint8) 384 (sa_data (ffi:c-array ffi:char 14))) 385 386 (ffi:def-c-struct sockaddr_in 387 #+macos (sin_len ffi:uint8) 388 (sin_family #-macos ffi:short 389 #+macos ffi:uint8) 390 (sin_port #-macos ffi:ushort 391 #+macos ffi:uint16) 392 (sin_addr ffi:uint32) 393 (sin_zero (ffi:c-array ffi:char 8))) 394 395 (ffi:def-c-struct timeval 396 (tv_sec ffi:long) 397 (tv_usec ffi:long)) 398 399 ;; foreign functions 400 (ffi:def-call-out %sendto (:name "sendto") 401 (:arguments (socket ffi:int) 402 (buffer ffi:c-pointer) 403 (length ffi:int) 404 (flags ffi:int) 405 (address (ffi:c-ptr sockaddr)) 406 (address-len ffi:int)) 407 #+win32 (:library "WS2_32") 408 #-win32 (:library :default) 409 (:language #-win32 :stdc 410 #+win32 :stdc-stdcall) 411 (:return-type ffi:int)) 412 413 (ffi:def-call-out %send (:name "send") 414 (:arguments (socket ffi:int) 415 (buffer ffi:c-pointer) 416 (length ffi:int) 417 (flags ffi:int)) 418 #+win32 (:library "WS2_32") 419 #-win32 (:library :default) 420 (:language #-win32 :stdc 421 #+win32 :stdc-stdcall) 422 (:return-type ffi:int)) 423 424 (ffi:def-call-out %recvfrom (:name "recvfrom") 425 (:arguments (socket ffi:int) 426 (buffer ffi:c-pointer) 427 (length ffi:int) 428 (flags ffi:int) 429 (address (ffi:c-ptr sockaddr) :in-out) 430 (address-len (ffi:c-ptr ffi:int) :in-out)) 431 #+win32 (:library "WS2_32") 432 #-win32 (:library :default) 433 (:language #-win32 :stdc 434 #+win32 :stdc-stdcall) 435 (:return-type ffi:int)) 436 437 (ffi:def-call-out %socket (:name "socket") 438 (:arguments (family ffi:int) 439 (type ffi:int) 440 (protocol ffi:int)) 441 #+win32 (:library "WS2_32") 442 #-win32 (:library :default) 443 (:language #-win32 :stdc 444 #+win32 :stdc-stdcall) 445 (:return-type ffi:int)) 446 447 (ffi:def-call-out %connect (:name "connect") 448 (:arguments (socket ffi:int) 449 (address (ffi:c-ptr sockaddr) :in) 450 (address_len socklen_t)) 451 #+win32 (:library "WS2_32") 452 #-win32 (:library :default) 453 (:language #-win32 :stdc 454 #+win32 :stdc-stdcall) 455 (:return-type ffi:int)) 456 457 (ffi:def-call-out %bind (:name "bind") 458 (:arguments (socket ffi:int) 459 (address (ffi:c-ptr sockaddr) :in) 460 (address_len socklen_t)) 461 #+win32 (:library "WS2_32") 462 #-win32 (:library :default) 463 (:language #-win32 :stdc 464 #+win32 :stdc-stdcall) 465 (:return-type ffi:int)) 466 467 (ffi:def-call-out %close (:name #-win32 "close" #+win32 "closesocket") 468 (:arguments (socket ffi:int)) 469 #+win32 (:library "WS2_32") 470 #-win32 (:library :default) 471 (:language #-win32 :stdc 472 #+win32 :stdc-stdcall) 473 (:return-type ffi:int)) 474 475 (ffi:def-call-out %getsockopt (:name "getsockopt") 476 (:arguments (sockfd ffi:int) 477 (level ffi:int) 478 (optname ffi:int) 479 (optval ffi:c-pointer) 480 (optlen (ffi:c-ptr socklen_t) :out)) 481 #+win32 (:library "WS2_32") 482 #-win32 (:library :default) 483 (:language #-win32 :stdc 484 #+win32 :stdc-stdcall) 485 (:return-type ffi:int)) 486 487 (ffi:def-call-out %setsockopt (:name "setsockopt") 488 (:arguments (sockfd ffi:int) 489 (level ffi:int) 490 (optname ffi:int) 491 (optval ffi:c-pointer) 492 (optlen socklen_t)) 493 #+win32 (:library "WS2_32") 494 #-win32 (:library :default) 495 (:language #-win32 :stdc 496 #+win32 :stdc-stdcall) 497 (:return-type ffi:int)) 498 499 (ffi:def-call-out %htonl (:name "htonl") 500 (:arguments (hostlong ffi:uint32)) 501 #+win32 (:library "WS2_32") 502 #-win32 (:library :default) 503 (:language #-win32 :stdc 504 #+win32 :stdc-stdcall) 505 (:return-type ffi:uint32)) 506 507 (ffi:def-call-out %htons (:name "htons") 508 (:arguments (hostshort ffi:uint16)) 509 #+win32 (:library "WS2_32") 510 #-win32 (:library :default) 511 (:language #-win32 :stdc 512 #+win32 :stdc-stdcall) 513 (:return-type ffi:uint16)) 514 515 (ffi:def-call-out %ntohl (:name "ntohl") 516 (:arguments (netlong ffi:uint32)) 517 #+win32 (:library "WS2_32") 518 #-win32 (:library :default) 519 (:language #-win32 :stdc 520 #+win32 :stdc-stdcall) 521 (:return-type ffi:uint32)) 522 523 (ffi:def-call-out %ntohs (:name "ntohs") 524 (:arguments (netshort ffi:uint16)) 525 #+win32 (:library "WS2_32") 526 #-win32 (:library :default) 527 (:language #-win32 :stdc 528 #+win32 :stdc-stdcall) 529 (:return-type ffi:uint16)) 530 531 (ffi:def-call-out %getsockname (:name "getsockname") 532 (:arguments (sockfd ffi:int) 533 (localaddr (ffi:c-ptr sockaddr) :in-out) 534 (addrlen (ffi:c-ptr socklen_t) :in-out)) 535 #+win32 (:library "WS2_32") 536 #-win32 (:library :default) 537 (:language #-win32 :stdc 538 #+win32 :stdc-stdcall) 539 (:return-type ffi:int)) 540 541 (ffi:def-call-out %getpeername (:name "getpeername") 542 (:arguments (sockfd ffi:int) 543 (peeraddr (ffi:c-ptr sockaddr) :in-out) 544 (addrlen (ffi:c-ptr socklen_t) :in-out)) 545 #+win32 (:library "WS2_32") 546 #-win32 (:library :default) 547 (:language #-win32 :stdc 548 #+win32 :stdc-stdcall) 549 (:return-type ffi:int)) 550 551 ;; socket constants 552 (defconstant +socket-af-inet+ 2) 553 (defconstant +socket-sock-dgram+ 2) 554 (defconstant +socket-ip-proto-udp+ 17) 555 556 (defconstant +sockopt-so-rcvtimeo+ #-linux #x1006 #+linux 20 "Socket receive timeout") 557 558 (defparameter *length-of-sockaddr_in* (ffi:sizeof 'sockaddr_in)) 559 560 (declaim (inline fill-sockaddr_in)) 561 (defun fill-sockaddr_in (sockaddr host port) 562 (let ((hbo (host-to-hbo host))) 563 (ffi:with-c-place (place sockaddr) 564 #+macos 565 (setf (ffi:slot place 'sin_len) *length-of-sockaddr_in*) 566 (setf (ffi:slot place 'sin_family) +socket-af-inet+ 567 (ffi:slot place 'sin_port) (%htons port) 568 (ffi:slot place 'sin_addr) (%htonl hbo))) 569 sockaddr)) 570 571 (defun socket-create-datagram (local-port 572 &key (local-host *wildcard-host*) 573 remote-host 574 remote-port) 575 (let ((sock (%socket +socket-af-inet+ +socket-sock-dgram+ +socket-ip-proto-udp+)) 576 (lsock_addr (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in) 577 local-host local-port)) 578 (rsock_addr (when remote-host 579 (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in) 580 remote-host (or remote-port local-port))))) 581 (unless (plusp sock) 582 (error "SOCKET-CREATE-DATAGRAM ERROR (socket): ~A" (os:errno))) 583 (unwind-protect 584 (let ((rv (%bind sock (ffi:cast (ffi:foreign-value lsock_addr) 'sockaddr) 585 *length-of-sockaddr_in*))) 586 (unless (zerop rv) 587 (error "SOCKET-CREATE-DATAGRAM ERROR (bind): ~A" (os:errno))) 588 (when rsock_addr 589 (let ((rv (%connect sock 590 (ffi:cast (ffi:foreign-value rsock_addr) 'sockaddr) 591 *length-of-sockaddr_in*))) 592 (unless (zerop rv) 593 (error "SOCKET-CREATE-DATAGRAM ERROR (connect): ~A" (os:errno)))))) 594 (ffi:foreign-free lsock_addr) 595 (when remote-host 596 (ffi:foreign-free rsock_addr))) 597 (make-datagram-socket sock :connected-p (if rsock_addr t nil)))) 598 599 (defun finalize-datagram-usocket (object) 600 (when (datagram-usocket-p object) 601 (socket-close object))) 602 603 (defmethod initialize-instance :after ((usocket datagram-usocket) &key) 604 (setf (slot-value usocket 'recv-buffer) 605 (ffi:allocate-shallow 'ffi:uint8 :count +max-datagram-packet-size+)) 606 ;; finalize the object 607 (ext:finalize usocket 'finalize-datagram-usocket)) 608 609 (defmethod socket-close ((usocket datagram-usocket)) 610 (with-slots (recv-buffer socket) usocket 611 (ffi:foreign-free recv-buffer) 612 (zerop (%close socket)))) 613 614 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key) 615 (let ((remote-address (ffi:allocate-shallow 'sockaddr_in)) 616 (remote-address-length (ffi:allocate-shallow 'ffi:int)) 617 nbytes (host 0) (port 0)) 618 (setf (ffi:foreign-value remote-address-length) 619 *length-of-sockaddr_in*) 620 (unwind-protect 621 (multiple-value-bind (n address address-length) 622 (%recvfrom (socket usocket) 623 (ffi:foreign-address (slot-value usocket 'recv-buffer)) 624 +max-datagram-packet-size+ 625 0 ; flags 626 (ffi:cast (ffi:foreign-value remote-address) 'sockaddr) 627 (ffi:foreign-value remote-address-length)) 628 (when (minusp n) 629 (error "SOCKET-RECEIVE ERROR: ~A" (os:errno))) 630 (setq nbytes n) 631 (when (= address-length *length-of-sockaddr_in*) 632 (let ((data (sockaddr-sa_data address))) 633 (setq host (ip-from-octet-buffer data :start 2) 634 port (port-from-octet-buffer data)))) 635 (cond ((plusp n) 636 (let ((return-buffer (ffi:foreign-value (slot-value usocket 'recv-buffer)))) 637 (if buffer ; replace exist buffer of create new return buffer 638 (let ((end-1 (min (or length (length buffer)) +max-datagram-packet-size+)) 639 (end-2 (min n +max-datagram-packet-size+))) 640 (replace buffer return-buffer :end1 end-1 :end2 end-2)) 641 (setq buffer (subseq return-buffer 0 (min n +max-datagram-packet-size+)))))) 642 ((zerop n)))) 643 (ffi:foreign-free remote-address) 644 (ffi:foreign-free remote-address-length)) 645 (values buffer nbytes host port))) 646 647 ;; implementation note: different from socket-receive, we know how many bytes we want to send everytime, 648 ;; so, a send buffer will not needed, and if there is a buffer, it's hard to fill its content like those 649 ;; in LispWorks. So, we allocate new foreign buffer for holding data (unknown sequence subtype) every time. 650 ;; 651 ;; I don't know if anyone is watching my coding work, but I think this design is reasonable for CLISP. 652 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)) 653 (declare (type sequence buffer) 654 (type (integer 0 *) size offset)) 655 (let ((remote-address 656 (when (and host port) 657 (fill-sockaddr_in (ffi:allocate-shallow 'sockaddr_in) host port))) 658 (send-buffer 659 (ffi:allocate-deep 'ffi:uint8 660 (if (zerop offset) 661 buffer 662 (subseq buffer offset (+ offset size))) 663 :count size :read-only t)) 664 (real-size (min size +max-datagram-packet-size+)) 665 (nbytes 0)) 666 (unwind-protect 667 (let ((n (if remote-address 668 (%sendto (socket usocket) 669 (ffi:foreign-address send-buffer) 670 real-size 671 0 ; flags 672 (ffi:cast (ffi:foreign-value remote-address) 'sockaddr) 673 *length-of-sockaddr_in*) 674 (%send (socket usocket) 675 (ffi:foreign-address send-buffer) 676 real-size 677 0)))) 678 (cond ((plusp n) 679 (setq nbytes n)) 680 ((zerop n) 681 (setq nbytes n)) 682 (t (error "SOCKET-SEND ERROR: ~A" (os:errno))))) 683 (ffi:foreign-free send-buffer) 684 (when remote-address 685 (ffi:foreign-free remote-address)) 686 nbytes))) 687 688 (declaim (inline get-socket-name)) 689 (defun get-socket-name (socket function) 690 (let ((address (ffi:allocate-shallow 'sockaddr_in)) 691 (address-length (ffi:allocate-shallow 'ffi:int)) 692 (host 0) (port 0)) 693 (setf (ffi:foreign-value address-length) *length-of-sockaddr_in*) 694 (unwind-protect 695 (multiple-value-bind (rv return-address return-address-length) 696 (funcall function socket 697 (ffi:cast (ffi:foreign-value address) 'sockaddr) 698 (ffi:foreign-value address-length)) 699 (declare (ignore return-address-length)) 700 (if (zerop rv) 701 (let ((data (sockaddr-sa_data return-address))) 702 (setq host (ip-from-octet-buffer data :start 2) 703 port (port-from-octet-buffer data))) 704 (error "GET-SOCKET-NAME ERROR: ~A" (os:errno)))) 705 (ffi:foreign-free address) 706 (ffi:foreign-free address-length)) 707 (values (hbo-to-vector-quad host) port))) 708 709 (defmethod get-local-name ((usocket datagram-usocket)) 710 (get-socket-name (socket usocket) '%getsockname)) 711 712 (defmethod get-peer-name ((usocket datagram-usocket)) 713 (get-socket-name (socket usocket) '%getpeername)) 714 715 ) ; progn