--- /dev/null
+/*
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package tofuproxy
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net"
+ "strings"
+
+ "go.cypherpunks.ru/ucspi"
+ "go.stargrave.org/tofuproxy/fifos"
+)
+
+var sessionCache = tls.NewLRUClientSessionCache(1024)
+
+func DialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
+ host := strings.Split(addr, ":")[0]
+ ccg := ClientCertificateGetter{host: host}
+ cfg := tls.Config{
+ VerifyPeerCertificate: func(
+ rawCerts [][]byte,
+ verifiedChains [][]*x509.Certificate,
+ ) error {
+ return verifyCert(host, nil, rawCerts, verifiedChains)
+ },
+ ClientSessionCache: sessionCache,
+ NextProtos: []string{"h2", "http/1.1"},
+ GetClientCertificate: ccg.get,
+ }
+ conn, dialErr := tls.Dial(network, addr, &cfg)
+ if dialErr != nil {
+ if _, ok := dialErr.(ErrRejected); ok {
+ return nil, dialErr
+ }
+ cfg.InsecureSkipVerify = true
+ cfg.VerifyPeerCertificate = func(
+ rawCerts [][]byte,
+ verifiedChains [][]*x509.Certificate,
+ ) error {
+ return verifyCert(host, dialErr, rawCerts, verifiedChains)
+ }
+ var err error
+ conn, err = tls.Dial(network, addr, &cfg)
+ if err != nil {
+ fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error())
+ return nil, err
+ }
+ }
+ connState := conn.ConnectionState()
+ if !connState.DidResume {
+ fifos.LogTLS <- fmt.Sprintf(
+ "%s\t%s %s %s\t%s\t%s",
+ addr,
+ ucspi.TLSVersion(connState.Version),
+ tls.CipherSuiteName(connState.CipherSuite),
+ connState.PeerCertificates[0].SignatureAlgorithm,
+ spkiHash(connState.PeerCertificates[0]),
+ connState.NegotiatedProtocol,
+ )
+ }
+ return conn, nil
+}