-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+manager, WARC/Gemini browser.
Home page: http://www.tofuproxy.stargrave.org/
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
"go.stargrave.org/tofuproxy"
"go.stargrave.org/tofuproxy/fifos"
"go.stargrave.org/tofuproxy/rounds"
+ ttls "go.stargrave.org/tofuproxy/tls"
)
func main() {
fifos.NoTAI = *notai
fifos.Start(*fifosDir)
- tofuproxy.Certs = *certs
- tofuproxy.CCerts = *ccerts
- tofuproxy.DNSSrv = *dnsSrv
+ ttls.Certs = *certs
+ ttls.CCerts = *ccerts
+ ttls.DNSSrv = *dnsSrv
tofuproxy.CACert = caCert
tofuproxy.CAPrv = caPrv
rounds.WARCOnly = *warcOnly
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
--- /dev/null
+@node Gemini
+@section Geminispace
+
+You can browse @url{https://en.wikipedia.org/wiki/Gemini_(protocol),
+geminispace} by going to @url{https://gemini/HOST/PATH}, where
+@var{HOST} and @var{PATH} are taken from ordinary gemini URL. For
+example to browse @url{gemini://gemini.circumlunar.space/docs/}, you must go
+through @command{tofuproxy} to @url{https://gemini/gemini.circumlunar.space/docs/}.
+
+If you request @url{https://gemini/} URL with @code{Accept: text/gemini}
+HTTP header, then gemfiles (@file{.gmi} will be passed as-is without
+conversion to HTML.
+
+No @code{INPUT} is supported currently.
WARC-related software is written on Python, that nowadays is close to be
impossible to install and use with all its broken dependencies system.
+@item And yet another piece of software is needed for browsing the
+@url{https://en.wikipedia.org/wiki/Gemini_(protocol), geminispace}?
+Too many bicycles already!
+
@end itemize
That is why I wrote @command{tofuproxy} -- pure Go HTTP proxy, MitMing
client too.
@item
-Ability to load @url{https://en.wikipedia.org/wiki/Web_ARChive, WARC}
-files, possibly compressed, possibly continued and replace responses.
+Ability to load and browse @url{https://en.wikipedia.org/wiki/Web_ARChive,
+WARC} archives, possibly @command{gzip}/@command{zstd} compressed.
@item
-
+Ability to browse geminispace, by transparent conversion to HTMLs with
+URL rewriting.
@end itemize
@include spies.texi
@include certs.texi
@include warcs.texi
+@include gemini.texi
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
}
caches.TLSAuthCacheM.RLock()
for host, tlsCert := range caches.TLSAuthCache {
- cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
- if err != nil {
- log.Fatalln(err)
+ subj := "NONE"
+ if len(tlsCert.Certificate) != 0 {
+ cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
+ if err != nil {
+ log.Fatalln(err)
+ }
+ subj = cert.Subject.String()
}
- if _, err = fd.WriteString(fmt.Sprintf("%s\t%s\n", host, cert.Subject)); err != nil {
+ if _, err = fd.WriteString(fmt.Sprintf("%s\t%s\n", host, subj)); err != nil {
break
}
}
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
"os/exec"
"path/filepath"
"strings"
+
+ ttls "go.stargrave.org/tofuproxy/tls"
)
func findInNetrc(host string) (string, string) {
l login
}}
`, realm, userInit, passInit))
- cmd := exec.Command(CmdWish)
+ cmd := exec.Command(ttls.CmdWish)
cmd.Stdin = &b
out, err := cmd.Output()
if err != nil {
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
--- /dev/null
+/*
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+package rounds
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "fmt"
+ "html"
+ "io"
+ "log"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "go.stargrave.org/tofuproxy/fifos"
+ ttls "go.stargrave.org/tofuproxy/tls"
+)
+
+const (
+ ContentTypeGemini = "text/gemini"
+ SchemeGemini = "gemini://"
+ GeminiEntrypoint = "https://gemini"
+ GeminiPort = ":1965"
+)
+
+var GemCodeName = map[int]string{
+ 10: "INPUT",
+ 11: "SENSITIVE INPUT",
+ 20: "SUCCESS",
+ 30: "REDIRECT - TEMPORARY",
+ 31: "REDIRECT - PERMANENT",
+ 40: "TEMPORARY FAILURE",
+ 41: "SERVER UNAVAILABLE",
+ 42: "CGI ERROR",
+ 43: "PROXY ERROR",
+ 44: "SLOW DOWN",
+ 50: "PERMANENT FAILURE",
+ 51: "NOT FOUND",
+ 52: "GONE",
+ 53: "PROXY REQUEST REFUSED",
+ 59: "BAD REQUEST",
+ 60: "CLIENT CERTIFICATE REQUIRED",
+ 61: "CERTIFICATE NOT AUTHORISED",
+ 62: "CERTIFICATE NOT VALID",
+}
+
+func absolutizeURL(host, u string, paths ...string) string {
+ host = strings.TrimSuffix(host, GeminiPort)
+ if strings.Contains(u, "://") {
+ return u
+ }
+ if strings.HasPrefix(u, "/") {
+ return GeminiEntrypoint + "/" + host + u
+ }
+ paths = append([]string{GeminiEntrypoint, host}, paths...)
+ paths = append(paths, u)
+ return strings.Join(paths, "/")
+}
+
+func geminifyURL(host, u string, paths ...string) string {
+ u = absolutizeURL(host, u, paths...)
+ if !strings.HasPrefix(u, SchemeGemini) {
+ return u
+ }
+ return GeminiEntrypoint + "/" + strings.TrimPrefix(u, SchemeGemini)
+}
+
+func RoundGemini(
+ host string,
+ resp *http.Response,
+ w http.ResponseWriter,
+ req *http.Request,
+) (bool, error) {
+ if host != "gemini" {
+ return true, nil
+ }
+ paths := strings.Split(strings.TrimPrefix(req.URL.Path, "/"), "/")
+ host, paths = paths[0], paths[1:]
+ hostWithPort := host
+ if !strings.Contains(hostWithPort, ":") {
+ hostWithPort += GeminiPort
+ }
+ conn, err := ttls.DialTLS(context.TODO(), "tcp", hostWithPort)
+ if err != nil {
+ log.Printf("%s: can not dial: %+v\n", req.URL, err)
+ return false, err
+ }
+ _, err = fmt.Fprintf(
+ conn, "%s%s/%s\r\n",
+ SchemeGemini, host, strings.Join(paths, "/"),
+ )
+ if err != nil {
+ log.Printf("%s: can not send request: %+v\n", req.URL, err)
+ return false, err
+ }
+ if len(paths) > 0 && paths[len(paths)-1] == "" {
+ paths = paths[:len(paths)-1]
+ }
+ br := bufio.NewReader(conn)
+ rawResp, err := br.ReadString('\n')
+ if err != nil {
+ log.Printf("%s: can not read response: %+v\n", req.URL, err)
+ return false, err
+ }
+ cols := strings.SplitN(rawResp, " ", 2)
+ if len(cols) < 2 {
+ err = fmt.Errorf("invalid response format: %s", rawResp)
+ log.Printf("%s: %s\n", req.URL, err)
+ return false, err
+ }
+ code, err := strconv.Atoi(cols[0])
+ if err != nil {
+ log.Printf("%s: can not parse response code: %+v\n", req.URL, err)
+ return false, err
+ }
+ codeName := GemCodeName[code]
+ if codeName == "" {
+ codeName = "UNKNOWN"
+ }
+ if 10 <= code && code <= 19 {
+ w.Header().Add("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "%s\n%d (%s): INPUT is not supported\n", cols[1], code, codeName)
+ return false, nil
+ }
+ if 30 <= code && code <= 39 {
+ w.Header().Add("Content-Type", "text/html")
+ w.WriteHeader(http.StatusOK)
+ u := geminifyURL(host, cols[1], paths...)
+ w.Write([]byte(
+ fmt.Sprintf(
+ `<!DOCTYPE html>
+<html><head><title>%d (%s) redirection</title></head>
+<body>Redirection to <a href="%s">%s</a></body></html>`,
+ code, codeName, u, u,
+ )))
+ fifos.LogRedir <- fmt.Sprintf(
+ "%s %s\t%d\t%s", req.Method, req.URL, code, cols[1],
+ )
+ return false, nil
+ }
+ if 40 <= code && code <= 49 {
+ w.Header().Add("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusBadGateway)
+ fmt.Fprintf(w, "%s\n%d (%s)\n", cols[1], code, codeName)
+ return false, nil
+ }
+ if 50 <= code && code <= 59 {
+ w.Header().Add("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusBadGateway)
+ fmt.Fprintf(w, "%s\n%d (%s)\n", cols[1], code, codeName)
+ return false, nil
+ }
+ if 60 <= code && code <= 69 {
+ w.Header().Add("Content-Type", "text/plain")
+ w.WriteHeader(http.StatusUnauthorized)
+ fmt.Fprintf(w, "%s\n%d (%s)\n", cols[1], code, codeName)
+ return false, nil
+ }
+ if !(20 <= code && code <= 29) {
+ err = fmt.Errorf("unknown response code: %d", code)
+ log.Printf("%s: %s\n", req.URL, err)
+ return false, err
+ }
+ contentType := strings.Split(strings.TrimRight(cols[1], "\r\n"), ";")[0]
+ if contentType == ContentTypeGemini &&
+ !strings.Contains(req.Header.Get("Accept"), ContentTypeGemini) {
+ w.Header().Add("Content-Type", "text/html")
+ w.WriteHeader(http.StatusOK)
+ raw, err := io.ReadAll(br)
+ if err != nil {
+ log.Printf("%s: can not read response body: %+v\n", req.URL, err)
+ return false, err
+ }
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, `<!DOCTYPE html>
+<html><head><title>%d (%s)</title></head><body>
+`, code, codeName)
+ pre := false
+ for _, line := range strings.Split(string(raw), "\n") {
+ if strings.HasPrefix(line, "```") {
+ if pre {
+ buf.WriteString("</pre>\n")
+ } else {
+ buf.WriteString("<pre>" + line[3:] + "\n")
+ }
+ pre = !pre
+ continue
+ }
+ if pre {
+ fmt.Fprintf(&buf, "%s\n", line)
+ continue
+ }
+ if strings.HasPrefix(line, "=> ") {
+ cols = strings.Fields(line)
+ u := geminifyURL(host, cols[1], paths...)
+ switch len(cols) {
+ case 2:
+ fmt.Fprintf(
+ &buf, "<a href=\"%s\">%s</a><br/>\n",
+ u, html.EscapeString(cols[1]),
+ )
+ default:
+ fmt.Fprintf(
+ &buf, "<a href=\"%s\">%s</a> (<tt>%s</tt>)<br/>\n",
+ u, html.EscapeString(strings.Join(cols[2:], " ")), cols[1],
+ )
+ }
+ continue
+ }
+ if strings.HasPrefix(line, "# ") {
+ fmt.Fprintf(&buf, "<h1>%s</h1>\n", html.EscapeString(line[2:]))
+ continue
+ }
+ if strings.HasPrefix(line, "## ") {
+ fmt.Fprintf(&buf, "<h2>%s</h2>\n", html.EscapeString(line[3:]))
+ continue
+ }
+ if strings.HasPrefix(line, "### ") {
+ fmt.Fprintf(&buf, "<h3>%s</h3>\n", html.EscapeString(line[4:]))
+ continue
+ }
+ if strings.HasPrefix(line, "* ") {
+ fmt.Fprintf(&buf, "• %s\n", html.EscapeString(line[2:]))
+ continue
+ }
+ if strings.HasPrefix(line, "> ") {
+ fmt.Fprintf(
+ &buf, "<blockquote><tt>%s</tt></blockquote>\n",
+ html.EscapeString(line[2:]),
+ )
+ continue
+ }
+ fmt.Fprintf(&buf, "%s<br/>\n", html.EscapeString(line))
+ }
+ buf.WriteString("</body></html>\n")
+ _, err = w.Write(buf.Bytes())
+ return false, err
+ }
+ w.Header().Add("Content-Type", contentType)
+ w.WriteHeader(http.StatusOK)
+ _, err = io.Copy(w, br)
+ if err != nil {
+ log.Printf("%s: can not read response body: %+v\n", req.URL, err)
+ }
+ return false, err
+}
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
location := resp.Header.Get("Location")
w.Write([]byte(
fmt.Sprintf(
- `<html><head><title>%d %s: %s redirection</title></head>
+ `<!DOCTYPE html>
+<html><head><title>%d %s: %s redirection</title></head>
<body>Redirection to <a href="%s">%s</a></body></html>`,
resp.StatusCode, http.StatusText(resp.StatusCode),
redirType, location, location,
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
package tofuproxy
import (
- "context"
"crypto"
"crypto/tls"
"crypto/x509"
"fmt"
"log"
- "net"
"net/http"
"strings"
"time"
-
- "go.cypherpunks.ru/ucspi"
- "go.stargrave.org/tofuproxy/fifos"
)
var (
req.URL.Host = h.host
roundTrip(w, req)
}
-
-func dialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
- host := strings.Split(addr, ":")[0]
- ccg := ClientCertificateGetter{host: host}
- cfg := tls.Config{
- VerifyPeerCertificate: func(
- rawCerts [][]byte,
- verifiedChains [][]*x509.Certificate,
- ) error {
- return verifyCert(host, nil, rawCerts, verifiedChains)
- },
- ClientSessionCache: sessionCache,
- NextProtos: []string{"h2", "http/1.1"},
- GetClientCertificate: ccg.get,
- }
- conn, dialErr := tls.Dial(network, addr, &cfg)
- if dialErr != nil {
- if _, ok := dialErr.(ErrRejected); ok {
- return nil, dialErr
- }
- cfg.InsecureSkipVerify = true
- cfg.VerifyPeerCertificate = func(
- rawCerts [][]byte,
- verifiedChains [][]*x509.Certificate,
- ) error {
- return verifyCert(host, dialErr, rawCerts, verifiedChains)
- }
- var err error
- conn, err = tls.Dial(network, addr, &cfg)
- if err != nil {
- fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error())
- return nil, err
- }
- }
- connState := conn.ConnectionState()
- if !connState.DidResume {
- fifos.LogTLS <- fmt.Sprintf(
- "%s\t%s %s %s\t%s\t%s",
- addr,
- ucspi.TLSVersion(connState.Version),
- tls.CipherSuiteName(connState.CipherSuite),
- connState.PeerCertificates[0].SignatureAlgorithm,
- spkiHash(connState.PeerCertificates[0]),
- connState.NegotiatedProtocol,
- )
- }
- return conn, nil
-}
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
--- /dev/null
+/*
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
+Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
+
+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 <http://www.gnu.org/licenses/>.
+*/
+
+package tofuproxy
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "net"
+ "strings"
+
+ "go.cypherpunks.ru/ucspi"
+ "go.stargrave.org/tofuproxy/fifos"
+)
+
+var sessionCache = tls.NewLRUClientSessionCache(1024)
+
+func DialTLS(ctx context.Context, network, addr string) (net.Conn, error) {
+ host := strings.Split(addr, ":")[0]
+ ccg := ClientCertificateGetter{host: host}
+ cfg := tls.Config{
+ VerifyPeerCertificate: func(
+ rawCerts [][]byte,
+ verifiedChains [][]*x509.Certificate,
+ ) error {
+ return verifyCert(host, nil, rawCerts, verifiedChains)
+ },
+ ClientSessionCache: sessionCache,
+ NextProtos: []string{"h2", "http/1.1"},
+ GetClientCertificate: ccg.get,
+ }
+ conn, dialErr := tls.Dial(network, addr, &cfg)
+ if dialErr != nil {
+ if _, ok := dialErr.(ErrRejected); ok {
+ return nil, dialErr
+ }
+ cfg.InsecureSkipVerify = true
+ cfg.VerifyPeerCertificate = func(
+ rawCerts [][]byte,
+ verifiedChains [][]*x509.Certificate,
+ ) error {
+ return verifyCert(host, dialErr, rawCerts, verifiedChains)
+ }
+ var err error
+ conn, err = tls.Dial(network, addr, &cfg)
+ if err != nil {
+ fifos.LogErr <- fmt.Sprintf("%s\t%s", addr, dialErr.Error())
+ return nil, err
+ }
+ }
+ connState := conn.ConnectionState()
+ if !connState.DidResume {
+ fifos.LogTLS <- fmt.Sprintf(
+ "%s\t%s %s %s\t%s\t%s",
+ addr,
+ ucspi.TLSVersion(connState.Version),
+ tls.CipherSuiteName(connState.CipherSuite),
+ connState.PeerCertificates[0].SignatureAlgorithm,
+ spkiHash(connState.PeerCertificates[0]),
+ connState.NegotiatedProtocol,
+ )
+ }
+ return conn, nil
+}
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
bind . <KeyPress> {switch -exact %%K {
q {exit 0} ; # reject once
+ n {puts "0:NONE" ; exit}
l login
}}
}
certs := make([]*x509.Certificate, 0, len(ents))
tlsCerts := make([]*tls.Certificate, 0, len(ents))
+ b.WriteString(".lb insert end \"0: NONE\"\n")
+ certs = append(certs, nil)
+ tlsCerts = append(tlsCerts, nil)
for i, ent := range ents {
p := filepath.Join(CCerts, ent.Name())
_, cert, err := ucspi.CertificateFromFile(p)
Certificate: [][]byte{cert.Raw},
PrivateKey: prv,
})
- b.WriteString(fmt.Sprintf(".lb insert end \"%d: %s\"\n", i, cert.Subject))
+ b.WriteString(fmt.Sprintf(".lb insert end \"%d: %s\"\n", i+1, cert.Subject))
}
// ioutil.WriteFile("/tmp/tls-auth-dialog.tcl", b.Bytes(), 0666)
cmd := exec.Command(CmdWish)
if err != nil {
return &tls.Certificate{}, nil
}
+ if i == 0 {
+ dummy := tls.Certificate{}
+ caches.TLSAuthCacheM.Lock()
+ caches.TLSAuthCache[g.host] = &dummy
+ caches.TLSAuthCacheM.Unlock()
+ return &dummy, nil
+ }
fifos.LogTLSAuth <- fmt.Sprintf("%s\t%s", g.host, certs[i].Subject)
caches.TLSAuthCacheM.Lock()
caches.TLSAuthCache[g.host] = tlsCerts[i]
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
"go.stargrave.org/tofuproxy/caches"
"go.stargrave.org/tofuproxy/fifos"
"go.stargrave.org/tofuproxy/rounds"
+ ttls "go.stargrave.org/tofuproxy/tls"
)
var (
MaxIdleConns: http.DefaultTransport.(*http.Transport).MaxIdleConns,
IdleConnTimeout: http.DefaultTransport.(*http.Transport).IdleConnTimeout * 2,
TLSHandshakeTimeout: time.Minute,
- DialTLSContext: dialTLS,
+ DialTLSContext: ttls.DialTLS,
ForceAttemptHTTP2: true,
}
proxyHeaders = map[string]struct{}{
host := strings.TrimSuffix(req.URL.Host, ":443")
for _, round := range []Round{
rounds.RoundNoHead,
+ rounds.RoundGemini,
rounds.RoundWARC,
rounds.RoundDenySpy,
rounds.RoundRedditOld,
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
/*
-tofuproxy -- flexible HTTP/WARC proxy with TLS certificates management
+tofuproxy -- flexible HTTP proxy, TLS terminator, X.509 certificates
+ manager, WARC/Gemini browser
Copyright (C) 2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify