Browse Source

cleaning up some stuff;

aeth 7 tháng trước cách đây
mục cha
commit
74dddf50f7
3 tập tin đã thay đổi với 116 bổ sung56 xóa
  1. 2 1
      cmd/kyoketsu-web/kyoketsu-web.go
  2. 1 1
      pkg/html/templates/ip_table.html
  3. 113 54
      pkg/webserver.go

+ 2 - 1
cmd/kyoketsu-web/kyoketsu-web.go

@@ -32,6 +32,7 @@ import (
 	"flag"
 	"log"
 	"net/http"
+	"os"
 	"sync"
 
 	kyoketsu "git.aetherial.dev/aeth/kyoketsu/pkg"
@@ -57,7 +58,7 @@ func main() {
 	hostsRepo := kyoketsu.NewSQLiteRepo(db)
 	var wg sync.WaitGroup
 	wg.Add(1)
-	go kyoketsu.RunHttpServer(*port, hostsRepo, kyoketsu.RetrieveScanDirectives())
+	go kyoketsu.RunHttpServer(*port, hostsRepo, kyoketsu.RetrieveScanDirectives(), os.Stdout)
 
 	if err = hostsRepo.Migrate(); err != nil {
 		log.Fatal(err)

+ 1 - 1
pkg/html/templates/ip_table.html

@@ -1,7 +1,7 @@
 {{ define "ip_table.html" }}
 <tr>
 <th scope="row"><p style="font-family: monospace;">{{ .Fqdn }}</p></th>
-<td><a href="{{ .FormatUrl }}" style="font-family: monospace;"> {{ .FormatUrl }}</a></td>
+<td><a href="{{ .FormatUrl }}" style="font-family: monospace;" target="_blank"> {{ .FormatUrl }}</a></td>
         <td><p style="font-family: monospace;">{{ .IpAddress }}</p></td>
         <td><p style="font-family: monospace;">{{ .Network }}</p></td>
         <td><p style="font-family: monospace;">{{ .PingResponse }}</p></td>

+ 113 - 54
pkg/webserver.go

@@ -10,6 +10,7 @@ import (
 	"net/http"
 	"path"
 	"strings"
+	"sync"
 )
 
 type ScanRequest struct {
@@ -27,7 +28,7 @@ Run a new webserver
 
 	:param port: port number to run the webserver on
 */
-func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap []int) {
+func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap []int, logStream io.Writer) {
 	assets := &AssetHandler{Root: content, RelPath: "static", EmbedRoot: "html"}
 	tmpl, err := template.ParseFS(content, "html/templates/*.html")
 	if err != nil {
@@ -38,7 +39,7 @@ func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap []int) {
 		log.Fatal(err)
 	}
 	htmlHndl := &HtmlHandler{Home: tmpl, TableEntry: iptable, DbHook: dbhook}
-	execHndl := &ExecutionHandler{DbHook: dbhook, PortMap: portmap, TableEntry: iptable}
+	execHndl := &ExecutionHandler{DbHook: dbhook, PortMap: portmap, TableEntry: iptable, stream: logStream}
 	http.Handle("/static/", assets)
 	http.Handle("/home", htmlHndl)
 	http.Handle("/subnets", htmlHndl)
@@ -53,57 +54,114 @@ type ExecutionHandler struct {
 	DbHook     TopologyDatabaseIO
 	TableEntry *template.Template
 	PortMap    []int
+	stream     io.Writer
 }
 
+func (e *ExecutionHandler) Log(vals ...string) {
+	e.stream.Write([]byte("KYOKETSU-WEB LOG ||| " + strings.Join(vals, " ||| ")))
+
+}
+
+/*
+Top level function to be routed to, this will spawn a suite of goroutines that will perform a concurrent scan on hosts and write back HTML data
+
+	:param w: an http.ResponseWriter that we will write data back to
+	:param r: a pointer to the request coming in from the client
+*/
 func (e *ExecutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	var input ScanRequest
-	b, err := io.ReadAll(r.Body)
-	defer r.Body.Close()
+	input, err := e.parseRequest(r)
 	if err != nil {
-		fmt.Fprintf(w, "There was an error processing your request: %s", err)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
-	err = json.Unmarshal(b, &input)
+
+	subnetMap, err := GetNetworkAddresses(input.IpAddress)
 	if err != nil {
-		fmt.Fprintf(w, "There was an error processing your request: %s", err)
+		http.Error(w, "Failed to get network addresses", http.StatusInternalServerError)
 		return
+	}
+
+	scanned := make(chan Host)
+	var wg sync.WaitGroup
+	var mu sync.Mutex
+	var errorRaised bool
 
+	wg.Add(1)
+	go e.processScannedData(w, e.TableEntry, scanned, &wg, &mu, &errorRaised)
+
+	NetSweep(subnetMap.Ipv4s, subnetMap.Mask, RetrieveScanDirectives(), scanned)
+	close(scanned)
+	wg.Wait()
+
+	if errorRaised {
+		http.Error(w, "Error during scan processing. Check logs for details.", http.StatusInternalServerError)
 	}
-	subnetMap, err := GetNetworkAddresses(input.IpAddress)
+}
+
+/*
+Parse the request sent in from the client
+
+	:param r: pointer to the http.Request coming in from the client
+*/
+func (e *ExecutionHandler) parseRequest(r *http.Request) (ScanRequest, error) {
+	var input ScanRequest
+	b, err := io.ReadAll(r.Body)
+	defer r.Body.Close()
 	if err != nil {
-		fmt.Fprintf(w, "There was an error processing your request: %s", err)
+		return input, fmt.Errorf("error reading request body: %w", err)
 	}
-	scanned := make(chan Host)
-	go func(wr http.ResponseWriter, templ *template.Template) {
-		for x := range scanned {
-			if len(x.ListeningPorts) > 0 {
+	err = json.Unmarshal(b, &input)
+	if err != nil {
+		return input, fmt.Errorf("error unmarshalling request body: %w", err)
+	}
+	return input, nil
+}
 
-				err := templ.Execute(wr, x)
-				if err != nil {
-					fmt.Println(err, x)
+/*
+Process the data that is created from the kyoketsu Web Scanner, and parse the data into an HTML template
 
-				}
+	    :param w: an http.ResponseWriter to write the template back into
+		:param templ: a pointer to the html template that will house the data
+		:param scanned: a channel with 'Host' structs coming through
+		:param wg: a pointer to a waitgroup that will get decremented when the function exits
+		:param mu: a pointer to a mutex that will control when the errorRaised singleton is modified
+		:param errorRaised: a pointer to a boolean that will signify if an error raised whilst processing data
+*/
+func (e *ExecutionHandler) processScannedData(w http.ResponseWriter, templ *template.Template, scanned chan Host, wg *sync.WaitGroup, mu *sync.Mutex, errorRaised *bool) {
+	defer wg.Done()
+	for x := range scanned {
+		if len(x.ListeningPorts) > 0 {
+			if err := templ.Execute(w, x); err != nil {
+				mu.Lock()
+				*errorRaised = true
+				mu.Unlock()
+				e.Log(err.Error())
+			}
 
-				host, err := e.DbHook.GetByIP(x.IpAddress)
-				if err != nil {
-					if err != ErrNotExists {
-						log.Fatal(err, " Couldnt access the database. Fatal error.\n")
-					}
-					_, err = e.DbHook.Create(x)
-					if err != nil {
-						log.Fatal(err, " Fatal error trying to read the database.\n")
-					}
-					continue
+			host, err := e.DbHook.GetByIP(x.IpAddress)
+			if err != nil {
+				if err != ErrNotExists {
+					mu.Lock()
+					*errorRaised = true
+					mu.Unlock()
+					e.Log(err.Error())
 				}
-				_, err = e.DbHook.Update(host.Id, x)
-				if err != nil {
-					log.Fatal(err, " fatal error when updating a record.\n")
+				if _, err := e.DbHook.Create(x); err != nil {
+					mu.Lock()
+					*errorRaised = true
+					mu.Unlock()
+					e.Log(err.Error())
 				}
-
+				continue
+			}
+			if _, err := e.DbHook.Update(host.Id, x); err != nil {
+				mu.Lock()
+				*errorRaised = true
+				mu.Unlock()
+				e.Log(err.Error())
 			}
 		}
-	}(w, e.TableEntry)
-	NetSweep(subnetMap.Ipv4s, subnetMap.Mask, RetrieveScanDirectives(), scanned)
+	}
 }
 
 // handlers //
@@ -114,22 +172,34 @@ type HtmlHandler struct {
 	DbHook     TopologyDatabaseIO
 }
 
+func (h *HtmlHandler) handleHome(w http.ResponseWriter, r *http.Request) {
+	if r.RequestURI == "/home" {
+		data, err := h.DbHook.All()
+		if err != nil {
+			http.Error(w, "There was an error reading from the database: "+err.Error(), http.StatusInternalServerError)
+		}
+		h.Home.Execute(w, data)
+		return
+	}
+
+}
+
 func (h *HtmlHandler) subnetQueryHandler(w http.ResponseWriter, r *http.Request) {
 	var req ScanRequest
 	b, err := io.ReadAll(r.Body)
 	defer r.Body.Close()
 	if err != nil {
-		fmt.Fprintf(w, "There was an error reading the input: %s", err)
+		http.Error(w, "There was an error reading the request: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 	err = json.Unmarshal(b, &req)
 	if err != nil {
-		fmt.Fprintf(w, "There was an error reading the input: %s", err)
+		http.Error(w, "There was an error reading the request: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 	data, err := h.DbHook.GetByNetwork(req.IpAddress)
 	if err != nil {
-		fmt.Fprintf(w, "There was an error reading from the database: %s", err)
+		http.Error(w, "There was an error reading the request: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 	for _, host := range data {
@@ -143,18 +213,18 @@ func (h *HtmlHandler) fqdnQueryHandler(w http.ResponseWriter, r *http.Request) {
 	b, err := io.ReadAll(r.Body)
 	defer r.Body.Close()
 	if err != nil {
-		fmt.Fprintf(w, "There was an error reading the input: %s", err)
+		http.Error(w, "There was an error reading the request: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 	err = json.Unmarshal(b, &req)
 	if err != nil {
-		fmt.Fprintf(w, "There was an error reading the input: %s", err)
+		http.Error(w, "There was an error reading the request: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 	dnsList := strings.Split(req.FqdnPattern, ",")
 	data, err := h.DbHook.FilterDnsPattern(dnsList)
 	if err != nil {
-		fmt.Fprintf(w, "There was an error reading from the database: %s", err)
+		http.Error(w, "There was an error reading the request: "+err.Error(), http.StatusBadRequest)
 		return
 	}
 	for _, host := range data {
@@ -170,20 +240,9 @@ Handler function for HTML serving
 	:param r: pointer to the http.Request coming in
 */
 func (h *HtmlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	if r.RequestURI == "/home" {
-		data, err := h.DbHook.All()
-		if err != nil {
-			fmt.Fprintf(w, "You have made it to the kyoketsu web server!\nThere was an error getting the db table, though.\n%s", err)
-		}
-		h.Home.Execute(w, data)
-		return
-	}
-	splitpath := strings.Split(r.RequestURI, "/")
-	if len(splitpath) <= 1 {
-		w.Header().Add("Location", "/home")
-		return
-	}
 	switch r.RequestURI {
+	case "/home":
+		h.handleHome(w, r)
 	case "/subnets":
 		h.subnetQueryHandler(w, r)
 	case "/excludefqdn":
@@ -222,7 +281,7 @@ func (a *AssetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 	b, err = a.Root.ReadFile(path.Join(assetPath...))
 	if err != nil {
-		fmt.Fprintf(w, "Error occured: %s. path split: '%s'\nAsset Path: %s", err, pathSp, assetPath)
+		http.Error(w, "Error occured when getting Asset: "+err.Error(), http.StatusBadRequest)
 	}
 	switch {
 	case strings.Contains(fname, "css"):