commit 51a832af15dcc84e17c9c026ad78aca7646e50ca Author: sejo Date: Thu Jan 20 14:44:06 2022 -0600 commit inicial diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..bfd32e2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,30 @@ +CHAOTIC SOFTWARE : BEWARE + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Don't expect support, ongoing development, or maintenance. side effects +may include uncomfortable clarity, dry mouth, and/or spiritual revolt. +For your own sanity, PLEASE FORK NOW! + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..5edec0d --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# chamorrx + +pequeño servidor gemini en go + +basado en el ejemplo de servidor de [go-gemini](https://git.sr.ht/~adnano/go-gemini) y algunos aspectos del servidor de [flounder](https://git.alexwennerberg.com/flounder/) + +# config + +el servidor espera una configuración con el siguiente formato, para indicar los hosts que existirán y desde dónde se servirán: + +``` +example.com /var/gemini/ejemplo +otrohost.com /var/gemini/otroejemplo +``` + +# licencia + +> THIS IS CHAOTIC SOFTWARE BEWARE diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..95d1b5b --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module chamorrx + +go 1.17 + +require git.sr.ht/~adnano/go-gemini v0.2.2 + +require ( + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect + golang.org/x/text v0.3.3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..654e195 --- /dev/null +++ b/go.sum @@ -0,0 +1,9 @@ +git.sr.ht/~adnano/go-gemini v0.2.2 h1:p2owKzrQ1wTgvPS5CZCPYArQyNUL8ZgYOHHrTjH9sdI= +git.sr.ht/~adnano/go-gemini v0.2.2/go.mod h1:hQ75Y0i5jSFL+FQ7AzWVAYr5LQsaFC7v3ZviNyj46dY= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/servidor.go b/servidor.go new file mode 100644 index 0000000..96cfa61 --- /dev/null +++ b/servidor.go @@ -0,0 +1,136 @@ +// servidor gemini basado en el ejemplo de go-gemini y algo del código de flounder +// https://git.sr.ht/~adnano/go-gemini +// https://git.alexwennerberg.com/flounder/ + +package main + +import ( + "context" + "log" + "os" + "os/signal" + "time" + "io" + "bufio" + + "path" + "path/filepath" + "strings" + + "git.sr.ht/~adnano/go-gemini" + "git.sr.ht/~adnano/go-gemini/certificate" +) + +// de flounder: https://git.alexwennerberg.com/flounder/file/utils.go.html +func cleanPath(thepath string) string { + res := filepath.FromSlash(path.Clean("/" + strings.Trim(thepath, "/"))) + if strings.Contains(res, "..") { // sanity check + return "" + } + return res +} + + +// mapa de hosts : directorios +var mapa map[string]string + +// inspirada por gmiPage de flounder: https://git.alexwennerberg.com/flounder/file/gemini.go.html +func gmiHandler(_ context.Context, w gemini.ResponseWriter, r *gemini.Request) { + + fileName := cleanPath(r.URL.Path) + // obtiene el hostname sin el puerto + host := strings.SplitN(r.URL.Host, ":", 2)[0] + + // utiliza el directorio indicado por el mapa + dir := mapa[host] + + fullPath := path.Join(dir, fileName) + if strings.Contains(fileName, "atom.xml") { + _, err := os.Stat(fullPath) + + if err == nil { + // inspirado de ServeFile en fs.go: + f, _ := os.Open(fullPath) + defer f.Close() + w.SetMediaType("application/atom+xml") + io.Copy(w, f) + return + } + } + + gemini.ServeFile(w, os.DirFS(dir), fileName) +} + +func main() { + config, err := os.Open("config.txt") + + log.Println("cargando configuración...") + if err != nil { + log.Fatal(err) + } + defer config.Close() + + scanner := bufio.NewScanner(config) + mapa = make(map[string]string) + + for scanner.Scan(){ + campos := strings.SplitN( scanner.Text(), " ", 2 ) + mapa[ campos[0] ] = campos[1] + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + log.Println("configuración cargada!") + + log.Println("registrando certificados...") + + certificates := &certificate.Store{} + + for host,_ := range mapa { + certificates.Register(host) + } + + if err := certificates.Load("certs"); err != nil { + log.Fatal(err) + } + + log.Println("certificados listos!") + + log.Println("iniciando servidor...") + mux := &gemini.Mux{} + mux.HandleFunc("/", gmiHandler) + + server := &gemini.Server{ + Addr: ":1965", + Handler: gemini.LoggingMiddleware(mux), + ReadTimeout: 30 * time.Second, + WriteTimeout: 1 * time.Minute, + GetCertificate: certificates.Get, + } + + // Listen for interrupt signal + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + + errch := make(chan error) + go func() { + ctx := context.Background() + errch <- server.ListenAndServe(ctx) + }() + + select { + case err := <-errch: + log.Fatal(err) + case <-c: + // Shutdown the server + log.Println("Shutting down...") + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + err := server.Shutdown(ctx) + if err != nil { + log.Fatal(err) + } + } +}