]> Sergey Matveev's repositories - tofuproxy.git/blob - tls/dial.go
-mod=vendor if vendor/ exists
[tofuproxy.git] / tls / dial.go
1 // tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU
2 //              manager, WARC/geminispace browser
3 // Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, version 3 of the License.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 package tofuproxy
18
19 import (
20         "context"
21         "crypto/tls"
22         "crypto/x509"
23         "fmt"
24         "net"
25
26         "go.cypherpunks.ru/ucspi"
27         "go.stargrave.org/tofuproxy/fifos"
28 )
29
30 var sessionCache = tls.NewLRUClientSessionCache(1024)
31
32 func DialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
33         host, _, _ := SplitHostPort(addr)
34         host = toLowerCaseASCII(host)
35         ccg := ClientCertificateGetter{host: host}
36         cfg := tls.Config{
37                 VerifyPeerCertificate: func(
38                         rawCerts [][]byte,
39                         verifiedChains [][]*x509.Certificate,
40                 ) error {
41                         return verifyCert(host, nil, rawCerts, verifiedChains)
42                 },
43                 ClientSessionCache:   sessionCache,
44                 NextProtos:           []string{"h2", "http/1.1"},
45                 GetClientCertificate: ccg.get,
46         }
47         conn, dialErr := tls.Dial(network, addr, &cfg)
48         if dialErr != nil {
49                 if _, ok := dialErr.(ErrRejected); ok {
50                         return nil, dialErr
51                 }
52                 cfg.InsecureSkipVerify = true
53                 cfg.VerifyPeerCertificate = func(
54                         rawCerts [][]byte,
55                         verifiedChains [][]*x509.Certificate,
56                 ) error {
57                         return verifyCert(host, dialErr, rawCerts, verifiedChains)
58                 }
59                 var err error
60                 conn, err = tls.Dial(network, addr, &cfg)
61                 if err != nil {
62                         fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error())
63                         return nil, err
64                 }
65         }
66         connState := conn.ConnectionState()
67         if !connState.DidResume {
68                 fifos.LogTLS <- fmt.Sprintf(
69                         "%s\t%s %s %s\t%s\t%s",
70                         addr,
71                         ucspi.TLSVersion(connState.Version),
72                         tls.CipherSuiteName(connState.CipherSuite),
73                         connState.PeerCertificates[0].SignatureAlgorithm,
74                         spkiHash(connState.PeerCertificates[0]),
75                         connState.NegotiatedProtocol,
76                 )
77         }
78         return conn, nil
79 }