usocket.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 --- usocket.lisp (27397B) --- 1 ;;;; -*- Mode: LISP; Base: 10; Syntax: ANSI-Common-lisp; Package: USOCKET -*- 2 ;;;; See LICENSE for licensing information. 3 4 (in-package :usocket) 5 6 (defparameter *wildcard-host* #(0 0 0 0) 7 "Hostname to pass when all interfaces in the current system are to 8 be bound. If this variable is passed to socket-listen, IPv6 capable 9 systems will also listen for IPv6 connections.") 10 11 (defparameter *auto-port* 0 12 "Port number to pass when an auto-assigned port number is wanted.") 13 14 (defparameter *version* #.(asdf:component-version (asdf:find-system :usocket)) 15 "usocket version string") 16 17 (defconstant +max-datagram-packet-size+ 65507 18 "The theoretical maximum amount of data in a UDP datagram. 19 20 The IPv4 UDP packets have a 16-bit length constraint, and IP+UDP header has 28-byte. 21 22 IP_MAXPACKET = 65535, /* netinet/ip.h */ 23 sizeof(struct ip) = 20, /* netinet/ip.h */ 24 sizeof(struct udphdr) = 8, /* netinet/udp.h */ 25 26 65535 - 20 - 8 = 65507 27 28 (But for UDP broadcast, the maximum message size is limited by the MTU size of the underlying link)") 29 30 (defclass usocket () 31 ((socket 32 :initarg :socket 33 :accessor socket 34 :documentation "Implementation specific socket object instance.'") 35 (wait-list 36 :initform nil 37 :accessor wait-list 38 :documentation "WAIT-LIST the object is associated with.") 39 (state 40 :initform nil 41 :accessor state 42 :documentation "Per-socket return value for the `wait-for-input' function. 43 44 The value stored in this slot can be any of 45 NIL - not ready 46 :READ - ready to read 47 :READ-WRITE - ready to read and write 48 :WRITE - ready to write 49 50 The last two remain unused in the current version. 51 ") 52 #+(and win32 (or sbcl ecl lispworks)) 53 (%ready-p 54 :initform nil 55 :accessor %ready-p 56 :documentation "Indicates whether the socket has been signalled 57 as ready for reading a new connection. 58 59 The value will be set to T by `wait-for-input-internal' (given the 60 right conditions) and reset to NIL by `socket-accept'. 61 62 Don't modify this slot or depend on it as it is really intended 63 to be internal only. 64 65 Note: Accessed, but not used for 'stream-usocket'. 66 " 67 )) 68 (:documentation 69 "The main socket class. 70 71 Sockets should be closed using the `socket-close' method.")) 72 73 (defgeneric socket-state (socket) 74 (:documentation "NIL - not ready 75 :READ - ready to read 76 :READ-WRITE - ready to read and write 77 :WRITE - ready to write")) 78 79 (defmethod socket-state ((socket usocket)) 80 (state socket)) 81 82 (defclass stream-usocket (usocket) 83 ((stream 84 :initarg :stream 85 :accessor socket-stream 86 :documentation "Stream instance associated with the socket." 87 ;; 88 ;;Iff an external-format was passed to `socket-connect' or `socket-listen' 89 ;;the stream is a flexi-stream. Otherwise the stream is implementation 90 ;;specific." 91 )) 92 (:documentation 93 "Stream socket class. 94 ' 95 Contrary to other sockets, these sockets may be closed either 96 with the `socket-close' method or by closing the associated stream 97 (which can be retrieved with the `socket-stream' accessor).")) 98 99 (defclass stream-server-usocket (usocket) 100 ((element-type 101 :initarg :element-type 102 :initform #-lispworks 'character 103 #+lispworks 'base-char 104 :reader element-type 105 :documentation "Default element type for streams created by 106 `socket-accept'.")) 107 (:documentation "Socket which listens for stream connections to 108 be initiated from remote sockets.")) 109 110 (defclass datagram-usocket (usocket) 111 ((connected-p :type boolean 112 :accessor connected-p 113 :initarg :connected-p) 114 #+(or cmu scl lispworks mcl 115 (and clisp ffi (not rawsock))) 116 (%open-p :type boolean 117 :accessor %open-p 118 :initform t 119 :documentation "Flag to indicate if usocket is open, 120 for GC on implementions operate on raw socket fd.") 121 #+(or lispworks mcl 122 (and clisp ffi (not rawsock))) 123 (recv-buffer :documentation "Private RECV buffer.") 124 #+(or lispworks mcl) 125 (send-buffer :documentation "Private SEND buffer.")) 126 (:documentation "UDP (inet-datagram) socket")) 127 128 (defun usocket-p (socket) 129 (typep socket 'usocket)) 130 131 (defun stream-usocket-p (socket) 132 (typep socket 'stream-usocket)) 133 134 (defun stream-server-usocket-p (socket) 135 (typep socket 'stream-server-usocket)) 136 137 (defun datagram-usocket-p (socket) 138 (typep socket 'datagram-usocket)) 139 140 (defun make-socket (&key socket) 141 "Create a usocket socket type from implementation specific socket." 142 (unless socket 143 (error 'invalid-socket-error)) 144 (make-stream-socket :socket socket)) 145 146 (defun make-stream-socket (&key socket stream) 147 "Create a usocket socket type from implementation specific socket 148 and stream objects. 149 150 Sockets returned should be closed using the `socket-close' method or 151 by closing the stream associated with the socket. 152 " 153 (unless socket 154 (error 'invalid-socket-error)) 155 (unless stream 156 (error 'invalid-socket-stream-error)) 157 (make-instance 'stream-usocket 158 :socket socket 159 :stream stream)) 160 161 (defun make-stream-server-socket (socket &key (element-type 162 #-lispworks 'character 163 #+lispworks 'base-char)) 164 "Create a usocket-server socket type from an 165 implementation-specific socket object. 166 167 The returned value is a subtype of `stream-server-usocket'. 168 " 169 (unless socket 170 (error 'invalid-socket-error)) 171 (make-instance 'stream-server-usocket 172 :socket socket 173 :element-type element-type)) 174 175 (defun make-datagram-socket (socket &key connected-p) 176 (unless socket 177 (error 'invalid-socket-error)) 178 (make-instance 'datagram-usocket 179 :socket socket 180 :connected-p connected-p)) 181 182 (defgeneric socket-accept (socket &key element-type) 183 (:documentation 184 "Accepts a connection from `socket', returning a `stream-socket'. 185 186 The stream associated with the socket returned has `element-type' when 187 explicitly specified, or the element-type passed to `socket-listen' otherwise.")) 188 189 (defgeneric socket-close (usocket) 190 (:documentation "Close a previously opened `usocket'.")) 191 192 (defmethod socket-close :before ((usocket usocket)) 193 (when (wait-list usocket) 194 (remove-waiter (wait-list usocket) usocket))) 195 196 ;; also see http://stackoverflow.com/questions/4160347/close-vs-shutdown-socket 197 (defgeneric socket-shutdown (usocket direction) 198 (:documentation "Shutdown communication on the socket in DIRECTION. 199 200 After a shutdown no input and/or output of the indicated DIRECTION 201 can be performed on the `usocket'. 202 203 DIRECTION should be either :INPUT or :OUTPUT or :IO")) 204 205 (defgeneric socket-send (usocket buffer length &key host port) 206 (:documentation "Send packets through a previously opend `usocket'.")) 207 208 (defgeneric socket-receive (usocket buffer length &key) 209 (:documentation "Receive packets from a previously opend `usocket'. 210 211 Returns 4 values: (values buffer size host port)")) 212 213 (defgeneric get-local-address (socket) 214 (:documentation "Returns the IP address of the socket.")) 215 216 (defgeneric get-peer-address (socket) 217 (:documentation 218 "Returns the IP address of the peer the socket is connected to.")) 219 220 (defgeneric get-local-port (socket) 221 (:documentation "Returns the IP port of the socket. 222 223 This function applies to both `stream-usocket' and `server-stream-usocket' 224 type objects.")) 225 226 (defgeneric get-peer-port (socket) 227 (:documentation "Returns the IP port of the peer the socket to.")) 228 229 (defgeneric get-local-name (socket) 230 (:documentation "Returns the IP address and port of the socket as values. 231 232 This function applies to both `stream-usocket' and `server-stream-usocket' 233 type objects.")) 234 235 (defgeneric get-peer-name (socket) 236 (:documentation 237 "Returns the IP address and port of the peer 238 the socket is connected to as values.")) 239 240 (defmacro with-connected-socket ((var socket) &body body) 241 "Bind `socket' to `var', ensuring socket destruction on exit. 242 243 `body' is only evaluated when `var' is bound to a non-null value. 244 245 The `body' is an implied progn form." 246 `(let ((,var ,socket)) 247 (unwind-protect 248 (when ,var 249 (with-mapped-conditions (,var) 250 ,@body)) 251 (when ,var 252 (socket-close ,var))))) 253 254 (defmacro with-client-socket ((socket-var stream-var &rest socket-connect-args) 255 &body body) 256 "Bind the socket resulting from a call to `socket-connect' with 257 the arguments `socket-connect-args' to `socket-var' and if `stream-var' is 258 non-nil, bind the associated socket stream to it." 259 `(with-connected-socket (,socket-var (socket-connect ,@socket-connect-args)) 260 ,(if (null stream-var) 261 `(progn ,@body) 262 `(let ((,stream-var (socket-stream ,socket-var))) 263 ,@body)))) 264 265 (defmacro with-server-socket ((var server-socket) &body body) 266 "Bind `server-socket' to `var', ensuring socket destruction on exit. 267 268 `body' is only evaluated when `var' is bound to a non-null value. 269 270 The `body' is an implied progn form." 271 `(with-connected-socket (,var ,server-socket) 272 ,@body)) 273 274 (defmacro with-socket-listener ((socket-var &rest socket-listen-args) 275 &body body) 276 "Bind the socket resulting from a call to `socket-listen' with arguments 277 `socket-listen-args' to `socket-var'." 278 `(with-server-socket (,socket-var (socket-listen ,@socket-listen-args)) 279 ,@body)) 280 281 (defstruct (wait-list (:constructor %make-wait-list)) 282 %wait ;; implementation specific 283 waiters ;; the list of all usockets 284 map) ;; maps implementation sockets to usockets 285 286 ;; Implementation specific: 287 ;; 288 ;; %setup-wait-list 289 ;; %add-waiter 290 ;; %remove-waiter 291 292 (defun make-wait-list (waiters) 293 (let ((wl (%make-wait-list))) 294 (setf (wait-list-map wl) (make-hash-table)) 295 (%setup-wait-list wl) 296 (dolist (x waiters wl) ; wl is returned 297 (add-waiter wl x)))) 298 299 (defun add-waiter (wait-list input) 300 (setf (gethash (socket input) (wait-list-map wait-list)) input 301 (wait-list input) wait-list) 302 (pushnew input (wait-list-waiters wait-list)) 303 (%add-waiter wait-list input)) 304 305 (defun remove-waiter (wait-list input) 306 (%remove-waiter wait-list input) 307 (setf (wait-list-waiters wait-list) 308 (remove input (wait-list-waiters wait-list)) 309 (wait-list input) nil) 310 (remhash (socket input) (wait-list-map wait-list))) 311 312 (defun remove-all-waiters (wait-list) 313 (dolist (waiter (wait-list-waiters wait-list)) 314 (%remove-waiter wait-list waiter)) 315 (setf (wait-list-waiters wait-list) nil) 316 (clrhash (wait-list-map wait-list))) 317 318 (defun wait-for-input (socket-or-sockets &key timeout ready-only 319 &aux (single-socket-p 320 (usocket-p socket-or-sockets))) 321 "Waits for one or more streams to become ready for reading from 322 the socket. When `timeout' (a non-negative real number) is 323 specified, wait `timeout' seconds, or wait indefinitely when 324 it isn't specified. A `timeout' value of 0 (zero) means polling. 325 326 Returns two values: the first value is the list of streams which 327 are readable (or in case of server streams acceptable). NIL may 328 be returned for this value either when waiting timed out or when 329 it was interrupted (EINTR). The second value is a real number 330 indicating the time remaining within the timeout period or NIL if 331 none. 332 333 Without the READY-ONLY arg, WAIT-FOR-INPUT will return all sockets in 334 the original list you passed it. This prevents a new list from being 335 consed up. Some users of USOCKET were reluctant to use it if it 336 wouldn't behave that way, expecting it to cost significant performance 337 to do the associated garbage collection. 338 339 Without the READY-ONLY arg, you need to check the socket STATE slot for 340 the values documented in usocket.lisp in the usocket class." 341 342 ;; for NULL sockets, return NIL with respect of TIMEOUT. 343 (when (null socket-or-sockets) 344 (when timeout 345 (sleep timeout)) 346 (return-from wait-for-input nil)) 347 348 ;; create a new wait-list if it's not created by the caller. 349 (unless (wait-list-p socket-or-sockets) 350 ;; OPTIMIZATION: in case socket-or-sockets is an atom, create the wait-list 351 ;; only once and store it into the usocket itself. 352 (let ((wl (if (and single-socket-p 353 (wait-list socket-or-sockets)) 354 (wait-list socket-or-sockets) ; reuse the per-usocket wait-list 355 (make-wait-list (if (listp socket-or-sockets) 356 socket-or-sockets (list socket-or-sockets)))))) 357 (multiple-value-bind (sockets to-result) 358 (wait-for-input wl :timeout timeout :ready-only ready-only) 359 ;; in case of single socket, keep the wait-list 360 (unless single-socket-p 361 (remove-all-waiters wl)) 362 (return-from wait-for-input 363 (values (if ready-only sockets socket-or-sockets) to-result))))) 364 365 (let* ((start (get-internal-real-time)) 366 (sockets-ready 0)) 367 (dolist (x (wait-list-waiters socket-or-sockets)) 368 (when (setf (state x) 369 #+(and win32 (or sbcl ecl)) nil ; they cannot rely on LISTEN 370 #-(and win32 (or sbcl ecl)) 371 (if (and (stream-usocket-p x) 372 (listen (socket-stream x))) 373 :read 374 nil)) 375 (incf sockets-ready))) 376 ;; the internal routine is responsibe for 377 ;; making sure the wait doesn't block on socket-streams of 378 ;; which theready- socket isn't ready, but there's space left in the 379 ;; buffer. socket-or-sockets is not destructed. 380 (wait-for-input-internal socket-or-sockets 381 :timeout (if (zerop sockets-ready) timeout 0)) 382 (let ((to-result (when timeout 383 (let ((elapsed (/ (- (get-internal-real-time) start) 384 internal-time-units-per-second))) 385 (when (< elapsed timeout) 386 (- timeout elapsed)))))) 387 ;; two return values: 388 ;; 1) the original wait-list, or available sockets (ready-only) 389 ;; 2) remaining timeout 390 (values (cond (ready-only 391 (cond (single-socket-p 392 (if (null (state (car (wait-list-waiters socket-or-sockets)))) 393 nil ; nothing left if the only socket is not waiting 394 (wait-list-waiters socket-or-sockets))) 395 (t (remove-if #'null (wait-list-waiters socket-or-sockets) :key #'state)))) 396 (t socket-or-sockets)) 397 to-result)))) 398 399 ;; 400 ;; Data utility functions 401 ;; 402 403 (defun integer-to-octet-buffer (integer buffer octets &key (start 0)) 404 (do ((b start (1+ b)) 405 (i (ash (1- octets) 3) ;; * 8 406 (- i 8))) 407 ((> 0 i) buffer) 408 (setf (aref buffer b) 409 (ldb (byte 8 i) integer)))) 410 411 (defun octet-buffer-to-integer (buffer octets &key (start 0)) 412 (let ((integer 0)) 413 (do ((b start (1+ b)) 414 (i (ash (1- octets) 3) ;; * 8 415 (- i 8))) 416 ((> 0 i) 417 integer) 418 (setf (ldb (byte 8 i) integer) 419 (aref buffer b))))) 420 421 (defmacro port-to-octet-buffer (port buffer &key (start 0)) 422 `(integer-to-octet-buffer ,port ,buffer 2 :start ,start)) 423 424 (defmacro ip-to-octet-buffer (ip buffer &key (start 0)) 425 `(integer-to-octet-buffer (host-byte-order ,ip) ,buffer 4 :start ,start)) 426 427 (defmacro port-from-octet-buffer (buffer &key (start 0)) 428 `(octet-buffer-to-integer ,buffer 2 :start ,start)) 429 430 (defmacro ip-from-octet-buffer (buffer &key (start 0)) 431 `(octet-buffer-to-integer ,buffer 4 :start ,start)) 432 433 ;; 434 ;; IPv4 utility functions 435 ;; 436 437 (defun list-of-strings-to-integers (list) 438 "Take a list of strings and return a new list of integers (from 439 parse-integer) on each of the string elements." 440 (let ((new-list nil)) 441 (dolist (element (reverse list)) 442 (push (parse-integer element) new-list)) 443 new-list)) 444 445 (defun ip-address-string-p (string) 446 "Return a true value if the given string could be an IP address." 447 (every (lambda (char) 448 (or (digit-char-p char) 449 (eql char #\.))) 450 string)) 451 452 (defun hbo-to-dotted-quad (integer) ; exported 453 "Host-byte-order integer to dotted-quad string conversion utility." 454 (let ((first (ldb (byte 8 24) integer)) 455 (second (ldb (byte 8 16) integer)) 456 (third (ldb (byte 8 8) integer)) 457 (fourth (ldb (byte 8 0) integer))) 458 (format nil "~A.~A.~A.~A" first second third fourth))) 459 460 (defun hbo-to-vector-quad (integer) ; exported 461 "Host-byte-order integer to dotted-quad string conversion utility." 462 (let ((first (ldb (byte 8 24) integer)) 463 (second (ldb (byte 8 16) integer)) 464 (third (ldb (byte 8 8) integer)) 465 (fourth (ldb (byte 8 0) integer))) 466 (vector first second third fourth))) 467 468 (defun vector-quad-to-dotted-quad (vector) ; exported 469 (format nil "~A.~A.~A.~A" 470 (aref vector 0) 471 (aref vector 1) 472 (aref vector 2) 473 (aref vector 3))) 474 475 (defun dotted-quad-to-vector-quad (string) ; exported 476 (let ((list (list-of-strings-to-integers (split-sequence #\. string)))) 477 (vector (first list) (second list) (third list) (fourth list)))) 478 479 (defgeneric host-byte-order (address)) ; exported 480 481 (defmethod host-byte-order ((string string)) 482 "Convert a string, such as 192.168.1.1, to host-byte-order, 483 such as 3232235777." 484 (let ((list (list-of-strings-to-integers (split-sequence #\. string)))) 485 (+ (* (first list) 256 256 256) (* (second list) 256 256) 486 (* (third list) 256) (fourth list)))) 487 488 (defmethod host-byte-order ((vector vector)) ; IPv4 only 489 "Convert a vector, such as #(192 168 1 1), to host-byte-order, such as 490 3232235777." 491 (+ (* (aref vector 0) 256 256 256) (* (aref vector 1) 256 256) 492 (* (aref vector 2) 256) (aref vector 3))) 493 494 (defmethod host-byte-order ((int integer)) 495 int) ; this assume input integer is already host-byte-order 496 497 ;; 498 ;; IPv6 utility functions 499 ;; 500 501 (defun vector-to-ipv6-host (vector) ; exported 502 (with-output-to-string (*standard-output*) 503 (loop with zeros-collapsed-p 504 with collapsing-zeros-p 505 for i below 16 by 2 506 for word = (+ (ash (aref vector i) 8) 507 (aref vector (1+ i))) 508 do (cond 509 ((and (zerop word) 510 (not collapsing-zeros-p) 511 (not zeros-collapsed-p)) 512 (setf collapsing-zeros-p t)) 513 ((or (not (zerop word)) 514 zeros-collapsed-p) 515 (when collapsing-zeros-p 516 (write-string ":") 517 (setf collapsing-zeros-p nil 518 zeros-collapsed-p t)) 519 (format t "~:[~;:~]~X" (plusp i) word))) 520 finally (when collapsing-zeros-p 521 (write-string "::"))))) 522 523 (defun split-ipv6-address (string) 524 (let ((pos 0) 525 word 526 double-colon-seen-p 527 words-before-double-colon 528 words-after-double-colon) 529 (loop 530 (multiple-value-setq (word pos) (parse-integer string :radix 16 :junk-allowed t :start pos)) 531 (labels ((at-end-p () 532 (= pos (length string))) 533 (looking-at-colon-p () 534 (char= (char string pos) #\:)) 535 (ensure-colon () 536 (unless (looking-at-colon-p) 537 (error "unsyntactic IPv6 address string ~S, expected a colon at position ~D" 538 string pos)) 539 (incf pos))) 540 (cond 541 ((null word) 542 (when double-colon-seen-p 543 (error "unsyntactic IPv6 address string ~S, can only have one double-colon filler mark" 544 string)) 545 (setf double-colon-seen-p t)) 546 (double-colon-seen-p 547 (push word words-after-double-colon)) 548 (t 549 (push word words-before-double-colon))) 550 (if (at-end-p) 551 (return (list (nreverse words-before-double-colon) (nreverse words-after-double-colon))) 552 (ensure-colon)))))) 553 554 (defun ipv6-host-to-vector (string) ; exported 555 (assert (> (length string) 2) () 556 "Unsyntactic IPv6 address literal ~S, expected at least three characters" string) 557 (destructuring-bind (words-before-double-colon words-after-double-colon) 558 (split-ipv6-address (concatenate 'string 559 (when (eql (char string 0) #\:) 560 "0") 561 string 562 (when (eql (char string (1- (length string))) #\:) 563 "0"))) 564 (let ((number-of-words-specified (+ (length words-before-double-colon) (length words-after-double-colon)))) 565 (assert (<= number-of-words-specified 8) () 566 "Unsyntactic IPv6 address literal ~S, too many colon separated address components" string) 567 (assert (or (= number-of-words-specified 8) words-after-double-colon) () 568 "Unsyntactic IPv6 address literal ~S, too few address components and no double-colon filler found" string) 569 (loop with vector = (make-array 16 :element-type '(unsigned-byte 8)) 570 for i below 16 by 2 571 for word in (append words-before-double-colon 572 (make-list (- 8 number-of-words-specified) :initial-element 0) 573 words-after-double-colon) 574 do (setf (aref vector i) (ldb (byte 8 8) word) 575 (aref vector (1+ i)) (ldb (byte 8 0) word)) 576 finally (return vector))))) 577 578 ;; exported since 0.8.0 579 (defun host-to-hostname (host) ; host -> string 580 "Translate a string, vector quad or 16 byte IPv6 address to a 581 stringified hostname." 582 (etypecase host 583 (string host) ; IPv4 or IPv6 584 ((or (vector t 4) ; IPv4 585 (array (unsigned-byte 8) (4))) 586 (vector-quad-to-dotted-quad host)) 587 ((or (vector t 16) ; IPv6 588 (array (unsigned-byte 8) (16))) 589 (vector-to-ipv6-host host)) 590 (integer (hbo-to-dotted-quad host)) ; integer input is IPv4 only 591 (null "0.0.0.0"))) ; null is IPv4 592 593 (defun ip= (ip1 ip2) ; exported 594 (etypecase ip1 595 (string (string= ip1 ; IPv4 or IPv6 596 (host-to-hostname ip2))) 597 ((or (vector t 4) ; IPv4 598 (array (unsigned-byte 8) (4)) ; IPv4 599 (vector t 16) ; IPv6 600 (array (unsigned-byte 8) (16))) ; IPv6 601 (equalp ip1 ip2)) 602 (integer (= ip1 ; IPv4 only 603 (host-byte-order ip2))))) ; convert ip2 to integer (hbo) 604 605 (defun ip/= (ip1 ip2) ; exported 606 (not (ip= ip1 ip2))) 607 608 ;; 609 ;; DNS helper functions 610 ;; 611 612 (defun get-host-by-name (name) 613 "0.7.1+: if there're IPv4 addresses, return the first IPv4 address." 614 (let* ((hosts (get-hosts-by-name name)) 615 (pos (position-if #'(lambda (ip) (= 4 (length ip))) hosts))) 616 (if pos (elt hosts pos) 617 (car hosts)))) 618 619 (defun get-random-host-by-name (name) 620 "0.7.1+: if there're IPv4 addresses, only return a random IPv4 address." 621 (let* ((hosts (get-hosts-by-name name)) 622 (ipv4-hosts (remove-if-not #'(lambda (ip) (= 4 (length ip))) hosts))) 623 (cond (ipv4-hosts 624 (elt ipv4-hosts (random (length ipv4-hosts)))) 625 (hosts 626 (elt hosts (random (length hosts))))))) 627 628 (defun host-to-vector-quad (host) ; internal 629 "Translate a host specification (vector quad, dotted quad or domain name) 630 to a vector quad." 631 (etypecase host 632 (string (let* ((ip (when (ip-address-string-p host) 633 (dotted-quad-to-vector-quad host)))) 634 (if (and ip (= 4 (length ip))) 635 ;; valid IP dotted quad? not sure 636 ip 637 (get-random-host-by-name host)))) 638 ((or (vector t 4) 639 (array (unsigned-byte 8) (4))) 640 host) 641 (integer (hbo-to-vector-quad host)))) 642 643 (defun host-to-hbo (host) ; internal 644 (etypecase host 645 (string (let ((ip (when (ip-address-string-p host) 646 (dotted-quad-to-vector-quad host)))) 647 (if (and ip (= 4 (length ip))) 648 (host-byte-order ip) 649 (host-to-hbo (get-host-by-name host))))) 650 ((or (vector t 4) 651 (array (unsigned-byte 8) (4))) 652 (host-byte-order host)) 653 (integer host))) 654 655 ;; 656 ;; Other utility functions 657 ;; 658 659 (defun split-timeout (timeout &optional (fractional 1000000)) 660 "Split real value timeout into seconds and microseconds. 661 Optionally, a different fractional part can be specified." 662 (multiple-value-bind 663 (secs sec-frac) 664 (truncate timeout 1) 665 (values secs 666 (truncate (* fractional sec-frac) 1)))) 667 668 ;; 669 ;; Setting of documentation for backend defined functions 670 ;; 671 672 ;; Documentation for the function 673 ;; 674 ;; (defun SOCKET-CONNECT (host port &key element-type nodelay some-other-keys...) ..) 675 ;; 676 (setf (documentation 'socket-connect 'function) 677 "Connect to `host' on `port'. `host' is assumed to be a string or 678 an IP address represented in vector notation, such as #(192 168 1 1). 679 `port' is assumed to be an integer. 680 681 `element-type' specifies the element type to use when constructing the 682 stream associated with the socket. The default is 'character. 683 684 `nodelay' Allows to disable/enable Nagle's algorithm (http://en.wikipedia.org/wiki/Nagle%27s_algorithm). 685 If this parameter is omitted, the behaviour is inherited from the 686 CL implementation (in most cases, Nagle's algorithm is 687 enabled by default, but for example in ACL it is disabled). 688 If the parameter is specified, one of these three values is possible: 689 T - Disable Nagle's algorithm; signals an UNSUPPORTED 690 condition if the implementation does not support explicit 691 manipulation with that option. 692 NIL - Leave Nagle's algorithm enabled on the socket; 693 signals an UNSUPPORTED condition if the implementation does 694 not support explicit manipulation with that option. 695 :IF-SUPPORTED - Disables Nagle's algorithm if the implementation 696 allows this, otherwises just ignore this option. 697 698 Returns a usocket object.") 699 700 ;; Documentation for the function 701 ;; 702 ;; (defun SOCKET-LISTEN (host port &key reuseaddress backlog element-type) ..) 703 ;;###FIXME: extend with default-element-type 704 (setf (documentation 'socket-listen 'function) 705 "Bind to interface `host' on `port'. `host' should be the 706 representation of an ready-interface address. The implementation is 707 not required to do an address lookup, making no guarantees that 708 hostnames will be correctly resolved. If `*wildcard-host*' or NIL is 709 passed for `host', the socket will be bound to all available 710 interfaces for the system. `port' can be selected by the IP stack by 711 passing `*auto-port*'. 712 713 Returns an object of type `stream-server-usocket'. 714 715 `reuse-address' and `backlog' are advisory parameters for setting socket 716 options at creation time. `element-type' is the element type of the 717 streams to be created by `socket-accept'. `reuseaddress' is supported for 718 backward compatibility (but deprecated); when both `reuseaddress' and 719 `reuse-address' have been specified, the latter takes precedence. 720 ") 721 722 ;;; Small utility functions mapping true/false to 1/0, moved here from option.lisp 723 724 (proclaim '(inline bool->int int->bool)) 725 726 (defun bool->int (bool) (if bool 1 0)) 727 (defun int->bool (int) (= 1 int))