webserver.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package kyoketsu
  2. import (
  3. "embed"
  4. "encoding/json"
  5. "fmt"
  6. "html/template"
  7. "io"
  8. "log"
  9. "net/http"
  10. "path"
  11. "strings"
  12. )
  13. type ScanRequest struct {
  14. IpAddress string `json:"ip_address"`
  15. }
  16. // Holding all static web server resources
  17. //
  18. //go:embed html/bootstrap-5.0.2-dist/js/* html/bootstrap-5.0.2-dist/css/* html/* html/templates/*
  19. var content embed.FS
  20. /*
  21. Run a new webserver
  22. :param port: port number to run the webserver on
  23. */
  24. func RunHttpServer(port int, dbhook TopologyDatabaseIO, portmap []int) {
  25. assets := &AssetHandler{Root: content, RelPath: "static", EmbedRoot: "html"}
  26. tmpl, err := template.ParseFS(content, "html/templates/*.html")
  27. if err != nil {
  28. log.Fatal(err)
  29. }
  30. iptable, err := template.ParseFS(content, "html/templates/ip_table.html")
  31. if err != nil {
  32. log.Fatal(err)
  33. }
  34. htmlHndl := &HtmlHandler{Home: tmpl, TableEntry: iptable, DbHook: dbhook}
  35. execHndl := &ExecutionHandler{DbHook: dbhook, PortMap: portmap, TableEntry: iptable}
  36. http.Handle("/static/", assets)
  37. http.Handle("/home", htmlHndl)
  38. http.Handle("/subnets", htmlHndl)
  39. http.Handle("/refresh", execHndl)
  40. log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
  41. }
  42. type ExecutionHandler struct {
  43. DbHook TopologyDatabaseIO
  44. TableEntry *template.Template
  45. PortMap []int
  46. }
  47. func (e *ExecutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  48. var input ScanRequest
  49. b, err := io.ReadAll(r.Body)
  50. defer r.Body.Close()
  51. if err != nil {
  52. fmt.Fprintf(w, "There was an error processing your request: %s", err)
  53. return
  54. }
  55. err = json.Unmarshal(b, &input)
  56. if err != nil {
  57. fmt.Fprintf(w, "There was an error processing your request: %s", err)
  58. return
  59. }
  60. subnetMap, err := GetNetworkAddresses(input.IpAddress)
  61. if err != nil {
  62. fmt.Fprintf(w, "There was an error processing your request: %s", err)
  63. }
  64. scanned := make(chan Host)
  65. go func(wr http.ResponseWriter, templ *template.Template) {
  66. for x := range scanned {
  67. if len(x.ListeningPorts) > 0 {
  68. templ.Execute(wr, x)
  69. host, err := e.DbHook.GetByIP(x.IpAddress)
  70. if err != nil {
  71. if err != ErrNotExists {
  72. log.Fatal(err, " Couldnt access the database. Fatal error.\n")
  73. }
  74. _, err = e.DbHook.Create(x)
  75. if err != nil {
  76. log.Fatal(err, " Fatal error trying to read the database.\n")
  77. }
  78. continue
  79. }
  80. _, err = e.DbHook.Update(host.Id, x)
  81. if err != nil {
  82. log.Fatal(err, " fatal error when updating a record.\n")
  83. }
  84. }
  85. }
  86. }(w, e.TableEntry)
  87. NetSweep(subnetMap.Ipv4s, subnetMap.Mask, RetrieveScanDirectives(), scanned)
  88. }
  89. // handlers //
  90. type HtmlHandler struct {
  91. Home *template.Template // pointer to the HTML homepage
  92. TableEntry *template.Template // pointer to the table entry html template
  93. DbHook TopologyDatabaseIO
  94. }
  95. /*
  96. Handler function for HTML serving
  97. :param w: http.ResponseWriter interface for sending data back
  98. :param r: pointer to the http.Request coming in
  99. */
  100. func (h *HtmlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  101. if r.RequestURI == "/home" {
  102. data, err := h.DbHook.All()
  103. if err != nil {
  104. fmt.Fprintf(w, "You have made it to the kyoketsu web server!\nThere was an error getting the db table, though.\n%s", err)
  105. }
  106. h.Home.Execute(w, data)
  107. return
  108. }
  109. splitpath := strings.Split(r.RequestURI, "/")
  110. if len(splitpath) <= 1 {
  111. w.Header().Add("Location", "/home")
  112. return
  113. }
  114. if r.RequestURI == "/subnets" {
  115. fmt.Println("request recv")
  116. var req ScanRequest
  117. b, err := io.ReadAll(r.Body)
  118. defer r.Body.Close()
  119. if err != nil {
  120. fmt.Fprintf(w, "There was an error reading the input: %s", err)
  121. return
  122. }
  123. err = json.Unmarshal(b, &req)
  124. if err != nil {
  125. fmt.Fprintf(w, "There was an error reading the input: %s", err)
  126. return
  127. }
  128. data, err := h.DbHook.GetByNetwork(req.IpAddress)
  129. if err != nil {
  130. fmt.Fprintf(w, "There was an error reading from the database: %s", err)
  131. return
  132. }
  133. for _, host := range data {
  134. h.TableEntry.Execute(w, host)
  135. }
  136. }
  137. }
  138. type AssetHandler struct {
  139. Root embed.FS // Should be able to use anything that implements the fs.FS interface for serving asset files
  140. EmbedRoot string // This is the root of the embeded file system
  141. RelPath string // The path that will be used for the handler, relative to the root of the webserver (/static, /assets, etc)
  142. }
  143. /*
  144. Handler function to serve out asset files (HTMX, bootstrap, pngs etc)
  145. :param w: http.ResponseWriter interface for sending data back to the caller
  146. :param r: pointer to an http.Request
  147. */
  148. func (a *AssetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  149. var uripath string // the path from the request
  150. var pathSp []string // the path from the request split, so that we can point the request path to the embedded fs
  151. var assetPath []string // the cleaned path for the requested asset
  152. var fname string // filename of the requested asset
  153. var ctype string
  154. var b []byte
  155. var err error
  156. uripath = strings.TrimPrefix(r.URL.Path, a.RelPath)
  157. uripath = strings.Trim(uripath, "/")
  158. pathSp = strings.Split(uripath, "/")
  159. fname = pathSp[len(pathSp)-1]
  160. assetPath = append(assetPath, a.EmbedRoot)
  161. for i := 1; i < len(pathSp); i++ {
  162. assetPath = append(assetPath, pathSp[i])
  163. }
  164. b, err = a.Root.ReadFile(path.Join(assetPath...))
  165. if err != nil {
  166. fmt.Fprintf(w, "Error occured: %s. path split: '%s'\nAsset Path: %s", err, pathSp, assetPath)
  167. }
  168. switch {
  169. case strings.Contains(fname, "css"):
  170. ctype = "text/css"
  171. case strings.Contains(fname, "js"):
  172. ctype = "text/javascript"
  173. case strings.Contains(fname, "html"):
  174. ctype = "text/html"
  175. case strings.Contains(fname, "json"):
  176. ctype = "application/json"
  177. case strings.Contains(fname, "png"):
  178. ctype = "image/png"
  179. default:
  180. ctype = "text"
  181. }
  182. w.Header().Add("Content-Type", ctype)
  183. fmt.Fprint(w, string(b))
  184. }