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