Files
DdnsClient/HttpServer.go
2021-07-21 15:38:38 +02:00

352 lines
7.4 KiB
Go

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
}