From 58d983891e5fae537f360987b4f54023d572fad3 Mon Sep 17 00:00:00 2001 From: Steve Date: Sat, 22 Feb 2020 16:17:38 -0500 Subject: [PATCH] use actual virtual hosts --- README.md | 4 ++++ config.yaml.sample | 3 +-- main.go | 18 ++++++++++-------- proto.go | 3 +-- server.go | 40 ++++++++++++++++++++++++---------------- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 79b7d4b..ce7b91b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Configuration is in config/yaml in one of the above directories. See the sample but a standard file looks like such: --- + port: 1965 active_capsules: - localhost localhost: @@ -22,6 +23,9 @@ but a standard file looks like such: KeyFile: "localhost.key" CertFile: "localhost.crt" +Where each "active_capsule" is a virtual Gemini capsule. SecretShop supports virtual Gemini capsules all listening on port 1965 +as well as multiple Gopher servers runnning (though not virtual Gopher hosts due to protocol limitations) + Please note that CGIDir currently not used (waiting on spec clarification). ## Building diff --git a/config.yaml.sample b/config.yaml.sample index ffbf15c..10361fa 100644 --- a/config.yaml.sample +++ b/config.yaml.sample @@ -1,4 +1,5 @@ --- +port: 1965 active_capsules: - localhost - localhost2 @@ -6,14 +7,12 @@ active_holes: - localhost3 localhost: Hostname: "localhost" - Port: "1965" RootDir: "/var/gemini" CGIDir: "/var/gemini/cgi" KeyFile: "localhost.key" CertFile: "localhost.crt" localhost2: Hostname: "gemini.foo.bar" - Port: "1966" RootDir: "/var/gemini2" CGIDir: "/var/gemini2/cgi" KeyFile: "localhost2.key" diff --git a/main.go b/main.go index 5fd2378..28d1e66 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ func main() { //Load configs active_capsules := viper.GetStringSlice("active_capsules") active_holes := viper.GetStringSlice("active_holes") + port := viper.GetString("port") capsule_list := make([]GeminiConfig, len(active_capsules)) hole_list := make([]GopherConfig, len(active_holes)) for i, c := range active_capsules { @@ -38,14 +39,14 @@ func main() { log.Printf("%v capsules loaded, %v gopherholes loaded", len(capsule_list), len(hole_list)) // Intialize servers wg := new(sync.WaitGroup) - wg.Add(len(capsule_list) + len(hole_list)) - for i, c := range capsule_list { - log.Printf("Starting capsule %v %v", i, c.Hostname) - go func(c interface{}) { - log.Fatal(ListenAndServeTLS(c.(GeminiConfig))) - wg.Done() - }(c) - } + wg.Add(1 + len(hole_list)) + + log.Printf("Starting gemini capsule") + go func(c interface{}) { + log.Fatal(ListenAndServeTLS(port, c.([]GeminiConfig))) + wg.Done() + }(capsule_list) + for i, h := range hole_list { log.Printf("Starting gopherhole %v %v", i, h.Hostname) go func(h interface{}) { @@ -56,6 +57,7 @@ func main() { wg.Done() }(h) } + log.Println("Done bringing up capsules and gopherholes") log.Println("Ho ho! You found me!") wg.Wait() diff --git a/proto.go b/proto.go index 6e4b766..de4090f 100644 --- a/proto.go +++ b/proto.go @@ -40,7 +40,6 @@ type Response struct { type GeminiConfig struct { Hostname string - Port string KeyFile string CertFile string RootDir string @@ -48,5 +47,5 @@ type GeminiConfig struct { } func (c *GeminiConfig) String() string { - return fmt.Sprintf("Gemini Config: %v:%v Files:%v CGI:%v", c.Hostname, c.Port, c.RootDir, c.CGIDir) + return fmt.Sprintf("Gemini Config: %v Files:%v CGI:%v", c.Hostname, c.RootDir, c.CGIDir) } diff --git a/server.go b/server.go index 38ab848..a7557ed 100644 --- a/server.go +++ b/server.go @@ -41,8 +41,7 @@ var ( type Server struct { Addr string // TCP address to listen on, ":gemini" if empty - Hostname string // FQDN Hostname to reach this server on - ServerRoot string //Root folder for gemini files + HostnameToRoot map[string]string //FQDN hostname to root folder } type conn struct { @@ -59,19 +58,32 @@ func (s *Server) newConn(rwc net.Conn) *conn { } return c } -func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { + +func ListenAndServeTLS(port string, cps []GeminiConfig) error { + server := &Server{Addr: ":" + port, HostnameToRoot: make(map[string]string)} + for _, c := range cps { + server.HostnameToRoot[c.Hostname] = c.RootDir + } + return server.ListenAndServeTLS(cps) +} + +func (s *Server) ListenAndServeTLS(configs []GeminiConfig) error { addr := s.Addr mime.AddExtensionType(".gmi", "text/gemini") mime.AddExtensionType(".gemini", "text/gemini") if addr == "" { addr = ":1965" } - - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - log.Fatalf("server: loadkeys: %s", err) + certs := make([]tls.Certificate, len(configs)) + for i, c := range configs { + cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile) + if err != nil { + log.Fatalf("Error loading certs: %s", err) + } + certs[i] = cert } - config := tls.Config{Certificates: []tls.Certificate{cert}} + + config := tls.Config{Certificates: certs} config.Rand = rand.Reader ln, err := tls.Listen("tcp", addr, &config) @@ -81,10 +93,6 @@ func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { return s.Serve(ln) } -func ListenAndServeTLS(cp GeminiConfig) error { - server := &Server{Addr: ":" + cp.Port, ServerRoot: cp.RootDir, Hostname: cp.Hostname} - return server.ListenAndServeTLS(cp.CertFile, cp.KeyFile) -} func (s *Server) Serve(l net.Listener) error { defer l.Close() @@ -135,7 +143,7 @@ func (c *conn) serve(ctx context.Context) { res = c.server.ParseRequest(req) } c.sendResponse(res) - log.Printf("%v: %v requested %v; responded with %v %v", c.server.Hostname, c.C.RemoteAddr(), req, res.Status, res.Meta) + log.Printf("%v requested %v; responded with %v %v", c.C.RemoteAddr(), req, res.Status, res.Meta) c.C.Close() } @@ -154,14 +162,14 @@ func (s *Server) ParseRequest(req string) Response { } if u.Host == "" { return Response{STATUS_BAD_REQUEST, "Need to specify a host", ""} - } else if u.Hostname() != s.Hostname { + } else if s.HostnameToRoot[u.Hostname()] == "" { return Response{STATUS_PROXY_REQUEST_REFUSED, "Proxying by Hostname not currently supported", ""} } if strings.Contains(u.Path, "..") { return Response{STATUS_PERMANENT_FAILURE, "Dots in path, assuming bad faith.", ""} } - selector := s.ServerRoot + u.Path + selector := s.HostnameToRoot[u.Hostname()] + u.Path fi, err := os.Stat(selector) switch { case err != nil: @@ -175,7 +183,7 @@ func (s *Server) ParseRequest(req string) Response { if strings.HasSuffix(u.Path, "/") { return generateDirectory(selector) } else { - return Response{STATUS_REDIRECT_PERMANENT, "gemini://" + s.Hostname + u.Path + "/", ""} + return Response{STATUS_REDIRECT_PERMANENT, "gemini://" + u.Hostname() + u.Path + "/", ""} } default: // it's a file