]> Sergey Matveev's repositories - tofuproxy.git/blob - tls/dial.go
172e3fc81cb353283dbf80ee9ffb40e7b3e3c04c
[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-2023 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         host = toLowerCaseASCII(host)
37         ccg := ClientCertificateGetter{host: host}
38         cfg := tls.Config{
39                 VerifyPeerCertificate: func(
40                         rawCerts [][]byte,
41                         verifiedChains [][]*x509.Certificate,
42                 ) error {
43                         return verifyCert(host, nil, rawCerts, verifiedChains)
44                 },
45                 ClientSessionCache:   sessionCache,
46                 NextProtos:           []string{"h2", "http/1.1"},
47                 GetClientCertificate: ccg.get,
48         }
49         conn, dialErr := tls.Dial(network, addr, &cfg)
50         if dialErr != nil {
51                 if _, ok := dialErr.(ErrRejected); ok {
52                         return nil, dialErr
53                 }
54                 cfg.InsecureSkipVerify = true
55                 cfg.VerifyPeerCertificate = func(
56                         rawCerts [][]byte,
57                         verifiedChains [][]*x509.Certificate,
58                 ) error {
59                         return verifyCert(host, dialErr, rawCerts, verifiedChains)
60                 }
61                 var err error
62                 conn, err = tls.Dial(network, addr, &cfg)
63                 if err != nil {
64                         fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error())
65                         return nil, err
66                 }
67         }
68         connState := conn.ConnectionState()
69         if !connState.DidResume {
70                 fifos.LogTLS <- fmt.Sprintf(
71                         "%s\t%s %s %s\t%s\t%s",
72                         addr,
73                         ucspi.TLSVersion(connState.Version),
74                         tls.CipherSuiteName(connState.CipherSuite),
75                         connState.PeerCertificates[0].SignatureAlgorithm,
76                         spkiHash(connState.PeerCertificates[0]),
77                         connState.NegotiatedProtocol,
78                 )
79         }
80         return conn, nil
81 }