Initial implementation of welcome/firstrun/installer page in go backend
This commit is contained in:
parent
c494e17df5
commit
302c3a767a
97
app.go
97
app.go
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -17,7 +16,6 @@ import (
|
||||||
"github.com/gobuffalo/packr"
|
"github.com/gobuffalo/packr"
|
||||||
|
|
||||||
"github.com/hmage/golibs/log"
|
"github.com/hmage/golibs/log"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// VersionString will be set through ldflags, contains current version
|
// VersionString will be set through ldflags, contains current version
|
||||||
|
@ -72,13 +70,10 @@ func run(args options) {
|
||||||
log.Printf("AdGuard Home is running as a service")
|
log.Printf("AdGuard Home is running as a service")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := askUsernamePasswordIfPossible()
|
config.firstRun = detectFirstRun()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the upgrade if necessary
|
// Do the upgrade if necessary
|
||||||
err = upgradeConfig()
|
err := upgradeConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -145,7 +140,9 @@ func run(args options) {
|
||||||
|
|
||||||
// Initialize and run the admin Web interface
|
// Initialize and run the admin Web interface
|
||||||
box := packr.NewBox("build/static")
|
box := packr.NewBox("build/static")
|
||||||
http.Handle("/", optionalAuthHandler(http.FileServer(box)))
|
// if not configured, redirect / to /install.html, otherwise redirect /install.html to /
|
||||||
|
http.Handle("/", postInstallHandler(optionalAuthHandler(http.FileServer(box))))
|
||||||
|
http.Handle("/install.html", preInstallHandler(http.FileServer(box)))
|
||||||
registerControlHandlers()
|
registerControlHandlers()
|
||||||
|
|
||||||
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
||||||
|
@ -222,14 +219,6 @@ func cleanup() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInput() (string, error) {
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
scanner.Scan()
|
|
||||||
text := scanner.Text()
|
|
||||||
err := scanner.Err()
|
|
||||||
return text, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// command-line arguments
|
// command-line arguments
|
||||||
type options struct {
|
type options struct {
|
||||||
verbose bool // is verbose logging enabled
|
verbose bool // is verbose logging enabled
|
||||||
|
@ -318,79 +307,3 @@ func loadOptions() options {
|
||||||
|
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
func promptAndGet(prompt string) (string, error) {
|
|
||||||
for {
|
|
||||||
fmt.Print(prompt)
|
|
||||||
input, err := getInput()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to get input, aborting: %s", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if len(input) != 0 {
|
|
||||||
return input, nil
|
|
||||||
}
|
|
||||||
// try again
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func promptAndGetPassword(prompt string) (string, error) {
|
|
||||||
for {
|
|
||||||
fmt.Print(prompt)
|
|
||||||
password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
|
||||||
fmt.Print("\n")
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to get input, aborting: %s", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if len(password) != 0 {
|
|
||||||
return string(password), nil
|
|
||||||
}
|
|
||||||
// try again
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func askUsernamePasswordIfPossible() error {
|
|
||||||
configFile := config.getConfigFilename()
|
|
||||||
_, err := os.Stat(configFile)
|
|
||||||
if !os.IsNotExist(err) {
|
|
||||||
// do nothing, file exists
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !terminal.IsTerminal(int(os.Stdin.Fd())) {
|
|
||||||
return nil // do nothing
|
|
||||||
}
|
|
||||||
if !terminal.IsTerminal(int(os.Stdout.Fd())) {
|
|
||||||
return nil // do nothing
|
|
||||||
}
|
|
||||||
fmt.Printf("Would you like to set user/password for the web interface authentication (yes/no)?\n")
|
|
||||||
yesno, err := promptAndGet("Please type 'yes' or 'no': ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if yesno[0] != 'y' && yesno[0] != 'Y' {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
username, err := promptAndGet("Please enter the username: ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
password, err := promptAndGetPassword("Please enter the password: ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
password2, err := promptAndGetPassword("Please enter password again: ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if password2 != password {
|
|
||||||
fmt.Printf("Passwords do not match! Aborting\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.AuthName = username
|
|
||||||
config.AuthPass = password
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ type logSettings struct {
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
ourConfigFilename string // Config filename (can be overridden via the command line arguments)
|
ourConfigFilename string // Config filename (can be overridden via the command line arguments)
|
||||||
ourBinaryDir string // Location of our directory, used to protect against CWD being somewhere else
|
ourBinaryDir string // Location of our directory, used to protect against CWD being somewhere else
|
||||||
|
firstRun bool // if set to true, don't run any services except HTTP web inteface, and serve only first-run html
|
||||||
|
|
||||||
BindHost string `yaml:"bind_host"`
|
BindHost string `yaml:"bind_host"`
|
||||||
BindPort int `yaml:"bind_port"`
|
BindPort int `yaml:"bind_port"`
|
||||||
|
@ -152,6 +153,10 @@ func readConfigFile() ([]byte, error) {
|
||||||
func (c *configuration) write() error {
|
func (c *configuration) write() error {
|
||||||
c.Lock()
|
c.Lock()
|
||||||
defer c.Unlock()
|
defer c.Unlock()
|
||||||
|
if config.firstRun {
|
||||||
|
log.Tracef("Silently refusing to write config because first run and not configured yet")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
configFile := config.getConfigFilename()
|
configFile := config.getConfigFilename()
|
||||||
log.Printf("Writing YAML file: %s", configFile)
|
log.Printf("Writing YAML file: %s", configFile)
|
||||||
yamlText, err := yaml.Marshal(&config)
|
yamlText, err := yaml.Marshal(&config)
|
||||||
|
|
130
control.go
130
control.go
|
@ -694,24 +694,43 @@ func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGetDefaultAddresses(w http.ResponseWriter, r *http.Request) {
|
type ipport struct {
|
||||||
type ipport struct {
|
|
||||||
IP string `json:"ip"`
|
IP string `json:"ip"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
}
|
}
|
||||||
data := struct {
|
|
||||||
|
type firstRunData struct {
|
||||||
Web ipport `json:"web"`
|
Web ipport `json:"web"`
|
||||||
DNS ipport `json:"dns"`
|
DNS ipport `json:"dns"`
|
||||||
}{}
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: replace mockup with actual data
|
func handleGetDefaultAddresses(w http.ResponseWriter, r *http.Request) {
|
||||||
data.Web.IP = "192.168.104.104"
|
data := firstRunData{}
|
||||||
data.Web.Port = 3000
|
ifaces, err := getValidNetInterfaces()
|
||||||
data.DNS.IP = "192.168.104.104"
|
if err != nil {
|
||||||
data.DNS.Port = 53
|
httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(ifaces) == 0 {
|
||||||
|
httpError(w, http.StatusServiceUnavailable, "Couldn't find any legible interface, plase try again later")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find an interface with an ipv4 address
|
||||||
|
addr := findIPv4IfaceAddr(ifaces)
|
||||||
|
if len(addr) == 0 {
|
||||||
|
httpError(w, http.StatusServiceUnavailable, "Couldn't find any interface with IPv4, plase try again later")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data.Web.IP = addr
|
||||||
|
data.DNS.IP = addr
|
||||||
|
data.Web.Port = 3000 // TODO: find out if port 80 is available -- if not, fall back to 3000
|
||||||
|
data.DNS.Port = 53 // TODO: find out if port 53 is available -- if not, show a big warning
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
err := json.NewEncoder(w).Encode(data)
|
err = json.NewEncoder(w).Encode(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err)
|
httpError(w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err)
|
||||||
return
|
return
|
||||||
|
@ -719,7 +738,7 @@ func handleGetDefaultAddresses(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSetAllSettings(w http.ResponseWriter, r *http.Request) {
|
func handleSetAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||||
newSettings := map[string]interface{}{}
|
newSettings := firstRunData{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&newSettings)
|
err := json.NewDecoder(r.Body).Decode(&newSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err)
|
httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err)
|
||||||
|
@ -727,48 +746,57 @@ func handleSetAllSettings(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
spew.Dump(newSettings)
|
spew.Dump(newSettings)
|
||||||
|
config.firstRun = false
|
||||||
|
config.BindHost = newSettings.Web.IP
|
||||||
|
config.BindPort = newSettings.Web.Port
|
||||||
|
config.DNS.BindHost = newSettings.DNS.IP
|
||||||
|
config.DNS.Port = newSettings.DNS.Port
|
||||||
|
config.AuthName = newSettings.Username
|
||||||
|
config.AuthPass = newSettings.Password
|
||||||
|
|
||||||
|
httpUpdateConfigReloadDNSReturnOK(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerControlHandlers() {
|
func registerControlHandlers() {
|
||||||
http.HandleFunc("/control/status", optionalAuth(ensureGET(handleStatus)))
|
http.HandleFunc("/control/status", postInstall(optionalAuth(ensureGET(handleStatus))))
|
||||||
http.HandleFunc("/control/enable_protection", optionalAuth(ensurePOST(handleProtectionEnable)))
|
http.HandleFunc("/control/enable_protection", postInstall(optionalAuth(ensurePOST(handleProtectionEnable))))
|
||||||
http.HandleFunc("/control/disable_protection", optionalAuth(ensurePOST(handleProtectionDisable)))
|
http.HandleFunc("/control/disable_protection", postInstall(optionalAuth(ensurePOST(handleProtectionDisable))))
|
||||||
http.HandleFunc("/control/querylog", optionalAuth(ensureGET(dnsforward.HandleQueryLog)))
|
http.HandleFunc("/control/querylog", postInstall(optionalAuth(ensureGET(dnsforward.HandleQueryLog))))
|
||||||
http.HandleFunc("/control/querylog_enable", optionalAuth(ensurePOST(handleQueryLogEnable)))
|
http.HandleFunc("/control/querylog_enable", postInstall(optionalAuth(ensurePOST(handleQueryLogEnable))))
|
||||||
http.HandleFunc("/control/querylog_disable", optionalAuth(ensurePOST(handleQueryLogDisable)))
|
http.HandleFunc("/control/querylog_disable", postInstall(optionalAuth(ensurePOST(handleQueryLogDisable))))
|
||||||
http.HandleFunc("/control/set_upstream_dns", optionalAuth(ensurePOST(handleSetUpstreamDNS)))
|
http.HandleFunc("/control/set_upstream_dns", postInstall(optionalAuth(ensurePOST(handleSetUpstreamDNS))))
|
||||||
http.HandleFunc("/control/test_upstream_dns", optionalAuth(ensurePOST(handleTestUpstreamDNS)))
|
http.HandleFunc("/control/test_upstream_dns", postInstall(optionalAuth(ensurePOST(handleTestUpstreamDNS))))
|
||||||
http.HandleFunc("/control/i18n/change_language", optionalAuth(ensurePOST(handleI18nChangeLanguage)))
|
http.HandleFunc("/control/i18n/change_language", postInstall(optionalAuth(ensurePOST(handleI18nChangeLanguage))))
|
||||||
http.HandleFunc("/control/i18n/current_language", optionalAuth(ensureGET(handleI18nCurrentLanguage)))
|
http.HandleFunc("/control/i18n/current_language", postInstall(optionalAuth(ensureGET(handleI18nCurrentLanguage))))
|
||||||
http.HandleFunc("/control/stats_top", optionalAuth(ensureGET(dnsforward.HandleStatsTop)))
|
http.HandleFunc("/control/stats_top", postInstall(optionalAuth(ensureGET(dnsforward.HandleStatsTop))))
|
||||||
http.HandleFunc("/control/stats", optionalAuth(ensureGET(dnsforward.HandleStats)))
|
http.HandleFunc("/control/stats", postInstall(optionalAuth(ensureGET(dnsforward.HandleStats))))
|
||||||
http.HandleFunc("/control/stats_history", optionalAuth(ensureGET(dnsforward.HandleStatsHistory)))
|
http.HandleFunc("/control/stats_history", postInstall(optionalAuth(ensureGET(dnsforward.HandleStatsHistory))))
|
||||||
http.HandleFunc("/control/stats_reset", optionalAuth(ensurePOST(dnsforward.HandleStatsReset)))
|
http.HandleFunc("/control/stats_reset", postInstall(optionalAuth(ensurePOST(dnsforward.HandleStatsReset))))
|
||||||
http.HandleFunc("/control/version.json", optionalAuth(handleGetVersionJSON))
|
http.HandleFunc("/control/version.json", postInstall(optionalAuth(handleGetVersionJSON)))
|
||||||
http.HandleFunc("/control/filtering/enable", optionalAuth(ensurePOST(handleFilteringEnable)))
|
http.HandleFunc("/control/filtering/enable", postInstall(optionalAuth(ensurePOST(handleFilteringEnable))))
|
||||||
http.HandleFunc("/control/filtering/disable", optionalAuth(ensurePOST(handleFilteringDisable)))
|
http.HandleFunc("/control/filtering/disable", postInstall(optionalAuth(ensurePOST(handleFilteringDisable))))
|
||||||
http.HandleFunc("/control/filtering/add_url", optionalAuth(ensurePUT(handleFilteringAddURL)))
|
http.HandleFunc("/control/filtering/add_url", postInstall(optionalAuth(ensurePUT(handleFilteringAddURL))))
|
||||||
http.HandleFunc("/control/filtering/remove_url", optionalAuth(ensureDELETE(handleFilteringRemoveURL)))
|
http.HandleFunc("/control/filtering/remove_url", postInstall(optionalAuth(ensureDELETE(handleFilteringRemoveURL))))
|
||||||
http.HandleFunc("/control/filtering/enable_url", optionalAuth(ensurePOST(handleFilteringEnableURL)))
|
http.HandleFunc("/control/filtering/enable_url", postInstall(optionalAuth(ensurePOST(handleFilteringEnableURL))))
|
||||||
http.HandleFunc("/control/filtering/disable_url", optionalAuth(ensurePOST(handleFilteringDisableURL)))
|
http.HandleFunc("/control/filtering/disable_url", postInstall(optionalAuth(ensurePOST(handleFilteringDisableURL))))
|
||||||
http.HandleFunc("/control/filtering/refresh", optionalAuth(ensurePOST(handleFilteringRefresh)))
|
http.HandleFunc("/control/filtering/refresh", postInstall(optionalAuth(ensurePOST(handleFilteringRefresh))))
|
||||||
http.HandleFunc("/control/filtering/status", optionalAuth(ensureGET(handleFilteringStatus)))
|
http.HandleFunc("/control/filtering/status", postInstall(optionalAuth(ensureGET(handleFilteringStatus))))
|
||||||
http.HandleFunc("/control/filtering/set_rules", optionalAuth(ensurePUT(handleFilteringSetRules)))
|
http.HandleFunc("/control/filtering/set_rules", postInstall(optionalAuth(ensurePUT(handleFilteringSetRules))))
|
||||||
http.HandleFunc("/control/safebrowsing/enable", optionalAuth(ensurePOST(handleSafeBrowsingEnable)))
|
http.HandleFunc("/control/safebrowsing/enable", postInstall(optionalAuth(ensurePOST(handleSafeBrowsingEnable))))
|
||||||
http.HandleFunc("/control/safebrowsing/disable", optionalAuth(ensurePOST(handleSafeBrowsingDisable)))
|
http.HandleFunc("/control/safebrowsing/disable", postInstall(optionalAuth(ensurePOST(handleSafeBrowsingDisable))))
|
||||||
http.HandleFunc("/control/safebrowsing/status", optionalAuth(ensureGET(handleSafeBrowsingStatus)))
|
http.HandleFunc("/control/safebrowsing/status", postInstall(optionalAuth(ensureGET(handleSafeBrowsingStatus))))
|
||||||
http.HandleFunc("/control/parental/enable", optionalAuth(ensurePOST(handleParentalEnable)))
|
http.HandleFunc("/control/parental/enable", postInstall(optionalAuth(ensurePOST(handleParentalEnable))))
|
||||||
http.HandleFunc("/control/parental/disable", optionalAuth(ensurePOST(handleParentalDisable)))
|
http.HandleFunc("/control/parental/disable", postInstall(optionalAuth(ensurePOST(handleParentalDisable))))
|
||||||
http.HandleFunc("/control/parental/status", optionalAuth(ensureGET(handleParentalStatus)))
|
http.HandleFunc("/control/parental/status", postInstall(optionalAuth(ensureGET(handleParentalStatus))))
|
||||||
http.HandleFunc("/control/safesearch/enable", optionalAuth(ensurePOST(handleSafeSearchEnable)))
|
http.HandleFunc("/control/safesearch/enable", postInstall(optionalAuth(ensurePOST(handleSafeSearchEnable))))
|
||||||
http.HandleFunc("/control/safesearch/disable", optionalAuth(ensurePOST(handleSafeSearchDisable)))
|
http.HandleFunc("/control/safesearch/disable", postInstall(optionalAuth(ensurePOST(handleSafeSearchDisable))))
|
||||||
http.HandleFunc("/control/safesearch/status", optionalAuth(ensureGET(handleSafeSearchStatus)))
|
http.HandleFunc("/control/safesearch/status", postInstall(optionalAuth(ensureGET(handleSafeSearchStatus))))
|
||||||
http.HandleFunc("/control/dhcp/status", optionalAuth(ensureGET(handleDHCPStatus)))
|
http.HandleFunc("/control/dhcp/status", postInstall(optionalAuth(ensureGET(handleDHCPStatus))))
|
||||||
http.HandleFunc("/control/dhcp/interfaces", optionalAuth(ensureGET(handleDHCPInterfaces)))
|
http.HandleFunc("/control/dhcp/interfaces", postInstall(optionalAuth(ensureGET(handleDHCPInterfaces))))
|
||||||
http.HandleFunc("/control/dhcp/set_config", optionalAuth(ensurePOST(handleDHCPSetConfig)))
|
http.HandleFunc("/control/dhcp/set_config", postInstall(optionalAuth(ensurePOST(handleDHCPSetConfig))))
|
||||||
http.HandleFunc("/control/dhcp/find_active_dhcp", optionalAuth(ensurePOST(handleDHCPFindActiveServer)))
|
http.HandleFunc("/control/dhcp/find_active_dhcp", postInstall(optionalAuth(ensurePOST(handleDHCPFindActiveServer))))
|
||||||
|
|
||||||
// TODO: move to registerInstallHandlers()
|
// TODO: move to registerInstallHandlers()
|
||||||
http.HandleFunc("/control/install/get_default_addresses", ensureGET(handleGetDefaultAddresses))
|
http.HandleFunc("/control/install/get_default_addresses", preInstall(ensureGET(handleGetDefaultAddresses)))
|
||||||
http.HandleFunc("/control/install/set_all_settings", ensurePOST(handleSetAllSettings))
|
http.HandleFunc("/control/install/set_all_settings", preInstall(ensurePOST(handleSetAllSettings)))
|
||||||
}
|
}
|
||||||
|
|
43
dhcp.go
43
dhcp.go
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -70,50 +69,14 @@ func handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||||
response := map[string]interface{}{}
|
response := map[string]interface{}{}
|
||||||
|
|
||||||
ifaces, err := net.Interfaces()
|
ifaces, err := getValidNetInterfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusInternalServerError, "Couldn't get list of interfaces: %s", err)
|
httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type responseInterface struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
MTU int `json:"mtu"`
|
|
||||||
HardwareAddr string `json:"hardware_address"`
|
|
||||||
Addresses []string `json:"ip_addresses"`
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range ifaces {
|
for i := range ifaces {
|
||||||
if ifaces[i].Flags&net.FlagLoopback != 0 {
|
response[ifaces[i].Name] = ifaces[i]
|
||||||
// it's a loopback, skip it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ifaces[i].Flags&net.FlagBroadcast == 0 {
|
|
||||||
// this interface doesn't support broadcast, skip it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ifaces[i].Flags&net.FlagPointToPoint != 0 {
|
|
||||||
// this interface is ppp, don't do dhcp over it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iface := responseInterface{
|
|
||||||
Name: ifaces[i].Name,
|
|
||||||
MTU: ifaces[i].MTU,
|
|
||||||
HardwareAddr: ifaces[i].HardwareAddr.String(),
|
|
||||||
}
|
|
||||||
addrs, errAddrs := ifaces[i].Addrs()
|
|
||||||
if errAddrs != nil {
|
|
||||||
httpError(w, http.StatusInternalServerError, "Failed to get addresses for interface %v: %s", ifaces[i].Name, errAddrs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, addr := range addrs {
|
|
||||||
iface.Addresses = append(iface.Addresses, addr.String())
|
|
||||||
}
|
|
||||||
if len(iface.Addresses) == 0 {
|
|
||||||
// this interface has no addresses, skip it
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
response[ifaces[i].Name] = iface
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(response)
|
err = json.NewEncoder(w).Encode(response)
|
||||||
|
|
157
helpers.go
157
helpers.go
|
@ -3,14 +3,18 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hmage/golibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
@ -84,24 +88,78 @@ type authHandler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (a *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if config.AuthName == "" || config.AuthPass == "" {
|
optionalAuth(a.handler.ServeHTTP)(w, r)
|
||||||
a.handler.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
user, pass, ok := r.BasicAuth()
|
|
||||||
if !ok || user != config.AuthName || pass != config.AuthPass {
|
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="dnsfilter"`)
|
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
|
||||||
w.Write([]byte("Unauthorised.\n"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.handler.ServeHTTP(w, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func optionalAuthHandler(handler http.Handler) http.Handler {
|
func optionalAuthHandler(handler http.Handler) http.Handler {
|
||||||
return &authHandler{handler}
|
return &authHandler{handler}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// first run / install
|
||||||
|
// -------------------
|
||||||
|
func detectFirstRun() bool {
|
||||||
|
configfile := config.ourConfigFilename
|
||||||
|
if !filepath.IsAbs(configfile) {
|
||||||
|
configfile = filepath.Join(config.ourBinaryDir, config.ourConfigFilename)
|
||||||
|
}
|
||||||
|
_, err := os.Stat(configfile)
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
// do nothing, file exists
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// preInstall lets the handler run only if firstRun is true, no redirects
|
||||||
|
func preInstall(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !config.firstRun {
|
||||||
|
// if it's not first run, don't let users access it (for example /install.html when configuration is done)
|
||||||
|
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// preInstallStruct wraps preInstall into a struct that can be returned as an interface where neccessary
|
||||||
|
type preInstallHandlerStruct struct {
|
||||||
|
handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *preInstallHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
preInstall(p.handler.ServeHTTP)(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// preInstallHandler returns http.Handler interface for preInstall wrapper
|
||||||
|
func preInstallHandler(handler http.Handler) http.Handler {
|
||||||
|
return &preInstallHandlerStruct{handler}
|
||||||
|
}
|
||||||
|
|
||||||
|
// postInstall lets the handler run only if firstRun is false, and redirects to /install.html otherwise
|
||||||
|
func postInstall(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if config.firstRun && !strings.HasPrefix(r.URL.Path, "/install.") {
|
||||||
|
http.Redirect(w, r, "/install.html", http.StatusSeeOther) // should not be cacheable
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type postInstallHandlerStruct struct {
|
||||||
|
handler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *postInstallHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
postInstall(p.handler.ServeHTTP)(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func postInstallHandler(handler http.Handler) http.Handler {
|
||||||
|
return &postInstallHandlerStruct{handler}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
// helper functions for parsing parameters from body
|
// helper functions for parsing parameters from body
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
@ -125,6 +183,81 @@ func parseParametersFromBody(r io.Reader) (map[string]string, error) {
|
||||||
return parameters, nil
|
return parameters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
// network interfaces
|
||||||
|
// ------------------
|
||||||
|
type netInterface struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MTU int `json:"mtu"`
|
||||||
|
HardwareAddr string `json:"hardware_address"`
|
||||||
|
Addresses []string `json:"ip_addresses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// getValidNetInterfaces() returns interfaces that are eligible for DNS and/or DHCP
|
||||||
|
// invalid interface is either a loopback, ppp interface, or the one that doesn't allow broadcasts
|
||||||
|
func getValidNetInterfaces() ([]netInterface, error) {
|
||||||
|
ifaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Couldn't get list of interfaces: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
netIfaces := []netInterface{}
|
||||||
|
|
||||||
|
for i := range ifaces {
|
||||||
|
if ifaces[i].Flags&net.FlagLoopback != 0 {
|
||||||
|
// it's a loopback, skip it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ifaces[i].Flags&net.FlagBroadcast == 0 {
|
||||||
|
// this interface doesn't support broadcast, skip it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ifaces[i].Flags&net.FlagPointToPoint != 0 {
|
||||||
|
// this interface is ppp, don't do dhcp over it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
iface := netInterface{
|
||||||
|
Name: ifaces[i].Name,
|
||||||
|
MTU: ifaces[i].MTU,
|
||||||
|
HardwareAddr: ifaces[i].HardwareAddr.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := ifaces[i].Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to get addresses for interface %v: %s", ifaces[i].Name, err)
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
iface.Addresses = append(iface.Addresses, addr.String())
|
||||||
|
}
|
||||||
|
if len(iface.Addresses) == 0 {
|
||||||
|
// this interface has no addresses, skip it
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
netIfaces = append(netIfaces, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
return netIfaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findIPv4IfaceAddr(ifaces []netInterface) string {
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
for _, addr := range iface.Addresses {
|
||||||
|
ip, _, err := net.ParseCIDR(addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("SHOULD NOT HAPPEN: got iface.Addresses element that's not a parseable CIDR: %s", addr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ip.To4() == nil {
|
||||||
|
log.Tracef("Ignoring IP that isn't IPv4: %s", ip)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return ip.To4().String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// debug logging helpers
|
// debug logging helpers
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
Loading…
Reference in New Issue