// tofuproxy -- flexible HTTP/HTTPS proxy, TLS terminator, X.509 TOFU // manager, WARC/geminispace browser // Copyright (C) 2021-2024 Sergey Matveev // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package tofuproxy import ( "crypto" "crypto/tls" "crypto/x509" "fmt" "log" "net/http" "time" ttls "go.stargrave.org/tofuproxy/tls" ) var ( TLSNextProtoS = make(map[string]func(*http.Server, *tls.Conn, http.Handler)) CACert *x509.Certificate CAPrv crypto.PrivateKey ) type Handler struct{} func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { if req.Method != http.MethodConnect { roundTrip(w, req) return } hj, ok := w.(http.Hijacker) if !ok { log.Fatalln("no hijacking") } conn, _, err := hj.Hijack() if err != nil { log.Fatalln(err) } defer conn.Close() fmt.Fprintf( conn, "%s %d %s\r\n\r\n", req.Proto, http.StatusOK, http.StatusText(http.StatusOK), ) host, _, _ := ttls.SplitHostPort(req.Host) hostCertsM.Lock() keypair, ok := hostCerts[host] if !ok || !keypair.cert.NotAfter.After(time.Now().Add(time.Hour)) { keypair = newX509Keypair(host, CACert, CAPrv) hostCerts[host] = keypair } hostCertsM.Unlock() tlsConn := tls.Server(conn, &tls.Config{ Certificates: []tls.Certificate{{ Certificate: [][]byte{keypair.cert.Raw}, PrivateKey: keypair.prv, }}, }) if err = tlsConn.Handshake(); err != nil { log.Printf("TLS error %s: %+v\n", host, err) return } srv := http.Server{ Handler: &HTTPSHandler{host: req.Host}, TLSNextProto: TLSNextProtoS, } err = srv.Serve(&SingleListener{conn: tlsConn}) if err != nil { if _, ok := err.(AlreadyAccepted); !ok { log.Printf("TLS serve error %s: %+v\n", host, err) return } } } type HTTPSHandler struct { host string } func (h *HTTPSHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { req.URL.Scheme = "https" req.URL.Host = h.host roundTrip(w, req) }