From 200cfd24334d343984963e83c41e9eafd60b3b07 Mon Sep 17 00:00:00 2001 From: Ivan Bertona Date: Mon, 10 Feb 2020 14:50:13 -0500 Subject: [PATCH] Add test for missing TLS certificates in response. --- acme/challenge_test.go | 28 ++++++++++++------- acme/mock_conn.go | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 acme/mock_conn.go diff --git a/acme/challenge_test.go b/acme/challenge_test.go index d079bf8f..844c8385 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -14,8 +14,8 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "io/ioutil" - "log" "math/big" "net" "net/http" @@ -1086,21 +1086,19 @@ func TestTLSALPN01Validate(t *testing.T) { oldb, err := json.Marshal(ch) assert.FatalError(t, err) - expErr := ConnectionErr(errors.Errorf("error doing TLS dial for %v:443: remote error: tls: internal error", ch.getValue())) + expErr := RejectedIdentifierErr(errors.Errorf("tls-alpn-01 challenge for %v resulted in no certificates", ch.getValue())) baseClone := ch.clone() baseClone.Error = expErr.ToACME() newCh := &tlsALPN01Challenge{baseClone} newb, err := json.Marshal(newCh) assert.FatalError(t, err) - srv, tlsDial := newTestTLSALPNServer(nil) - srv.Start() - return test{ - srv: srv, - ch: ch, + ch: ch, vo: validateOptions{ - tlsDial: tlsDial, + tlsDial: func(network, addr string, config *tls.Config) (*tls.Conn, error) { + return tls.Client(&noopConn{}, config), nil + }, }, db: &db.MockNoSQLDB{ MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) { @@ -1630,13 +1628,25 @@ func newTestTLSALPNServer(validationCert *tls.Certificate) (*httptest.Server, tl } srv.Listener = tls.NewListener(srv.Listener, srv.TLS) - srv.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // hush + //srv.Config.ErrorLog = log.New(ioutil.Discard, "", 0) // hush return srv, func(network, addr string, config *tls.Config) (conn *tls.Conn, err error) { return tls.DialWithDialer(&net.Dialer{Timeout: time.Second}, "tcp", srv.Listener.Addr().String(), config) } } +// noopConn is a mock net.Conn that does nothing. +type noopConn struct{} + +func (c *noopConn) Read(_ []byte) (n int, err error) { return 0, io.EOF } +func (c *noopConn) Write(_ []byte) (n int, err error) { return 0, io.EOF } +func (c *noopConn) Close() error { return nil } +func (c *noopConn) LocalAddr() net.Addr { return &net.IPAddr{IP: net.IPv4zero, Zone: ""} } +func (c *noopConn) RemoteAddr() net.Addr { return &net.IPAddr{IP: net.IPv4zero, Zone: ""} } +func (c *noopConn) SetDeadline(t time.Time) error { return nil } +func (c *noopConn) SetReadDeadline(t time.Time) error { return nil } +func (c *noopConn) SetWriteDeadline(t time.Time) error { return nil } + func newTLSALPNValidationCert(keyAuthHash []byte, obsoleteOID, critical bool, names ...string) (*tls.Certificate, error) { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { diff --git a/acme/mock_conn.go b/acme/mock_conn.go new file mode 100644 index 00000000..590fbed1 --- /dev/null +++ b/acme/mock_conn.go @@ -0,0 +1,61 @@ +package acme + +/* +type Conn interface { + // Read reads data from the connection. + // Read can be made to time out and return an Error with Timeout() == true + // after a fixed time limit; see SetDeadline and SetReadDeadline. + Read(b []byte) (n int, err error) + + // Write writes data to the connection. + // Write can be made to time out and return an Error with Timeout() == true + // after a fixed time limit; see SetDeadline and SetWriteDeadline. + Write(b []byte) (n int, err error) + + // Close closes the connection. + // Any blocked Read or Write operations will be unblocked and return errors. + Close() error + + // LocalAddr returns the local network address. + LocalAddr() Addr + + // RemoteAddr returns the remote network address. + RemoteAddr() Addr + + // SetDeadline sets the read and write deadlines associated + // with the connection. It is equivalent to calling both + // SetReadDeadline and SetWriteDeadline. + // + // A deadline is an absolute time after which I/O operations + // fail with a timeout (see type Error) instead of + // blocking. The deadline applies to all future and pending + // I/O, not just the immediately following call to Read or + // Write. After a deadline has been exceeded, the connection + // can be refreshed by setting a deadline in the future. + // + // An idle timeout can be implemented by repeatedly extending + // the deadline after successful Read or Write calls. + // + // A zero value for t means I/O operations will not time out. + // + // Note that if a TCP connection has keep-alive turned on, + // which is the default unless overridden by Dialer.KeepAlive + // or ListenConfig.KeepAlive, then a keep-alive failure may + // also return a timeout error. On Unix systems a keep-alive + // failure on I/O can be detected using + // errors.Is(err, syscall.ETIMEDOUT). + SetDeadline(t time.Time) error + + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + // A zero value for t means Read will not time out. + SetReadDeadline(t time.Time) error + + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + // Even if write times out, it may return n > 0, indicating that + // some of the data was successfully written. + // A zero value for t means Write will not time out. + SetWriteDeadline(t time.Time) error +} +*/