download: Verify TLS certificates unless asked not to.
Fixes <http://bugs.gnu.org/24466>. Reported by Leo Famulari <leo@famulari.name>. * guix/build/download.scm (%x509-certificate-directory): New variable. (make-credendials-with-ca-trust-files, peer-certificate) (assert-valid-server-certificate, print-tls-certificate-error): New procedures. Add 'print-tls-certificate-error' as an exception printer for 'tls-certificate-error'. (tls-wrap): Add #:verify-certificate? parameter and honor it. (open-connection-for-uri): Likewise. (http-fetch): Likewise. (url-fetch): Likewise. * guix/download.scm (url-fetch)[builder]: Pass #:verify-certificate? #f. * guix/scripts/lint.scm (probe-uri): Add case for 'tls-certificate-error'. (validate-uri): Likewise. * doc/guix.texi (Invoking guix download): Mention 'SSL_CERT_DIR'.
This commit is contained in:
		
							parent
							
								
									a00fbe8adf
								
							
						
					
					
						commit
						bc3c41ce36
					
				
					 4 changed files with 132 additions and 16 deletions
				
			
		| 
						 | 
					@ -4768,6 +4768,11 @@ they are not available, an error is raised.  @xref{Guile Preparations,
 | 
				
			||||||
how to install the GnuTLS bindings for Guile,, gnutls-guile,
 | 
					how to install the GnuTLS bindings for Guile,, gnutls-guile,
 | 
				
			||||||
GnuTLS-Guile}, for more information.
 | 
					GnuTLS-Guile}, for more information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@command{guix download} verifies HTTPS server certificates by loading
 | 
				
			||||||
 | 
					the certificates of X.509 authorities from the directory pointed to by
 | 
				
			||||||
 | 
					the @code{SSL_CERT_DIR} environment variable (@pxref{X.509
 | 
				
			||||||
 | 
					Certificates}).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The following option is available:
 | 
					The following option is available:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@table @code
 | 
					@table @code
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@
 | 
				
			||||||
  #:use-module (srfi srfi-11)
 | 
					  #:use-module (srfi srfi-11)
 | 
				
			||||||
  #:use-module (srfi srfi-19)
 | 
					  #:use-module (srfi srfi-19)
 | 
				
			||||||
  #:use-module (srfi srfi-26)
 | 
					  #:use-module (srfi srfi-26)
 | 
				
			||||||
 | 
					  #:autoload   (ice-9 ftw) (scandir)
 | 
				
			||||||
  #:use-module (ice-9 match)
 | 
					  #:use-module (ice-9 match)
 | 
				
			||||||
  #:use-module (ice-9 format)
 | 
					  #:use-module (ice-9 format)
 | 
				
			||||||
  #:export (open-socket-for-uri
 | 
					  #:export (open-socket-for-uri
 | 
				
			||||||
| 
						 | 
					@ -273,14 +274,78 @@ out if the connection could not be established in less than TIMEOUT seconds."
 | 
				
			||||||
