Expose FetchChain and BuildChain helpers for external callers
This commit is contained in:
parent
17ecf3beb5
commit
9c54f5b0fb
1 changed files with 108 additions and 0 deletions
108
checker/fetch.go
Normal file
108
checker/fetch.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// This file is part of the happyDomain (R) project.
|
||||
// Copyright (c) 2020-2026 happyDomain
|
||||
// Authors: Pierre-Olivier Mercier, et al.
|
||||
//
|
||||
// This program is offered under a commercial and under the AGPL license.
|
||||
// For commercial licensing, contact us at <contact@happydomain.org>.
|
||||
//
|
||||
// For AGPL licensing:
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package checker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FetchChain dials host:port, optionally upgrades the connection via STARTTLS,
|
||||
// completes a TLS handshake (InsecureSkipVerify so callers receive the chain
|
||||
// even when PKIX would reject it), and returns the peer certificates leaf
|
||||
// first.
|
||||
//
|
||||
// starttls is the protocol name as registered (smtp, submission, imap, pop3,
|
||||
// ldap, xmpp-client, ...); pass "" for direct TLS. AutoSTARTTLS provides
|
||||
// well-known port defaults.
|
||||
func FetchChain(ctx context.Context, host string, port uint16, starttls string, timeout time.Duration) ([]*x509.Certificate, error) {
|
||||
host = strings.TrimSuffix(host, ".")
|
||||
addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
|
||||
|
||||
dialCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
conn, err := (&net.Dialer{}).DialContext(dialCtx, "tcp", addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dial %s: %w", addr, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if dl, ok := dialCtx.Deadline(); ok {
|
||||
_ = conn.SetDeadline(dl)
|
||||
}
|
||||
|
||||
if starttls != "" {
|
||||
up, ok := starttlsUpgraders[starttls]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported starttls protocol %q", starttls)
|
||||
}
|
||||
if err := up(conn, host); err != nil {
|
||||
return nil, fmt.Errorf("starttls-%s: %w", starttls, err)
|
||||
}
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(conn, &tls.Config{
|
||||
ServerName: host,
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
if err := tlsConn.HandshakeContext(dialCtx); err != nil {
|
||||
return nil, fmt.Errorf("tls handshake: %w", err)
|
||||
}
|
||||
state := tlsConn.ConnectionState()
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return nil, fmt.Errorf("server presented no certificate")
|
||||
}
|
||||
return state.PeerCertificates, nil
|
||||
}
|
||||
|
||||
// BuildChain produces a CertInfo per peer certificate (leaf first), with the
|
||||
// four (selector, matching_type) DANE hash pairs precomputed. This is the
|
||||
// same projection probe() applies internally; exported so HTTP handlers can
|
||||
// reuse it without re-deriving the format.
|
||||
func BuildChain(certs []*x509.Certificate) []CertInfo {
|
||||
return buildChain(certs)
|
||||
}
|
||||
|
||||
// AutoSTARTTLS maps a well-known port to the STARTTLS dialect FetchChain
|
||||
// should drive. Returns "" when the port has no auto-mapping (caller should
|
||||
// then use direct TLS or pass an explicit dialect).
|
||||
func AutoSTARTTLS(port uint16) string {
|
||||
switch port {
|
||||
case 25, 587:
|
||||
return "smtp"
|
||||
case 143:
|
||||
return "imap"
|
||||
case 110:
|
||||
return "pop3"
|
||||
case 389:
|
||||
return "ldap"
|
||||
case 5222:
|
||||
return "xmpp-client"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue