2016-10-12 09:37:18 +02:00
|
|
|
|
;;;; 00-repl-server.test --- REPL server. -*- mode: scheme; coding: utf-8; -*-
|
|
|
|
|
|
;;;;
|
2017-03-31 21:38:08 -07:00
|
|
|
|
;;;; Copyright (C) 2016, 2017 Free Software Foundation, Inc.
|
2016-10-12 09:37:18 +02:00
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; This library is free software; you can redistribute it and/or
|
|
|
|
|
|
;;;; modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
|
;;;; License as published by the Free Software Foundation; either
|
|
|
|
|
|
;;;; version 3 of the License, or (at your option) any later version.
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; This library is distributed in the hope that it will be useful,
|
|
|
|
|
|
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
|
;;;; Lesser General Public License for more details.
|
|
|
|
|
|
;;;;
|
|
|
|
|
|
;;;; You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
|
;;;; License along with this library; if not, write to the Free Software
|
|
|
|
|
|
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
|
|
|
|
|
|
(define-module (repl-server)
|
|
|
|
|
|
#:use-module (system repl server)
|
|
|
|
|
|
#:use-module (ice-9 match)
|
|
|
|
|
|
#:use-module (ice-9 rdelim)
|
|
|
|
|
|
#:use-module (web uri)
|
|
|
|
|
|
#:use-module (web request)
|
|
|
|
|
|
#:use-module (test-suite lib))
|
|
|
|
|
|
|
2020-01-22 21:23:16 -06:00
|
|
|
|
;; FIXME: replace with mkdtemp! (or equivalent) when available
|
|
|
|
|
|
(define (make-tempdir)
|
|
|
|
|
|
(let loop ((try 0)
|
|
|
|
|
|
(n (random:uniform)))
|
|
|
|
|
|
(let* ((path (string-append "/tmp/repl-server-test-" (number->string n)))
|
|
|
|
|
|
(dir (false-if-exception (mkdir path #o700))))
|
|
|
|
|
|
(cond
|
|
|
|
|
|
(dir path)
|
|
|
|
|
|
((> try 10)
|
|
|
|
|
|
(error "Unable to create directory in /tmp for 00-repl-server.test"))
|
|
|
|
|
|
(else (loop (1+ try) (random:uniform)))))))
|
|
|
|
|
|
|
2016-10-12 09:37:18 +02:00
|
|
|
|
(define (call-with-repl-server proc)
|
|
|
|
|
|
"Set up a REPL server in a separate process and call PROC with a
|
|
|
|
|
|
socket connected to that server."
|
2020-01-22 21:23:16 -06:00
|
|
|
|
(let* ((tmpdir (make-tempdir))
|
|
|
|
|
|
(sockaddr (make-socket-address AF_UNIX (string-append tmpdir "/repl-server")))
|
|
|
|
|
|
(client-socket (socket AF_UNIX SOCK_STREAM 0)))
|
|
|
|
|
|
(false-if-exception (delete-file (sockaddr:path sockaddr)))
|
|
|
|
|
|
(false-if-exception (rmdir tmpdir))
|
2016-10-12 09:37:18 +02:00
|
|
|
|
|
2017-04-04 07:33:41 -07:00
|
|
|
|
;; The REPL server requires thread. The test requires fork.
|
|
|
|
|
|
(unless (and (provided? 'threads) (provided? 'fork))
|
2017-03-01 12:19:39 +01:00
|
|
|
|
(throw 'unsupported))
|
|
|
|
|
|
|
2016-10-12 09:37:18 +02:00
|
|
|
|
(match (primitive-fork)
|
|
|
|
|
|
(0
|
|
|
|
|
|
(dynamic-wind
|
|
|
|
|
|
(const #t)
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(let ((server-socket (socket AF_UNIX SOCK_STREAM 0)))
|
|
|
|
|
|
(bind server-socket sockaddr)
|
|
|
|
|
|
(set! %load-verbosely #f)
|
|
|
|
|
|
|
|
|
|
|
|
(close-fdes 2)
|
|
|
|
|
|
|
|
|
|
|
|
;; Arrange so that the alarming "possible break-in attempt"
|
|
|
|
|
|
;; message doesn't show up when running the test suite.
|
|
|
|
|
|
(dup2 (open-fdes "/dev/null" O_WRONLY) 2)
|
|
|
|
|
|
|
|
|
|
|
|
(run-server server-socket)))
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(primitive-exit 0))))
|
|
|
|
|
|
(pid
|
2017-03-01 12:19:39 +01:00
|
|
|
|
(sigaction SIGPIPE SIG_IGN)
|
2016-10-12 09:37:18 +02:00
|
|
|
|
(dynamic-wind
|
|
|
|
|
|
(const #t)
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
;; XXX: We can't synchronize with the server's 'accept' call
|
|
|
|
|
|
;; because it's buried inside 'run-server', hence this hack.
|
|
|
|
|
|
(let loop ((tries 0))
|
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(connect client-socket sockaddr))
|
|
|
|
|
|
(lambda args
|
2016-11-04 22:44:32 +01:00
|
|
|
|
(when (memv (system-error-errno args)
|
|
|
|
|
|
(list ENOENT ECONNREFUSED))
|
|
|
|
|
|
(when (> tries 30)
|
|
|
|
|
|
(throw 'unresolved))
|
|
|
|
|
|
(usleep 100)
|
2016-10-12 09:37:18 +02:00
|
|
|
|
(loop (+ tries 1))))))
|
|
|
|
|
|
|
|
|
|
|
|
(proc client-socket))
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(false-if-exception (close-port client-socket))
|
2017-03-01 12:19:39 +01:00
|
|
|
|
(false-if-exception (kill pid SIGTERM))
|
|
|
|
|
|
(sigaction SIGPIPE SIG_DFL)))))))
|
2016-10-12 09:37:18 +02:00
|
|
|
|
|
|
|
|
|
|
(define-syntax-rule (with-repl-server client-socket body ...)
|
|
|
|
|
|
"Evaluate BODY... in a context where CLIENT-SOCKET is bound to a
|
|
|
|
|
|
socket connected to a fresh REPL server."
|
|
|
|
|
|
(call-with-repl-server
|
|
|
|
|
|
(lambda (client-socket)
|
|
|
|
|
|
body ...)))
|
|
|
|
|
|
|
|
|
|
|
|
(define (read-until-prompt port str)
|
|
|
|
|
|
"Read from PORT until STR has been read or the end-of-file was
|
|
|
|
|
|
reached."
|
|
|
|
|
|
(let loop ()
|
|
|
|
|
|
(match (read-line port)
|
|
|
|
|
|
((? eof-object?)
|
|
|
|
|
|
#t)
|
|
|
|
|
|
(line
|
|
|
|
|
|
(or (string=? line str) (loop))))))
|
|
|
|
|
|
|
|
|
|
|
|
(define %last-line-before-prompt
|
|
|
|
|
|
"Enter `,help' for help.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; REPL server tests.
|
|
|
|
|
|
;;;
|
|
|
|
|
|
;;; Since we call 'primitive-fork', these tests must run before any
|
|
|
|
|
|
;;; tests that create threads.
|
|
|
|
|
|
|
|
|
|
|
|
(with-test-prefix "repl-server"
|
|
|
|
|
|
|
|
|
|
|
|
(pass-if-equal "simple expression"
|
|
|
|
|
|
"scheme@(repl-server)> $1 = 42\n"
|
|
|
|
|
|
(with-repl-server socket
|
|
|
|
|
|
(read-until-prompt socket %last-line-before-prompt)
|
2016-11-04 22:45:51 +01:00
|
|
|
|
|
|
|
|
|
|
;; Wait until 'repl-reader' in boot-9 has written the prompt.
|
|
|
|
|
|
;; Otherwise, if we write too quickly, 'repl-reader' checks for
|
|
|
|
|
|
;; 'char-ready?' and doesn't print the prompt.
|
|
|
|
|
|
(match (select (list socket) '() (list socket) 3)
|
|
|
|
|
|
(((_) () ())
|
|
|
|
|
|
(display "(+ 40 2)\n(quit)\n" socket)
|
|
|
|
|
|
(read-string socket)))))
|
2016-10-12 09:37:18 +02:00
|
|
|
|
|
|
|
|
|
|
(pass-if "HTTP inter-protocol attack" ;CVE-2016-8606
|
|
|
|
|
|
(with-repl-server socket
|
|
|
|
|
|
;; Avoid SIGPIPE when the server closes the connection.
|
|
|
|
|
|
(sigaction SIGPIPE SIG_IGN)
|
|
|
|
|
|
|
|
|
|
|
|
(read-until-prompt socket %last-line-before-prompt)
|
|
|
|
|
|
|
|
|
|
|
|
;; Simulate an HTTP inter-protocol attack.
|
|
|
|
|
|
(write-request (build-request (string->uri "http://localhost"))
|
|
|
|
|
|
socket)
|
|
|
|
|
|
|
|
|
|
|
|
;; Make sure the server reacts by closing the connection. If it
|
|
|
|
|
|
;; fails to do that, this test hangs.
|
|
|
|
|
|
(catch 'system-error
|
|
|
|
|
|
(lambda ()
|
|
|
|
|
|
(let loop ((n 0))
|
|
|
|
|
|
(display "(+ 40 2)\n(quit)\n" socket) ;trigger EPIPE
|
|
|
|
|
|
(read-string socket)
|
|
|
|
|
|
(if (> n 5)
|
|
|
|
|
|
#f ;failure
|
|
|
|
|
|
(begin
|
|
|
|
|
|
(sleep 1)
|
|
|
|
|
|
(loop (+ 1 n))))))
|
|
|
|
|
|
(lambda args
|
|
|
|
|
|
(->bool (memv (system-error-errno args)
|
2017-03-31 21:38:08 -07:00
|
|
|
|
(list ECONNRESET EPIPE ECONNABORTED))))))))
|
2016-10-12 09:37:18 +02:00
|
|
|
|
|
|
|
|
|
|
;;; Local Variables:
|
|
|
|
|
|
;;; eval: (put 'with-repl-server 'scheme-indent-function 1)
|
|
|
|
|
|
;;; End:
|