package config import ( "fmt" "log" "net" "os" "path/filepath" "strconv" "strings" "git.sr.ht/~sircmpwn/getopt" "github.com/vaughan0/go-ini" "git.sr.ht/~sircmpwn/core-go/crypto" ) var ( Debug bool Addr string InternalIPNet []net.IPNet ) // Just loads the config files func LoadFiles() ini.File { var ( config ini.File err error ) // Only loads from one of these locations for _, path := range []string{ "config.ini", "../config.ini", "/etc/sr.ht/config.ini", "/etc/sr.ht/*.ini", } { matches, err_ := filepath.Glob(path) if err_ != nil { panic(err) // only happens on bad input } for _, f := range matches { if config == nil { config, err = ini.LoadFile(f) } else { err = config.LoadFile(f) } if err != nil { break } else { log.Printf("Loaded config from %s", f) } } if len(matches) > 0 { break } } if err != nil { log.Fatalf("Failed to load config file: %v", err) } return config } // Loads the application configuration, reads options from the command line, // and initializes some internals based on these results. func LoadConfig(defaultAddr string) ini.File { Addr = defaultAddr opts, _, err := getopt.Getopts(os.Args, "b:d") if err != nil { panic(err) } for _, opt := range opts { switch opt.Option { case 'b': Addr = opt.Value case 'd': Debug = true } } config := LoadFiles() crypto.InitCrypto(config) nets, ok := config.Get("sr.ht", "internal-ipnet") if !ok { nets = "127.0.0.0/8,::1/128," + // Loopback "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7," + // Private "169.254.0.0/16,fe80::/64" // Link local } for _, n := range strings.Split(nets, ",") { _, net, err := net.ParseCIDR(n) if err != nil { panic(fmt.Errorf("[sr.ht]internal-ipnet: %w", err)) } InternalIPNet = append(InternalIPNet, *net) } return config } // Returns the URL (scheme, host, port) at which the web application for a // given service can be found. // // Set "external" to true if the URL will be accessed by an external user (i.e. // outside of the LAN). func GetOrigin(conf ini.File, svc string, external bool) string { if external { origin, _ := conf.Get(svc, "origin") return origin } origin, ok := conf.Get(svc, "internal-origin") if ok { return origin } origin, _ = conf.Get(svc, "origin") return origin } // Returns the URL (scheme, host, port) at which the API for a given service // can be found. Does not include the /query path! // // Set "external" to true if the URL will be accessed by an external user (i.e. // outside of the LAN). func GetAPI(conf ini.File, svc string, external bool) string { var candidates []string if external { candidates = []string{ "api-origin", "origin", } } else { candidates = []string{ "api-internal-origin", "internal-origin", "api-origin", "origin", } } for _, name := range candidates { origin, ok := conf.Get(svc, name) if ok { return origin } } panic(fmt.Errorf("No suitable origin configured for requested API")) } const DefaultQueueSize = 512 // Returns the configured integer value for the given section and variable name. // If nothing is configured, the default value will be returned. // If an invalid integer is configured, this function will panic. func GetInt(conf ini.File, section, name string, defValue int) int { value := defValue if s, ok := conf.Get(section, name); ok { var err error if value, err = strconv.Atoi(s); err != nil { panic(fmt.Errorf("[%s]%s: %w", section, name, err)) } } return value } // Returns the configured boolean value for the given section and variable name. // If nothing is configured, the default value will be returned. // If an invalid boolean is configured, this function will panic. func GetBool(conf ini.File, section, name string, defValue bool) bool { value := defValue if s, ok := conf.Get(section, name); ok { switch s { case "true", "yes", "on", "1": value = true case "false", "no", "off", "0": value = false default: panic(fmt.Errorf("[%s]%s: invalid boolean value", section, name)) } } return value } // Returns true if the given IP address is part of the internal networks as // per the configuration of [sr.ht]internal-ipnet. func IsInternalIP(ip net.IP) bool { for _, net := range InternalIPNet { if net.Contains(ip) { return true } } return false }