package main import ( "crypto/subtle" "encoding/json" "fmt" "github.com/gorilla/mux" log "github.com/sirupsen/logrus" "net" "net/http" auth "github.com/abbot/go-http-auth" ) type HttpServer struct { _server *http.Server _database *Database _oauth *OAuth2 _renderer *Renderer _dnsclient *DnsUpdate _ipAddresses map[string][]string } type GuiSetting struct { FilterString *string } func getIP(r *http.Request) (string, error) { var host string forwarded := r.Header.Get("X-FORWARDED-FOR") if forwarded != "" { host = forwarded } else { host = r.RemoteAddr } host,_ , err := net.SplitHostPort(host) if err != nil { return "", err } return host, nil } func (h *HttpServer) doaction(w http.ResponseWriter, r *http.Request, params *AdminPageParams){ if action := r.URL.Query().Get("action"); action != "" { switch action { case "update": host := r.URL.Query().Get("host") ip := r.URL.Query().Get("ip") if !h._database.ExistHost(host) { params.Alerts = append(params.Alerts, Alert{Type:"error", Message: fmt.Sprintf("host %s does not exist", host)}) break } err := h._dnsclient.Update(host, ip) if err != nil { params.Alerts = append(params.Alerts, Alert{Type:"error", Message: err.Error()}) } else { params.Alerts = append(params.Alerts, Alert{Type:"success", Message: "successfully updated host"}) } break case "delete": host := r.URL.Query().Get("host") log.Info("deleting host " + host) h._database.DeleteHost(host) h._dnsclient.Remove(host) delete(h._ipAddresses, host) break case "externalresolve": host := r.URL.Query().Get("host") ips, err := h._dnsclient.ExternalResolve(host) if err != nil { params.Alerts = append(params.Alerts, Alert{ Type: "error", Message: fmt.Sprintf("host %s could not be resolved", host), }) } else { params.Alerts = append(params.Alerts, Alert{ Type: "success", Message: fmt.Sprintf("host %s resolved to following addresses:\n %s", host, fmt.Sprint(ips)), }) } case "resolve": host := r.URL.Query().Get("host") ipAddresses, err := h._dnsclient.Resolve(host) if err == nil && len(ipAddresses) > 0 { params.Alerts = append(params.Alerts, Alert{ Type: "success", Message: fmt.Sprintf("host %s resolved to following addresses:\n %s", host, fmt.Sprint(ipAddresses)), }) h._ipAddresses[host] = ipAddresses } else { params.Alerts = append(params.Alerts, Alert{ Type: "error", Message: fmt.Sprintf("host %s could not be resolved", host), }) delete(h._ipAddresses, host) } case "add": host := r.URL.Query().Get("host") pw, _, err := h._database.CreateHost(host) if err == nil { params.Alerts = append(params.Alerts, Alert{ Type: "success", Message: fmt.Sprintf("created host %s\npassword: %s", host, pw), }) } else { params.Alerts = append(params.Alerts, Alert{ Type: "error", Message: fmt.Sprintf("could not create host %s.\n reason: %s", host, err.Error()), }) } break } } else { } } func (h *HttpServer) doguisetting(w http.ResponseWriter, r *http.Request) *GuiSetting{ settings := new(GuiSetting) settings.FilterString = nil if setting := r.URL.Query().Get("setting"); setting != "" { switch setting { case "filter": host := r.URL.Query().Get("host") settings.FilterString = &host break } } return settings } func (h *HttpServer) adminPage(w http.ResponseWriter, r *http.Request) { token, ok := h._oauth.checkOAuth(w, r, true) if !ok { return } params := new(AdminPageParams) params.Alerts = make([]Alert, 0) params.Hosts = make(map[string]string) params.IpAddresses = h._ipAddresses params.LogoutUrl = h._oauth.LogoutUrl h.doaction(w,r, params) settings := h.doguisetting(w,r) claims, _ := h._oauth.GetClaims(w, token) params.Claims = *claims if settings.FilterString != nil { params.Hosts = h._database.GetExistingHosts(*settings.FilterString) params.Alerts = append( params.Alerts, Alert{ Type: "info", Message: fmt.Sprintf("returned %d hosts", len(params.Hosts)), }) } else { params.Hosts = h._database.GetExistingHosts("") } h._renderer.RenderAdminPage(w, params) } func (h *HttpServer) registerHandler(w http.ResponseWriter, r *http.Request) { if _, ok := h._oauth.checkOAuth(w, r, false); ok { return } host := r.URL.Query().Get("host") clientip, err := getIP(r) if err != nil { log.Fatal(err) } if h._database.IsBannedHost(clientip) { h._database.IncrementBanHost(clientip) log.Error("host ", clientip, " is banned!") w.WriteHeader(http.StatusForbidden) return } if h._database.ExistHost(host) { h._database.IncrementBanHost(clientip) w.WriteHeader(http.StatusNotAcceptable) return } token, err := h._database.CreateHost(host) if err != nil { w.WriteHeader(http.StatusInternalServerError) return } ret := map[string]string { "host": host, "token": token, } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(ret) } func (h *HttpServer) updateHandler(w http.ResponseWriter, r *http.Request) { hosts := r.URL.Query()["host"] tokens := r.URL.Query()["token"] myips := r.URL.Query()["myip"] var myip string clientip, err := getIP(r) if h._database.IsBannedHost(clientip) { h._database.IncrementBanHost(clientip) log.Error("host ", clientip, " is banned!") w.WriteHeader(http.StatusForbidden) return } if err != nil { log.Fatal(err) } if len(hosts) == 0 { h._database.IncrementBanHost(clientip) w.WriteHeader(http.StatusNotAcceptable) return } if len(tokens) == 0 { h._database.IncrementBanHost(clientip) w.WriteHeader(http.StatusNotAcceptable) return } if len(myips) == 0 { myip = clientip if err != nil { w.WriteHeader(http.StatusInternalServerError) return } } else { myip = myips[0] } host := hosts[0] token := tokens[0] if !h._database.Authorize(host, token) { h._database.IncrementBanHost(clientip) w.WriteHeader(http.StatusForbidden) return } log.Info("authorization successful.") log.Info("will update host ", host, " ip ", myip) if !h._database.ExistHost(host) { w.WriteHeader(http.StatusInternalServerError) return } else { err := h._dnsclient.Update(host, myip) if err != nil { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) } } } func (h *HttpServer) Listen() { h._server.ListenAndServe() } func Secret(user, realm string) string { if user == "john" { // password is "hello" return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1" } return "" } func CreateHttpServer(config *Config) *HttpServer { var httpserver *HttpServer var err error httpserver = new(HttpServer) r := mux.NewRouter() httpserver._ipAddresses = make(map[string][]string) httpserver._renderer, err = CreateRenderer(config) if err != nil { log.Fatal(err) } httpserver._database, err = CreateDatabase(config) if err != nil { log.Fatal(err) } httpserver._oauth, err = CreateOAuth2(config) if err != nil { log.Fatal(err) } httpserver._dnsclient = NewTestInstance() httpserver._server = &http.Server{ Addr: config.Addr, Handler: r, } auth := auth.NewBasicAuthenticator(config.AuthRealm, func(user string) { return httpserver._database._db.Get("hosts/"+ user) }) r.HandleFunc("/register", httpserver.registerHandler) r.HandleFunc("/update", httpserver.updateHandler) r.HandleFunc("/admin", httpserver.adminPage) return httpserver }