diff --git a/config.go b/config.go new file mode 100644 index 0000000..c6ccef1 --- /dev/null +++ b/config.go @@ -0,0 +1,41 @@ +package main + +import ( + "github.com/BurntSushi/toml" +) + +type Config struct { + Port int + Hostname string + CertPath string + KeyPath string + DocBase string + HomeDocBase string + LogPath string +} + +func getConfig(filename string) (Config, error) { + + var config Config + + // Defaults + config.Port = 196 + config.Hostname = "localhost" + config.CertPath = "cert.pem" + config.KeyPath = "key.pem" + config.DocBase = "/var/gemini/" + config.HomeDocBase = "users" + config.LogPath = "molly.log" + + // Return defaults if no filename given + if filename == "" { + return config, nil + } + + // Attempt to overwrite defaults from file + _, err := toml.DecodeFile(filename, &config) + if err != nil { + return config, err + } + return config, nil +} diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..171cde1 --- /dev/null +++ b/handler.go @@ -0,0 +1,35 @@ +package main + +import ( + "bufio" + "fmt" + "net" + "net/url" +) + +func handleGeminiRequest(conn net.Conn, config Config) { + defer conn.Close() + + // Read request + reader := bufio.NewReaderSize(conn, 1024) + request, overflow, err := reader.ReadLine() + if overflow { + conn.Write([]byte("59 Request too long!r\n")) + return + } else if err != nil { + conn.Write([]byte("40 Unknown error reading request!r\n")) + return + } + + // Parse request as URL + URL, err := url.Parse(string(request)) + if err != nil { + conn.Write([]byte("59 Error parsing URL!r\n")) + return + } + + // Generic response + conn.Write([]byte("20 text/gemini\r\n")) + body := fmt.Sprintf("Molly at %s says \"Hi!\" from %s.\n", URL.Host, URL.Path) + conn.Write([]byte(body)) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..67e8fb3 --- /dev/null +++ b/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "crypto/tls" + "flag" + "fmt" + "log" + "os" +) + +func main() { + var conf_file string + + // Parse args and read config + flag.StringVar(&conf_file, "c", "", "Path to config file") + flag.Parse() + if conf_file == "" { + _, err := os.Stat("/etc/molly.conf") + if !os.IsNotExist(err) { + conf_file = "/etc/molly.conf" + } + } + config, err := getConfig(conf_file) + if err != nil { + fmt.Println("Error reading config file " + conf_file) + os.Exit(1) + } + + // Read TLS files, create TLS config + cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath) + if err != nil { + log.Fatal(err) + } + tlscfg := &tls.Config{ + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, + } + + // Create TLS listener + listener, err := tls.Listen("tcp", ":1965", tlscfg) + if err != nil { + fmt.Println(err) + return + } + defer listener.Close() + + // Infinite serve loop + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal(err) + } + go handleGeminiRequest(conn, config) + } + +}