wrappers.go 10 KB

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