debug_server.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Brawler
  3. *
  4. * Copyright 2019 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package main
  11. import (
  12. "bytes"
  13. "crypto/tls"
  14. "flag"
  15. "fmt"
  16. "io"
  17. "io/ioutil"
  18. "net/http"
  19. "os"
  20. "path/filepath"
  21. "strings"
  22. "sync"
  23. "time"
  24. "unicode"
  25. "devt.de/krotik/brawler/api"
  26. "devt.de/krotik/brawler/api/web"
  27. v1 "devt.de/krotik/brawler/api/web/v1"
  28. "devt.de/krotik/brawler/engine"
  29. "devt.de/krotik/brawler/rumble"
  30. "devt.de/krotik/common/cryptutil"
  31. "devt.de/krotik/common/errorutil"
  32. "devt.de/krotik/common/fileutil"
  33. "devt.de/krotik/common/httputil"
  34. )
  35. /*
  36. debugServer starts the Brawler debug server.
  37. */
  38. func debugServer() error {
  39. var ok bool
  40. var err error
  41. fmt.Println(fmt.Sprintf("Brawler Debug Server %v", engine.ProductVersion))
  42. certDir := flag.String("ssl-dir", "ssl", "Directory containing the ssl key.pem and cert.pem files")
  43. webDir := flag.String("web-dir", "web", "Directory containing the web files")
  44. webExport := flag.String("web", ":9090", "Run the web interface through a https interface on the specified host:port")
  45. showHelp := flag.Bool("help", false, "Show this help message")
  46. flag.Usage = func() {
  47. fmt.Println()
  48. fmt.Println(fmt.Sprintf("Usage of %s debug-server [options]", os.Args[0]))
  49. fmt.Println()
  50. flag.PrintDefaults()
  51. fmt.Println()
  52. }
  53. flag.CommandLine.Parse(os.Args[2:])
  54. if *showHelp {
  55. flag.Usage()
  56. return nil
  57. }
  58. if _, err = loadCert(*certDir); err == nil {
  59. // Set debug as default logging level
  60. api.DefaultLogLevel = rumble.Debug
  61. // Register REST endpoints for version 1
  62. web.RegisterRestEndpoints(v1.V1EndpointMap)
  63. web.RegisterRestEndpoints(web.GeneralEndpointMap)
  64. // Create the default engine
  65. de := api.GetEngine(DefaultEngineName, true)
  66. de.Proc.Start()
  67. // Ensure web folder
  68. if ok, err = fileutil.PathExists(*webDir); err == nil && !ok {
  69. fmt.Println("Creating web folder")
  70. err = extractWebFiles(*webDir)
  71. }
  72. // Start the web server
  73. if ok, _ = fileutil.PathExists(*webDir); err == nil && ok {
  74. fs := http.FileServer(http.Dir(*webDir))
  75. web.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  76. fs.ServeHTTP(w, r)
  77. })
  78. // Start HTTPS server and enable REST API
  79. hs := &httputil.HTTPServer{}
  80. var wg sync.WaitGroup
  81. wg.Add(1)
  82. fmt.Println("Starting server on: ", *webExport)
  83. go hs.RunHTTPSServer(*certDir, "cert.pem", "key.pem",
  84. *webExport, &wg)
  85. // Wait until the server has started
  86. wg.Wait()
  87. if err = hs.LastError; err == nil {
  88. // Read server certificate and write a fingerprint file
  89. fpfile := filepath.Join(*webDir, "fingerprint.json")
  90. fmt.Println("Writing fingerprint file: ", fpfile)
  91. certs, _ := cryptutil.ReadX509CertsFromFile(filepath.Join(*certDir, "cert.pem"))
  92. if len(certs) > 0 {
  93. buf := bytes.Buffer{}
  94. buf.WriteString("{\n")
  95. buf.WriteString(fmt.Sprintf(` "md5" : "%s",`, cryptutil.Md5CertFingerprint(certs[0])))
  96. buf.WriteString("\n")
  97. buf.WriteString(fmt.Sprintf(` "sha1" : "%s",`, cryptutil.Sha1CertFingerprint(certs[0])))
  98. buf.WriteString("\n")
  99. buf.WriteString(fmt.Sprintf(` "sha256" : "%s"`, cryptutil.Sha256CertFingerprint(certs[0])))
  100. buf.WriteString("\n")
  101. buf.WriteString("}\n")
  102. ioutil.WriteFile(fpfile, buf.Bytes(), 0644)
  103. }
  104. // Add to the wait group so we can wait for the shutdown
  105. wg.Add(1)
  106. fmt.Println("Waiting for shutdown")
  107. wg.Wait()
  108. }
  109. }
  110. }
  111. return err
  112. }
  113. /*
  114. loadCert loads the SSL key and certificate.
  115. */
  116. func loadCert(certDir string) (*tls.Certificate, error) {
  117. var ok bool
  118. var err error
  119. // Load ssl key and certificate
  120. if ok, _ = fileutil.PathExists(certDir); !ok {
  121. if err = os.MkdirAll(certDir, 0700); err == nil {
  122. err = cryptutil.GenCert(certDir, "cert.pem", "key.pem", "localhost",
  123. "", 365*24*time.Hour, true, 2048, "")
  124. }
  125. }
  126. if err == nil {
  127. var cert tls.Certificate
  128. cert, err = tls.LoadX509KeyPair(filepath.Join(certDir, "cert.pem"),
  129. filepath.Join(certDir, "key.pem"))
  130. if err == nil {
  131. fmt.Println(fmt.Sprintf("Using ssl key.pem and cert.pem from: %s", certDir))
  132. return &cert, nil
  133. }
  134. }
  135. return nil, err
  136. }
  137. /*
  138. extractWebFiles extracts the web files from the executable.
  139. */
  140. func extractWebFiles(webfolder string) error {
  141. end := "####"
  142. marker := fmt.Sprintf("%v%v%v", end, "WEBZIP", end)
  143. exename, err := filepath.Abs(os.Args[0])
  144. errorutil.AssertOk(err)
  145. if ok, _ := fileutil.PathExists(exename); !ok {
  146. // Try an optional .exe suffix which might work on Windows
  147. exename += ".exe"
  148. }
  149. stat, err := os.Stat(exename)
  150. if err != nil {
  151. return err
  152. }
  153. // Open the executable
  154. f, err := os.Open(exename)
  155. if err != nil {
  156. return err
  157. }
  158. defer f.Close()
  159. found := false
  160. buf := make([]byte, 4096)
  161. buf2 := make([]byte, len(marker)+10)
  162. var pos int64
  163. // Look for the marker which marks the beginning of the attached zip file
  164. for i, err := f.Read(buf); err == nil; i, err = f.Read(buf) {
  165. // Check if the marker could be in the read string
  166. if strings.Contains(string(buf), "#") {
  167. // Marker was found - read a bit more to ensure we got the full marker
  168. if i2, err := f.Read(buf2); err == nil || err == io.EOF {
  169. candidateString := string(append(buf, buf2...))
  170. // Now determine the position if the zip file
  171. if markerIndex := strings.Index(candidateString, marker); markerIndex >= 0 {
  172. start := int64(markerIndex + len(marker))
  173. for unicode.IsSpace(rune(candidateString[start])) || unicode.IsControl(rune(candidateString[start])) {
  174. start++ // Skip final control characters \n or \r\n
  175. }
  176. pos += start
  177. found = true
  178. break
  179. }
  180. pos += int64(i2)
  181. }
  182. }
  183. pos += int64(i)
  184. }
  185. if err == nil {
  186. if found {
  187. // Extract the zip
  188. if _, err = f.Seek(pos, 0); err == nil {
  189. zipLen := stat.Size() - pos
  190. if err = os.MkdirAll(webfolder, 0755); err == nil {
  191. err = fileutil.UnzipReader(io.NewSectionReader(f, pos, zipLen), zipLen, webfolder, false)
  192. }
  193. }
  194. } else {
  195. err = fmt.Errorf("Could not find web content marker in executable - invalid executable")
  196. }
  197. }
  198. return err
  199. }