Add function to write known hosts to io.Writer

This commit is contained in:
adnano 2020-09-27 14:18:30 -04:00
parent 2eb7fb9ba4
commit 013b2a4d3e
5 changed files with 32 additions and 43 deletions

View File

@ -168,7 +168,7 @@ func (resp *Response) read(r *bufio.Reader) error {
// Client represents a Gemini client. // Client represents a Gemini client.
type Client struct { type Client struct {
// KnownHosts is a list of known hosts that the client trusts. // KnownHosts is a list of known hosts that the client trusts.
KnownHosts *KnownHosts KnownHosts KnownHosts
// CertificateStore contains all the certificates that the client has stored. // CertificateStore contains all the certificates that the client has stored.
CertificateStore *CertificateStore CertificateStore *CertificateStore
@ -210,12 +210,10 @@ func (c *Client) Send(req *Request) (*Response, error) {
} }
// Check that the client trusts the certificate // Check that the client trusts the certificate
if c.TrustCertificate == nil { if c.TrustCertificate == nil {
if c.KnownHosts == nil { if err := c.KnownHosts.Lookup(cert); err != nil {
return ErrCertificateNotTrusted
} else if err := c.KnownHosts.Lookup(cert); err != nil {
return err return err
} }
} else if err := c.TrustCertificate(cert, c.KnownHosts); err != nil { } else if err := c.TrustCertificate(cert, &c.KnownHosts); err != nil {
return err return err
} }
return nil return nil

View File

@ -19,15 +19,8 @@ var (
) )
func init() { func init() {
// Load the list of known hosts client = &gemini.Client{}
knownHosts, err := gemini.LoadKnownHosts() client.KnownHosts.Load()
if err != nil {
log.Fatal(err)
}
client = &gemini.Client{
KnownHosts: knownHosts,
}
client.TrustCertificate = func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error { client.TrustCertificate = func(cert *x509.Certificate, knownHosts *gemini.KnownHosts) error {
err := knownHosts.Lookup(cert) err := knownHosts.Lookup(cert)
@ -53,12 +46,10 @@ func init() {
} }
// Configure a client side certificate. // Configure a client side certificate.
// To generate a certificate, run: // To generate a TLS key pair, run:
//
// openssl genrsa -out client.key 2048
// openssl ecparam -genkey -name secp384r1 -out client.key
// openssl req -new -x509 -sha512 -key client.key -out client.crt -days 365
// //
// go run -tags=example ../cert
var cert tls.Certificate
cert, err = tls.LoadX509KeyPair("examples/client/localhost.crt", "examples/client/localhost.key") cert, err = tls.LoadX509KeyPair("examples/client/localhost.crt", "examples/client/localhost.key")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -13,10 +13,7 @@ func main() {
// Load a TLS key pair. // Load a TLS key pair.
// To generate a TLS key pair, run: // To generate a TLS key pair, run:
// //
// openssl genrsa -out server.key 2048 // go run -tags=example ../cert
// openssl ecparam -genkey -name secp384r1 -out server.key
// openssl req -new -x509 -sha512 -key server.key -out server.crt -days 365
//
cert, err := tls.LoadX509KeyPair("examples/server/localhost.crt", "examples/server/localhost.key") cert, err := tls.LoadX509KeyPair("examples/server/localhost.crt", "examples/server/localhost.key")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -60,11 +60,7 @@ func init() {
var setupDefaultClientOnce sync.Once var setupDefaultClientOnce sync.Once
func setupDefaultClient() { func setupDefaultClient() {
knownHosts, err := LoadKnownHosts() DefaultClient.KnownHosts.Load()
if err != nil {
knownHosts = &KnownHosts{}
}
DefaultClient.KnownHosts = knownHosts
} }
// Send sends a Gemini request and returns a Gemini response. // Send sends a Gemini request and returns a Gemini response.

37
tofu.go
View File

@ -15,50 +15,50 @@ import (
) )
// KnownHosts represents a list of known hosts. // KnownHosts represents a list of known hosts.
// The zero value for KnownHosts is an empty list ready to use.
type KnownHosts struct { type KnownHosts struct {
hosts []KnownHost hosts []KnownHost
file *os.File file *os.File
} }
// LoadKnownHosts loads the known hosts from the default known hosts path. // Load loads the known hosts from the default known hosts path, which is
// The default path is $XDG_DATA_HOME/gemini/known_hosts // `$XDG_DATA_HOME/gemini/known_hosts`.
// It creates the path and any of its parent directories if they do not exist. // It creates the path and any of its parent directories if they do not exist.
// The returned KnownHosts appends to the file whenever a certificate is added. // KnownHosts will append to the file whenever a certificate is added.
func LoadKnownHosts() (*KnownHosts, error) { func (k *KnownHosts) Load() error {
path, err := defaultKnownHostsPath() path, err := defaultKnownHostsPath()
if err != nil { if err != nil {
return nil, err return err
} }
return LoadKnownHostsFrom(path) return k.LoadFrom(path)
} }
// LoadKnownHosts loads the known hosts from the provided path. // LoadFrom loads the known hosts from the provided path.
// It creates the path and any of its parent directories if they do not exist. // It creates the path and any of its parent directories if they do not exist.
// The returned KnownHosts appends to the file whenever a certificate is added. // KnownHosts will append to the file whenever a certificate is added.
func LoadKnownHostsFrom(path string) (*KnownHosts, error) { func (k *KnownHosts) LoadFrom(path string) error {
if dir := filepath.Dir(path); dir != "." { if dir := filepath.Dir(path); dir != "." {
err := os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return nil, err return err
} }
} }
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDONLY, 0644) f, err := os.OpenFile(path, os.O_CREATE|os.O_RDONLY, 0644)
if err != nil { if err != nil {
return nil, err return err
} }
k := &KnownHosts{}
k.Parse(f) k.Parse(f)
f.Close() f.Close()
// Open the file for append-only use // Open the file for append-only use
f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil { if err != nil {
return nil, err return err
} }
k.file = f k.file = f
return k, nil return nil
} }
// Add adds a certificate to the KnownHosts. // Add adds a certificate to the list of known hosts.
// If KnownHosts was loaded from a file, Add will append to the file. // If KnownHosts was loaded from a file, Add will append to the file.
func (k *KnownHosts) Add(cert *x509.Certificate) { func (k *KnownHosts) Add(cert *x509.Certificate) {
host := NewKnownHost(cert) host := NewKnownHost(cert)
@ -125,6 +125,13 @@ func (k *KnownHosts) Parse(r io.Reader) {
} }
} }
// Write writes the known hosts to the provided io.Writer.
func (k *KnownHosts) Write(w io.Writer) {
for _, h := range k.hosts {
h.Write(w)
}
}
// KnownHost represents a known host. // KnownHost represents a known host.
type KnownHost struct { type KnownHost struct {
Hostname string // e.g. gemini.circumlunar.space Hostname string // e.g. gemini.circumlunar.space