Compare commits
No commits in common. "46221582c76e0b506dc81100cf22154f12079a53" and "72e92e1219c5d9fb93c85218050cdae3c4f8715b" have entirely different histories.
46221582c7
...
72e92e1219
2
go.mod
2
go.mod
|
@ -2,4 +2,4 @@ module konbata
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require git.sr.ht/~adnano/go-gemini v0.2.2
|
require git.sr.ht/~adnano/go-gemini v0.2.2 // indirect
|
||||||
|
|
124
hosts.go
124
hosts.go
|
@ -1,124 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var hosts map[string]Host
|
|
||||||
|
|
||||||
type Host struct {
|
|
||||||
// string since why convert it to bytes and handle errors when you can just not
|
|
||||||
Fingerprint string
|
|
||||||
NotAfter time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func populateHosts() error {
|
|
||||||
file, err := os.OpenFile(filepath.Join(xdgDataHome(), "konbata", "known_hosts_2"), os.O_RDONLY|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
hosts = make(map[string]Host)
|
|
||||||
for scanner.Scan() {
|
|
||||||
if scanner.Text() == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
values := strings.Split(scanner.Text(), " ")
|
|
||||||
if len(values) != 3 {
|
|
||||||
return errors.New("malformed host line encountered")
|
|
||||||
}
|
|
||||||
hostname := values[0]
|
|
||||||
fingerprint := values[1]
|
|
||||||
timeInt, err := strconv.ParseInt(values[2], 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hosts[hostname] = Host{
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
NotAfter: time.UnixMicro(timeInt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveHostsTmp(filename string) error {
|
|
||||||
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
err = file.Truncate(0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
writer := bufio.NewWriter(file)
|
|
||||||
for hostname, host := range hosts {
|
|
||||||
_, err = writer.WriteString(hostname)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = writer.WriteRune(' ')
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = writer.WriteString(host.Fingerprint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = writer.WriteRune(' ')
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = writer.WriteString(strconv.FormatInt(host.NotAfter.UnixMicro(), 10))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = writer.WriteRune('\n')
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return writer.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveHosts() error {
|
|
||||||
filename := filepath.Join(xdgDataHome(), "konbata", "known_hosts_2")
|
|
||||||
tmpFilename := filepath.Join(xdgDataHome(), "konbata", "known_hosts_2.tmp")
|
|
||||||
err := saveHostsTmp(tmpFilename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Rename(tmpFilename, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TrustCertificate(hostname string, cert *x509.Certificate) error {
|
|
||||||
hash := sha256.New()
|
|
||||||
hash.Write(cert.Raw)
|
|
||||||
fingerprint := hex.EncodeToString(hash.Sum(nil))
|
|
||||||
currentTime := time.Now()
|
|
||||||
|
|
||||||
host, ok := hosts[hostname]
|
|
||||||
if !ok || currentTime.After(host.NotAfter) {
|
|
||||||
if currentTime.Before(cert.NotBefore) {
|
|
||||||
return errors.New("cert is used before its not before value")
|
|
||||||
}
|
|
||||||
hosts[hostname] = Host{
|
|
||||||
Fingerprint: fingerprint,
|
|
||||||
NotAfter: cert.NotAfter,
|
|
||||||
}
|
|
||||||
return saveHosts()
|
|
||||||
} else if host.Fingerprint != fingerprint {
|
|
||||||
return errors.New("fingerprint does not match!")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
34
main.go
34
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -9,18 +10,31 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
"git.sr.ht/~adnano/go-gemini"
|
||||||
|
"git.sr.ht/~adnano/go-gemini/tofu"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TIMEOUT_NS time.Duration = time.Duration(60_000_000_000)
|
const TIMEOUT_NS time.Duration = time.Duration(60_000_000_000)
|
||||||
const MAX_FILE_SIZE int64 = 512 * 1024
|
const MAX_FILE_SIZE int64 = 512 * 1024
|
||||||
|
|
||||||
|
var (
|
||||||
|
hosts tofu.KnownHosts
|
||||||
|
hostsfile *tofu.HostWriter
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
err := populateHosts()
|
path := filepath.Join(xdgDataHome(), "konbata", "known_hosts")
|
||||||
|
err := hosts.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostsfile, err = tofu.OpenHostsFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +50,22 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trustCertificate(hostname string, cert *x509.Certificate) error {
|
||||||
|
host := tofu.NewHost(hostname, cert.Raw)
|
||||||
|
knownHost, ok := hosts.Lookup(hostname)
|
||||||
|
if ok {
|
||||||
|
// Check fingerprint
|
||||||
|
if knownHost.Fingerprint != host.Fingerprint {
|
||||||
|
return errors.New("fingerprint does not match!")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts.Add(host)
|
||||||
|
hostsfile.WriteHost(host)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func do(client gemini.Client, ctx context.Context, req *gemini.Request, via []*gemini.Request) (*gemini.Response, *gemini.Request, error) {
|
func do(client gemini.Client, ctx context.Context, req *gemini.Request, via []*gemini.Request) (*gemini.Response, *gemini.Request, error) {
|
||||||
if target, exists := predirs[req.URL.String()]; exists {
|
if target, exists := predirs[req.URL.String()]; exists {
|
||||||
via = append(via, req)
|
via = append(via, req)
|
||||||
|
@ -121,7 +151,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
client := gemini.Client{
|
client := gemini.Client{
|
||||||
TrustCertificate: TrustCertificate,
|
TrustCertificate: trustCertificate,
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), TIMEOUT_NS)
|
ctx, cancel := context.WithTimeout(context.Background(), TIMEOUT_NS)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
Loading…
Reference in New Issue