1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-29 19:30:36 +02:00
guile/test-suite/tests/00-socket.test
Michael Gran c9a19a03f8 Cygwin/MSYS: 00-socket.test: abstract sockets are unsupported
Binding to AF_UNIX abstract sockets is not supported on Cygwin and
presumably MSYS as well.

* test-suite/tests/00-socket.text ("AF_UNIX abstract"): throw unsupported
  on Cygwin and MSYS
2025-03-22 06:42:21 -07:00

587 lines
17 KiB
Scheme
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;;; 00-socket.test --- test socket functions -*- scheme -*-
;;;;
;;;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010,
;;;; 2011, 2012, 2013, 2014, 2017, 2021 Free Software Foundation, Inc.
;;;;
;;;; 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
;; This test runs early, so that we can fork before any threads are
;; created in other tests.
(define-module (test-suite test-socket)
#:use-module (rnrs bytevectors)
#:use-module (srfi srfi-26)
#:use-module (test-suite lib))
(define (skip-on-darwin)
(when (string-ci=? "darwin" (utsname:sysname (uname)))
(throw 'untested)))
(define (skip-on-cygwin-msys)
(when (or (string-contains %host-type "cygwin")
(string-contains %host-type "msys"))
;; See https://cygwin.com/pipermail/cygwin/2023-June/253785.html
(throw 'unsupported)))
;;;
;;; inet-ntop
;;;
(if (defined? 'inet-ntop)
(with-test-prefix "inet-ntop"
(with-test-prefix "ipv6"
(pass-if "0"
(string? (inet-ntop AF_INET6 0)))
(pass-if "2^128-1"
(string? (inet-ntop AF_INET6 (1- (ash 1 128)))))
(pass-if-exception "-1" exception:out-of-range
(inet-ntop AF_INET6 -1))
(pass-if-exception "2^128" exception:out-of-range
(inet-ntop AF_INET6 (ash 1 128)))
(pass-if-exception "2^1024" exception:out-of-range
(inet-ntop AF_INET6 (ash 1 1024))))))
;;;
;;; inet-pton
;;;
(if (defined? 'inet-pton)
(with-test-prefix "inet-pton"
(with-test-prefix "ipv6"
(pass-if "00:00:00:00:00:00:00:00"
(eqv? 0 (inet-pton AF_INET6 "00:00:00:00:00:00:00:00")))
(pass-if "0:0:0:0:0:0:0:1"
(eqv? 1 (inet-pton AF_INET6 "0:0:0:0:0:0:0:1")))
(pass-if "::1"
(eqv? 1 (inet-pton AF_INET6 "::1")))
(pass-if "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
(eqv? #xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
(inet-pton AF_INET6
"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF")))
(pass-if "F000:0000:0000:0000:0000:0000:0000:0000"
(eqv? #xF0000000000000000000000000000000
(inet-pton AF_INET6
"F000:0000:0000:0000:0000:0000:0000:0000")))
(pass-if "0F00:0000:0000:0000:0000:0000:0000:0000"
(eqv? #x0F000000000000000000000000000000
(inet-pton AF_INET6
"0F00:0000:0000:0000:0000:0000:0000:0000")))
(pass-if "0000:0000:0000:0000:0000:0000:0000:00F0"
(eqv? #xF0
(inet-pton AF_INET6
"0000:0000:0000:0000:0000:0000:0000:00F0"))))))
(if (defined? 'inet-ntop)
(with-test-prefix "inet-ntop"
(with-test-prefix "ipv4"
(pass-if "127.0.0.1"
(equal? "127.0.0.1" (inet-ntop AF_INET INADDR_LOOPBACK))))
(if (defined? 'AF_INET6)
(with-test-prefix "ipv6"
(pass-if "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
(string-ci=? "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
(inet-ntop AF_INET6 (- (expt 2 128) 1))))
(pass-if "::1"
(equal? "::1" (inet-ntop AF_INET6 1)))))))
;;;
;;; make-socket-address
;;;
(with-test-prefix "make-socket-address"
(if (defined? 'AF_INET)
(pass-if "AF_INET"
(let ((sa (make-socket-address AF_INET 123456 80)))
(and (= (sockaddr:fam sa) AF_INET)
(= (sockaddr:addr sa) 123456)
(= (sockaddr:port sa) 80)))))
(if (defined? 'AF_INET6)
(pass-if "AF_INET6"
;; Since the platform doesn't necessarily support `scopeid', we won't
;; test it.
(let ((sa* (make-socket-address AF_INET6 123456 80 1))
(sa+ (make-socket-address AF_INET6 123456 80)))
(and (= (sockaddr:fam sa*) (sockaddr:fam sa+) AF_INET6)
(= (sockaddr:addr sa*) (sockaddr:addr sa+) 123456)
(= (sockaddr:port sa*) (sockaddr:port sa+) 80)
(= (sockaddr:flowinfo sa*) 1)))))
(if (defined? 'AF_UNIX)
(begin
(pass-if "AF_UNIX"
(let ((sa (make-socket-address AF_UNIX "/tmp/unix-socket")))
(and (= (sockaddr:fam sa) AF_UNIX)
(string=? (sockaddr:path sa) "/tmp/unix-socket"))))
(pass-if "AF_UNIX abstract"
(let ((sa (make-socket-address AF_UNIX "\x00/tmp/abstract-socket")))
(and (= (sockaddr:fam sa) AF_UNIX)
(string=? (sockaddr:path sa) "\x00/tmp/abstract-socket")))))))
;;;
;;; setsockopt
;;;
(with-test-prefix "setsockopt AF_INET"
(if (and (defined? 'AF_INET) (defined? 'TCP_NODELAY))
(pass-if "IPPROTO_TCP TCP_NODELAY"
(let ((sock (socket AF_INET SOCK_STREAM 0)))
(setsockopt sock IPPROTO_TCP TCP_NODELAY 1)
(not (eqv? 0 (getsockopt sock IPPROTO_TCP TCP_NODELAY)))))))
;;;
;;; AF_UNIX sockets and `make-socket-address'
;;;
(define %tmpdir
;; Honor `$TMPDIR', which tmpnam(3) doesn't do.
(or (getenv "TMPDIR") "/tmp"))
(define %curdir
;; Remember the current working directory.
(getcwd))
;; Temporarily cd to %TMPDIR. The goal is to work around path name
;; limitations, which can lead to exceptions like:
;;
;; (misc-error "scm_to_sockaddr"
;; "unix address path too long: ~A"
;; ("/tmp/nix-build-fb7bph4ifh0vr3ihigm702dzffdnapfj-guile-coverage-1.9.5.drv-0/guile-test-socket-1258553296-77619")
;; #f)
(false-if-exception (chdir %tmpdir))
(define (temp-file-path)
;; Return a temporary file name, assuming the current directory is %TMPDIR.
(string-append "guile-test-socket-"
(number->string (current-time)) "-"
(number->string (random 100000))))
(define (primitive-fork-if-available)
(if (not (provided? 'fork))
-1
(primitive-fork)))
(if (defined? 'AF_UNIX)
(with-test-prefix "AF_UNIX/SOCK_DGRAM"
;; testing `bind' and `sendto' and datagram sockets
(let ((server-socket (socket AF_UNIX SOCK_DGRAM 0))
(server-bound? #f)
(path (temp-file-path)))
(pass-if "bind"
(catch 'system-error
(lambda ()
(bind server-socket AF_UNIX path)
(set! server-bound? #t)
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
(else (apply throw args)))))))
(pass-if "bind/sockaddr"
(let* ((sock (socket AF_UNIX SOCK_STREAM 0))
(path (temp-file-path))
(sockaddr (make-socket-address AF_UNIX path)))
(catch 'system-error
(lambda ()
(bind sock sockaddr)
(false-if-exception (delete-file path))
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
(else (apply throw args))))))))
(pass-if "sendto"
(if (not server-bound?)
(throw 'unresolved)
(let ((client (socket AF_UNIX SOCK_DGRAM 0))
(message (string->utf8 "hello")))
(> (sendto client message AF_UNIX path) 0))))
(pass-if "sendto/sockaddr"
(if (not server-bound?)
(throw 'unresolved)
(let ((client (socket AF_UNIX SOCK_DGRAM 0))
(message (string->utf8 "hello"))
(sockaddr (make-socket-address AF_UNIX path)))
(> (sendto client message sockaddr) 0))))
(false-if-exception (delete-file path)))))
(if (defined? 'AF_UNIX)
(with-test-prefix "AF_UNIX/SOCK_STREAM"
;; testing `bind', `listen' and `connect' on stream-oriented sockets
(let ((server-socket (socket AF_UNIX SOCK_STREAM 0))
(server-bound? #f)
(server-listening? #f)
(server-pid #f)
(path (temp-file-path)))
(pass-if "bind"
(catch 'system-error
(lambda ()
(bind server-socket AF_UNIX path)
(set! server-bound? #t)
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
(else (apply throw args)))))))
(pass-if "bind/sockaddr"
(let* ((sock (socket AF_UNIX SOCK_STREAM 0))
(path (temp-file-path))
(sockaddr (make-socket-address AF_UNIX path)))
(catch 'system-error
(lambda ()
(bind sock sockaddr)
(false-if-exception (delete-file path))
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
(else (apply throw args))))))))
(pass-if "listen"
(if (not server-bound?)
(throw 'unresolved)
(begin
(listen server-socket 123)
(set! server-listening? #t)
#t)))
(force-output (current-output-port))
(force-output (current-error-port))
(when server-listening?
(let ((pid (primitive-fork-if-available)))
;; Spawn a server process.
(case pid
((-1) ;; fork not available
#f)
((0) ;; the kid: serve two connections and exit
(let serve ((conn
(false-if-exception (accept server-socket)))
(count 1))
(if (not conn)
(exit 1)
(if (> count 0)
(serve (false-if-exception (accept server-socket))
(- count 1)))))
(exit 0))
(else ;; the parent
(set! server-pid pid)
#t))))
(pass-if "connect"
(if (not server-pid)
(throw 'unresolved)
(let ((s (socket AF_UNIX SOCK_STREAM 0)))
(connect s AF_UNIX path)
#t)))
(pass-if "connect/sockaddr"
(if (not server-pid)
(throw 'unresolved)
(let ((s (socket AF_UNIX SOCK_STREAM 0)))
(connect s (make-socket-address AF_UNIX path))
#t)))
(pass-if "accept"
(if (not server-pid)
(throw 'unresolved)
(let ((status (cdr (waitpid server-pid))))
(eqv? 0 (status:exit-val status)))))
(false-if-exception (delete-file path))
#t)
;; testing `bind', `listen' and `connect' on abstract stream-oriented sockets
(let ((server-socket (socket AF_UNIX SOCK_STREAM 0))
(server-bound? #f)
(server-listening? #f)
(server-pid #f)
(path (temp-file-path)))
(false-if-exception (delete-file path))
(set! path (string-append "\x00" path))
(pass-if "bind abstract"
(skip-on-darwin)
(skip-on-cygwin-msys)
(catch 'system-error
(lambda ()
(bind server-socket AF_UNIX path)
(set! server-bound? #t)
#t)
(lambda args
(let ((errno (system-error-errno args)))
(if (= errno EADDRINUSE)
(throw 'unresolved)
(apply throw args))))))
(pass-if "listen abstract"
(skip-on-darwin)
(if (not server-bound?)
(throw 'unresolved)
(begin
(listen server-socket 123)
(set! server-listening? #t)
#t)))
(force-output (current-output-port))
(force-output (current-error-port))
(when server-listening?
(let ((pid (primitive-fork-if-available)))
;; Spawn a server process.
(case pid
((-1) ;; fork not available
#f)
((0) ;; the kid: serve one connection and exit
(let serve ((conn
(false-if-exception (accept server-socket)))
(count 0))
(if (not conn)
(exit 1)
(exit 0))))
(else ;; the parent
(set! server-pid pid)
#t))))
(pass-if "connect abstract"
(skip-on-darwin)
(if (not server-pid)
(throw 'unresolved)
(let ((s (socket AF_UNIX SOCK_STREAM 0)))
(display "connect abstract\n")
(connect s AF_UNIX path)
#t)))
(pass-if "accept abstract"
(skip-on-darwin)
(if (not server-pid)
(throw 'unresolved)
(begin
(let ((status (cdr (waitpid server-pid))))
(eqv? 0 (status:exit-val status))))))
#t)
;; Testing `send', `recv!' & co. on stream-oriented sockets (with
;; a bit of duplication with the above.)
(let ((server-socket (socket AF_UNIX SOCK_STREAM 0))
(server-bound? #f)
(server-listening? #f)
(server-pid #f)
(message "hello, world!")
(path (temp-file-path)))
(define (sub-bytevector bv len)
(let ((c (make-bytevector len)))
(bytevector-copy! bv 0 c 0 len)
c))
(pass-if "bind (bis)"
(catch 'system-error
(lambda ()
(bind server-socket AF_UNIX path)
(set! server-bound? #t)
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
(else (apply throw args)))))))
(pass-if "listen (bis)"
(if (not server-bound?)
(throw 'unresolved)
(begin
(listen server-socket 123)
(set! server-listening? #t)
#t)))
(force-output (current-output-port))
(force-output (current-error-port))
(if server-listening?
(let ((pid (primitive-fork-if-available)))
;; Spawn a server process.
(case pid
((-1)
#f)
((0) ;; the kid: send MESSAGE and exit
(exit
(false-if-exception
(let ((conn (car (accept server-socket)))
(bv (string->utf8 message)))
(= (bytevector-length bv)
(send conn bv))))))
(else ;; the parent
(set! server-pid pid)
#t))))
(pass-if "recv!"
(if (not server-pid)
(throw 'unresolved)
(let ((s (socket AF_UNIX SOCK_STREAM 0)))
(connect s AF_UNIX path)
(let* ((buf (make-bytevector 123))
(received (recv! s buf)))
(string=? (utf8->string (sub-bytevector buf received))
message)))))
(pass-if "accept (bis)"
(if (not server-pid)
(throw 'unresolved)
(let ((status (cdr (waitpid server-pid))))
(eqv? 0 (status:exit-val status)))))
(false-if-exception (delete-file path))
#t)))
(if (defined? 'AF_INET6)
(with-test-prefix "AF_INET6/SOCK_STREAM"
;; testing `bind', `listen' and `connect' on stream-oriented sockets
(let ((server-socket
;; Some platforms don't support this protocol/family combination.
(false-if-exception (socket AF_INET6 SOCK_STREAM 0)))
(server-bound? #f)
(server-listening? #f)
(server-pid #f)
(ipv6-addr 1) ; ::1
(server-port 8889)
(client-port 9998))
(pass-if "bind"
(if (not server-socket)
(throw 'unresolved))
(catch 'system-error
(lambda ()
(bind server-socket AF_INET6 ipv6-addr server-port)
(set! server-bound? #t)
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
;; On Linux-based systems, when `ipv6' support is
;; missing (for instance, `ipv6' is loaded and
;; /proc/sys/net/ipv6/conf/all/disable_ipv6 is set
;; to 1), the socket call above succeeds but
;; bind(2) fails like this.
((= errno EADDRNOTAVAIL) (throw 'unresolved))
(else (apply throw args)))))))
(pass-if "bind/sockaddr"
(let* ((sock (false-if-exception (socket AF_INET6 SOCK_STREAM 0)))
(sockaddr (make-socket-address AF_INET6 ipv6-addr client-port)))
(if (not sock)
(throw 'unresolved))
(catch 'system-error
(lambda ()
(bind sock sockaddr)
#t)
(lambda args
(let ((errno (system-error-errno args)))
(cond ((= errno EADDRINUSE) (throw 'unresolved))
((= errno EADDRNOTAVAIL) (throw 'unresolved))
(else (apply throw args))))))))
(pass-if "listen"
(if (not server-bound?)
(throw 'unresolved)
(begin
(listen server-socket 123)
(set! server-listening? #t)
#t)))
(force-output (current-output-port))
(force-output (current-error-port))
(if server-listening?
(let ((pid (primitive-fork-if-available)))
;; Spawn a server process.
(case pid
((-1)
#f)
((0) ;; the kid: serve two connections and exit
(let serve ((conn
(false-if-exception (accept server-socket)))
(count 1))
(if (not conn)
(exit 1)
(if (> count 0)
(serve (false-if-exception (accept server-socket))
(- count 1)))))
(exit 0))
(else ;; the parent
(set! server-pid pid)
#t))))
(pass-if "connect"
(if (not server-pid)
(throw 'unresolved)
(let ((s (socket AF_INET6 SOCK_STREAM 0)))
(connect s AF_INET6 ipv6-addr server-port)
#t)))
(pass-if "connect/sockaddr"
(if (not server-pid)
(throw 'unresolved)
(let ((s (socket AF_INET6 SOCK_STREAM 0)))
(connect s (make-socket-address AF_INET6 ipv6-addr server-port))
#t)))
(pass-if "accept"
(if (not server-pid)
(throw 'unresolved)
(let ((status (cdr (waitpid server-pid))))
(eqv? 0 (status:exit-val status)))))
#t)))
;; Switch back to the previous directory.
(false-if-exception (chdir %curdir))