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>
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.
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.
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/>.
28 ttls "go.stargrave.org/tofuproxy/tls"
32 TLSNextProtoS = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
33 CACert *x509.Certificate
34 CAPrv crypto.PrivateKey
39 func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
40 if req.Method != http.MethodConnect {
44 hj, ok := w.(http.Hijacker)
46 log.Fatalln("no hijacking")
48 conn, _, err := hj.Hijack()
54 conn, "%s %d %s\r\n\r\n",
55 req.Proto, http.StatusOK, http.StatusText(http.StatusOK),
57 host, _, _ := ttls.SplitHostPort(req.Host)
59 keypair, ok := hostCerts[host]
60 if !ok || !keypair.cert.NotAfter.After(time.Now().Add(time.Hour)) {
61 keypair = newX509Keypair(host, CACert, CAPrv)
62 hostCerts[host] = keypair
65 tlsConn := tls.Server(conn, &tls.Config{
66 Certificates: []tls.Certificate{{
67 Certificate: [][]byte{keypair.cert.Raw},
68 PrivateKey: keypair.prv,
71 if err = tlsConn.Handshake(); err != nil {
72 log.Printf("TLS error %s: %+v\n", host, err)
76 Handler: &HTTPSHandler{host: req.Host},
77 TLSNextProto: TLSNextProtoS,
79 err = srv.Serve(&SingleListener{conn: tlsConn})
81 if _, ok := err.(AlreadyAccepted); !ok {
82 log.Printf("TLS serve error %s: %+v\n", host, err)
88 type HTTPSHandler struct {
92 func (h *HTTPSHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
93 req.URL.Scheme = "https"