]> Sergey Matveev's repositories - tofuproxy.git/blobdiff - tls/dial.go
gemini:// support
[tofuproxy.git] / tls / dial.go
diff --git a/tls/dial.go b/tls/dial.go
new file mode 100644 (file)
index 0000000..a22dc5f
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+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
+}