]> Sergey Matveev's repositories - tofuproxy.git/blob - tls.go
Ability to choose ECDSA/EdDSA algorithms
[tofuproxy.git] / tls.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         "crypto"
23         "crypto/tls"
24         "crypto/x509"
25         "fmt"
26         "log"
27         "net/http"
28         "time"
29
30         ttls "go.stargrave.org/tofuproxy/tls"
31 )
32
33 var (
34         TLSNextProtoS = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
35         CACert        *x509.Certificate
36         CAPrv         crypto.PrivateKey
37         sessionCache  = tls.NewLRUClientSessionCache(1024)
38 )
39
40 type Handler struct{}
41
42 func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
43         if req.Method != http.MethodConnect {
44                 roundTrip(w, req)
45                 return
46         }
47         hj, ok := w.(http.Hijacker)
48         if !ok {
49                 log.Fatalln("no hijacking")
50         }
51         conn, _, err := hj.Hijack()
52         if err != nil {
53                 log.Fatalln(err)
54         }
55         defer conn.Close()
56         fmt.Fprintf(
57                 conn, "%s %d %s\r\n\r\n",
58                 req.Proto, http.StatusOK, http.StatusText(http.StatusOK),
59         )
60         host, _, _ := ttls.SplitHostPort(req.Host)
61         hostCertsM.Lock()
62         keypair, ok := hostCerts[host]
63         if !ok || !keypair.cert.NotAfter.After(time.Now().Add(time.Hour)) {
64                 keypair = newX509Keypair(host, CACert, CAPrv)
65                 hostCerts[host] = keypair
66         }
67         hostCertsM.Unlock()
68         tlsConn := tls.Server(conn, &tls.Config{
69                 Certificates: []tls.Certificate{{
70                         Certificate: [][]byte{keypair.cert.Raw},
71                         PrivateKey:  keypair.prv,
72                 }},
73         })
74         if err = tlsConn.Handshake(); err != nil {
75                 log.Printf("TLS error %s: %+v\n", host, err)
76                 return
77         }
78         srv := http.Server{
79                 Handler:      &HTTPSHandler{host: req.Host},
80                 TLSNextProto: TLSNextProtoS,
81         }
82         err = srv.Serve(&SingleListener{conn: tlsConn})
83         if err != nil {
84                 if _, ok := err.(AlreadyAccepted); !ok {
85                         log.Printf("TLS serve error %s: %+v\n", host, err)
86                         return
87                 }
88         }
89 }
90
91 type HTTPSHandler struct {
92         host string
93 }
94
95 func (h *HTTPSHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
96         req.URL.Scheme = "https"
97         req.URL.Host = h.host
98         roundTrip(w, req)
99 }