session record port using PORT as its underlying communication port."
 | 
					session record port using PORT as its underlying communication port."
 | 
				
			||||||
  (hashq-set! %tls-ports record-port port))
 | 
					  (hashq-set! %tls-ports record-port port))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define (tls-wrap port server)
 | 
					(define %x509-certificate-directory
 | 
				
			||||||
 | 
					  ;; The directory where X.509 authority PEM certificates are stored.
 | 
				
			||||||
 | 
					  (make-parameter (or (getenv "GUIX_TLS_CERTIFICATE_DIRECTORY")
 | 
				
			||||||
 | 
					                      (getenv "SSL_CERT_DIR"))))  ;like OpenSSL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (make-credendials-with-ca-trust-files directory)
 | 
				
			||||||
 | 
					  "Return certificate credentials with X.509 authority certificates read from
 | 
				
			||||||
 | 
					DIRECTORY.  Those authority certificates are checked when
 | 
				
			||||||
 | 
					'peer-certificate-status' is later called."
 | 
				
			||||||
 | 
					  (let ((cred  (make-certificate-credentials))
 | 
				
			||||||
 | 
					        (files (or (scandir directory
 | 
				
			||||||
 | 
					                            (lambda (file)
 | 
				
			||||||
 | 
					                              (string-suffix? ".pem" file)))
 | 
				
			||||||
 | 
					                   '())))
 | 
				
			||||||
 | 
					    (for-each (lambda (file)
 | 
				
			||||||
 | 
					                (set-certificate-credentials-x509-trust-file!
 | 
				
			||||||
 | 
					                 cred (string-append directory "/" file)
 | 
				
			||||||
 | 
					                 x509-certificate-format/pem))
 | 
				
			||||||
 | 
					              (or files '()))
 | 
				
			||||||
 | 
					    cred))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (peer-certificate session)
 | 
				
			||||||
 | 
					  "Return the certificate of the remote peer in SESSION."
 | 
				
			||||||
 | 
					  (match (session-peer-certificate-chain session)
 | 
				
			||||||
 | 
					    ((first _ ...)
 | 
				
			||||||
 | 
					     (import-x509-certificate first x509-certificate-format/der))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (assert-valid-server-certificate session server)
 | 
				
			||||||
 | 
					  "Return #t if the certificate of the remote peer for SESSION is a valid
 | 
				
			||||||
 | 
					certificate for SERVER, where SERVER is the expected host name of peer."
 | 
				
			||||||
 | 
					  (define cert
 | 
				
			||||||
 | 
					    (peer-certificate session))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; First check whether the server's certificate matches SERVER.
 | 
				
			||||||
 | 
					  (unless (x509-certificate-matches-hostname? cert server)
 | 
				
			||||||
 | 
					    (throw 'tls-certificate-error 'host-mismatch cert server))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; Second check its validity and reachability from the set of authority
 | 
				
			||||||
 | 
					  ;; certificates loaded via 'set-certificate-credentials-x509-trust-file!'.
 | 
				
			||||||
 | 
					  (match (peer-certificate-status session)
 | 
				
			||||||
 | 
					    (()                                           ;certificate is valid
 | 
				
			||||||
 | 
					     #t)
 | 
				
			||||||
 | 
					    ((statuses ...)
 | 
				
			||||||
 | 
					     (throw 'tls-certificate-error 'invalid-certificate cert server
 | 
				
			||||||
 | 
					            statuses))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define (print-tls-certificate-error port key args default-printer)
 | 
				
			||||||
 | 
					  "Print the TLS certificate error represented by ARGS in an intelligible
 | 
				
			||||||
 | 
					way."
 | 
				
			||||||
 | 
					  (match args
 | 
				
			||||||
 | 
					    (('host-mismatch cert server)
 | 
				
			||||||
 | 
					     (format port
 | 
				
			||||||
 | 
					             "X.509 server certificate for '~a' does not match: ~a~%"
 | 
				
			||||||
 | 
					             server (x509-certificate-dn cert)))
 | 
				
			||||||
 | 
					    (('invalid-certificate cert server statuses)
 | 
				
			||||||
 | 
					     (format port
 | 
				
			||||||
 | 
					             "X.509 certificate of '~a' could not be verified:~%~{  ~a~%~}"
 | 
				
			||||||
 | 
					             server
 | 
				
			||||||
 | 
					             (map certificate-status->string statuses)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(set-exception-printer! 'tls-certificate-error
 | 
				
			||||||
 | 
					                        print-tls-certificate-error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(define* (tls-wrap port server #:key (verify-certificate? #t))
 | 
				
			||||||
  "Return PORT wrapped in a TLS connection to SERVER.  SERVER must be a DNS
 | 
					  "Return PORT wrapped in a TLS connection to SERVER.  SERVER must be a DNS
 | 
				
			||||||
host name without trailing dot."
 | 
					host name without trailing dot."
 | 
				
			||||||
  (define (log level str)
 | 
					  (define (log level str)
 | 
				
			||||||
    (format (current-error-port)
 | 
					    (format (current-error-port)
 | 
				
			||||||
            "gnutls: [~a|~a] ~a" (getpid) level str))
 | 
					            "gnutls: [~a|~a] ~a" (getpid) level str))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (let ((session (make-session connection-end/client)))
 | 
					  (let ((session  (make-session connection-end/client))
 | 
				
			||||||
 | 
					        (ca-certs (%x509-certificate-directory)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ;; Some servers such as 'cloud.github.com' require the client to support
 | 
					    ;; Some servers such as 'cloud.github.com' require the client to support
 | 
				
			||||||
    ;; the 'SERVER NAME' extension.  However, 'set-session-server-name!' is
 | 
					    ;; the 'SERVER NAME' extension.  However, 'set-session-server-name!' is
 | 
				
			||||||
| 
						 | 
					@ -301,13 +366,27 @@ host name without trailing dot."
 | 
				
			||||||
    ;; <https://tools.ietf.org/html/rfc7568>.
 | 
					    ;; <https://tools.ietf.org/html/rfc7568>.
 | 
				
			||||||
    (set-session-priorities! session "NORMAL:%COMPAT:-VERS-SSL3.0")
 | 
					    (set-session-priorities! session "NORMAL:%COMPAT:-VERS-SSL3.0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (set-session-credentials! session (make-certificate-credentials))
 | 
					    (set-session-credentials! session
 | 
				
			||||||
 | 
					                              (if (and verify-certificate? ca-certs)
 | 
				
			||||||
 | 
					                                  (make-credendials-with-ca-trust-files
 | 
				
			||||||
 | 
					                                   ca-certs)
 | 
				
			||||||
 | 
					                                  (make-certificate-credentials)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ;; Uncomment the following lines in case of debugging emergency.
 | 
					    ;; Uncomment the following lines in case of debugging emergency.
 | 
				
			||||||
    ;;(set-log-level! 10)
 | 
					    ;;(set-log-level! 10)
 | 
				
			||||||
    ;;(set-log-procedure! log)
 | 
					    ;;(set-log-procedure! log)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (handshake session)
 | 
					    (handshake session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ;; Verify the server's certificate if needed.
 | 
				
			||||||
 | 
					    (when verify-certificate?
 | 
				
			||||||
 | 
					      (catch 'tls-certificate-error
 | 
				
			||||||
 | 
					        (lambda ()
 | 
				
			||||||
 | 
					          (assert-valid-server-certificate session server))
 | 
				
			||||||
 | 
					        (lambda args
 | 
				
			||||||
 | 
					          (close-port port)
 | 
				
			||||||
 | 
					          (apply throw args))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    (let ((record (session-record-port session)))
 | 
					    (let ((record (session-record-port session)))
 | 
				
			||||||
      ;; Since we use `fileno' above, the file descriptor behind PORT would be
 | 
					      ;; Since we use `fileno' above, the file descriptor behind PORT would be
 | 
				
			||||||
      ;; closed when PORT is GC'd.  If we used `port->fdes', it would instead
 | 
					      ;; closed when PORT is GC'd.  If we used `port->fdes', it would instead
 | 
				
			||||||
| 
						 | 
					@ -374,9 +453,13 @@ ETIMEDOUT error is raised."
 | 
				
			||||||
              (apply throw args)
 | 
					              (apply throw args)
 | 
				
			||||||
              (loop (cdr addresses))))))))
 | 
					              (loop (cdr addresses))))))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define* (open-connection-for-uri uri #:key timeout)
 | 
					(define* (open-connection-for-uri uri
 | 
				
			||||||
 | 
					                                  #:key
 | 
				
			||||||
 | 
					                                  timeout
 | 
				
			||||||
 | 
					                                  (verify-certificate? #t))
 | 
				
			||||||
  "Like 'open-socket-for-uri', but also handle HTTPS connections.  The
 | 
					  "Like 'open-socket-for-uri', but also handle HTTPS connections.  The
 | 
				
			||||||
resulting port must be closed with 'close-connection'."
 | 
					resulting port must be closed with 'close-connection'.  When
 | 
				
			||||||
 | 
					VERIFY-CERTIFICATE? is true, verify HTTPS server certificates."
 | 
				
			||||||
  (define https?
 | 
					  (define https?
 | 
				
			||||||
    (eq? 'https (uri-scheme uri)))
 | 
					    (eq? 'https (uri-scheme uri)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -403,7 +486,8 @@ resulting port must be closed with 'close-connection'."
 | 
				
			||||||
       (setvbuf s _IOFBF %http-receive-buffer-size)
 | 
					       (setvbuf s _IOFBF %http-receive-buffer-size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
       (if https?
 | 
					       (if https?
 | 
				
			||||||
           (tls-wrap s (uri-host uri))
 | 
					           (tls-wrap s (uri-host uri)
 | 
				
			||||||
 | 
					                     #:verify-certificate? verify-certificate?)
 | 
				
			||||||
           s)))))
 | 
					           s)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define (close-connection port)
 | 
					(define (close-connection port)
 | 
				
			||||||
| 
						 | 
					@ -588,10 +672,11 @@ Return the resulting target URI."
 | 
				
			||||||
                    #:query    (uri-query    ref)
 | 
					                    #:query    (uri-query    ref)
 | 
				
			||||||
                    #:fragment (uri-fragment ref)))))
 | 
					                    #:fragment (uri-fragment ref)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define* (http-fetch uri file #:key timeout)
 | 
					(define* (http-fetch uri file #:key timeout (verify-certificate? #t))
 | 
				
			||||||
  "Fetch data from URI and write it to FILE; when TIMEOUT is true, bail out if
 | 
					  "Fetch data from URI and write it to FILE; when TIMEOUT is true, bail out if
 | 
				
			||||||
the connection could not be established in less than TIMEOUT seconds.  Return
 | 
					the connection could not be established in less than TIMEOUT seconds.  Return
 | 
				
			||||||
FILE on success."
 | 
					FILE on success.  When VERIFY-CERTIFICATE? is true, verify HTTPS
 | 
				
			||||||
 | 
					certificates; otherwise simply ignore them."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (define post-2.0.7?
 | 
					  (define post-2.0.7?
 | 
				
			||||||
    (or (> (string->number (major-version)) 2)
 | 
					    (or (> (string->number (major-version)) 2)
 | 
				
			||||||
| 
						 | 
					@ -618,7 +703,10 @@ FILE on success."
 | 
				
			||||||
          (_ '()))))
 | 
					          (_ '()))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (let*-values (((connection)
 | 
					  (let*-values (((connection)
 | 
				
			||||||
                 (open-connection-for-uri uri #:timeout timeout))
 | 
					                 (open-connection-for-uri uri
 | 
				
			||||||
 | 
					                                          #:timeout timeout
 | 
				
			||||||
 | 
					                                          #:verify-certificate?
 | 
				
			||||||
 | 
					                                          verify-certificate?))
 | 
				
			||||||
                ((resp bv-or-port)
 | 
					                ((resp bv-or-port)
 | 
				
			||||||
                 ;; XXX: `http-get*' was introduced in 2.0.7, and replaced by
 | 
					                 ;; XXX: `http-get*' was introduced in 2.0.7, and replaced by
 | 
				
			||||||
                 ;; #:streaming? in 2.0.8.  We know we're using it within the
 | 
					                 ;; #:streaming? in 2.0.8.  We know we're using it within the
 | 
				
			||||||
| 
						 | 
					@ -659,7 +747,9 @@ FILE on success."
 | 
				
			||||||
         (format #t "following redirection to `~a'...~%"
 | 
					         (format #t "following redirection to `~a'...~%"
 | 
				
			||||||
                 (uri->string uri))
 | 
					                 (uri->string uri))
 | 
				
			||||||
         (close connection)
 | 
					         (close connection)
 | 
				
			||||||
         (http-fetch uri file #:timeout timeout)))
 | 
					         (http-fetch uri file
 | 
				
			||||||
 | 
					                     #:timeout timeout
 | 
				
			||||||
 | 
					                     #:verify-certificate? verify-certificate?)))
 | 
				
			||||||
      (else
 | 
					      (else
 | 
				
			||||||
       (error "download failed" (uri->string uri)
 | 
					       (error "download failed" (uri->string uri)
 | 
				
			||||||
              code (response-reason-phrase resp))))))
 | 
					              code (response-reason-phrase resp))))))
 | 
				
			||||||
| 
						 | 
					@ -699,7 +789,7 @@ Return a list of URIs."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(define* (url-fetch url file
 | 
					(define* (url-fetch url file
 | 
				
			||||||
                    #:key
 | 
					                    #:key
 | 
				
			||||||
                    (timeout 10)
 | 
					                    (timeout 10) (verify-certificate? #t)
 | 
				
			||||||
                    (mirrors '()) (content-addressed-mirrors '())
 | 
					                    (mirrors '()) (content-addressed-mirrors '())
 | 
				
			||||||
                    (hashes '()))
 | 
					                    (hashes '()))
 | 
				
			||||||
  "Fetch FILE from URL; URL may be either a single string, or a list of
 | 
					  "Fetch FILE from URL; URL may be either a single string, or a list of
 | 
				
			||||||
| 
						 | 
					@ -713,7 +803,10 @@ HASHES must be a list of algorithm/hash pairs, where each algorithm is a
 | 
				
			||||||
symbol such as 'sha256 and each hash is a bytevector.
 | 
					symbol such as 'sha256 and each hash is a bytevector.
 | 
				
			||||||
CONTENT-ADDRESSED-MIRRORS must be a list of procedures that, given a hash
 | 
					CONTENT-ADDRESSED-MIRRORS must be a list of procedures that, given a hash
 | 
				
			||||||
algorithm and a hash, return a URL where the specified data can be retrieved
 | 
					algorithm and a hash, return a URL where the specified data can be retrieved
 | 
				
			||||||
or #f."
 | 
					or #f.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When VERIFY-CERTIFICATE? is true, validate HTTPS server certificates;
 | 
				
			||||||
 | 
					otherwise simply ignore them."
 | 
				
			||||||
  (define uri
 | 
					  (define uri
 | 
				
			||||||
    (append-map (cut maybe-expand-mirrors <> mirrors)
 | 
					    (append-map (cut maybe-expand-mirrors <> mirrors)
 | 
				
			||||||
                (match url
 | 
					                (match url
 | 
				
			||||||
| 
						 | 
					@ -725,9 +818,13 @@ or #f."
 | 
				
			||||||
            file (uri->string uri))
 | 
					            file (uri->string uri))
 | 
				
			||||||
    (case (uri-scheme uri)
 | 
					    (case (uri-scheme uri)
 | 
				
			||||||
      ((http https)
 | 
					      ((http https)
 | 
				
			||||||
       (false-if-exception* (http-fetch uri file #:timeout timeout)))
 | 
					       (false-if-exception* (http-fetch uri file
 | 
				
			||||||
 | 
					                                        #:verify-certificate?
 | 
				
			||||||
 | 
					                                        verify-certificate?
 | 
				
			||||||
 | 
					                                        #:timeout timeout)))
 | 
				
			||||||
      ((ftp)
 | 
					      ((ftp)
 | 
				
			||||||
       (false-if-exception* (ftp-fetch uri file #:timeout timeout)))
 | 
					       (false-if-exception* (ftp-fetch uri file
 | 
				
			||||||
 | 
					                                       #:timeout timeout)))
 | 
				
			||||||
      (else
 | 
					      (else
 | 
				
			||||||
       (format #t "skipping URI with unsupported scheme: ~s~%"
 | 
					       (format #t "skipping URI with unsupported scheme: ~s~%"
 | 
				
			||||||
               uri)
 | 
					               uri)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -372,7 +372,11 @@ in the store."
 | 
				
			||||||
                       #:hashes
 | 
					                       #:hashes
 | 
				
			||||||
                       (value-from-environment "guix download hashes")
 | 
					                       (value-from-environment "guix download hashes")
 | 
				
			||||||
                       #:content-addressed-mirrors
 | 
					                       #:content-addressed-mirrors
 | 
				
			||||||
                       (primitive-load #$%content-addressed-mirror-file))))))
 | 
					                       (primitive-load #$%content-addressed-mirror-file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                       ;; No need to validate certificates since we know the
 | 
				
			||||||
 | 
					                       ;; hash of the expected result.
 | 
				
			||||||
 | 
					                       #:verify-certificate? #f)))))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (let ((uri (and (string? url) (string->uri url))))
 | 
					  (let ((uri (and (string? url) (string->uri url))))
 | 
				
			||||||
    (if (or (and (string? url) (not uri))
 | 
					    (if (or (and (string? url) (not uri))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -369,7 +369,8 @@ for connections to complete; when TIMEOUT is #f, wait as long as needed."
 | 
				
			||||||
              ;; This can happen if the server returns an invalid HTTP header,
 | 
					              ;; This can happen if the server returns an invalid HTTP header,
 | 
				
			||||||
              ;; as is the case with the 'Date' header at sqlite.org.
 | 
					              ;; as is the case with the 'Date' header at sqlite.org.
 | 
				
			||||||
              (values 'invalid-http-response #f))
 | 
					              (values 'invalid-http-response #f))
 | 
				
			||||||
             ((getaddrinfo-error system-error gnutls-error)
 | 
					             ((getaddrinfo-error system-error
 | 
				
			||||||
 | 
					               gnutls-error tls-certificate-error)
 | 
				
			||||||
              (values key args))
 | 
					              (values key args))
 | 
				
			||||||
             (else
 | 
					             (else
 | 
				
			||||||
              (apply throw key args))))))
 | 
					              (apply throw key args))))))
 | 
				
			||||||
| 
						 | 
					@ -457,6 +458,15 @@ suspiciously small file (~a bytes)")
 | 
				
			||||||
                               (cons status argument))))
 | 
					                               (cons status argument))))
 | 
				
			||||||
                     field)
 | 
					                     field)
 | 
				
			||||||
       #f)
 | 
					       #f)
 | 
				
			||||||
 | 
					      ((tls-certificate-error)
 | 
				
			||||||
 | 
					       (emit-warning package
 | 
				
			||||||
 | 
					                     (format #f
 | 
				
			||||||
 | 
					                             (_ "TLS certificate error: ~a")
 | 
				
			||||||
 | 
					                             (call-with-output-string
 | 
				
			||||||
 | 
					                               (lambda (port)
 | 
				
			||||||
 | 
					                                 (print-exception port #f
 | 
				
			||||||
 | 
					                                                  'tls-certificate-error
 | 
				
			||||||
 | 
					                                                  argument))))))
 | 
				
			||||||
      ((invalid-http-response gnutls-error)
 | 
					      ((invalid-http-response gnutls-error)
 | 
				
			||||||
       ;; Probably a misbehaving server; ignore.
 | 
					       ;; Probably a misbehaving server; ignore.
 | 
				
			||||||
       #f)
 | 
					       #f)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue