wrappers.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. uptimeMonitor.CleanRecords()
  97. go func() {
  98. uptimeMonitor.ExecuteUptimeCheck()
  99. }()
  100. SystemWideLogger.PrintAndLog("uptime-monitor", "Uptime monitor config updated", nil)
  101. }
  102. }
  103. // Generate uptime monitor targets from reverse proxy rules
  104. func GetUptimeTargetsFromReverseProxyRules(dp *dynamicproxy.Router) []*uptime.Target {
  105. hosts := dp.GetProxyEndpointsAsMap()
  106. UptimeTargets := []*uptime.Target{}
  107. for hostid, target := range hosts {
  108. if target.Disabled {
  109. //Skip those proxy rules that is disabled
  110. continue
  111. }
  112. isMultipleUpstreams := len(target.ActiveOrigins) > 1
  113. for i, origin := range target.ActiveOrigins {
  114. url := "http://" + origin.OriginIpOrDomain
  115. protocol := "http"
  116. if origin.RequireTLS {
  117. url = "https://" + origin.OriginIpOrDomain
  118. protocol = "https"
  119. }
  120. //Add the root url
  121. hostIdAndName := hostid
  122. if isMultipleUpstreams {
  123. hostIdAndName = hostIdAndName + " (upstream:" + strconv.Itoa(i) + ")"
  124. }
  125. UptimeTargets = append(UptimeTargets, &uptime.Target{
  126. ID: hostIdAndName,
  127. Name: hostIdAndName,
  128. URL: url,
  129. Protocol: protocol,
  130. ProxyType: uptime.ProxyType_Host,
  131. })
  132. //Add each virtual directory into the list
  133. for _, vdir := range target.VirtualDirectories {
  134. url := "http://" + vdir.Domain
  135. protocol := "http"
  136. if origin.RequireTLS {
  137. url = "https://" + vdir.Domain
  138. protocol = "https"
  139. }
  140. //Add the root url
  141. UptimeTargets = append(UptimeTargets, &uptime.Target{
  142. ID: hostid + vdir.MatchingPath,
  143. Name: hostid + vdir.MatchingPath,
  144. URL: url,
  145. Protocol: protocol,
  146. ProxyType: uptime.ProxyType_Vdir,
  147. })
  148. }
  149. }
  150. }
  151. return UptimeTargets
  152. }
  153. // Handle rendering up time monitor data
  154. func HandleUptimeMonitorListing(w http.ResponseWriter, r *http.Request) {
  155. if uptimeMonitor != nil {
  156. uptimeMonitor.HandleUptimeLogRead(w, r)
  157. } else {
  158. http.Error(w, "500 - Internal Server Error", http.StatusInternalServerError)
  159. return
  160. }
  161. }
  162. /*
  163. Static Web Server
  164. */
  165. // Handle port change, if root router is using internal static web server
  166. // update the root router as well
  167. func HandleStaticWebServerPortChange(w http.ResponseWriter, r *http.Request) {
  168. newPort, err := utils.PostInt(r, "port")
  169. if err != nil {
  170. utils.SendErrorResponse(w, "invalid port number given")
  171. return
  172. }
  173. if dynamicProxyRouter.Root.DefaultSiteOption == dynamicproxy.DefaultSite_InternalStaticWebServer {
  174. //Update the root site as well
  175. newDraftingRoot := dynamicProxyRouter.Root.Clone()
  176. newDraftingRoot.ActiveOrigins = []*loadbalance.Upstream{
  177. {
  178. OriginIpOrDomain: "127.0.0.1:" + strconv.Itoa(newPort),
  179. RequireTLS: false,
  180. SkipCertValidations: false,
  181. SkipWebSocketOriginCheck: true,
  182. Weight: 0,
  183. },
  184. }
  185. activatedNewRoot, err := dynamicProxyRouter.PrepareProxyRoute(newDraftingRoot)
  186. if err != nil {
  187. utils.SendErrorResponse(w, "unable to update root routing rule")
  188. return
  189. }
  190. //Replace the root
  191. dynamicProxyRouter.Root = activatedNewRoot
  192. SaveReverseProxyConfig(newDraftingRoot)
  193. }
  194. err = staticWebServer.ChangePort(strconv.Itoa(newPort))
  195. if err != nil {
  196. utils.SendErrorResponse(w, err.Error())
  197. return
  198. }
  199. utils.SendOK(w)
  200. }
  201. /*
  202. mDNS Scanning
  203. */
  204. // Handle listing current registered mdns nodes
  205. func HandleMdnsListing(w http.ResponseWriter, r *http.Request) {
  206. if mdnsScanner == nil {
  207. utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
  208. return
  209. }
  210. js, _ := json.Marshal(previousmdnsScanResults)
  211. utils.SendJSONResponse(w, string(js))
  212. }
  213. func HandleMdnsScanning(w http.ResponseWriter, r *http.Request) {
  214. if mdnsScanner == nil {
  215. utils.SendErrorResponse(w, "mDNS scanner is disabled on this host")
  216. return
  217. }
  218. domain, err := utils.PostPara(r, "domain")
  219. var hosts []*mdns.NetworkHost
  220. if err != nil {
  221. //Search for arozos node
  222. hosts = mdnsScanner.Scan(30, "")
  223. previousmdnsScanResults = hosts
  224. } else {
  225. //Search for other nodes
  226. hosts = mdnsScanner.Scan(30, domain)
  227. }
  228. js, _ := json.Marshal(hosts)
  229. utils.SendJSONResponse(w, string(js))
  230. }
  231. // handle ip scanning
  232. func HandleIpScan(w http.ResponseWriter, r *http.Request) {
  233. cidr, err := utils.PostPara(r, "cidr")
  234. if err != nil {
  235. //Ip range mode
  236. start, err := utils.PostPara(r, "start")
  237. if err != nil {
  238. utils.SendErrorResponse(w, "missing start ip")
  239. return
  240. }
  241. end, err := utils.PostPara(r, "end")
  242. if err != nil {
  243. utils.SendErrorResponse(w, "missing end ip")
  244. return
  245. }
  246. discoveredHosts, err := ipscan.ScanIpRange(start, end)
  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. } else {
  254. //CIDR mode
  255. discoveredHosts, err := ipscan.ScanCIDRRange(cidr)
  256. if err != nil {
  257. utils.SendErrorResponse(w, err.Error())
  258. return
  259. }
  260. js, _ := json.Marshal(discoveredHosts)
  261. utils.SendJSONResponse(w, string(js))
  262. }
  263. }
  264. /*
  265. WAKE ON LAN
  266. Handle wake on LAN
  267. Support following methods
  268. /?set=xxx&name=xxx Record a new MAC address into the database
  269. /?wake=xxx Wake a server given its MAC address
  270. /?del=xxx Delete a server given its MAC address
  271. / Default: list all recorded WoL MAC address
  272. */
  273. func HandleWakeOnLan(w http.ResponseWriter, r *http.Request) {
  274. set, _ := utils.PostPara(r, "set")
  275. del, _ := utils.PostPara(r, "del")
  276. wake, _ := utils.PostPara(r, "wake")
  277. if set != "" {
  278. //Get the name of the describing server
  279. servername, err := utils.PostPara(r, "name")
  280. if err != nil {
  281. utils.SendErrorResponse(w, "invalid server name given")
  282. return
  283. }
  284. //Check if the given mac address is a valid mac address
  285. set = strings.TrimSpace(set)
  286. if !wakeonlan.IsValidMacAddress(set) {
  287. utils.SendErrorResponse(w, "invalid mac address given")
  288. return
  289. }
  290. //Store this into the database
  291. sysdb.Write("wolmac", set, servername)
  292. utils.SendOK(w)
  293. } else if wake != "" {
  294. //Wake the target up by MAC address
  295. if !wakeonlan.IsValidMacAddress(wake) {
  296. utils.SendErrorResponse(w, "invalid mac address given")
  297. return
  298. }
  299. SystemWideLogger.PrintAndLog("WoL", "Sending Wake on LAN magic packet to "+wake, nil)
  300. err := wakeonlan.WakeTarget(wake)
  301. if err != nil {
  302. utils.SendErrorResponse(w, err.Error())
  303. return
  304. }
  305. utils.SendOK(w)
  306. } else if del != "" {
  307. if !wakeonlan.IsValidMacAddress(del) {
  308. utils.SendErrorResponse(w, "invalid mac address given")
  309. return
  310. }
  311. sysdb.Delete("wolmac", del)
  312. utils.SendOK(w)
  313. } else {
  314. //List all the saved WoL MAC Address
  315. entries, err := sysdb.ListTable("wolmac")
  316. if err != nil {
  317. utils.SendErrorResponse(w, "unknown error occured")
  318. return
  319. }
  320. type MacAddrRecord struct {
  321. ServerName string
  322. MacAddr string
  323. }
  324. results := []*MacAddrRecord{}
  325. for _, keypairs := range entries {
  326. macAddr := string(keypairs[0])
  327. serverName := ""
  328. json.Unmarshal(keypairs[1], &serverName)
  329. results = append(results, &MacAddrRecord{
  330. ServerName: serverName,
  331. MacAddr: macAddr,
  332. })
  333. }
  334. js, _ := json.Marshal(results)
  335. utils.SendJSONResponse(w, string(js))
  336. }
  337. }
  338. /*
  339. Zoraxy Host Info
  340. */
  341. func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) {
  342. type ZoraxyInfo struct {
  343. Version string
  344. NodeUUID string
  345. Development bool
  346. BootTime int64
  347. EnableSshLoopback bool
  348. ZerotierConnected bool
  349. }
  350. info := ZoraxyInfo{
  351. Version: version,
  352. NodeUUID: nodeUUID,
  353. Development: development,
  354. BootTime: bootTime,
  355. EnableSshLoopback: *allowSshLoopback,
  356. ZerotierConnected: ganManager.ControllerID != "",
  357. }
  358. js, _ := json.MarshalIndent(info, "", " ")
  359. utils.SendJSONResponse(w, string(js))
  360. }
  361. func HandleGeoIpLookup(w http.ResponseWriter, r *http.Request) {
  362. ip, err := utils.GetPara(r, "ip")
  363. if err != nil {
  364. utils.SendErrorResponse(w, "ip not given")
  365. return
  366. }
  367. cc, err := geodbStore.ResolveCountryCodeFromIP(ip)
  368. if err != nil {
  369. utils.SendErrorResponse(w, err.Error())
  370. return
  371. }
  372. js, _ := json.Marshal(cc)
  373. utils.SendJSONResponse(w, string(js))
  374. }