Another big refactor, splitting the Config struct in two.
The split reflects that between variables which can and cannot be overridden by .molly files, and this greatly simplifies the processing of said files, getting rid of the need for lots of ugly temporary variable thrashing.
This commit is contained in:
parent
e70ec82594
commit
eb85a6e94c
12 changed files with 129 additions and 167 deletions
|
@ -27,7 +27,7 @@ func enforceCertificateValidity(clientCerts []*x509.Certificate, conn net.Conn,
|
|||
}
|
||||
}
|
||||
|
||||
func handleCertificateZones(URL *url.URL, clientCerts []*x509.Certificate, config Config, conn net.Conn, logEntry *LogEntry) {
|
||||
func handleCertificateZones(URL *url.URL, clientCerts []*x509.Certificate, config UserConfig, conn net.Conn, logEntry *LogEntry) {
|
||||
authorised := true
|
||||
for zone, allowedFingerprints := range config.CertificateZones {
|
||||
matched, err := regexp.Match(zone, []byte(URL.Path))
|
||||
|
|
200
config.go
200
config.go
|
@ -9,195 +9,167 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
type SysConfig struct {
|
||||
Port int
|
||||
Hostname string
|
||||
CertPath string
|
||||
KeyPath string
|
||||
DocBase string
|
||||
HomeDocBase string
|
||||
GeminiExt string
|
||||
DefaultLang string
|
||||
DefaultEncoding string
|
||||
AccessLog string
|
||||
ErrorLog string
|
||||
ReadMollyFiles bool
|
||||
TempRedirects map[string]string
|
||||
PermRedirects map[string]string
|
||||
MimeOverrides map[string]string
|
||||
DocBase string
|
||||
HomeDocBase string
|
||||
CGIPaths []string
|
||||
SCGIPaths map[string]string
|
||||
CertificateZones map[string][]string
|
||||
ReadMollyFiles bool
|
||||
AllowTLS12 bool
|
||||
DirectorySort string
|
||||
DirectorySubdirsFirst bool
|
||||
DirectoryReverse bool
|
||||
DirectoryTitles bool
|
||||
}
|
||||
|
||||
type MollyFile struct {
|
||||
type UserConfig struct {
|
||||
GeminiExt string
|
||||
DefaultLang string
|
||||
DefaultEncoding string
|
||||
TempRedirects map[string]string
|
||||
PermRedirects map[string]string
|
||||
MimeOverrides map[string]string
|
||||
CertificateZones map[string][]string
|
||||
DefaultLang string
|
||||
DefaultEncoding string
|
||||
DirectorySort string
|
||||
DirectorySubdirsFirst bool
|
||||
DirectoryReverse bool
|
||||
DirectoryTitles bool
|
||||
}
|
||||
|
||||
func getConfig(filename string) (Config, error) {
|
||||
func getConfig(filename string) (SysConfig, UserConfig, error) {
|
||||
|
||||
var config Config
|
||||
var sysConfig SysConfig
|
||||
var userConfig UserConfig
|
||||
|
||||
// Defaults
|
||||
config.Port = 1965
|
||||
config.Hostname = "localhost"
|
||||
config.CertPath = "cert.pem"
|
||||
config.KeyPath = "key.pem"
|
||||
config.DocBase = "/var/gemini/"
|
||||
config.HomeDocBase = "users"
|
||||
config.GeminiExt = "gmi"
|
||||
config.DefaultLang = ""
|
||||
config.DefaultEncoding = ""
|
||||
config.AccessLog = "access.log"
|
||||
config.ErrorLog = ""
|
||||
config.TempRedirects = make(map[string]string)
|
||||
config.PermRedirects = make(map[string]string)
|
||||
config.CGIPaths = make([]string, 0)
|
||||
config.SCGIPaths = make(map[string]string)
|
||||
config.AllowTLS12 = true
|
||||
config.DirectorySort = "Name"
|
||||
config.DirectorySubdirsFirst = false
|
||||
sysConfig.Port = 1965
|
||||
sysConfig.Hostname = "localhost"
|
||||
sysConfig.CertPath = "cert.pem"
|
||||
sysConfig.KeyPath = "key.pem"
|
||||
sysConfig.AccessLog = "access.log"
|
||||
sysConfig.ErrorLog = ""
|
||||
sysConfig.DocBase = "/var/gemini/"
|
||||
sysConfig.HomeDocBase = "users"
|
||||
sysConfig.CGIPaths = make([]string, 0)
|
||||
sysConfig.SCGIPaths = make(map[string]string)
|
||||
sysConfig.ReadMollyFiles = false
|
||||
sysConfig.AllowTLS12 = true
|
||||
|
||||
userConfig.GeminiExt = "gmi"
|
||||
userConfig.DefaultLang = ""
|
||||
userConfig.DefaultEncoding = ""
|
||||
userConfig.TempRedirects = make(map[string]string)
|
||||
userConfig.PermRedirects = make(map[string]string)
|
||||
userConfig.DirectorySort = "Name"
|
||||
userConfig.DirectorySubdirsFirst = false
|
||||
|
||||
// Return defaults if no filename given
|
||||
if filename == "" {
|
||||
return config, nil
|
||||
return sysConfig, userConfig, nil
|
||||
}
|
||||
|
||||
// Attempt to overwrite defaults from file
|
||||
_, err := toml.DecodeFile(filename, &config)
|
||||
_, err := toml.DecodeFile(filename, &sysConfig)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
_, err = toml.DecodeFile(filename, &userConfig)
|
||||
if err != nil {
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
|
||||
// Force hostname to lowercase
|
||||
config.Hostname = strings.ToLower(config.Hostname)
|
||||
sysConfig.Hostname = strings.ToLower(sysConfig.Hostname)
|
||||
|
||||
// Validate pseudo-enums
|
||||
switch config.DirectorySort {
|
||||
case "Name", "Size", "Time":
|
||||
default:
|
||||
return config, errors.New("Invalid DirectorySort value.")
|
||||
switch userConfig.DirectorySort {
|
||||
case "Name", "Size", "Time":
|
||||
default:
|
||||
return sysConfig, userConfig, errors.New("Invalid DirectorySort value.")
|
||||
}
|
||||
|
||||
// Absolutise paths
|
||||
config.DocBase, err = filepath.Abs(config.DocBase)
|
||||
sysConfig.DocBase, err = filepath.Abs(sysConfig.DocBase)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
config.CertPath, err = filepath.Abs(config.CertPath)
|
||||
sysConfig.CertPath, err = filepath.Abs(sysConfig.CertPath)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
config.KeyPath, err = filepath.Abs(config.KeyPath)
|
||||
sysConfig.KeyPath, err = filepath.Abs(sysConfig.KeyPath)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
if config.AccessLog != "" && config.AccessLog != "-" {
|
||||
config.AccessLog, err = filepath.Abs(config.AccessLog)
|
||||
if sysConfig.AccessLog != "" && sysConfig.AccessLog != "-" {
|
||||
sysConfig.AccessLog, err = filepath.Abs(sysConfig.AccessLog)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
}
|
||||
if config.ErrorLog != "" {
|
||||
config.ErrorLog, err = filepath.Abs(config.ErrorLog)
|
||||
if sysConfig.ErrorLog != "" {
|
||||
sysConfig.ErrorLog, err = filepath.Abs(sysConfig.ErrorLog)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
}
|
||||
|
||||
// Absolutise CGI paths
|
||||
for index, cgiPath := range config.CGIPaths {
|
||||
for index, cgiPath := range sysConfig.CGIPaths {
|
||||
if !filepath.IsAbs(cgiPath) {
|
||||
config.CGIPaths[index] = filepath.Join(config.DocBase, cgiPath)
|
||||
sysConfig.CGIPaths[index] = filepath.Join(sysConfig.DocBase, cgiPath)
|
||||
}
|
||||
}
|
||||
|
||||
// Expand CGI paths
|
||||
var cgiPaths []string
|
||||
for _, cgiPath := range config.CGIPaths {
|
||||
for _, cgiPath := range sysConfig.CGIPaths {
|
||||
expandedPaths, err := filepath.Glob(cgiPath)
|
||||
if err != nil {
|
||||
return config, errors.New("Error expanding CGI path glob " + cgiPath + ": " + err.Error())
|
||||
return sysConfig, userConfig, errors.New("Error expanding CGI path glob " + cgiPath + ": " + err.Error())
|
||||
}
|
||||
cgiPaths = append(cgiPaths, expandedPaths...)
|
||||
}
|
||||
config.CGIPaths = cgiPaths
|
||||
sysConfig.CGIPaths = cgiPaths
|
||||
|
||||
// Absolutise SCGI paths
|
||||
for index, scgiPath := range config.SCGIPaths {
|
||||
config.SCGIPaths[index], err = filepath.Abs( scgiPath)
|
||||
for index, scgiPath := range sysConfig.SCGIPaths {
|
||||
sysConfig.SCGIPaths[index], err = filepath.Abs( scgiPath)
|
||||
if err != nil {
|
||||
return config, err
|
||||
return sysConfig, userConfig, err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate redirects
|
||||
for _, value := range config.TempRedirects {
|
||||
for _, value := range userConfig.TempRedirects {
|
||||
if strings.Contains(value, "://") && !strings.HasPrefix(value, "gemini://") {
|
||||
return config, errors.New("Invalid cross-protocol redirect to " + value)
|
||||
return sysConfig, userConfig, errors.New("Invalid cross-protocol redirect to " + value)
|
||||
}
|
||||
}
|
||||
for _, value := range config.PermRedirects {
|
||||
for _, value := range userConfig.PermRedirects {
|
||||
if strings.Contains(value, "://") && !strings.HasPrefix(value, "gemini://") {
|
||||
return config, errors.New("Ignoring cross-protocol redirect to " + value)
|
||||
return sysConfig, userConfig, errors.New("Ignoring cross-protocol redirect to " + value)
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
return sysConfig, userConfig, nil
|
||||
}
|
||||
|
||||
func parseMollyFiles(path string, config *Config) {
|
||||
func parseMollyFiles(path string, docBase string, config UserConfig) UserConfig {
|
||||
// Replace config variables which use pointers with new ones,
|
||||
// so that changes made here aren't reflected everywhere.
|
||||
newTempRedirects := make(map[string]string)
|
||||
for key, value := range config.TempRedirects {
|
||||
newTempRedirects[key] = value
|
||||
}
|
||||
config.TempRedirects = newTempRedirects
|
||||
newPermRedirects := make(map[string]string)
|
||||
for key, value := range config.PermRedirects {
|
||||
newPermRedirects[key] = value
|
||||
}
|
||||
config.PermRedirects = newPermRedirects
|
||||
newMimeOverrides := make(map[string]string)
|
||||
for key, value := range config.MimeOverrides {
|
||||
newMimeOverrides[key] = value
|
||||
}
|
||||
config.MimeOverrides = newMimeOverrides
|
||||
newCertificateZones := make(map[string][]string)
|
||||
for key, value := range config.CertificateZones {
|
||||
newCertificateZones[key] = value
|
||||
}
|
||||
config.CertificateZones = newCertificateZones
|
||||
// Initialise MollyFile using main Config
|
||||
var mollyFile MollyFile
|
||||
mollyFile.GeminiExt = config.GeminiExt
|
||||
mollyFile.DefaultLang = config.DefaultLang
|
||||
mollyFile.DefaultEncoding = config.DefaultEncoding
|
||||
mollyFile.DirectorySort = config.DirectorySort
|
||||
mollyFile.DirectorySubdirsFirst = config.DirectorySubdirsFirst
|
||||
mollyFile.DirectoryReverse = config.DirectoryReverse
|
||||
mollyFile.DirectoryTitles = config.DirectoryTitles
|
||||
config.TempRedirects = make(map[string]string)
|
||||
config.PermRedirects = make(map[string]string)
|
||||
config.MimeOverrides = make(map[string]string)
|
||||
config.CertificateZones = make(map[string][]string)
|
||||
|
||||
// Build list of directories to check
|
||||
var dirs []string
|
||||
dirs = append(dirs, path)
|
||||
for {
|
||||
if path == filepath.Clean(config.DocBase) {
|
||||
if path == filepath.Clean(docBase) {
|
||||
break
|
||||
}
|
||||
subpath := filepath.Dir(path)
|
||||
|
@ -219,38 +191,28 @@ func parseMollyFiles(path string, config *Config) {
|
|||
continue
|
||||
}
|
||||
// If the file exists and we can read it, try to parse it
|
||||
_, err = toml.DecodeFile(mollyPath, &mollyFile)
|
||||
_, err = toml.DecodeFile(mollyPath, &config)
|
||||
if err != nil {
|
||||
log.Println("Error parsing .molly file " + mollyPath + ": " + err.Error())
|
||||
continue
|
||||
}
|
||||
// Overwrite main Config using MollyFile
|
||||
config.GeminiExt = mollyFile.GeminiExt
|
||||
config.DefaultLang = mollyFile.DefaultLang
|
||||
config.DefaultEncoding = mollyFile.DefaultEncoding
|
||||
config.DirectorySort = mollyFile.DirectorySort
|
||||
config.DirectorySubdirsFirst = mollyFile.DirectorySubdirsFirst
|
||||
config.DirectoryReverse = mollyFile.DirectoryReverse
|
||||
config.DirectoryTitles = mollyFile.DirectoryTitles
|
||||
for key, value := range mollyFile.TempRedirects {
|
||||
|
||||
for key, value := range config.TempRedirects {
|
||||
if strings.Contains(value, "://") && !strings.HasPrefix(value, "gemini://") {
|
||||
log.Println("Ignoring cross-protocol redirect to " + value + " in .molly file " + mollyPath)
|
||||
continue
|
||||
}
|
||||
config.TempRedirects[key] = value
|
||||
}
|
||||
for key, value := range mollyFile.PermRedirects {
|
||||
for key, value := range config.PermRedirects {
|
||||
if strings.Contains(value, "://") && !strings.HasPrefix(value, "gemini://") {
|
||||
log.Println("Ignoring cross-protocol redirect to " + value + " in .molly file " + mollyPath)
|
||||
continue
|
||||
}
|
||||
config.PermRedirects[key] = value
|
||||
}
|
||||
for key, value := range mollyFile.MimeOverrides {
|
||||
config.MimeOverrides[key] = value
|
||||
}
|
||||
for key, value := range mollyFile.CertificateZones {
|
||||
config.CertificateZones[key] = value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func generateDirectoryListing(URL *url.URL, path string, config Config) (string, error) {
|
||||
func generateDirectoryListing(URL *url.URL, path string, config UserConfig) (string, error) {
|
||||
var listing string
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
|
@ -82,7 +82,7 @@ func generateDirectoryListing(URL *url.URL, path string, config Config) (string,
|
|||
return listing, nil
|
||||
}
|
||||
|
||||
func generatePrettyFileLabel(info os.FileInfo, path string, config Config) string {
|
||||
func generatePrettyFileLabel(info os.FileInfo, path string, config UserConfig) string {
|
||||
var size string
|
||||
if info.IsDir() {
|
||||
size = " "
|
||||
|
|
10
dynamic.go
10
dynamic.go
|
@ -15,7 +15,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func handleCGI(config Config, path string, cgiPath string, URL *url.URL, logEntry *LogEntry, conn net.Conn) {
|
||||
func handleCGI(config SysConfig, path string, cgiPath string, URL *url.URL, logEntry *LogEntry, conn net.Conn) {
|
||||
// Find the shortest leading part of path which maps to an executable file.
|
||||
// Call this part scriptPath, and everything after it pathInfo.
|
||||
components := strings.Split(path, "/")
|
||||
|
@ -86,7 +86,7 @@ func handleCGI(config Config, path string, cgiPath string, URL *url.URL, logEntr
|
|||
conn.Write(response)
|
||||
}
|
||||
|
||||
func handleSCGI(URL *url.URL, scgiPath string, scgiSocket string, config Config, logEntry *LogEntry, conn net.Conn) {
|
||||
func handleSCGI(URL *url.URL, scgiPath string, scgiSocket string, config SysConfig, logEntry *LogEntry, conn net.Conn) {
|
||||
|
||||
// Connect to socket
|
||||
socket, err := net.Dial("unix", scgiSocket)
|
||||
|
@ -148,7 +148,7 @@ func handleSCGI(URL *url.URL, scgiPath string, scgiSocket string, config Config,
|
|||
}
|
||||
}
|
||||
|
||||
func prepareCGIVariables(config Config, URL *url.URL, conn net.Conn, script_path string, path_info string) map[string]string {
|
||||
func prepareCGIVariables(config SysConfig, URL *url.URL, conn net.Conn, script_path string, path_info string) map[string]string {
|
||||
vars := prepareGatewayVariables(config, URL, conn)
|
||||
vars["GATEWAY_INTERFACE"] = "CGI/1.1"
|
||||
vars["SCRIPT_PATH"] = script_path
|
||||
|
@ -156,7 +156,7 @@ func prepareCGIVariables(config Config, URL *url.URL, conn net.Conn, script_path
|
|||
return vars
|
||||
}
|
||||
|
||||
func prepareSCGIVariables(config Config, URL *url.URL, scgiPath string, conn net.Conn) map[string]string {
|
||||
func prepareSCGIVariables(config SysConfig, URL *url.URL, scgiPath string, conn net.Conn) map[string]string {
|
||||
vars := prepareGatewayVariables(config, URL, conn)
|
||||
vars["SCGI"] = "1"
|
||||
vars["CONTENT_LENGTH"] = "0"
|
||||
|
@ -165,7 +165,7 @@ func prepareSCGIVariables(config Config, URL *url.URL, scgiPath string, conn net
|
|||
return vars
|
||||
}
|
||||
|
||||
func prepareGatewayVariables(config Config, URL *url.URL, conn net.Conn) map[string]string {
|
||||
func prepareGatewayVariables(config SysConfig, URL *url.URL, conn net.Conn) map[string]string {
|
||||
vars := make(map[string]string)
|
||||
vars["QUERY_STRING"] = URL.RawQuery
|
||||
vars["REQUEST_METHOD"] = ""
|
||||
|
|
30
handler.go
30
handler.go
|
@ -36,7 +36,7 @@ func isSubdir(subdir, superdir string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func handleGeminiRequest(conn net.Conn, config Config, accessLogEntries chan LogEntry, wg *sync.WaitGroup) {
|
||||
func handleGeminiRequest(conn net.Conn, sysConfig SysConfig, config UserConfig, accessLogEntries chan LogEntry, wg *sync.WaitGroup) {
|
||||
defer conn.Close()
|
||||
defer wg.Done()
|
||||
var tlsConn (*tls.Conn) = conn.(*tls.Conn)
|
||||
|
@ -75,7 +75,7 @@ func handleGeminiRequest(conn net.Conn, config Config, accessLogEntries chan Log
|
|||
if strings.HasSuffix(requestedHost, ".") {
|
||||
requestedHost = requestedHost[:len(requestedHost)-1]
|
||||
}
|
||||
if requestedHost != config.Hostname || (URL.Port() != "" && URL.Port() != strconv.Itoa(config.Port)) {
|
||||
if requestedHost != sysConfig.Hostname || (URL.Port() != "" && URL.Port() != strconv.Itoa(sysConfig.Port)) {
|
||||
conn.Write([]byte("53 No proxying to other hosts or ports!\r\n"))
|
||||
logEntry.Status = 53
|
||||
return
|
||||
|
@ -89,7 +89,7 @@ func handleGeminiRequest(conn net.Conn, config Config, accessLogEntries chan Log
|
|||
}
|
||||
|
||||
// Resolve URI path to actual filesystem path, including following symlinks
|
||||
raw_path := resolvePath(URL.Path, config)
|
||||
raw_path := resolvePath(URL.Path, sysConfig)
|
||||
path, err := filepath.EvalSymlinks(raw_path)
|
||||
if err!= nil {
|
||||
log.Println("Error evaluating path " + raw_path + " for symlinks: " + err.Error())
|
||||
|
@ -99,7 +99,7 @@ func handleGeminiRequest(conn net.Conn, config Config, accessLogEntries chan Log
|
|||
|
||||
// If symbolic links have been used to escape the intended document directory,
|
||||
// deny all knowledge
|
||||
isSub, err := isSubdir(path, config.DocBase)
|
||||
isSub, err := isSubdir(path, sysConfig.DocBase)
|
||||
if err != nil {
|
||||
log.Println("Error testing whether path " + path + " is below DocBase: " + err.Error())
|
||||
}
|
||||
|
@ -114,15 +114,15 @@ func handleGeminiRequest(conn net.Conn, config Config, accessLogEntries chan Log
|
|||
|
||||
// Paranoid security measures:
|
||||
// Fail ASAP if the URL has mapped to a sensitive file
|
||||
if path == config.CertPath || path == config.KeyPath || path == config.AccessLog || path == config.ErrorLog || filepath.Base(path) == ".molly" {
|
||||
if path == sysConfig.CertPath || path == sysConfig.KeyPath || path == sysConfig.AccessLog || path == sysConfig.ErrorLog || filepath.Base(path) == ".molly" {
|
||||
conn.Write([]byte("51 Not found!\r\n"))
|
||||
logEntry.Status = 51
|
||||
return
|
||||
}
|
||||
|
||||
// Read Molly files
|
||||
if config.ReadMollyFiles {
|
||||
parseMollyFiles(path, &config)
|
||||
if sysConfig.ReadMollyFiles {
|
||||
config = parseMollyFiles(path, sysConfig.DocBase, config)
|
||||
}
|
||||
|
||||
// Check whether this URL is in a certificate zone
|
||||
|
@ -138,17 +138,17 @@ func handleGeminiRequest(conn net.Conn, config Config, accessLogEntries chan Log
|
|||
}
|
||||
|
||||
// Check whether this URL is mapped to an SCGI app
|
||||
for scgiPath, scgiSocket := range config.SCGIPaths {
|
||||
for scgiPath, scgiSocket := range sysConfig.SCGIPaths {
|
||||
if strings.HasPrefix(URL.Path, scgiPath) {
|
||||
handleSCGI(URL, scgiPath, scgiSocket, config, &logEntry, conn)
|
||||
handleSCGI(URL, scgiPath, scgiSocket, sysConfig, &logEntry, conn)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether this URL is in a configured CGI path
|
||||
for _, cgiPath := range config.CGIPaths {
|
||||
for _, cgiPath := range sysConfig.CGIPaths {
|
||||
if strings.HasPrefix(path, cgiPath) {
|
||||
handleCGI(config, path, cgiPath, URL, &logEntry, conn)
|
||||
handleCGI(sysConfig, path, cgiPath, URL, &logEntry, conn)
|
||||
if logEntry.Status != 0 {
|
||||
return
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ func readRequest(conn net.Conn, logEntry *LogEntry) (*url.URL, error) {
|
|||
return URL, nil
|
||||
}
|
||||
|
||||
func resolvePath(path string, config Config) string {
|
||||
func resolvePath(path string, config SysConfig) string {
|
||||
// Handle tildes
|
||||
if strings.HasPrefix(path, "/~") {
|
||||
bits := strings.Split(path, "/")
|
||||
|
@ -226,7 +226,7 @@ func resolvePath(path string, config Config) string {
|
|||
return path
|
||||
}
|
||||
|
||||
func handleRedirects(URL *url.URL, config Config, conn net.Conn, logEntry *LogEntry) {
|
||||
func handleRedirects(URL *url.URL, config UserConfig, conn net.Conn, logEntry *LogEntry) {
|
||||
handleRedirectsInner(URL, config.TempRedirects, 30, conn, logEntry)
|
||||
handleRedirectsInner(URL, config.PermRedirects, 31, conn, logEntry)
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ func handleRedirectsInner(URL *url.URL, redirects map[string]string, status int,
|
|||
}
|
||||
}
|
||||
|
||||
func serveDirectory(URL *url.URL, path string, logEntry *LogEntry, conn net.Conn, config Config) {
|
||||
func serveDirectory(URL *url.URL, path string, logEntry *LogEntry, conn net.Conn, config UserConfig) {
|
||||
// Redirect to add trailing slash if missing
|
||||
// (otherwise relative links don't work properly)
|
||||
if !strings.HasSuffix(URL.Path, "/") {
|
||||
|
@ -281,7 +281,7 @@ func serveDirectory(URL *url.URL, path string, logEntry *LogEntry, conn net.Conn
|
|||
}
|
||||
}
|
||||
|
||||
func serveFile(path string, logEntry *LogEntry, conn net.Conn, config Config) {
|
||||
func serveFile(path string, logEntry *LogEntry, conn net.Conn, config UserConfig) {
|
||||
// Get MIME type of files
|
||||
ext := filepath.Ext(path)
|
||||
var mimeType string
|
||||
|
|
34
launch.go
34
launch.go
|
@ -16,12 +16,12 @@ import (
|
|||
|
||||
var VERSION = "0.0.0"
|
||||
|
||||
func launch(config Config, privInfo userInfo) int {
|
||||
func launch(sysConfig SysConfig, userConfig UserConfig, privInfo userInfo) int {
|
||||
var err error
|
||||
|
||||
// Open log files
|
||||
if config.ErrorLog != "" {
|
||||
errorLogFile, err := os.OpenFile(config.ErrorLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if sysConfig.ErrorLog != "" {
|
||||
errorLogFile, err := os.OpenFile(sysConfig.ErrorLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Println("Error opening error log file: " + err.Error())
|
||||
return 1
|
||||
|
@ -32,10 +32,10 @@ func launch(config Config, privInfo userInfo) int {
|
|||
log.SetFlags(log.Ldate|log.Ltime)
|
||||
|
||||
var accessLogFile *os.File
|
||||
if config.AccessLog == "-" {
|
||||
if sysConfig.AccessLog == "-" {
|
||||
accessLogFile = os.Stdout
|
||||
} else if config.AccessLog != "" {
|
||||
accessLogFile, err = os.OpenFile(config.AccessLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
} else if sysConfig.AccessLog != "" {
|
||||
accessLogFile, err = os.OpenFile(sysConfig.AccessLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Println("Error opening access log file: " + err.Error())
|
||||
return 1
|
||||
|
@ -45,22 +45,22 @@ func launch(config Config, privInfo userInfo) int {
|
|||
|
||||
// Read TLS files, create TLS config
|
||||
// Check key file permissions first
|
||||
info, err := os.Stat(config.KeyPath)
|
||||
info, err := os.Stat(sysConfig.KeyPath)
|
||||
if err != nil {
|
||||
log.Println("Error opening TLS key file: " + err.Error())
|
||||
return 1
|
||||
}
|
||||
if uint64(info.Mode().Perm())&0444 == 0444 {
|
||||
log.Println("Refusing to use world-readable TLS key file " + config.KeyPath)
|
||||
log.Println("Refusing to use world-readable TLS key file " + sysConfig.KeyPath)
|
||||
return 1
|
||||
}
|
||||
// Check certificate hostname matches server hostname
|
||||
info, err = os.Stat(config.CertPath)
|
||||
info, err = os.Stat(sysConfig.CertPath)
|
||||
if err != nil {
|
||||
log.Println("Error opening TLS certificate file: " + err.Error())
|
||||
return 1
|
||||
}
|
||||
certFile, err := os.Open(config.CertPath)
|
||||
certFile, err := os.Open(sysConfig.CertPath)
|
||||
if err != nil {
|
||||
log.Println("Error opening TLS certificate file: " + err.Error())
|
||||
return 1
|
||||
|
@ -76,7 +76,7 @@ func launch(config Config, privInfo userInfo) int {
|
|||
return 1
|
||||
}
|
||||
certx509, err := x509.ParseCertificate(certDer.Bytes)
|
||||
err = certx509.VerifyHostname(config.Hostname)
|
||||
err = certx509.VerifyHostname(sysConfig.Hostname)
|
||||
if err != nil {
|
||||
log.Println("Invalid TLS certificate: " + err.Error())
|
||||
return 1
|
||||
|
@ -88,7 +88,7 @@ func launch(config Config, privInfo userInfo) int {
|
|||
}
|
||||
|
||||
// Load certificate and private key
|
||||
cert, err := tls.LoadX509KeyPair(config.CertPath, config.KeyPath)
|
||||
cert, err := tls.LoadX509KeyPair(sysConfig.CertPath, sysConfig.KeyPath)
|
||||
if err != nil {
|
||||
log.Println("Error loading TLS keypair: " + err.Error())
|
||||
return 1
|
||||
|
@ -96,7 +96,7 @@ func launch(config Config, privInfo userInfo) int {
|
|||
var tlscfg tls.Config
|
||||
tlscfg.Certificates = []tls.Certificate{cert}
|
||||
tlscfg.ClientAuth = tls.RequestClientCert
|
||||
if config.AllowTLS12 {
|
||||
if sysConfig.AllowTLS12 {
|
||||
tlscfg.MinVersion = tls.VersionTLS12
|
||||
} else {
|
||||
tlscfg.MinVersion = tls.VersionTLS13
|
||||
|
@ -110,14 +110,14 @@ func launch(config Config, privInfo userInfo) int {
|
|||
}
|
||||
|
||||
// Apply security restrictions
|
||||
err = enableSecurityRestrictions(config, privInfo)
|
||||
err = enableSecurityRestrictions(sysConfig, privInfo)
|
||||
if err != nil {
|
||||
log.Println("Exiting due to failure to apply security restrictions.")
|
||||
return 1
|
||||
}
|
||||
|
||||
// Create TLS listener
|
||||
listener, err := tls.Listen("tcp", ":"+strconv.Itoa(config.Port), &tlscfg)
|
||||
listener, err := tls.Listen("tcp", ":"+strconv.Itoa(sysConfig.Port), &tlscfg)
|
||||
if err != nil {
|
||||
log.Println("Error creating TLS listener: " + err.Error())
|
||||
return 1
|
||||
|
@ -126,7 +126,7 @@ func launch(config Config, privInfo userInfo) int {
|
|||
|
||||
// Start log handling routines
|
||||
var accessLogEntries chan LogEntry
|
||||
if config.AccessLog == "" {
|
||||
if sysConfig.AccessLog == "" {
|
||||
accessLogEntries = nil
|
||||
} else {
|
||||
accessLogEntries = make(chan LogEntry, 10)
|
||||
|
@ -156,7 +156,7 @@ func launch(config Config, privInfo userInfo) int {
|
|||
conn, err := listener.Accept()
|
||||
if err == nil {
|
||||
wg.Add(1)
|
||||
go handleGeminiRequest(conn, config, accessLogEntries, &wg)
|
||||
go handleGeminiRequest(conn, sysConfig, userConfig, accessLogEntries, &wg)
|
||||
} else {
|
||||
select {
|
||||
case <-shutdown:
|
||||
|
|
4
main.go
4
main.go
|
@ -25,12 +25,12 @@ func main() {
|
|||
}
|
||||
|
||||
// Read config
|
||||
config, err := getConfig(conf_file)
|
||||
sysConfig, userConfig, err := getConfig(conf_file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Run server and exit
|
||||
var dummy userInfo
|
||||
os.Exit(launch(config, dummy))
|
||||
os.Exit(launch(sysConfig, userConfig, dummy))
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func main() {
|
|||
}
|
||||
|
||||
// Read config
|
||||
config, err := getConfig(conf_file)
|
||||
sysConfig, userConfig, err := getConfig(conf_file)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -51,5 +51,5 @@ func main() {
|
|||
}
|
||||
|
||||
// Run server and exit
|
||||
os.Exit(launch(config, privInfo))
|
||||
os.Exit(launch(sysConfig, userConfig, privInfo))
|
||||
}
|
||||
|
|
|
@ -9,6 +9,6 @@ type userInfo struct {
|
|||
// This is intended to be called immediately prior to accepting client
|
||||
// connections and may be used to establish a security "jail" for the molly
|
||||
// brown executable.
|
||||
func enableSecurityRestrictions(config Config, ui userInfo) error {
|
||||
func enableSecurityRestrictions(config SysConfig, ui userInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
func enableSecurityRestrictions(config Config, ui userInfo) error {
|
||||
func enableSecurityRestrictions(config SysConfig, ui userInfo) error {
|
||||
|
||||
// Prior to Go 1.6, setuid did not work reliably on Linux
|
||||
// So, absolutely refuse to run as root
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
// operations available to the molly brown executable. Please note that (S)CGI
|
||||
// processes that molly brown spawns or communicates with are unrestricted
|
||||
// and should pledge their own restrictions and unveil their own files.
|
||||
func enableSecurityRestrictions(config Config, ui userInfo) error {
|
||||
func enableSecurityRestrictions(config SysConfig, ui userInfo) error {
|
||||
|
||||
// Setuid to an unprivileged user
|
||||
err := DropPrivs(ui)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
package main
|
||||
|
||||
func enableSecurityRestrictions(config Config, ui userInfo) error {
|
||||
func enableSecurityRestrictions(config SysConfig, ui userInfo) error {
|
||||
|
||||
// Setuid to an unprivileged user
|
||||
return DropPrivs(ui)
|
||||
|
|
Loading…
Reference in a new issue