Coverage report: /home/ellis/.stash/lisp/cl-plus-ssl/src/streams.lisp
Kind | Covered | All | % |
expression | 238 | 606 | 39.3 |
branch | 14 | 64 | 21.9 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;;; -*- Mode: LISP; Syntax: COMMON-LISP; indent-tabs-mode: nil; coding: utf-8; show-trailing-whitespace: t -*-
3
;;; Copyright (C) 2001, 2003 Eric Marsden
4
;;; Copyright (C) 2005 David Lichteblau
5
;;; Copyright (C) 2007 Pixel // pinterface
6
;;; "the conditions and ENSURE-SSL-FUNCALL are by Jochen Schmidt."
7
;;; Copyright (C) contributors as per cl+ssl git history
9
;;; See LICENSE for details.
11
(eval-when (:compile-toplevel)
13
(optimize (speed 3) (space 1) (safety 1) (debug 0) (compilation-speed 0))))
17
;; Default Cipher List
18
(defvar *default-cipher-list* nil)
20
(defparameter *default-buffer-size* 2048
21
"The default size for input and output buffers of SSL-STREAM objects")
24
(trivial-gray-stream-mixin
25
fundamental-binary-input-stream
26
fundamental-binary-output-stream)
29
:accessor ssl-stream-socket)
31
:initarg :close-callback
32
:accessor ssl-close-callback)
35
:accessor ssl-stream-handle)
39
:accessor ssl-stream-deadline)
41
:accessor ssl-stream-output-buffer)
44
:accessor ssl-stream-output-pointer)
46
:accessor ssl-stream-input-buffer)
49
:accessor ssl-stream-peeked-byte)))
51
(defmethod initialize-instance :after ((stream ssl-stream)
53
(buffer-size *default-buffer-size*)
54
(input-buffer-size buffer-size)
55
(output-buffer-size buffer-size)
57
(setf (ssl-stream-output-buffer stream)
58
(make-buffer output-buffer-size))
59
(setf (ssl-stream-input-buffer stream)
60
(make-buffer input-buffer-size)))
62
(defmethod print-object ((object ssl-stream) stream)
63
(print-unreadable-object (object stream :type t)
64
(format stream "for ~A" (ssl-stream-socket object))))
66
(defclass ssl-server-stream (ssl-stream)
69
:accessor ssl-stream-certificate)
72
:accessor ssl-stream-key)))
74
(defmethod stream-element-type ((stream ssl-stream))
77
(defmethod close ((stream ssl-stream) &key abort)
79
((ssl-stream-handle stream)
82
(ensure-ssl-funcall stream
85
(ssl-stream-handle stream)))
86
(ssl-free (ssl-stream-handle stream))
87
(setf (ssl-stream-handle stream) nil)
88
(when (streamp (ssl-stream-socket stream))
89
(close (ssl-stream-socket stream) :abort abort))
90
(when (ssl-close-callback stream)
91
(funcall (ssl-close-callback stream)))
96
(defmethod open-stream-p ((stream ssl-stream))
97
(and (ssl-stream-handle stream) t))
99
(defmethod stream-listen ((stream ssl-stream))
100
(or (ssl-stream-peeked-byte stream)
101
(setf (ssl-stream-peeked-byte stream)
102
(let* ((buf (ssl-stream-input-buffer stream))
103
(handle (ssl-stream-handle stream))
104
(*bio-blockp* nil) ;; for the Lisp-BIO
105
(n (with-pointer-to-vector-data (ptr buf)
106
(nonblocking-ssl-funcall
107
stream #'plusp #'ssl-read handle ptr 1))))
108
(and (> n 0) (buffer-elt buf 0))))))
110
(defmethod stream-read-byte ((stream ssl-stream))
112
(ssl-stream-peeked-byte stream)
113
(setf (ssl-stream-peeked-byte stream) nil))
115
(let ((buf (ssl-stream-input-buffer stream))
116
(handle (ssl-stream-handle stream)))
117
(with-pointer-to-vector-data (ptr buf)
119
stream #'plusp #'ssl-read handle ptr 1))
121
(ssl-error-zero-return () ;SSL_read returns 0 on end-of-file
124
(defmethod stream-read-sequence ((stream ssl-stream) seq start end &key)
125
(when (and (< start end) (ssl-stream-peeked-byte stream))
126
(setf (elt seq start) (ssl-stream-peeked-byte stream))
127
(setf (ssl-stream-peeked-byte stream) nil)
129
(let ((buf (ssl-stream-input-buffer stream))
130
(handle (ssl-stream-handle stream)))
132
for length = (min (- end start) (buffer-length buf))
137
(with-pointer-to-vector-data (ptr buf)
139
stream #'plusp #'ssl-read handle ptr length))))
140
(s/b-replace seq buf :start1 start :end1 (+ start read-bytes))
141
(incf start read-bytes))
142
(ssl-error-zero-return () ;SSL_read returns 0 on end-of-file
144
;; fixme: kein out-of-file wenn (zerop start)?
147
(defmethod stream-write-byte ((stream ssl-stream) b)
148
(let ((buf (ssl-stream-output-buffer stream)))
149
(when (eql (buffer-length buf) (ssl-stream-output-pointer stream))
150
(force-output stream))
151
(setf (buffer-elt buf (ssl-stream-output-pointer stream)) b)
152
(incf (ssl-stream-output-pointer stream)))
155
(defmacro while (cond &body body)
156
`(do () ((not ,cond)) ,@body))
158
(defmethod stream-write-sequence ((stream ssl-stream) seq start end &key)
159
(let ((buf (ssl-stream-output-buffer stream)))
160
(when (> (+ (- end start) (ssl-stream-output-pointer stream)) (buffer-length buf))
161
;; not enough space left? flush buffer.
162
(force-output stream)
163
;; still doesn't fit?
164
(while (> (- end start) (buffer-length buf))
165
(b/s-replace buf seq :start2 start)
166
(incf start (buffer-length buf))
167
(setf (ssl-stream-output-pointer stream) (buffer-length buf))
168
(force-output stream)))
170
:start1 (ssl-stream-output-pointer stream)
173
(incf (ssl-stream-output-pointer stream) (- end start)))
176
(defmethod stream-finish-output ((stream ssl-stream))
177
(stream-force-output stream))
179
(defmethod stream-force-output ((stream ssl-stream))
180
(let ((buf (ssl-stream-output-buffer stream))
181
(fill-ptr (ssl-stream-output-pointer stream))
182
(handle (ssl-stream-handle stream)))
183
(when (plusp fill-ptr)
185
(error "output operation on closed SSL stream"))
186
(with-pointer-to-vector-data (ptr buf)
187
(ensure-ssl-funcall stream #'plusp #'ssl-write handle ptr fill-ptr))
188
(setf (ssl-stream-output-pointer stream) 0))))
190
#+(and clozure-common-lisp (not windows))
191
(defun install-nonblock-flag (fd)
192
(ccl::fd-set-flags fd (logior (ccl::fd-get-flags fd)
193
;; read-from-string is necessary because
194
;; CLISP and perhaps other Lisps are confused
196
;; "undefined dispatch character $",
197
;; even though the defun in conditionalized by
198
;; #+clozure-common-lisp
199
#.(read-from-string "#$O_NONBLOCK"))))
201
#+(and sbcl (not win32))
202
(defun install-nonblock-flag (fd)
205
(logior (sb-posix:fcntl fd sb-posix::f-getfl)
206
sb-posix::o-nonblock)))
208
#-(or (and clozure-common-lisp (not windows)) sbcl)
209
(defun install-nonblock-flag (fd)
210
(declare (ignore fd)))
213
(defun install-nonblock-flag (fd)
214
(when (boundp 'sockint::fionbio)
215
(sockint::ioctl fd sockint::fionbio 1)))
217
;;; interface functions
220
(defvar *default-unwrap-stream-p* t
221
"Default value for UNWRAP-STREAM-P function parameter.
223
If true (the default), cl+ssl will try to extract file descriptor
224
from the given TCP Lisp stream and tell OpenSSL to use a socket BIO
225
based on that file descriptor;
226
otherwise use a Lisp BIO wrapping the TCP Lisp stream.")
228
(defun install-handle-and-bio (stream handle socket unwrap-stream-p)
229
(setf (ssl-stream-handle stream) handle)
230
(when unwrap-stream-p
231
(let ((fd (stream-fd socket)))
236
(install-nonblock-flag socket)
237
(ssl-set-fd handle socket))
239
(ssl-set-bio handle (bio-new-lisp) (bio-new-lisp))))
241
;; The below call setting +SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER+ mode
242
;; existed since commit 5bd5225.
243
;; It is implemented wrong - ssl-ctx-ctrl expects
244
;; a context as the first parameter, not handle.
245
;; It was lucky to not crush on Linux and Windows,
246
;; untill crash was detedcted on OpenBSD + LibreSSL.
247
;; See https://github.com/cl-plus-ssl/cl-plus-ssl/pull/42.
248
;; We keep this code commented but not removed because
249
;; we don't know what David Lichteblau meant when
250
;; added this - maybe he has some idea?
251
;; (Although modifying global context is a bad
252
;; thing to do for install-handle-and-bio function,
253
;; also we don't see a need for movable buffer -
254
;; we don't repeat calls to ssl functions with
257
;; (ssl-ctx-ctrl handle
259
;; +SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER+
260
;; (cffi:null-pointer))
264
(defun install-key-and-cert (handle key certificate)
266
(unless (eql 1 (ssl-use-certificate-file handle
269
(error 'ssl-error-initialize
270
:reason (format nil "Can't load certificate ~A" certificate))))
272
(unless (eql 1 (ssl-use-privatekey-file handle
275
(error 'ssl-error-initialize :reason (format nil "Can't load private key file ~A" key)))))
277
(defun x509-certificate-names (x509-certificate)
278
(unless (cffi:null-pointer-p x509-certificate)
279
(cffi:with-foreign-pointer (buf 1024)
280
(let ((issuer-name (x509-get-issuer-name x509-certificate))
281
(subject-name (x509-get-subject-name x509-certificate)))
283
(unless (cffi:null-pointer-p issuer-name)
284
(x509-name-oneline issuer-name buf 1024)
285
(cffi:foreign-string-to-lisp buf))
286
(unless (cffi:null-pointer-p subject-name)
287
(x509-name-oneline subject-name buf 1024)
288
(cffi:foreign-string-to-lisp buf)))))))
290
(defmethod ssl-stream-handle ((stream flexi-streams:flexi-stream))
291
(ssl-stream-handle (flexi-streams:flexi-stream-stream stream)))
293
(defun ssl-stream-x509-certificate (ssl-stream)
294
(compat-ssl-get1-peer-certificate (ssl-stream-handle ssl-stream)))
296
(defun ssl-load-global-verify-locations (&rest pathnames)
297
"PATHNAMES is a list of pathnames to PEM files containing server and CA certificates.
298
Install these certificates to use for verifying on all SSL connections.
299
After RELOAD, you need to call this again."
301
(dolist (path pathnames)
302
(let ((namestring (namestring (truename path))))
303
(cffi:with-foreign-strings ((cafile namestring))
304
(unless (eql 1 (ssl-ctx-load-verify-locations
307
(cffi:null-pointer)))
308
(error "ssl-ctx-load-verify-locations failed."))))))
310
(defun ssl-set-global-default-verify-paths ()
311
"Load the system default verification certificates.
312
After RELOAD, you need to call this again."
314
(unless (eql 1 (ssl-ctx-set-default-verify-paths *ssl-global-context*))
315
(error "ssl-ctx-set-default-verify-paths failed.")))
317
(defun ssl-check-verify-p ()
318
"DEPRECATED. Use the (MAKE-SSL-CLIENT-STREAM .. :VERIFY ?) to enable/disable verification.
319
Also, MAKE-CONTEXT has :VERIFY-MODE option.
321
Return true if SSL connections will error if the certificate doesn't verify."
322
(and *ssl-check-verify-p* (not (eq *ssl-check-verify-p* :unspecified))))
324
(defun (setf ssl-check-verify-p) (check-verify-p)
325
"DEPRECATED. Use the (MAKE-SSL-CLIENT-STREAM .. :VERIFY ?) to enable/disable verification.
326
Also, MAKE-CONTEXT has :VERIFY-MODE option.
328
If CHECK-VERIFY-P is true, signal connection errors if the server certificate doesn't verify."
329
(setf *ssl-check-verify-p* (not (null check-verify-p))))
331
(defun ssl-verify-init (&key
333
(verify-locations nil))
335
Use the (MAKE-SSL-CLIENT-STREAM .. :VERIFY ?) to enable/disable verification.
336
Use (MAKE-CONTEXT ... :VERIFY-LOCATION ? :VERIFY-DEPTH ?) to control the verification depth and locations.
337
MAKE-CONTEXT also allows to enab/disable verification."
338
(check-type verify-depth (or null integer))
341
(ssl-ctx-set-verify-depth *ssl-global-context* verify-depth))
342
(when verify-locations
343
(apply #'ssl-load-global-verify-locations verify-locations)
344
;; This makes (setf (ssl-check-verify) nil) persistent
345
(unless (null *ssl-check-verify-p*)
346
(setf (ssl-check-verify-p) t))
349
(defun maybe-verify-client-stream (ssl-stream verify-mode hostname)
350
;; VERIFY-MODE is one of NIL, :OPTIONAL, :REQUIRED
351
;; HOSTNAME is either NIL or a string.
353
(let* ((handle (ssl-stream-handle ssl-stream))
354
(srv-cert (compat-ssl-get1-peer-certificate handle)))
357
(when (and (eq :required verify-mode)
358
(cffi:null-pointer-p srv-cert))
359
(error 'server-certificate-missing
360
:format-control "The server didn't present a certificate."))
361
(let ((err (ssl-get-verify-result handle)))
362
(unless (eql err +x509-v-ok+)
363
(error 'ssl-error-verify :stream ssl-stream :error-code err)))
365
(not (cffi:null-pointer-p srv-cert)))
366
(or (verify-hostname srv-cert hostname)
367
;; verify-hostname must either return true
368
;; or signal an error
369
(error "Unexpected NIL returned by CL+SSL:VERIFY-HOSTNAME for ~A"
371
(unless (cffi:null-pointer-p srv-cert)
372
(x509-free srv-cert))))))
374
(defun handle-external-format (stream ef)
376
(flexi-streams:make-flexi-stream stream :external-format ef)
379
(defmacro with-new-ssl ((var) &body body)
380
(alexandria:with-gensyms (ssl)
381
`(let* ((,ssl (ssl-new *ssl-global-context*))
383
(when (cffi:null-pointer-p ,ssl)
384
(error 'ssl-error-call :message "Unable to create SSL structure" :queue (read-ssl-error-queue)))
385
(handler-bind ((error (lambda (_)
390
(defvar *make-ssl-client-stream-verify-default*
391
(if (member :windows *features*) ; by trivial-features
392
;; On Windows we can't yet initizlise context with
393
;; trusted certifying authorities from system configuration.
394
;; ssl-ctx-set-default-verify-paths only helps
395
;; on Unix-like platforms.
396
;; See https://github.com/cl-plus-ssl/cl-plus-ssl/issues/54.
399
"Helps to mitigate the change in default behaviour of
400
MAKE-SSL-CLIENT-STREAM - previously it worked as if :VERIFY NIL
401
but then :VERIFY :REQUIRED became the default on non-Windows platforms.
402
Change this variable if you want the previous behaviour.")
404
(defun make-alpn-proto-string (protocols)
405
"Convert list of protocol names to the wire-format byte string."
406
(with-output-to-string (s)
407
(dolist (proto protocols)
408
(check-type proto string)
409
(write-char (code-char (length proto)) s)
410
(write-string proto s))))
412
;; fixme: free the context when errors happen in this function
413
(defun make-ssl-client-stream (socket
415
(unwrap-stream-p *default-unwrap-stream-p*)
419
(verify (if (ssl-check-verify-p)
421
*make-ssl-client-stream-verify-default*))
423
certificate key password
424
(cipher-list *default-cipher-list*)
426
(buffer-size *default-buffer-size*)
427
(input-buffer-size buffer-size)
428
(output-buffer-size buffer-size))
429
"Performs TLS/SSL handshake over the specified SOCKET using
430
the SSL_connect OpenSSL function and returns a Lisp stream that
431
uses OpenSSL library to encrypt the output data when sending
432
it to the socket and to decrypt the input received.
434
Uses a global SSL_CTX instance, which can be overriden
435
by WITH-GLOBAL-CONTEXT. (The global SSL_CTX is
436
passed as a parameter to an internall call of SSL_new.)
438
SOCKET - represents the socket to be wrapped into an SSL stream.
439
Can be either a Lisp stream (of an implementation-dependent type) for that
440
socket, or an integer file descriptor of that socket. If that's a
441
stream, it will be closed automatically when the SSL stream
442
is closed. Also, on CCL, (CCL:STREAM-DEADLINE SOCKET) will be used
443
as a deadline for 'socket BIO' mode.
444
See README.md / Usage / Timeouts and Deadlines for more information.
445
If that's a file descriptor, it is not closed automatically
446
(you can use CLOSE-CALLBACK to arrange for that).
448
UNWRAP-STREAM-P - if true, (STREAM-FD SOCKET) will be attempted
449
to extract the file descriptor. Otherwise the SOCKET
450
is left as is. Anyway, if in result we end up with an integer
451
file descriptor, a socket BIO is used; if we end up with a
452
stream - Lisp BIO is used. This parameter defaults to
453
*DEFAULT-UNWRAP-STREAM-P* which is initalized to true.
454
See README.md / Usage for more information on BIO types.
456
HOSTNAME if specified, will be sent by client during TLS negotiation,
457
according to the Server Name Indication (SNI) extension to the TLS.
458
If we connect to a server handling multiple domain names,
459
this extension enables such server to choose certificate for the
460
right domain. Also the HOSTNAME is used for hostname verification
461
(if verification is enabled by VERIFY).
463
CLOSE-CALLBACK - a function to be called when the created
464
ssl stream is CL:CLOSE'ed. The only argument is this ssl stream.
466
EXTERNAL-FORMAT - if NIL (the default), a plain (UNSIGNED-BYTE 8)
467
ssl stream is returned. With a non-NIL external-format, a flexi-stream
468
capable of character I/O will be returned instead, with the specified
469
value as its initial external format.
471
VERIFY can be specified either as NIL if no check should be performed,
472
:OPTIONAL to verify the server's certificate if server presents one or
473
:REQUIRED to verify the server's certificate and fail if an invalid
474
or no certificate was presented. Defaults to
475
*MAKE-SSL-CLIENT-STREAM-VERIFY-DEFAULT* which is initialized
478
The verification includes verifying the HOSTNAME against the server
479
ceritificate, using the VERIFY-HOSTNAME function.
481
An error is signalled in case of the certificate or hostname
482
verification failure.
484
Note, the VERIFY logic expects that the global
485
SSL_CTX object does not have the SSL_VERIFY_PEER
486
flag enabled - the default for the cl+ssl's global SSL_CTX.
487
If the current global SSL_CTX object has SSL_VERIFY_PEER enabled,
488
the SSL_Connect will perform certificate (but not hostname)
489
verification on its own, and an error will be signalled for a
490
bad certificate even with :VERIFY NIL.
492
ALPN-PROTOCOLS, if specified, should be a list of alpn protocol names,
493
such as \"h2\", that will be offered to the server. The protocol
494
selected by the server can be retrieved with
495
GET-SELECTED-ALPN-PROTOCOL.
497
CERTIFICATE is the path to a file containing a PEM-encoded certificate.
498
Note, if one certificate will be used for multiple TLS connections,
499
it's better to load it into a common SSL_CTX (context) object rather
500
than reading it for every new connection.
502
KEY is the path to a PEM-encoded private key file of that certificate.
504
PASSWORD the password to use for decryptipon of the KEY (if encrypted).
506
CIPHER-LIST - If not NIL, must be a string to pass to SSL_set_cipher_list.
507
An ERROR is signalled if SSL_CTX_set_cipher_list fails.
508
Defaults to *DEFAULT-CIPHER-LIST* which is initialized to NIL.
510
METHOD - usually you want to leave the default value. It is used
511
to compute the parameter for OpenSSL function SSL_CTX_new when
512
creating the global SSL_CTX object for cl+ssl. This parameter only has
513
effect on the first call, when the global SSL_CTX is not yet created.
514
The default value is TLS_method on OpenSSL > 1.1.0 and SSLv23_method
515
for older OpenSSL versions.
517
BUFFER-SIZE - default value for both the INPUT-BUFFER-SIZE and
518
OUTPUT-BUFFER-SIZE parameters. In turn defaults to the
519
*DEFAULT-BUFFER-SIZE* special variable.
521
INPUT-BUFFER-SIZE - size of the input buffer of the ssl stream.
522
Defaults to the BUFFER-SIZE parameter.
524
OUTPUT-BUFFER-SIZE - size of the output buffer of the ssl stream.
525
Defaults to the BUFFER-SIZE parameter.
527
(ensure-initialized :method method)
528
(let ((stream (make-instance 'ssl-stream
530
:close-callback close-callback
531
:input-buffer-size input-buffer-size
532
:output-buffer-size output-buffer-size)))
533
(with-new-ssl (handle)
535
(cffi:with-foreign-string (chostname hostname)
536
(ssl-set-tlsext-host-name handle chostname)))
538
(cffi:with-foreign-string ((string len) (make-alpn-proto-string alpn-protocols))
539
(ssl-set-alpn-protos handle string (1- len))))
540
(setf socket (install-handle-and-bio stream handle socket unwrap-stream-p))
541
(ssl-set-connect-state handle)
542
(when (and cipher-list
543
(zerop (ssl-set-cipher-list handle cipher-list)))
544
(error 'ssl-error-initialize
546
"Can't set SSL cipher list: SSL_set_cipher_list returned 0"))
547
(with-pem-password (password)
548
(install-key-and-cert handle key certificate))
549
(collecting-verify-error (handle)
550
(ensure-ssl-funcall stream #'plusp #'ssl-connect handle))
551
(maybe-verify-client-stream stream verify hostname)
552
(handle-external-format stream external-format))))
554
;; fixme: free the context when errors happen in this function
555
(defun make-ssl-server-stream (socket
557
(unwrap-stream-p *default-unwrap-stream-p*)
560
certificate key password
561
(cipher-list *default-cipher-list*)
563
(buffer-size *default-buffer-size*)
564
(input-buffer-size buffer-size)
565
(output-buffer-size buffer-size))
566
"Performs server-side TLS handshake over the specified SOCKET using
567
the SSL_accept OpenSSL function and returns a Lisp stream that
568
uses OpenSSL library to encrypt the output data when sending
569
it to the socket and to decrypt the input received.
571
Uses a global SSL_CTX instance, which can be overriden
572
by WITH-GLOBAL-CONTEXT. (The global SSL_CTX is
573
passed as a parameter to an internall call of SSL_new.)
575
All parameters have the same meaning as documented
576
for MAKE-SSL-CLIENT-STREAM.
578
(ensure-initialized :method method)
579
(let ((stream (make-instance 'ssl-server-stream
581
:close-callback close-callback
582
:certificate certificate
584
:input-buffer-size input-buffer-size
585
:output-buffer-size output-buffer-size)))
586
(with-new-ssl (handle)
587
(setf socket (install-handle-and-bio stream handle socket unwrap-stream-p))
588
(ssl-set-accept-state handle)
589
(when (and cipher-list
590
(zerop (ssl-set-cipher-list handle cipher-list)))
591
(error 'ssl-error-initialize
593
"Can't set SSL cipher list: SSL_set_cipher_list returned 0"))
594
(with-pem-password (password)
595
(install-key-and-cert handle key certificate))
596
(collecting-verify-error (handle)
597
(ensure-ssl-funcall stream #'plusp #'ssl-accept handle))
598
(handle-external-format stream external-format))))
600
(defun get-selected-alpn-protocol (ssl-stream)
601
"A wrapper around SSL_get0_alpn_selected.
602
Returns the ALPN protocol selected by server, or NIL if none was selected.
604
SSL-STREAM is the client ssl stream returned by make-ssl-client-stream. "
605
(cffi:with-foreign-objects ((ptr :pointer) (len :pointer))
606
(ssl-get0-alpn-selected (ssl-stream-handle ssl-stream) ptr len)
607
(cffi:foreign-string-to-lisp (cffi:mem-ref ptr :pointer)
608
:count (cffi:mem-ref len :int))))
610
(defgeneric stream-fd (stream)
611
(:documentation "The STREAM's file descriptor as an integer,
612
if known / implemented for the current lisp.
613
Otherwise the STREAM itself. The result of this function can be
614
passed to MAKE-SSL-CLIENT-STREAM and MAKE-SSL-SERVER-STREAM."))
615
(defmethod stream-fd (stream) stream)
618
(defmethod stream-fd ((stream sb-sys:fd-stream))
619
(sb-sys:fd-stream-fd stream))
622
(defmethod stream-fd ((stream system:fd-stream))
623
(system:fd-stream-fd stream))
626
(defmethod stream-fd ((stream ccl::basic-stream))
627
(ccl::ioblock-device (ccl::stream-ioblock stream t)))
630
(defmethod stream-fd ((stream stream))
631
;; sockets appear to be direct instances of STREAM
632
(ext:stream-handles stream))
635
(defmethod stream-fd ((stream two-way-stream))
636
(si:file-stream-fd (two-way-stream-input-stream stream)))
639
(defmethod stream-fd ((stream stream))
640
(socket:socket-os-fd stream))
643
(defmethod stream-fd ((stream comm::socket-stream))
644
(comm:socket-stream-socket stream))
648
(require :abcl-contrib)
651
;;; N.b. Getting the file descriptor from a socket is not supported
652
;;; by any published JVM API, so every JVM implementation may behave
653
;;; somewhat differently. By using the ability of
654
;;; jss:get-java-fields to access private fields, it is usually
655
;;; possible to "find" an access path to read the underlying integer
656
;;; value of the file decriptor, which is all we need to pass to
658
(defmethod stream-fd ((stream system::socket-stream))
660
(macrolet ((saving-error (&body body)
663
(serious-condition (c)
666
(flet ((get-java-fields (object fields) ;; Thanks to Cyrus Harmon
667
(reduce (lambda (x y)
668
(jss:get-java-field x y t))
670
:initial-value object))
673
(make-string-input-stream
674
(java:jstatic "getProperty"
676
"java.specification.version")))))
677
(let ((input-stream (java:jcall "getWrappedInputStream" ;; TODO: define this as a constant
678
(two-way-stream-input-stream stream))))
679
(or ;; starting from openjdk 14, according to Mark Evenson
680
;; in https://github.com/cl-plus-ssl/cl-plus-ssl/pull/103
681
(saving-error (get-java-fields input-stream
682
'("in" "this$0" "sc" "fd" "fd")))
683
;; This seen to work for the following Java:
684
;; - On my local Linux machine
686
;; openjdk version "1.8.0_292"
687
;; OpenJDK Runtime Environment (Zulu 8.54.0.21-CA-linux64) (build 1.8.0_292-b10)
688
;; OpenJDK 64-Bit Server VM (Zulu 8.54.0.21-CA-linux64) (build 25.292-b10, mixed mode)
689
;; - Java 11.0.14 Debian
690
;; OpenJDK 64-Bit Server VM
691
;; (Printed by ABCL startup in GitHub Actions Linux VM
692
;; running Docker image clfoundation/cl-devel:2022-02-09)
693
(saving-error (get-java-fields input-stream
694
'("in" "impl" "fd" "fd")))
695
(saving-error (get-java-fields input-stream
696
'("in" "ch" "fdVal")))
697
(warn "cl+ssl:stream-fd: all approaches failed. stream: ~A, jvm-version: ~S, internal input-stream: ~A, errors:~%~{~A~^~%~}"
699
(ignore-errors (jvm-version))
701
(nreverse errors)))))))))