1
0
mirror of https://git.sr.ht/~adnano/go-gemini synced 2024-11-23 16:52:06 +01:00
Go implementation of the Gemini protocol
Go to file
adnano f38da56732 Handle multiple DNS names
Each DNS name gets its own entry in the known hosts file
2020-09-27 14:22:41 -04:00
examples Add function to write known hosts to io.Writer 2020-09-27 14:18:30 -04:00
.gitignore Implement certificate creation 2020-09-27 13:50:48 -04:00
cert.go Implement certificate creation 2020-09-27 13:50:48 -04:00
client.go Add function to write known hosts to io.Writer 2020-09-27 14:18:30 -04:00
gemini_test.go Add test 2020-09-25 11:27:26 -04:00
gemini.go Add function to write known hosts to io.Writer 2020-09-27 14:18:30 -04:00
go.mod Initial commit 2020-09-21 15:49:09 -04:00
LICENSE Add LICENSE 2020-09-21 15:55:27 -04:00
README.md Implement certificate creation 2020-09-27 13:50:48 -04:00
server.go Reject requests containing '..' in them 2020-09-26 17:13:13 -04:00
tofu.go Handle multiple DNS names 2020-09-27 14:22:41 -04:00

go-gemini

GoDoc

go-gemini implements the Gemini protocol in Go.

It aims to provide an API similar to that of net/http to make it easy to develop Gemini clients and servers.

Examples

See examples/client and examples/server for an example client and server.

To run the examples:

go run -tags=example ./examples/server

Overview

A quick overview of the Gemini protocol:

  1. Client opens connection
  2. Server accepts connection
  3. Client and server complete a TLS handshake
  4. Client validates server certificate
  5. Client sends request
  6. Server sends response header
  7. Server sends response body (only for successful responses)
  8. Server closes connection
  9. Client handles response

The way this is implemented in this package is like so:

  1. Client makes a request with NewRequest. The client then sends the request with (*Client).Send(*Request) (*Response, error). The client then determines whether to trust the certificate in TrustCertificte(*x509.Certificate, *KnownHosts) bool. (See TOFU).
  2. Server recieves the request and constructs a response. The server calls the Serve(*ResponseWriter, *Request) method on the Handler field. The handler writes the response. The server then closes the connection.
  3. Client recieves the response as a *Response. The client then handles the response.

TOFU

go-gemini makes it easy to implement Trust On First Use in your clients.

Clients can load the default list of known hosts:

client := &Client{}
knownHosts, err := gemini.LoadKnownHosts()
if err != nil {
	log.Fatal(err)
}
client.KnownHosts = knownHosts

Clients can then specify how to trust certificates in the TrustCertificate field:

client.TrustCertificate = func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
	// If the certificate is in the known hosts list, allow the connection
	return knownHosts.Lookup(cert)
}

Advanced clients can prompt the user for what to do when encountering an unknown certificate:

client.TrustCertificate = func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
	err := knownHosts.Lookup(cert)
	if err != nil {
		switch err {
		case gemini.ErrCertificateNotTrusted:
			// Alert the user that the certificate is not trusted
			alertUser()
		case gemini.ErrCertificateUnknown:
			// Prompt the user to trust the certificate
			if userTrustsCertificateTemporarily() {
				// Temporarily trust the certificate
				return nil
			} else if userTrustsCertificatePermanently() {
				// Add the certificate to the known hosts file
				knownHosts.Add(cert)
				return nil
			}
		}
	}
	return err
}