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