use actual virtual hosts

This commit is contained in:
Steve 2020-02-22 16:17:38 -05:00
parent 694693e93c
commit 58d983891e
5 changed files with 40 additions and 28 deletions

View File

@ -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

View File

@ -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"

18
main.go
View File

@ -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()

View File

@ -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)
}

View File

@ -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