wrappers.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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. "fmt"
  18. "net/http"
  19. "strconv"
  20. "strings"
  21. "time"
  22. "imuslab.com/zoraxy/mod/dynamicproxy"
  23. "imuslab.com/zoraxy/mod/dynamicproxy/loadbalance"
  24. "imuslab.com/zoraxy/mod/ipscan"
  25. "imuslab.com/zoraxy/mod/mdns"
  26. "imuslab.com/zoraxy/mod/uptime"
  27. "imuslab.com/zoraxy/mod/utils"
  28. "imuslab.com/zoraxy/mod/wakeonlan"
  29. )
  30. /*
  31. Proxy Utils
  32. */
  33. //Check if site support TLS
  34. func HandleCheckSiteSupportTLS(w http.ResponseWriter, r *http.Request) {
  35. targetURL, err := utils.PostPara(r, "url")
  36. if err != nil {
  37. utils.SendErrorResponse(w, "invalid url given")
  38. return
  39. }
  40. httpsUrl := fmt.Sprintf("https://%s", targetURL)
  41. httpUrl := fmt.Sprintf("http://%s", targetURL)
  42. client := http.Client{Timeout: 5 * time.Second}
  43. resp, err := client.Head(httpsUrl)
  44. if err == nil && resp.StatusCode == http.StatusOK {
  45. js, _ := json.Marshal("https")
  46. utils.SendJSONResponse(w, string(js))
  47. return
  48. }
  49. resp, err = client.Head(httpUrl)
  50. if err == nil && resp.StatusCode == http.StatusOK {
  51. js, _ := json.Marshal("http")
  52. utils.SendJSONResponse(w, string(js))
  53. return
  54. }
  55. utils.SendErrorResponse(w, "invalid url given")
  56. }
  57. /*
  58. Statistic Summary
  59. */
  60. // Handle conversion of statistic daily summary to country summary
  61. func HandleCountryDistrSummary(w http.ResponseWriter, r *http.Request) {
  62. requestClientCountry := map[string]int{}
  63. statisticCollector.DailySummary.RequestClientIp.Range(func(key, value interface{}) bool {
  64. //Get this client country of original
  65. clientIp := key.(string)
  66. //requestCount := value.(int)
  67. ci, err := geodbStore.ResolveCountryCodeFromIP(clientIp)
  68. if err != nil {
  69. return true
  70. }
  71. isoCode := ci.CountryIsoCode
  72. if isoCode == "" {
  73. //local or reserved addr
  74. isoCode = "local"
  75. }
  76. uc, ok := requestClientCountry[isoCode]
  77. if !ok {
  78. //Create the counter
  79. requestClientCountry[isoCode] = 1
  80. } else {
  81. requestClientCountry[isoCode] = uc + 1
  82. }
  83. return true
  84. })
  85. js, _ := json.Marshal(requestClientCountry)
  86. utils.SendJSONResponse(w, string(js))
  87. }
  88. /*
  89. Up Time Monitor
  90. */
  91. // Update uptime monitor targets after rules updated
  92. // See https://github.com/tobychui/zoraxy/issues/77
  93. func UpdateUptimeMonitorTargets() {
  94. if uptimeMonitor != nil {
  95. uptimeMonitor.Config.Targets = GetUptimeTargetsFromReverseProxyRules(dynamicProxyRouter)
  96. go func() {
  97. uptimeMonitor.ExecuteUptimeCheck()
  98. }()
  99. SystemWideLogger.PrintAndLog("Uptime", "Uptime monitor config updated", nil)
  100. }
  101. }
  102. // Generate uptime monitor targets from reverse proxy rules
  103. func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
  104. hosts := dp.GetProxyEndpointsAsMap()
  105. UptimeTargets := []*uptime.Target{}
  106. for hostid, target := range hosts {
  107. for _, origin := range target.Origins {
  108. url := "http://" + origin.OriginIpOrDomain
  109. protocol := "http"
  110. if origin.RequireTLS {
  111. url = "https://" + origin.OriginIpOrDomain
  112. protocol = "https"
  113. }
  114. //Add the root url
  115. UptimeTargets = append(UptimeTargets, &uptime.Target{
  116. ID: hostid,
  117. Name: hostid,
  118. URL: url,
  119. Protocol: protocol,
  120. ProxyType: uptime.ProxyType_Host,
  121. })
  122. //Add each virtual directory into the list
  123. for _, vdir := range target.VirtualDirectories {
  124. url := "http://" + vdir.Domain
  125. protocol := "http"
  126. if origin.RequireTLS {
  127. url = "https://" + vdir.Domain
  128. protocol = "https"
  129. }
  130. //Add the root url
  131. UptimeTargets = append(UptimeTargets, &uptime.Target{
  132. ID: hostid + vdir.MatchingPath,
  133. Name: hostid + vdir.MatchingPath,
  134. URL: url,
  135. Protocol: protocol,
  136. ProxyType: uptime.ProxyType_Vdir,
  137. })
  138. }
  139. }
  140. }
  141. return UptimeTargets
  142. }
  143. // Handle rendering up time monitor data
  144. func HandleUptimeMonitorListing(w http.ResponseWriter, r *http.Request) {
  145. if uptimeMonitor != nil {
  146. uptimeMonitor.HandleUptimeLogRead(w, r)
  147. } else {
  148. http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
  149. return
  150. }
  151. }
  152. /*
  153. Static Web Server
  154. */
  155. // Handle port change, if root router is using internal static web server
  156. // update the root router as well
  157. func HandleStaticWebServerPortChange(w http.ResponseWriter, r *http.Request) {
  158. newPort, err := utils.PostInt(r, "port")
  159. if err != nil {
  160. utils.SendErrorResponse(w, "invalid port number given")
  161. return
  162. }
  163. if dynamicProxyRouter.Root.DefaultSiteOption == dynamicproxy.DefaultSite_InternalStaticWebServer {
  164. //Update the root site as well
  165. newDraftingRoot := dynamicProxyRouter.Root.Clone()
  166. newDraftingRoot.Origins = []*loadbalance.Upstream{
  167. {
  168. OriginIpOrDomain: "127.0.0.1:" + strconv.Itoa(newPort),
  169. RequireTLS: false,
  170. SkipCertValidations: false,
  171. SkipWebSocketOriginCheck: true,
  172. PreferedCountryCode: []string{},
  173. Priority: 0,
  174. },
  175. }
  176. activatedNewRoot, err := dynamicProxyRouter.PrepareProxyRoute(newDraftingRoot)
  177. if err != nil {
  178. utils.SendErrorResponse(w, "unable to update root routing rule")
  179. return
  180. }
  181. //Replace the root
  182. dynamicProxyRouter.Root = activatedNewRoot
  183. SaveReverseProxyConfig(newDraftingRoot)
  184. }
  185. err = staticWebServer.ChangePort(strconv.Itoa(newPort))
  186. if err != nil {
  187. utils.SendErrorResponse(w, err.Error())
  188. return
  189. }
  190. utils.SendOK(w)
  191. }
  192. /*
  193. mDNS Scanning
  194. */
  195. // Handle listing current registered mdns nodes
  196. func HandleMdnsListing(w http.ResponseWriter, r *http.Request) {
  197. if mdnsScanner == nil {
  198. utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
  199. return
  200. }
  201. js, _ := json.Marshal(previousmdnsScanResults)
  202. utils.SendJSONResponse(w, string(js))
  203. }
  204. func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
  205. if mdnsScanner == nil {
  206. utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
  207. return
  208. }
  209. domain, err := utils.PostPara(r, "domain")
  210. var hosts []*mdns.NetworkHost
  211. if err != nil {
  212. //Search for arozos node
  213. hosts = mdnsScanner.Scan(30, "")
  214. previousmdnsScanResults = hosts
  215. } else {
  216. //Search for other nodes
  217. hosts = mdnsScanner.Scan(30, domain)
  218. }
  219. js, _ := json.Marshal(hosts)
  220. utils.SendJSONResponse(w, string(js))
  221. }
  222. // handle ip scanning
  223. func HandleIpScan(w http.ResponseWriter, r *http.Request) {
  224. cidr, err := utils.PostPara(r, "cidr")
  225. if err != nil {
  226. //Ip range mode
  227. start, err := utils.PostPara(r, "start")
  228. if err != nil {
  229. utils.SendErrorResponse(w, "missing start ip")
  230. return
  231. }
  232. end, err := utils.PostPara(r, "end")
  233. if err != nil {
  234. utils.SendErrorResponse(w, "missing end ip")
  235. return
  236. }
  237. discoveredHosts, err := ipscan.ScanIpRange(start, end)
  238. if err != nil {
  239. utils.SendErrorResponse(w, err.Error())
  240. return
  241. }
  242. js, _ := json.Marshal(discoveredHosts)
  243. utils.SendJSONResponse(w, string(js))
  244. } else {
  245. //CIDR mode
  246. discoveredHosts, err := ipscan.ScanCIDRRange(cidr)
  247. if err != nil {
  248. utils.SendErrorResponse(w, err.Error())
  249. return
  250. }
  251. js, _ := json.Marshal(discoveredHosts)
  252. utils.SendJSONResponse(w, string(js))
  253. }
  254. }
  255. /*
  256. WAKE ON LAN
  257. Handle wake on LAN
  258. Support following methods
  259. /?set=xxx&name=xxx Record a new MAC address into the database
  260. /?wake=xxx Wake a server given its MAC address
  261. /?del=xxx Delete a server given its MAC address
  262. / Default: list all recorded WoL MAC address
  263. */
  264. func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
  265. set, _ := utils.PostPara(r, "set")
  266. del, _ := utils.PostPara(r, "del")
  267. wake, _ := utils.PostPara(r, "wake")
  268. if set != "" {
  269. //Get the name of the describing server
  270. servername, err := utils.PostPara(r, "name")
  271. if err != nil {
  272. utils.SendErrorResponse(w, "invalid server name given")
  273. return
  274. }
  275. //Check if the given mac address is a valid mac address
  276. set = strings.TrimSpace(set)
  277. if !wakeonlan.IsValidMacAddress(set) {
  278. utils.SendErrorResponse(w, "invalid mac address given")
  279. return
  280. }
  281. //Store this into the database
  282. sysdb.Write("wolmac", set, servername)
  283. utils.SendOK(w)
  284. } else if wake != "" {
  285. //Wake the target up by MAC address
  286. if !wakeonlan.IsValidMacAddress(wake) {
  287. utils.SendErrorResponse(w, "invalid mac address given")
  288. return
  289. }
  290. SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil)
  291. err := wakeonlan.WakeTarget(wake)
  292. if err != nil {
  293. utils.SendErrorResponse(w, err.Error())
  294. return
  295. }
  296. utils.SendOK(w)
  297. } else if del != "" {
  298. if !wakeonlan.IsValidMacAddress(del) {
  299. utils.SendErrorResponse(w, "invalid mac address given")
  300. return
  301. }
  302. sysdb.Delete("wolmac", del)
  303. utils.SendOK(w)
  304. } else {
  305. //List all the saved WoL MAC Address
  306. entries, err := sysdb.ListTable("wolmac")
  307. if err != nil {
  308. utils.SendErrorResponse(w, "unknown error occured")
  309. return
  310. }
  311. type MacAddrRecord struct {
  312. ServerName string
  313. MacAddr string
  314. }
  315. results := []*MacAddrRecord{}
  316. for _, keypairs := range entries {
  317. macAddr := string(keypairs[0])
  318. serverName := ""
  319. json.Unmarshal(keypairs[1], &serverName)
  320. results = append(results, &MacAddrRecord{
  321. ServerName: serverName,
  322. MacAddr: macAddr,
  323. })
  324. }
  325. js, _ := json.Marshal(results)
  326. utils.SendJSONResponse(w, string(js))
  327. }
  328. }
  329. /*
  330. Zoraxy Host Info
  331. */
  332. func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) {
  333. type ZoraxyInfo struct {
  334. Version string
  335. NodeUUID string
  336. Development bool
  337. BootTime int64
  338. EnableSshLoopback bool
  339. ZerotierConnected bool
  340. }
  341. info := ZoraxyInfo{
  342. Version: version,
  343. NodeUUID: nodeUUID,
  344. Development: development,
  345. BootTime: bootTime,
  346. EnableSshLoopback: *allowSshLoopback,
  347. ZerotierConnected: ganManager.ControllerID != "",
  348. }
  349. js, _ := json.MarshalIndent(info, "", " ")
  350. utils.SendJSONResponse(w, string(js))
  351. }
  352. func HandleGeoIpLookup(w http.ResponseWriter, r *http.Request) {
  353. ip, err := utils.GetPara(r, "ip")
  354. if err != nil {
  355. utils.SendErrorResponse(w, "ip not given")
  356. return
  357. }
  358. cc, err := geodbStore.ResolveCountryCodeFromIP(ip)
  359. if err != nil {
  360. utils.SendErrorResponse(w, err.Error())
  361. return
  362. }
  363. js, _ := json.Marshal(cc)
  364. utils.SendJSONResponse(w, string(js))
  365. }