wrappers.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. package main
  2. /*
  3. Wrappers.go
  4. This script provide wrapping functions
  5. for modules that do not provide
  6. handler interface within the modules
  7. --- NOTES ---
  8. If your module have more than one layer
  9. or require state keeping, please move
  10. the abstraction up one layer into
  11. your own module. Do not keep state on
  12. the global scope other than single
  13. Manager instance
  14. */
  15. import (
  16. "encoding/json"
  17. "net/http"
  18. "strconv"
  19. "strings"
  20. "imuslab.com/zoraxy/mod/dynamicproxy"
  21. "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
  22. "imuslab.com/zoraxy/mod/mdns"
  23. "imuslab.com/zoraxy/mod/uptime"
  24. "imuslab.com/zoraxy/mod/utils"
  25. "imuslab.com/zoraxy/mod/wakeonlan"
  26. )
  27. /*
  28. Statistic Summary
  29. */
  30. // Handle conversion of statistic daily summary to country summary
  31. func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
  32. requestClientCountry := map[string]int{}
  33. statisticCollector.DailySummary.RequestClientIp.Range(func(key, value interface{}) bool {
  34. //Get this client country of original
  35. clientIp := key.(string)
  36. //requestCount := value.(int)
  37. ci, err := geodbStore.ResolveCountryCodeFromIP(clientIp)
  38. if err != nil {
  39. return true
  40. }
  41. isoCode := ci.CountryIsoCode
  42. if isoCode == "" {
  43. //local or reserved addr
  44. isoCode = "local"
  45. }
  46. uc, ok := requestClientCountry[isoCode]
  47. if !ok {
  48. //Create the counter
  49. requestClientCountry[isoCode] = 1
  50. } else {
  51. requestClientCountry[isoCode] = uc + 1
  52. }
  53. return true
  54. })
  55. js, _ := json.Marshal(requestClientCountry)
  56. utils.SendJSONResponse(w, string(js))
  57. }
  58. /*
  59. Up Time Monitor
  60. */
  61. // Update uptime monitor targets after rules updated
  62. // See https://github.com/tobychui/zoraxy/issues/77
  63. func UpdateUptimeMonitorTargets() {
  64. if uptimeMonitor != nil {
  65. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  66. uptimeMonitor.CleanRecords()
  67. go func() {
  68. uptimeMonitor.ExecuteUptimeCheck()
  69. }()
  70. SystemWideLogger.PrintAndLog("uptime-monitor", "Uptime monitor config updated", nil)
  71. }
  72. }
  73. // Generate uptime monitor targets from reverse proxy rules
  74. func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
  75. hosts := dp.GetProxyEndpointsAsMap()
  76. UptimeTargets := []*uptime.Target{}
  77. for hostid, target := range hosts {
  78. if target.Disabled || target.DisableUptimeMonitor {
  79. //Skip those proxy rules that is disabled
  80. continue
  81. }
  82. isMultipleUpstreams := len(target.ActiveOrigins) > 1
  83. for i, origin := range target.ActiveOrigins {
  84. url := "http://" + origin.OriginIpOrDomain
  85. protocol := "http"
  86. if origin.RequireTLS {
  87. url = "https://" + origin.OriginIpOrDomain
  88. protocol = "https"
  89. }
  90. //Add the root url
  91. hostIdAndName := hostid
  92. if isMultipleUpstreams {
  93. hostIdAndName = hostIdAndName + " (upstream:" + strconv.Itoa(i) + ")"
  94. }
  95. UptimeTargets = append(UptimeTargets, &uptime.Target{
  96. ID: hostIdAndName,
  97. Name: hostIdAndName,
  98. URL: url,
  99. Protocol: protocol,
  100. ProxyType: uptime.ProxyType_Host,
  101. })
  102. //Add each virtual directory into the list
  103. for _, vdir := range target.VirtualDirectories {
  104. url := "http://" + vdir.Domain
  105. protocol := "http"
  106. if origin.RequireTLS {
  107. url = "https://" + vdir.Domain
  108. protocol = "https"
  109. }
  110. //Add the root url
  111. UptimeTargets = append(UptimeTargets, &uptime.Target{
  112. ID: hostid + vdir.MatchingPath,
  113. Name: hostid + vdir.MatchingPath,
  114. URL: url,
  115. Protocol: protocol,
  116. ProxyType: uptime.ProxyType_Vdir,
  117. })
  118. }
  119. }
  120. }
  121. return UptimeTargets
  122. }
  123. // Handle rendering up time monitor data
  124. func HandleUptimeMonitorListing(w http.ResponseWriter, r *http.Request) {
  125. if uptimeMonitor != nil {
  126. uptimeMonitor.HandleUptimeLogRead(w, r)
  127. } else {
  128. http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
  129. return
  130. }
  131. }
  132. /*
  133. Static Web Server
  134. */
  135. // Handle port change, if root router is using internal static web server
  136. // update the root router as well
  137. func HandleStaticWebServerPortChange(w http.ResponseWriter, r *http.Request) {
  138. newPort, err := utils.PostInt(r, "port")
  139. if err != nil {
  140. utils.SendErrorResponse(w, "invalid port number given")
  141. return
  142. }
  143. if dynamicProxyRouter.Root.DefaultSiteOption == dynamicproxy.DefaultSite_InternalStaticWebServer {
  144. //Update the root site as well
  145. newDraftingRoot := dynamicProxyRouter.Root.Clone()
  146. newDraftingRoot.ActiveOrigins = []*loadbalance.Upstream{
  147. {
  148. OriginIpOrDomain: "127.0.0.1:" + strconv.Itoa(newPort),
  149. RequireTLS: false,
  150. SkipCertValidations: false,
  151. SkipWebSocketOriginCheck: true,
  152. Weight: 0,
  153. },
  154. }
  155. activatedNewRoot, err := dynamicProxyRouter.PrepareProxyRoute(newDraftingRoot)
  156. if err != nil {
  157. utils.SendErrorResponse(w, "unable to update root routing rule")
  158. return
  159. }
  160. //Replace the root
  161. dynamicProxyRouter.Root = activatedNewRoot
  162. SaveReverseProxyConfig(newDraftingRoot)
  163. }
  164. err = staticWebServer.ChangePort(strconv.Itoa(newPort))
  165. if err != nil {
  166. utils.SendErrorResponse(w, err.Error())
  167. return
  168. }
  169. utils.SendOK(w)
  170. }
  171. /*
  172. mDNS Scanning
  173. */
  174. // Handle listing current registered mdns nodes
  175. func HandleMdnsListing(w http.ResponseWriter, r *http.Request) {
  176. if mdnsScanner == nil {
  177. utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
  178. return
  179. }
  180. js, _ := json.Marshal(previousmdnsScanResults)
  181. utils.SendJSONResponse(w, string(js))
  182. }
  183. func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
  184. if mdnsScanner == nil {
  185. utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
  186. return
  187. }
  188. domain, err := utils.PostPara(r, "domain")
  189. var hosts []*mdns.NetworkHost
  190. if err != nil {
  191. //Search for arozos node
  192. hosts = mdnsScanner.Scan(30, "")
  193. previousmdnsScanResults = hosts
  194. } else {
  195. //Search for other nodes
  196. hosts = mdnsScanner.Scan(30, domain)
  197. }
  198. js, _ := json.Marshal(hosts)
  199. utils.SendJSONResponse(w, string(js))
  200. }
  201. /*
  202. WAKE ON LAN
  203. Handle wake on LAN
  204. Support following methods
  205. /?set=xxx&name=xxx Record a new MAC address into the database
  206. /?wake=xxx Wake a server given its MAC address
  207. /?del=xxx Delete a server given its MAC address
  208. / Default: list all recorded WoL MAC address
  209. */
  210. func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
  211. set, _ := utils.PostPara(r, "set")
  212. del, _ := utils.PostPara(r, "del")
  213. wake, _ := utils.PostPara(r, "wake")
  214. if set != "" {
  215. //Get the name of the describing server
  216. servername, err := utils.PostPara(r, "name")
  217. if err != nil {
  218. utils.SendErrorResponse(w, "invalid server name given")
  219. return
  220. }
  221. //Check if the given mac address is a valid mac address
  222. set = strings.TrimSpace(set)
  223. if !wakeonlan.IsValidMacAddress(set) {
  224. utils.SendErrorResponse(w, "invalid mac address given")
  225. return
  226. }
  227. //Store this into the database
  228. sysdb.Write("wolmac", set, servername)
  229. utils.SendOK(w)
  230. } else if wake != "" {
  231. //Wake the target up by MAC address
  232. if !wakeonlan.IsValidMacAddress(wake) {
  233. utils.SendErrorResponse(w, "invalid mac address given")
  234. return
  235. }
  236. SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil)
  237. err := wakeonlan.WakeTarget(wake)
  238. if err != nil {
  239. utils.SendErrorResponse(w, err.Error())
  240. return
  241. }
  242. utils.SendOK(w)
  243. } else if del != "" {
  244. if !wakeonlan.IsValidMacAddress(del) {
  245. utils.SendErrorResponse(w, "invalid mac address given")
  246. return
  247. }
  248. sysdb.Delete("wolmac", del)
  249. utils.SendOK(w)
  250. } else {
  251. //List all the saved WoL MAC Address
  252. entries, err := sysdb.ListTable("wolmac")
  253. if err != nil {
  254. utils.SendErrorResponse(w, "unknown error occured")
  255. return
  256. }
  257. type MacAddrRecord struct {
  258. ServerName string
  259. MacAddr string
  260. }
  261. results := []*MacAddrRecord{}
  262. for _, keypairs := range entries {
  263. macAddr := string(keypairs[0])
  264. serverName := ""
  265. json.Unmarshal(keypairs[1], &serverName)
  266. results = append(results, &MacAddrRecord{
  267. ServerName: serverName,
  268. MacAddr: macAddr,
  269. })
  270. }
  271. js, _ := json.Marshal(results)
  272. utils.SendJSONResponse(w, string(js))
  273. }
  274. }
  275. /*
  276. Zoraxy Host Info
  277. */
  278. func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) {
  279. type ZoraxyInfo struct {
  280. Version string
  281. NodeUUID string
  282. Development bool
  283. BootTime int64
  284. EnableSshLoopback bool
  285. ZerotierConnected bool
  286. }
  287. displayUUID := nodeUUID
  288. displayAllowSSHLB := *allowSshLoopback
  289. displayBootTime := bootTime
  290. if !authAgent.CheckAuth(r) {
  291. displayUUID = "Unauthorized"
  292. displayAllowSSHLB = false
  293. displayBootTime = 0
  294. }
  295. info := ZoraxyInfo{
  296. Version: SYSTEM_VERSION,
  297. NodeUUID: displayUUID,
  298. Development: DEVELOPMENT_BUILD,
  299. BootTime: displayBootTime,
  300. EnableSshLoopback: displayAllowSSHLB,
  301. ZerotierConnected: ganManager.ControllerID != "",
  302. }
  303. js, _ := json.MarshalIndent(info, "", " ")
  304. utils.SendJSONResponse(w, string(js))
  305. }
  306. func HandleGeoIpLookup(w http.ResponseWriter, r *http.Request) {
  307. ip, err := utils.GetPara(r, "ip")
  308. if err != nil {
  309. utils.SendErrorResponse(w, "ip not given")
  310. return
  311. }
  312. cc, err := geodbStore.ResolveCountryCodeFromIP(ip)
  313. if err != nil {
  314. utils.SendErrorResponse(w, err.Error())
  315. return
  316. }
  317. js, _ := json.Marshal(cc)
  318. utils.SendJSONResponse(w, string(js))
  319. }