Server.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package dynamicproxy
  2. import (
  3. _ "embed"
  4. "errors"
  5. "log"
  6. "net/http"
  7. "os"
  8. "strings"
  9. "imuslab.com/zoraxy/mod/geodb"
  10. )
  11. /*
  12. Server.go
  13. Main server for dynamic proxy core
  14. Routing Handler Priority (High to Low)
  15. - Blacklist
  16. - Whitelist
  17. - Redirectable
  18. - Subdomain Routing
  19. - Vitrual Directory Routing
  20. */
  21. var (
  22. //go:embed tld.json
  23. rawTldMap []byte
  24. )
  25. func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  26. /*
  27. Special Routing Rules, bypass most of the limitations
  28. */
  29. //Check if there are external routing rule matches.
  30. //If yes, route them via external rr
  31. matchedRoutingRule := h.Parent.GetMatchingRoutingRule(r)
  32. if matchedRoutingRule != nil {
  33. //Matching routing rule found. Let the sub-router handle it
  34. if matchedRoutingRule.UseSystemAccessControl {
  35. //This matching rule request system access control.
  36. //check access logic
  37. respWritten := h.handleAccessRouting(w, r)
  38. if respWritten {
  39. return
  40. }
  41. }
  42. matchedRoutingRule.Route(w, r)
  43. return
  44. }
  45. /*
  46. General Access Check
  47. */
  48. respWritten := h.handleAccessRouting(w, r)
  49. if respWritten {
  50. return
  51. }
  52. /*
  53. Redirection Routing
  54. */
  55. //Check if this is a redirection url
  56. if h.Parent.Option.RedirectRuleTable.IsRedirectable(r) {
  57. statusCode := h.Parent.Option.RedirectRuleTable.HandleRedirect(w, r)
  58. h.logRequest(r, statusCode != 500, statusCode, "redirect", "")
  59. return
  60. }
  61. //Extract request host to see if it is virtual directory or subdomain
  62. domainOnly := r.Host
  63. if strings.Contains(r.Host, ":") {
  64. hostPath := strings.Split(r.Host, ":")
  65. domainOnly = hostPath[0]
  66. }
  67. /*
  68. Subdomain Routing
  69. */
  70. if strings.Contains(r.Host, ".") {
  71. //This might be a subdomain. See if there are any subdomain proxy router for this
  72. sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
  73. if sep != nil {
  74. if sep.RequireBasicAuth {
  75. err := h.handleBasicAuthRouting(w, r, sep)
  76. if err != nil {
  77. return
  78. }
  79. }
  80. h.subdomainRequest(w, r, sep)
  81. return
  82. }
  83. }
  84. /*
  85. Virtual Directory Routing
  86. */
  87. //Clean up the request URI
  88. proxyingPath := strings.TrimSpace(r.RequestURI)
  89. targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath)
  90. if targetProxyEndpoint != nil {
  91. if targetProxyEndpoint.RequireBasicAuth {
  92. err := h.handleBasicAuthRouting(w, r, targetProxyEndpoint)
  93. if err != nil {
  94. return
  95. }
  96. }
  97. h.proxyRequest(w, r, targetProxyEndpoint)
  98. } else if !strings.HasSuffix(proxyingPath, "/") {
  99. potentialProxtEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath + "/")
  100. if potentialProxtEndpoint != nil {
  101. //Missing tailing slash. Redirect to target proxy endpoint
  102. http.Redirect(w, r, r.RequestURI+"/", http.StatusTemporaryRedirect)
  103. } else {
  104. //Passthrough the request to root
  105. h.proxyRequest(w, r, h.Parent.Root)
  106. }
  107. } else {
  108. //No routing rules found.
  109. if h.Parent.RootRoutingOptions.EnableRedirectForUnsetRules {
  110. //Route to custom domain
  111. if h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget == "" {
  112. //Not set. Redirect to first level of domain redirectable
  113. fld, err := h.getTopLevelRedirectableDomain(domainOnly)
  114. if err != nil {
  115. //Redirect to proxy root
  116. log.Println("[Router] Unable to resolve top level redirectable domain: " + err.Error())
  117. h.proxyRequest(w, r, h.Parent.Root)
  118. } else {
  119. log.Println("[Router] Redirecting request from " + domainOnly + " to " + fld)
  120. h.logRequest(r, false, 307, "root-redirect", domainOnly)
  121. http.Redirect(w, r, fld, http.StatusTemporaryRedirect)
  122. }
  123. return
  124. } else {
  125. //Redirect to target
  126. h.logRequest(r, false, 307, "root-redirect", domainOnly)
  127. http.Redirect(w, r, h.Parent.RootRoutingOptions.UnsetRuleRedirectTarget, http.StatusTemporaryRedirect)
  128. return
  129. }
  130. } else {
  131. //Route to root
  132. h.proxyRequest(w, r, h.Parent.Root)
  133. }
  134. }
  135. }
  136. // Handle access routing logic. Return true if the request is handled or blocked by the access control logic
  137. // if the return value is false, you can continue process the response writer
  138. func (h *ProxyHandler) handleAccessRouting(w http.ResponseWriter, r *http.Request) bool {
  139. //Check if this ip is in blacklist
  140. clientIpAddr := geodb.GetRequesterIP(r)
  141. if h.Parent.Option.GeodbStore.IsBlacklisted(clientIpAddr) {
  142. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  143. w.WriteHeader(http.StatusForbidden)
  144. template, err := os.ReadFile("./web/forbidden.html")
  145. if err != nil {
  146. w.Write([]byte("403 - Forbidden"))
  147. } else {
  148. w.Write(template)
  149. }
  150. h.logRequest(r, false, 403, "blacklist", "")
  151. return true
  152. }
  153. //Check if this ip is in whitelist
  154. if !h.Parent.Option.GeodbStore.IsWhitelisted(clientIpAddr) {
  155. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  156. w.WriteHeader(http.StatusForbidden)
  157. template, err := os.ReadFile("./web/forbidden.html")
  158. if err != nil {
  159. w.Write([]byte("403 - Forbidden"))
  160. } else {
  161. w.Write(template)
  162. }
  163. h.logRequest(r, false, 403, "whitelist", "")
  164. return true
  165. }
  166. return false
  167. }
  168. // GetTopLevelRedirectableDomain returns the toppest level of domain
  169. // that is redirectable. E.g. a.b.c.example.co.uk will return example.co.uk
  170. func (h *ProxyHandler) getTopLevelRedirectableDomain(unsetSubdomainHost string) (string, error) {
  171. parts := strings.Split(unsetSubdomainHost, ".")
  172. if len(parts) > 1 {
  173. newDomain := strings.Join(parts[1:], ".")
  174. return "//" + newDomain, nil
  175. }
  176. return "", errors.New("unsupported top level domain given")
  177. }