Server.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package dynamicproxy
  2. import (
  3. _ "embed"
  4. "errors"
  5. "log"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "strings"
  10. "imuslab.com/zoraxy/mod/geodb"
  11. )
  12. /*
  13. Server.go
  14. Main server for dynamic proxy core
  15. Routing Handler Priority (High to Low)
  16. - Blacklist
  17. - Whitelist
  18. - Redirectable
  19. - Subdomain Routing
  20. - Vitrual Directory Routing
  21. */
  22. var (
  23. //go:embed tld.json
  24. rawTldMap []byte
  25. )
  26. func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  27. /*
  28. Special Routing Rules, bypass most of the limitations
  29. */
  30. //Check if there are external routing rule matches.
  31. //If yes, route them via external rr
  32. matchedRoutingRule := h.Parent.GetMatchingRoutingRule(r)
  33. if matchedRoutingRule != nil {
  34. //Matching routing rule found. Let the sub-router handle it
  35. if matchedRoutingRule.UseSystemAccessControl {
  36. //This matching rule request system access control.
  37. //check access logic
  38. respWritten := h.handleAccessRouting(w, r)
  39. if respWritten {
  40. return
  41. }
  42. }
  43. matchedRoutingRule.Route(w, r)
  44. return
  45. }
  46. /*
  47. General Access Check
  48. */
  49. respWritten := h.handleAccessRouting(w, r)
  50. if respWritten {
  51. return
  52. }
  53. /*
  54. Redirection Routing
  55. */
  56. //Check if this is a redirection url
  57. if h.Parent.Option.RedirectRuleTable.IsRedirectable(r) {
  58. statusCode := h.Parent.Option.RedirectRuleTable.HandleRedirect(w, r)
  59. h.logRequest(r, statusCode != 500, statusCode, "redirect", "")
  60. return
  61. }
  62. //Extract request host to see if it is virtual directory or subdomain
  63. domainOnly := r.Host
  64. if strings.Contains(r.Host, ":") {
  65. hostPath := strings.Split(r.Host, ":")
  66. domainOnly = hostPath[0]
  67. }
  68. /*
  69. Subdomain Routing
  70. */
  71. if strings.Contains(r.Host, ".") {
  72. //This might be a subdomain. See if there are any subdomain proxy router for this
  73. sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
  74. if sep != nil {
  75. if sep.RequireBasicAuth {
  76. err := h.handleBasicAuthRouting(w, r, sep)
  77. if err != nil {
  78. return
  79. }
  80. }
  81. h.subdomainRequest(w, r, sep)
  82. return
  83. }
  84. }
  85. /*
  86. Virtual Directory Routing
  87. */
  88. //Clean up the request URI
  89. proxyingPath := strings.TrimSpace(r.RequestURI)
  90. targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath)
  91. if targetProxyEndpoint != nil {
  92. if targetProxyEndpoint.RequireBasicAuth {
  93. err := h.handleBasicAuthRouting(w, r, targetProxyEndpoint)
  94. if err != nil {
  95. return
  96. }
  97. }
  98. h.proxyRequest(w, r, targetProxyEndpoint)
  99. } else if !strings.HasSuffix(proxyingPath, "/") {
  100. potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
  101. if potentialProxtEndpoint != nil {
  102. //Missing tailing slash. Redirect to target proxy endpoint
  103. http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
  104. } else {
  105. //Passthrough the request to root
  106. h.handleRootRouting(w, r)
  107. }
  108. } else {
  109. //No routing rules found.
  110. h.handleRootRouting(w, r)
  111. }
  112. }
  113. /*
  114. handleRootRouting
  115. This function handle root routing situations where there are no subdomain
  116. , vdir or special routing rule matches the requested URI.
  117. Once entered this routing segment, the root routing options will take over
  118. for the routing logic.
  119. */
  120. func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request) {
  121. domainOnly := r.Host
  122. if strings.Contains(r.Host, ":") {
  123. hostPath := strings.Split(r.Host, ":")
  124. domainOnly = hostPath[0]
  125. }
  126. if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules {
  127. //Route to custom domain
  128. if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" {
  129. //Not set. Redirect to first level of domain redirectable
  130. fld, err := h.getTopLevelRedirectableDomain(domainOnly)
  131. if err != nil {
  132. //Redirect to proxy root
  133. h.proxyRequest(w, r, h.Parent.Root)
  134. } else {
  135. log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld)
  136. h.logRequest(r, false, 307, "root-redirect", domainOnly)
  137. http.Redirect(w, r, fld, http.StatusTemporaryRedirect)
  138. }
  139. return
  140. } else if h.isTopLevelRedirectableDomain(domainOnly) {
  141. //This is requesting a top level private domain that should be serving root
  142. h.proxyRequest(w, r, h.Parent.Root)
  143. } else {
  144. //Validate the redirection target URL
  145. parsedURL, err := url.Parse(h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget)
  146. if err != nil {
  147. //Error when parsing target. Send to root
  148. h.proxyRequest(w, r, h.Parent.Root)
  149. return
  150. }
  151. hostname := parsedURL.Hostname()
  152. if domainOnly != hostname {
  153. //Redirect to target
  154. h.logRequest(r, false, 307, "root-redirect", domainOnly)
  155. http.Redirect(w, r, h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget, http.StatusTemporaryRedirect)
  156. return
  157. } else {
  158. //Loopback request due to bad settings (Shd leave it empty)
  159. //Forward it to root proxy
  160. h.proxyRequest(w, r, h.Parent.Root)
  161. }
  162. }
  163. } else {
  164. //Route to root
  165. h.proxyRequest(w, r, h.Parent.Root)
  166. }
  167. }
  168. // Handle access routing logic. Return true if the request is handled or blocked by the access control logic
  169. // if the return value is false, you can continue process the response writer
  170. func (h *ProxyHandler) handleAccessRouting(w http.ResponseWriter, r *http.Request) bool {
  171. //Check if this ip is in blacklist
  172. clientIpAddr := geodb.GetRequesterIP(r)
  173. if h.Parent.Option.GeodbStore.IsBlacklisted(clientIpAddr) {
  174. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  175. w.WriteHeader(http.StatusForbidden)
  176. template, err := os.ReadFile("./web/forbidden.html")
  177. if err != nil {
  178. w.Write([]byte("403 - Forbidden"))
  179. } else {
  180. w.Write(template)
  181. }
  182. h.logRequest(r, false, 403, "blacklist", "")
  183. return true
  184. }
  185. //Check if this ip is in whitelist
  186. if !h.Parent.Option.GeodbStore.IsWhitelisted(clientIpAddr) {
  187. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  188. w.WriteHeader(http.StatusForbidden)
  189. template, err := os.ReadFile("./web/forbidden.html")
  190. if err != nil {
  191. w.Write([]byte("403 - Forbidden"))
  192. } else {
  193. w.Write(template)
  194. }
  195. h.logRequest(r, false, 403, "whitelist", "")
  196. return true
  197. }
  198. return false
  199. }
  200. // Return if the given host is already topped (e.g. example.com or example.co.uk) instead of
  201. // a host with subdomain (e.g. test.example.com)
  202. func (h *ProxyHandler) isTopLevelRedirectableDomain(requestHost string) bool {
  203. parts := strings.Split(requestHost, ".")
  204. if len(parts) > 2 {
  205. //Cases where strange tld is used like .co.uk or .com.hk
  206. _, ok := h.Parent.tldMap[strings.Join(parts[1:], ".")]
  207. if ok {
  208. //Already topped
  209. return true
  210. }
  211. } else {
  212. //Already topped
  213. return true
  214. }
  215. return false
  216. }
  217. // GetTopLevelRedirectableDomain returns the toppest level of domain
  218. // that is redirectable. E.g. a.b.c.example.co.uk will return example.co.uk
  219. func (h *ProxyHandler) getTopLevelRedirectableDomain(unsetSubdomainHost string) (string, error) {
  220. parts := strings.Split(unsetSubdomainHost, ".")
  221. if h.isTopLevelRedirectableDomain(unsetSubdomainHost) {
  222. //Already topped
  223. return "", errors.New("already at top level domain")
  224. }
  225. for i := 0; i < len(parts); i++ {
  226. possibleTld := parts[i:]
  227. _, ok := h.Parent.tldMap[strings.Join(possibleTld, ".")]
  228. if ok {
  229. //This is tld length
  230. tld := strings.Join(parts[i-1:], ".")
  231. return "//" + tld, nil
  232. }
  233. }
  234. return "", errors.New("unsupported top level domain given")
  235. }