lispworks.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 --- lispworks.lisp (40914B) --- 1 ;;;; See LICENSE for licensing information. 2 3 (in-package :usocket) 4 5 (eval-when (:compile-toplevel :load-toplevel :execute) 6 (require "comm") 7 8 #+lispworks3 9 (error "LispWorks 3 is not supported")) 10 11 ;;; --------------------------------------------------------------------------- 12 ;;; Warn if multiprocessing is not running on Lispworks 13 14 (defun check-for-multiprocessing-started (&optional errorp) 15 (unless mp:*current-process* 16 (funcall (if errorp 'error 'warn) 17 "You must start multiprocessing on Lispworks by calling~ 18 ~%~3t(~s)~ 19 ~%for ~s function properly." 20 'mp:initialize-multiprocessing 21 'wait-for-input))) 22 23 (eval-when (:load-toplevel :execute) 24 (check-for-multiprocessing-started)) 25 26 #+win32 27 (eval-when (:load-toplevel :execute) 28 (fli:register-module "ws2_32")) 29 30 (fli:define-foreign-function (get-host-name-internal "gethostname" :source) 31 ((return-string (:reference-return (:ef-mb-string :limit 257))) 32 (namelen :int)) 33 :lambda-list (&aux (namelen 256) return-string) 34 :result-type :int 35 #+win32 :module 36 #+win32 "ws2_32") 37 38 (defun get-host-name () 39 (multiple-value-bind (return-code name) 40 (get-host-name-internal) 41 (when (zerop return-code) 42 name))) 43 44 #+win32 45 (defun remap-maybe-for-win32 (z) 46 (mapcar #'(lambda (x) 47 (cons (mapcar #'(lambda (y) (+ 10000 y)) (car x)) 48 (cdr x))) 49 z)) 50 51 (defparameter +lispworks-error-map+ 52 #+win32 53 (append (remap-maybe-for-win32 +unix-errno-condition-map+) 54 (remap-maybe-for-win32 +unix-errno-error-map+)) 55 #-win32 56 (append +unix-errno-condition-map+ 57 +unix-errno-error-map+)) 58 59 (defun raise-usock-err (errno socket &optional condition (host-or-ip nil)) 60 (let ((usock-error 61 (cdr (assoc errno +lispworks-error-map+ :test #'member)))) 62 (if usock-error 63 (if (subtypep usock-error 'error) 64 (cond ((subtypep usock-error 'ns-error) 65 (error usock-error :socket socket :host-or-ip host-or-ip)) 66 (t 67 (error usock-error :socket socket))) 68 (cond ((subtypep usock-error 'ns-condition) 69 (signal usock-error :socket socket :host-or-ip host-or-ip)) 70 (t 71 (signal usock-error :socket socket)))) 72 (error 'unknown-error 73 :socket socket 74 :real-error condition 75 :errno errno)))) 76 77 (defun handle-condition (condition &optional (socket nil) (host-or-ip nil)) 78 "Dispatch correct usocket condition." 79 (typecase condition 80 (condition (let ((errno #-win32 (lw:errno-value) 81 #+win32 (wsa-get-last-error))) 82 (unless (zerop errno) 83 (raise-usock-err errno socket condition host-or-ip)))))) 84 85 (defconstant *socket_sock_dgram* 2 86 "Connectionless, unreliable datagrams of fixed maximum length.") 87 88 (defconstant *socket_ip_proto_udp* 17) 89 90 (defconstant *sockopt_so_rcvtimeo* 91 #-linux #x1006 92 #+linux 20 93 "Socket receive timeout") 94 95 (defconstant *sockopt_so_sndtimeo* 96 #-linux #x1007 97 #+linux 21 98 "Socket send timeout") 99 100 (fli:define-c-struct timeval 101 (tv-sec :long) 102 (tv-usec :long)) 103 104 ;;; ssize_t 105 ;;; recvfrom(int socket, void *restrict buffer, size_t length, int flags, 106 ;;; struct sockaddr *restrict address, socklen_t *restrict address_len); 107 (fli:define-foreign-function (%recvfrom "recvfrom" :source) 108 ((socket :int) 109 (buffer (:pointer (:unsigned :byte))) 110 (length :int) 111 (flags :int) 112 (address (:pointer (:struct comm::sockaddr))) 113 (address-len (:pointer :int))) 114 :result-type :int 115 #+win32 :module 116 #+win32 "ws2_32") 117 118 ;;; ssize_t 119 ;;; sendto(int socket, const void *buffer, size_t length, int flags, 120 ;;; const struct sockaddr *dest_addr, socklen_t dest_len); 121 (fli:define-foreign-function (%sendto "sendto" :source) 122 ((socket :int) 123 (buffer (:pointer (:unsigned :byte))) 124 (length :int) 125 (flags :int) 126 (address (:pointer (:struct comm::sockaddr))) 127 (address-len :int)) 128 :result-type :int 129 #+win32 :module 130 #+win32 "ws2_32") 131 132 #-win32 133 (defun set-socket-receive-timeout (socket-fd seconds) 134 "Set socket option: RCVTIMEO, argument seconds can be a float number" 135 (declare (type integer socket-fd) 136 (type number seconds)) 137 (multiple-value-bind (sec usec) (truncate seconds) 138 (fli:with-dynamic-foreign-objects ((timeout (:struct timeval))) 139 (fli:with-foreign-slots (tv-sec tv-usec) timeout 140 (setf tv-sec sec 141 tv-usec (truncate (* 1000000 usec))) 142 (if (zerop (comm::setsockopt socket-fd 143 comm::*sockopt_sol_socket* 144 *sockopt_so_rcvtimeo* 145 (fli:copy-pointer timeout 146 :type '(:pointer :void)) 147 (fli:size-of '(:struct timeval)))) 148 seconds))))) 149 150 #-win32 151 (defun set-socket-send-timeout (socket-fd seconds) 152 "Set socket option: SNDTIMEO, argument seconds can be a float number" 153 (declare (type integer socket-fd) 154 (type number seconds)) 155 (multiple-value-bind (sec usec) (truncate seconds) 156 (fli:with-dynamic-foreign-objects ((timeout (:struct timeval))) 157 (fli:with-foreign-slots (tv-sec tv-usec) timeout 158 (setf tv-sec sec 159 tv-usec (truncate (* 1000000 usec))) 160 (if (zerop (comm::setsockopt socket-fd 161 comm::*sockopt_sol_socket* 162 *sockopt_so_sndtimeo* 163 (fli:copy-pointer timeout 164 :type '(:pointer :void)) 165 (fli:size-of '(:struct timeval)))) 166 seconds))))) 167 168 #+win32 169 (defun set-socket-receive-timeout (socket-fd seconds) 170 "Set socket option: RCVTIMEO, argument seconds can be a float number. 171 On win32, you must bind the socket before use this function." 172 (declare (type integer socket-fd) 173 (type number seconds)) 174 (fli:with-dynamic-foreign-objects ((timeout :int)) 175 (setf (fli:dereference timeout) 176 (truncate (* 1000 seconds))) 177 (if (zerop (comm::setsockopt socket-fd 178 comm::*sockopt_sol_socket* 179 *sockopt_so_rcvtimeo* 180 (fli:copy-pointer timeout 181 :type '(:pointer :char)) 182 (fli:size-of :int))) 183 seconds))) 184 185 #+win32 186 (defun set-socket-send-timeout (socket-fd seconds) 187 "Set socket option: SNDTIMEO, argument seconds can be a float number. 188 On win32, you must bind the socket before use this function." 189 (declare (type integer socket-fd) 190 (type number seconds)) 191 (fli:with-dynamic-foreign-objects ((timeout :int)) 192 (setf (fli:dereference timeout) 193 (truncate (* 1000 seconds))) 194 (if (zerop (comm::setsockopt socket-fd 195 comm::*sockopt_sol_socket* 196 *sockopt_so_sndtimeo* 197 (fli:copy-pointer timeout 198 :type '(:pointer :char)) 199 (fli:size-of :int))) 200 seconds))) 201 202 #-win32 203 (defun get-socket-receive-timeout (socket-fd) 204 "Get socket option: RCVTIMEO, return value is a float number" 205 (declare (type integer socket-fd)) 206 (fli:with-dynamic-foreign-objects ((timeout (:struct timeval)) 207 (len :int)) 208 (comm::getsockopt socket-fd 209 comm::*sockopt_sol_socket* 210 *sockopt_so_rcvtimeo* 211 (fli:copy-pointer timeout 212 :type '(:pointer :void)) 213 len) 214 (fli:with-foreign-slots (tv-sec tv-usec) timeout 215 (float (+ tv-sec (/ tv-usec 1000000)))))) 216 217 #-win32 218 (defun get-socket-send-timeout (socket-fd) 219 "Get socket option: SNDTIMEO, return value is a float number" 220 (declare (type integer socket-fd)) 221 (fli:with-dynamic-foreign-objects ((timeout (:struct timeval)) 222 (len :int)) 223 (comm::getsockopt socket-fd 224 comm::*sockopt_sol_socket* 225 *sockopt_so_sndtimeo* 226 (fli:copy-pointer timeout 227 :type '(:pointer :void)) 228 len) 229 (fli:with-foreign-slots (tv-sec tv-usec) timeout 230 (float (+ tv-sec (/ tv-usec 1000000)))))) 231 232 #+win32 233 (defun get-socket-receive-timeout (socket-fd) 234 "Get socket option: RCVTIMEO, return value is a float number" 235 (declare (type integer socket-fd)) 236 (fli:with-dynamic-foreign-objects ((timeout :int) 237 (len :int)) 238 (comm::getsockopt socket-fd 239 comm::*sockopt_sol_socket* 240 *sockopt_so_rcvtimeo* 241 (fli:copy-pointer timeout 242 :type '(:pointer :void)) 243 len) 244 (float (/ (fli:dereference timeout) 1000)))) 245 246 #+win32 247 (defun get-socket-send-timeout (socket-fd) 248 "Get socket option: SNDTIMEO, return value is a float number" 249 (declare (type integer socket-fd)) 250 (fli:with-dynamic-foreign-objects ((timeout :int) 251 (len :int)) 252 (comm::getsockopt socket-fd 253 comm::*sockopt_sol_socket* 254 *sockopt_so_sndtimeo* 255 (fli:copy-pointer timeout 256 :type '(:pointer :void)) 257 len) 258 (float (/ (fli:dereference timeout) 1000)))) 259 260 #+(or lispworks4 lispworks5.0) 261 (defun set-socket-tcp-nodelay (socket-fd new-value) 262 "Set socket option: TCP_NODELAY, argument is a fixnum (0 or 1)" 263 (declare (type integer socket-fd) 264 (type (integer 0 1) new-value)) 265 (fli:with-dynamic-foreign-objects ((zero-or-one :int)) 266 (setf (fli:dereference zero-or-one) new-value) 267 (when (zerop (comm::setsockopt socket-fd 268 comm::*sockopt_sol_socket* 269 comm::*sockopt_tcp_nodelay* 270 (fli:copy-pointer zero-or-one 271 :type '(:pointer #+win32 :char #-win32 :void)) 272 (fli:size-of :int))) 273 new-value))) 274 275 (defun get-socket-tcp-nodelay (socket-fd) 276 "Get socket option: TCP_NODELAY, return value is a fixnum (0 or 1)" 277 (declare (type integer socket-fd)) 278 (fli:with-dynamic-foreign-objects ((zero-or-one :int) 279 (len :int)) 280 (if (zerop (comm::getsockopt socket-fd 281 comm::*sockopt_sol_socket* 282 comm::*sockopt_tcp_nodelay* 283 (fli:copy-pointer zero-or-one 284 :type '(:pointer #+win32 :char #-win32 :void)) 285 len)) 286 zero-or-one 0))) ; on error, return 0 287 288 (defun initialize-dynamic-sockaddr (hostname service protocol &aux (original-hostname hostname)) 289 (declare (ignorable original-hostname)) 290 #+(or lispworks4 lispworks5 lispworks6.0) 291 (let ((server-addr (fli:allocate-dynamic-foreign-object 292 :type '(:struct comm::sockaddr_in)))) 293 (values (comm::initialize-sockaddr_in 294 server-addr 295 comm::*socket_af_inet* 296 hostname 297 service protocol) 298 comm::*socket_af_inet* 299 server-addr 300 (fli:pointer-element-size server-addr))) 301 #-(or lispworks4 lispworks5 lispworks6.0) ; version>=6.1 302 (progn 303 (when (stringp hostname) 304 (setq hostname (comm:string-ip-address hostname)) 305 (unless hostname 306 (let ((resolved-hostname (comm:get-host-entry original-hostname :fields '(:address)))) 307 (unless resolved-hostname 308 (return-from initialize-dynamic-sockaddr :unknown-host)) 309 (setq hostname resolved-hostname)))) 310 (if (or (null hostname) 311 (integerp hostname) 312 (comm:ipv6-address-p hostname)) 313 (let ((server-addr (fli:allocate-dynamic-foreign-object 314 :type '(:struct comm::lw-sockaddr)))) 315 (multiple-value-bind (error family) 316 (comm::initialize-sockaddr_in 317 server-addr 318 hostname 319 service protocol) 320 (values error family 321 server-addr 322 (if (eql family comm::*socket_af_inet*) 323 (fli:size-of '(:struct comm::sockaddr_in)) 324 (fli:size-of '(:struct comm::sockaddr_in6)))))) 325 :bad-host))) 326 327 (defun open-udp-socket (&key local-address local-port read-timeout 328 (address-family comm::*socket_af_inet*)) 329 "Open a unconnected UDP socket. 330 For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL), 331 for binding on random free unused port, set LOCAL-PORT to 0." 332 333 ;; Note: move (ensure-sockets) here to make sure delivered applications 334 ;; correctly have networking support initialized. 335 ;; 336 ;; Following words was from Martin Simmons, forwarded by Camille Troillard: 337 338 ;; Calling comm::ensure-sockets at load time looks like a bug in Lispworks-udp 339 ;; (it is too early and also unnecessary). 340 341 ;; The LispWorks comm package calls comm::ensure-sockets when it is needed, so I 342 ;; think open-udp-socket should probably do it too. Calling it more than once is 343 ;; safe and it will be very fast after the first time. 344 #+win32 (comm::ensure-sockets) 345 346 (let ((socket-fd (comm::socket address-family *socket_sock_dgram* *socket_ip_proto_udp*))) 347 (if socket-fd 348 (progn 349 (when read-timeout (set-socket-receive-timeout socket-fd read-timeout)) 350 (if local-port 351 (fli:with-dynamic-foreign-objects () 352 (multiple-value-bind (error local-address-family 353 client-addr client-addr-length) 354 (initialize-dynamic-sockaddr local-address local-port "udp") 355 (if (or error (not (eql address-family local-address-family))) 356 (progn 357 (comm::close-socket socket-fd) 358 (error "cannot resolve hostname ~S, service ~S: ~A" 359 local-address local-port (or error "address family mismatch"))) 360 (if (comm::bind socket-fd client-addr client-addr-length) 361 ;; success, return socket fd 362 socket-fd 363 (progn 364 (comm::close-socket socket-fd) 365 (error "cannot bind")))))) 366 socket-fd)) 367 (error "cannot create socket")))) 368 369 (defun connect-to-udp-server (hostname service 370 &key local-address local-port read-timeout) 371 "Something like CONNECT-TO-TCP-SERVER" 372 (fli:with-dynamic-foreign-objects () 373 (multiple-value-bind (error address-family server-addr server-addr-length) 374 (initialize-dynamic-sockaddr hostname service "udp") 375 (when error 376 (error "cannot resolve hostname ~S, service ~S: ~A" 377 hostname service error)) 378 (let ((socket-fd (open-udp-socket :local-address local-address 379 :local-port local-port 380 :read-timeout read-timeout 381 :address-family address-family))) 382 (if socket-fd 383 (if (comm::connect socket-fd server-addr server-addr-length) 384 ;; success, return socket fd 385 socket-fd 386 ;; fail, close socket and return nil 387 (progn 388 (comm::close-socket socket-fd) 389 (error "cannot connect"))) 390 (error "cannot create socket")))))) 391 392 (defun socket-connect (host port &key (protocol :stream) (element-type 'base-char) 393 timeout deadline (nodelay t) 394 local-host local-port) 395 ;; What's the meaning of this keyword? 396 (when deadline 397 (unimplemented 'deadline 'socket-connect)) 398 399 #+(and lispworks4 (not lispworks4.4)) ; < 4.4.5 400 (when timeout 401 (unsupported 'timeout 'socket-connect :minimum "LispWorks 4.4.5")) 402 403 #+lispworks4 404 (when local-host 405 (unsupported 'local-host 'socket-connect :minimum "LispWorks 5.0")) 406 #+lispworks4 407 (when local-port 408 (unsupported 'local-port 'socket-connect :minimum "LispWorks 5.0")) 409 410 (ecase protocol 411 (:stream 412 (let ((hostname (host-to-hostname host)) 413 (stream)) 414 (setq stream 415 (with-mapped-conditions (nil host) 416 (comm:open-tcp-stream hostname port 417 :element-type element-type 418 #-(and lispworks4 (not lispworks4.4)) ; >= 4.4.5 419 #-(and lispworks4 (not lispworks4.4)) 420 :timeout timeout 421 #-lispworks4 #-lispworks4 422 #-lispworks4 #-lispworks4 423 :local-address (when local-host (host-to-hostname local-host)) 424 :local-port local-port 425 #-(or lispworks4 lispworks5.0) ; >= 5.1 426 #-(or lispworks4 lispworks5.0) 427 :nodelay nodelay))) 428 429 ;; Then handle `nodelay' separately for older versions <= 5.0 430 #+(or lispworks4 lispworks5.0) 431 (when (and stream nodelay) 432 (set-socket-tcp-nodelay 433 (comm:socket-stream-socket stream) 434 (bool->int nodelay))) ; ":if-supported" maps to 1 too. 435 436 (if stream 437 (make-stream-socket :socket (comm:socket-stream-socket stream) 438 :stream stream) 439 ;; if no other error catched by above with-mapped-conditions and still fails, then it's a timeout 440 (error 'timeout-error)))) 441 (:datagram 442 (let ((usocket (make-datagram-socket 443 (if (and host port) 444 (with-mapped-conditions (nil host) 445 (connect-to-udp-server (host-to-hostname host) port 446 :local-address (and local-host (host-to-hostname local-host)) 447 :local-port local-port 448 :read-timeout timeout)) 449 (with-mapped-conditions (nil local-host) 450 (open-udp-socket :local-address (and local-host (host-to-hostname local-host)) 451 :local-port local-port 452 :read-timeout timeout))) 453 :connected-p (and host port t)))) 454 usocket)))) 455 456 (defun socket-listen (host port 457 &key reuseaddress 458 (reuse-address nil reuse-address-supplied-p) 459 (backlog 5) 460 (element-type 'base-char)) 461 #+lispworks4.1 462 (unsupported 'host 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1") 463 #+lispworks4.1 464 (unsupported 'backlog 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1") 465 466 (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress)) 467 (comm::*use_so_reuseaddr* reuseaddress) 468 (hostname (host-to-hostname host)) 469 (socket-res-list (with-mapped-conditions (nil host) 470 (multiple-value-list 471 #-lispworks4.1 (comm::create-tcp-socket-for-service 472 port :address hostname :backlog backlog) 473 #+lispworks4.1 (comm::create-tcp-socket-for-service port)))) 474 (sock (if (not (or (second socket-res-list) (third socket-res-list))) 475 (first socket-res-list) 476 (when (eq (second socket-res-list) :bind) 477 (error 'address-in-use-error))))) 478 (make-stream-server-socket sock :element-type element-type))) 479 480 ;; Note: COMM::GET-FD-FROM-SOCKET contains addition socket wait operations, which 481 ;; should NOT be applied on socket FDs who have already been called on W-F-I, 482 ;; so we have to check the %READY-P slot to decide if this waiting is necessary, 483 ;; or SOCKET-ACCEPT will just hang. -- Chun Tian (binghe), May 1, 2011 484 485 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type) 486 (let* ((socket (with-mapped-conditions (usocket) 487 #+win32 488 (if (%ready-p usocket) 489 (comm::accept-connection-to-socket (socket usocket)) 490 (comm::get-fd-from-socket (socket usocket))) 491 #-win32 492 (comm::get-fd-from-socket (socket usocket)))) 493 (stream (make-instance 'comm:socket-stream 494 :socket socket 495 :direction :io 496 :element-type (or element-type 497 (element-type usocket))))) 498 #+win32 499 (when socket 500 (setf (%ready-p usocket) nil)) 501 (make-stream-socket :socket socket :stream stream))) 502 503 ;; Sockets and their streams are different objects 504 ;; close the stream in order to make sure buffers 505 ;; are correctly flushed and the socket closed. 506 (defmethod socket-close ((usocket stream-usocket)) 507 "Close socket." 508 (close (socket-stream usocket))) 509 510 (defmethod socket-close ((usocket usocket)) 511 (with-mapped-conditions (usocket) 512 (comm::close-socket (socket usocket)))) 513 514 (defmethod socket-close :after ((socket datagram-usocket)) 515 "Additional socket-close method for datagram-usocket" 516 (setf (%open-p socket) nil)) 517 518 (defconstant +shutdown-read+ 0) 519 (defconstant +shutdown-write+ 1) 520 (defconstant +shutdown-read-write+ 2) 521 522 ;;; int 523 ;;; shutdown(int socket, int what); 524 (fli:define-foreign-function (%shutdown "shutdown" :source) 525 ((socket :int) 526 (what :int)) 527 :result-type :int 528 #+win32 :module 529 #+win32 "ws2_32") 530 531 (defmethod socket-shutdown ((usocket datagram-usocket) direction) 532 (unless (member direction '(:input :output :io)) 533 (error 'invalid-argument-error)) 534 (let ((what (case direction 535 (:input +shutdown-read+) 536 (:output +shutdown-write+) 537 (:io +shutdown-read-write+)))) 538 (with-mapped-conditions (usocket) 539 #-(or lispworks4 lispworks5 lispworks6) ; lispworks 7.0+ 540 (comm::shutdown (socket usocket) what) 541 #+(or lispworks4 lispworks5 lispworks6) 542 (= 0 (%shutdown (socket usocket) what))))) 543 544 (defmethod socket-shutdown ((usocket stream-usocket) direction) 545 (unless (member direction '(:input :output :io)) 546 (error 'invalid-argument-error)) 547 (with-mapped-conditions (usocket) 548 #-(or lispworks4 lispworks5 lispworks6) 549 (comm:socket-stream-shutdown (socket usocket) direction) 550 #+(or lispworks4 lispworks5 lispworks6) 551 (let ((what (case direction 552 (:input +shutdown-read+) 553 (:output +shutdown-write+) 554 (:io +shutdown-read-write+)))) 555 (= 0 (%shutdown (comm:socket-stream-socket (socket usocket)) what))))) 556 557 (defmethod initialize-instance :after ((socket datagram-usocket) &key) 558 (setf (slot-value socket 'send-buffer) 559 (make-array +max-datagram-packet-size+ 560 :element-type '(unsigned-byte 8) 561 :allocation :static)) 562 (setf (slot-value socket 'recv-buffer) 563 (make-array +max-datagram-packet-size+ 564 :element-type '(unsigned-byte 8) 565 :allocation :static))) 566 567 (defvar *length-of-sockaddr_in* 568 (fli:size-of '(:struct comm::sockaddr_in))) 569 570 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0) 571 &aux (socket-fd (socket usocket)) 572 (message (slot-value usocket 'send-buffer))) ; TODO: multiple threads send together? 573 "Send message to a socket, using sendto()/send()" 574 (declare (type integer socket-fd) 575 (type sequence buffer)) 576 (when host (setq host (host-to-hostname host))) 577 (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte)) 578 (replace message buffer :start2 offset :end2 (+ offset size)) 579 (let ((n (if (and host port) 580 (fli:with-dynamic-foreign-objects () 581 (multiple-value-bind (error family client-addr client-addr-length) 582 (initialize-dynamic-sockaddr host port "udp") 583 (declare (ignore family)) 584 (when error 585 (error "cannot resolve hostname ~S, port ~S: ~A" 586 host port error)) 587 (%sendto socket-fd ptr (min size +max-datagram-packet-size+) 0 588 (fli:copy-pointer client-addr :type '(:struct comm::sockaddr)) 589 client-addr-length))) 590 (comm::%send socket-fd ptr (min size +max-datagram-packet-size+) 0)))) 591 (declare (type fixnum n)) 592 (if (plusp n) 593 n 594 (let ((errno #-win32 (lw:errno-value) 595 #+win32 (wsa-get-last-error))) 596 (if (zerop errno) 597 n 598 (raise-usock-err errno socket-fd host))))))) 599 600 (defmethod socket-receive ((socket datagram-usocket) buffer length &key timeout (max-buffer-size +max-datagram-packet-size+)) 601 "Receive message from socket, read-timeout is a float number in seconds. 602 603 This function will return 4 values: 604 1. receive buffer 605 2. number of receive bytes 606 3. remote address 607 4. remote port" 608 (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer 609 (integer 0) ; size 610 (unsigned-byte 32) ; host 611 (unsigned-byte 16)) ; port 612 (type sequence buffer)) 613 (let ((socket-fd (socket socket)) 614 (message (slot-value socket 'recv-buffer)) ; TODO: how multiple threads do this in parallel? 615 (read-timeout timeout) 616 old-timeout) 617 (declare (type integer socket-fd)) 618 (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in)) 619 (len :int 620 #-(or lispworks4 lispworks5.0) ; <= 5.0 621 :initial-element *length-of-sockaddr_in*)) 622 #+(or lispworks4 lispworks5.0) ; <= 5.0 623 (setf (fli:dereference len) *length-of-sockaddr_in*) 624 (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte)) 625 ;; setup new read timeout 626 (when read-timeout 627 (setf old-timeout (get-socket-receive-timeout socket-fd)) 628 (set-socket-receive-timeout socket-fd read-timeout)) 629 (let ((n (%recvfrom socket-fd ptr max-buffer-size 0 630 (fli:copy-pointer client-addr :type '(:struct comm::sockaddr)) 631 len))) 632 (declare (type fixnum n)) 633 ;; restore old read timeout 634 (when (and read-timeout (/= old-timeout read-timeout)) 635 (set-socket-receive-timeout socket-fd old-timeout)) 636 ;; Frank James' patch: reset the %read-p for WAIT-FOR-INPUT 637 #+win32 (setf (%ready-p socket) nil) 638 (if (plusp n) 639 (values (if buffer 640 (replace buffer message 641 :end1 (min length max-buffer-size) 642 :end2 (min n max-buffer-size)) 643 (subseq message 0 (min n max-buffer-size))) 644 (min n max-buffer-size) 645 (comm::ntohl (fli:foreign-slot-value 646 (fli:foreign-slot-value client-addr 647 'comm::sin_addr 648 :object-type '(:struct comm::sockaddr_in) 649 :type '(:struct comm::in_addr) 650 :copy-foreign-object nil) 651 'comm::s_addr 652 :object-type '(:struct comm::in_addr))) 653 (comm::ntohs (fli:foreign-slot-value client-addr 654 'comm::sin_port 655 :object-type '(:struct comm::sockaddr_in) 656 :type '(:unsigned :short) 657 :copy-foreign-object nil))) 658 (let ((errno #-win32 (lw:errno-value) 659 #+win32 (wsa-get-last-error))) 660 (if (zerop errno) 661 (values nil n 0 0) 662 (raise-usock-err errno socket-fd))))))))) 663 664 (defmethod get-local-name ((usocket usocket)) 665 (multiple-value-bind 666 (address port) 667 (comm:get-socket-address (socket usocket)) 668 (values (hbo-to-vector-quad address) port))) 669 670 (defmethod get-peer-name ((usocket stream-usocket)) 671 (multiple-value-bind 672 (address port) 673 (comm:get-socket-peer-address (socket usocket)) 674 (values (hbo-to-vector-quad address) port))) 675 676 (defmethod get-local-address ((usocket usocket)) 677 (nth-value 0 (get-local-name usocket))) 678 679 (defmethod get-peer-address ((usocket stream-usocket)) 680 (nth-value 0 (get-peer-name usocket))) 681 682 (defmethod get-local-port ((usocket usocket)) 683 (nth-value 1 (get-local-name usocket))) 684 685 (defmethod get-peer-port ((usocket stream-usocket)) 686 (nth-value 1 (get-peer-name usocket))) 687 688 #-(or lispworks4 lispworks5 lispworks6.0) ; version>= 6.1 689 (defun ipv6-address-p (hostname) 690 (when (stringp hostname) 691 (setq hostname (comm:string-ip-address hostname)) 692 (unless hostname 693 (let ((resolved-hostname (comm:get-host-entry hostname :fields '(:address)))) 694 (unless resolved-hostname 695 (return-from ipv6-address-p nil)) 696 (setq hostname resolved-hostname)))) 697 (comm:ipv6-address-p hostname)) 698 699 (defun lw-hbo-to-vector-quad (hbo) 700 #+(or lispworks4 lispworks5 lispworks6.0) 701 (hbo-to-vector-quad hbo) 702 #-(or lispworks4 lispworks5 lispworks6.0) ; version>= 6.1 703 (if (comm:ipv6-address-p hbo) 704 (ipv6-host-to-vector (comm:ipv6-address-string hbo)) 705 (hbo-to-vector-quad hbo))) 706 707 (defun get-hosts-by-name (name) 708 (with-mapped-conditions (nil name) 709 (mapcar #'lw-hbo-to-vector-quad 710 (comm:get-host-entry name :fields '(:addresses))))) 711 712 (defun get-host-by-address (address) 713 (with-mapped-conditions (nil address) 714 nil)) ;; TODO 715 716 (defun os-socket-handle (usocket) 717 (socket usocket)) 718 719 (defun usocket-listen (usocket) 720 (if (stream-usocket-p usocket) 721 (when (listen (socket-stream usocket)) 722 usocket) 723 (when (comm::socket-listen (socket usocket)) 724 usocket))) 725 726 ;;; 727 ;;; Non Windows implementation 728 ;;; The Windows implementation needs to resort to the Windows API in order 729 ;;; to achieve what we want (what we want is waiting without busy-looping) 730 ;;; 731 732 #-win32 733 (progn 734 735 (defun %setup-wait-list (wait-list) 736 (declare (ignore wait-list))) 737 738 (defun %add-waiter (wait-list waiter) 739 (declare (ignore wait-list waiter))) 740 741 (defun %remove-waiter (wait-list waiter) 742 (declare (ignore wait-list waiter))) 743 744 (defun wait-for-input-internal (wait-list &key timeout) 745 (with-mapped-conditions () 746 ;; unfortunately, it's impossible to share code between 747 ;; non-win32 and win32 platforms... 748 ;; Can we have a sane -pref. complete [UDP!?]- API next time, please? 749 (dolist (x (wait-list-waiters wait-list)) 750 (mp:notice-fd (os-socket-handle x))) 751 (labels ((wait-function (socks) 752 (let (rv) 753 (dolist (x socks rv) 754 (when (usocket-listen x) 755 (setf (state x) :READ 756 rv t)))))) 757 (if timeout 758 (mp:process-wait-with-timeout "Waiting for a socket to become active" 759 (truncate timeout) 760 #'wait-function 761 (wait-list-waiters wait-list)) 762 (mp:process-wait "Waiting for a socket to become active" 763 #'wait-function 764 (wait-list-waiters wait-list)))) 765 (dolist (x (wait-list-waiters wait-list)) 766 (mp:unnotice-fd (os-socket-handle x))) 767 wait-list)) 768 769 ) ; end of block 770 771 772 ;;; 773 ;;; The Windows side of the story 774 ;;; We want to wait without busy looping 775 ;;; This code only works in threads which don't have (hidden) 776 ;;; windows which need to receive messages. There are workarounds in the Windows API 777 ;;; but are those available to 'us'. 778 ;;; 779 780 781 #+win32 782 (progn 783 784 ;; LispWorks doesn't provide an interface to wait for a socket 785 ;; to become ready (under Win32, that is) meaning that we need 786 ;; to resort to system calls to achieve the same thing. 787 ;; Luckily, it provides us access to the raw socket handles (as we 788 ;; wrote the code above. 789 790 (defconstant fd-read 1) 791 (defconstant fd-read-bit 0) 792 (defconstant fd-write 2) 793 (defconstant fd-write-bit 1) 794 (defconstant fd-oob 4) 795 (defconstant fd-oob-bit 2) 796 (defconstant fd-accept 8) 797 (defconstant fd-accept-bit 3) 798 (defconstant fd-connect 16) 799 (defconstant fd-connect-bit 4) 800 (defconstant fd-close 32) 801 (defconstant fd-close-bit 5) 802 (defconstant fd-qos 64) 803 (defconstant fd-qos-bit 6) 804 (defconstant fd-group-qos 128) 805 (defconstant fd-group-qos-bit 7) 806 (defconstant fd-routing-interface 256) 807 (defconstant fd-routing-interface-bit 8) 808 (defconstant fd-address-list-change 512) 809 (defconstant fd-address-list-change-bit 9) 810 811 (defconstant fd-max-events 10) 812 813 (defconstant fionread 1074030207) 814 815 816 ;; Note: 817 ;; 818 ;; If special finalization has to occur for a given 819 ;; system resource (handle), an associated object should 820 ;; be created. A special cleanup action should be added 821 ;; to the system and a special cleanup action should 822 ;; be flagged on all objects created for resources like it 823 ;; 824 ;; We have 2 functions to do so: 825 ;; * hcl:add-special-free-action (function-symbol) 826 ;; * hcl:flag-special-free-action (object) 827 ;; 828 ;; Note that the special free action will be called on all 829 ;; objects which have been flagged for special free, so be 830 ;; sure to check for the right argument type! 831 832 (fli:define-foreign-type ws-socket () '(:unsigned :int)) 833 (fli:define-foreign-type win32-handle () '(:unsigned :int)) 834 (fli:define-c-struct wsa-network-events 835 (network-events :long) 836 (error-code (:c-array :int 10))) 837 838 (fli:define-foreign-function (wsa-event-create "WSACreateEvent" :source) 839 () 840 :lambda-list nil 841 :result-type :int 842 :module "ws2_32") 843 844 (fli:define-foreign-function (wsa-event-close "WSACloseEvent" :source) 845 ((event-object win32-handle)) 846 :result-type :int 847 :module "ws2_32") 848 849 ;; not used 850 (fli:define-foreign-function (wsa-reset-event "WSAResetEvent" :source) 851 ((event-object win32-handle)) 852 :result-type :int 853 :module "ws2_32") 854 855 (fli:define-foreign-function (wsa-enum-network-events "WSAEnumNetworkEvents" :source) 856 ((socket ws-socket) 857 (event-object win32-handle) 858 (network-events (:reference-return wsa-network-events))) 859 :result-type :int 860 :module "ws2_32") 861 862 (fli:define-foreign-function (wsa-event-select "WSAEventSelect" :source) 863 ((socket ws-socket) 864 (event-object win32-handle) 865 (network-events :long)) 866 :result-type :int 867 :module "ws2_32") 868 869 (fli:define-foreign-function (wsa-get-last-error "WSAGetLastError" :source) 870 () 871 :result-type :int 872 :module "ws2_32") 873 874 (fli:define-foreign-function (wsa-ioctlsocket "ioctlsocket" :source) 875 ((socket :long) (cmd :long) (argp (:ptr :long))) 876 :result-type :int 877 :module "ws2_32") 878 879 880 ;; The Windows system 881 882 883 ;; Now that we have access to the system calls, this is the plan: 884 885 ;; 1. Receive a wait-list with associated sockets to wait for 886 ;; 2. Add all those sockets to an event handle 887 ;; 3. Listen for an event on that handle (we have a LispWorks system:: internal for that) 888 ;; 4. After listening, detect if there are errors 889 ;; (this step is different from Unix, where we can have only one error) 890 ;; 5. If so, raise one of them 891 ;; 6. If not so, return the sockets which have input waiting for them 892 893 894 (defun maybe-wsa-error (rv &optional socket) 895 (unless (zerop rv) 896 (raise-usock-err (wsa-get-last-error) socket))) 897 898 (defun bytes-available-for-read (socket) 899 (fli:with-dynamic-foreign-objects ((int-ptr :long)) 900 (let ((rv (wsa-ioctlsocket (os-socket-handle socket) fionread int-ptr))) 901 (if (= 0 rv) 902 (fli:dereference int-ptr) 903 0)))) 904 905 (defun socket-ready-p (socket) 906 (if (typep socket 'stream-usocket) 907 (< 0 (bytes-available-for-read socket)) 908 (%ready-p socket))) 909 910 (defun waiting-required (sockets) 911 (notany #'socket-ready-p sockets)) 912 913 (defun wait-for-input-internal (wait-list &key timeout) 914 (when (waiting-required (wait-list-waiters wait-list)) 915 (system:wait-for-single-object (wait-list-%wait wait-list) 916 "Waiting for socket activity" timeout)) 917 (update-ready-and-state-slots wait-list)) 918 919 (defun map-network-events (func network-events) 920 (let ((event-map (fli:foreign-slot-value network-events 'network-events)) 921 (error-array (fli:foreign-slot-pointer network-events 'error-code))) 922 (unless (zerop event-map) 923 (dotimes (i fd-max-events) 924 (unless (zerop (ldb (byte 1 i) event-map)) ;;### could be faster with ash and logand? 925 (funcall func (fli:foreign-aref error-array i))))))) 926 927 (defun update-ready-and-state-slots (wait-list) 928 (loop with sockets = (wait-list-waiters wait-list) 929 for socket in sockets do 930 (if (or (and (stream-usocket-p socket) 931 (listen (socket-stream socket))) 932 (%ready-p socket)) 933 (setf (state socket) :READ) 934 (multiple-value-bind 935 (rv network-events) 936 (wsa-enum-network-events (os-socket-handle socket) 937 (wait-list-%wait wait-list) 938 t) 939 (if (zerop rv) 940 (map-network-events #'(lambda (err-code) 941 (if (zerop err-code) 942 (setf (%ready-p socket) t 943 (state socket) :READ) 944 (raise-usock-err err-code socket))) 945 network-events) 946 (maybe-wsa-error rv socket)))))) 947 948 ;; The wait-list part 949 950 (defun free-wait-list (wl) 951 (when (wait-list-p wl) 952 (unless (null (wait-list-%wait wl)) 953 (wsa-event-close (wait-list-%wait wl)) 954 (setf (wait-list-%wait wl) nil)))) 955 956 (eval-when (:load-toplevel :execute) 957 (hcl:add-special-free-action 'free-wait-list)) 958 959 (defun %setup-wait-list (wait-list) 960 (hcl:flag-special-free-action wait-list) 961 (setf (wait-list-%wait wait-list) (wsa-event-create))) 962 963 (defun %add-waiter (wait-list waiter) 964 (let ((events (etypecase waiter 965 (stream-server-usocket (logior fd-connect fd-accept fd-close)) 966 (stream-usocket (logior fd-connect fd-read fd-oob fd-close)) 967 (datagram-usocket (logior fd-read))))) 968 (maybe-wsa-error 969 (wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) events) 970 waiter))) 971 972 (defun %remove-waiter (wait-list waiter) 973 (maybe-wsa-error 974 (wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) 0) 975 waiter)) 976 977 ) ; end of WIN32-block 978 979 (defun set-socket-reuse-address (socket-fd reuse-address-p) 980 (declare (type integer socket-fd) 981 (type boolean reuse-address-p)) 982 (fli:with-dynamic-foreign-objects ((value :int)) 983 (setf (fli:dereference value) (if reuse-address-p 1 0)) 984 (if (zerop (comm::setsockopt socket-fd 985 comm::*sockopt_sol_socket* 986 comm::*sockopt_so_reuseaddr* 987 (fli:copy-pointer value 988 :type '(:pointer :void)) 989 (fli:size-of :int))) 990 reuse-address-p))) 991 992 (defun get-socket-reuse-address (socket-fd) 993 (declare (type integer socket-fd)) 994 (fli:with-dynamic-foreign-objects ((value :int) (len :int)) 995 (if (zerop (comm::getsockopt socket-fd 996 comm::*sockopt_sol_socket* 997 comm::*sockopt_so_reuseaddr* 998 (fli:copy-pointer value 999 :type '(:pointer :void)) 1000 len)) 1001 (= 1 (fli:dereference value)))))