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>
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.
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.
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/>.
30 ttls "go.stargrave.org/tofuproxy/tls"
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)
42 func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
43 if req.Method != http.MethodConnect {
47 hj, ok := w.(http.Hijacker)
49 log.Fatalln("no hijacking")
51 conn, _, err := hj.Hijack()
57 conn, "%s %d %s\r\n\r\n",
58 req.Proto, http.StatusOK, http.StatusText(http.StatusOK),
60 host, _, _ := ttls.SplitHostPort(req.Host)
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
68 tlsConn := tls.Server(conn, &tls.Config{
69 Certificates: []tls.Certificate{{
70 Certificate: [][]byte{keypair.cert.Raw},
71 PrivateKey: keypair.prv,
74 if err = tlsConn.Handshake(); err != nil {
75 log.Printf("TLS error %s: %+v\n", host, err)
79 Handler: &HTTPSHandler{host: req.Host},
80 TLSNextProto: TLSNextProtoS,
82 err = srv.Serve(&SingleListener{conn: tlsConn})
84 if _, ok := err.(AlreadyAccepted); !ok {
85 log.Printf("TLS serve error %s: %+v\n", host, err)
91 type HTTPSHandler struct {
95 func (h *HTTPSHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
96 req.URL.Scheme = "https"