package main import ( "bytes" "context" "crypto/md5" "crypto/tls" "crypto/x509" "encoding/hex" "fmt" "log" "net" "os" "strings" "tildegit.org/tjp/gus/gemini" guslog "tildegit.org/tjp/gus/contrib/log" ) func main() { // Get TLS files from the environment certfile, keyfile := envConfig() // build a TLS configuration suitable for gemini tlsconf, err := gemini.FileTLS(certfile, keyfile) if err != nil { log.Fatal(err) } // set up the network listener listener, err := net.Listen("tcp4", ":1965") if err != nil { log.Fatal(err) } // add stdout logging to the request handler handler := guslog.Requests(os.Stdout, nil)(inspectHandler) // run the server gemini.NewServer(context.Background(), tlsconf, listener, handler).Serve() } func envConfig() (string, string) { certfile, ok := os.LookupEnv("SERVER_CERTIFICATE") if !ok { log.Fatal("missing SERVER_CERTIFICATE environment variable") } keyfile, ok := os.LookupEnv("SERVER_PRIVATEKEY") if !ok { log.Fatal("missing SERVER_PRIVATEKEY environment variable") } return certfile, keyfile } func inspectHandler(ctx context.Context, req *gemini.Request) *gemini.Response { // build and return a ```-wrapped description of the connection TLS state body := "```\n" + displayTLSState(req.TLSState) + "\n```" return gemini.Success("text/gemini", bytes.NewBufferString(body)) } func displayTLSState(state *tls.ConnectionState) string { builder := &strings.Builder{} builder.WriteString("Version: ") builder.WriteString(map[uint16]string{ tls.VersionTLS10: "TLSv1.0", tls.VersionTLS11: "TLSv1.1", tls.VersionTLS12: "TLSv1.2", tls.VersionTLS13: "TLSv1.3", tls.VersionSSL30: "SSLv3", }[state.Version]) builder.WriteString("\n") builder.WriteString(fmt.Sprintf("Handshake complete: %t\n", state.HandshakeComplete)) builder.WriteString(fmt.Sprintf("Did resume: %t\n", state.DidResume)) builder.WriteString(fmt.Sprintf("Cipher suite: %x\n", state.CipherSuite)) builder.WriteString(fmt.Sprintf("Negotiated protocol: %q\n", state.NegotiatedProtocol)) builder.WriteString(fmt.Sprintf("Server name: %s\n", state.ServerName)) builder.WriteString(fmt.Sprintf("Certificates (%d)\n", len(state.PeerCertificates))) for i, cert := range state.PeerCertificates { builder.WriteString(fmt.Sprintf(" #%d: %s\n", i+1, fingerprint(cert))) } return builder.String() } func fingerprint(cert *x509.Certificate) []byte { raw := md5.Sum(cert.Raw) dst := make([]byte, hex.EncodedLen(len(raw))) hex.Encode(dst, raw[:]) return dst }