1
0

pathrule.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package pathrule
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "net/http"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. "imuslab.com/zoraxy/mod/utils"
  10. )
  11. /*
  12. Pathrules.go
  13. This script handle advance path settings and rules on particular
  14. paths of the incoming requests
  15. */
  16. type Options struct {
  17. Enabled bool //If the pathrule is enabled.
  18. ConfigFolder string //The folder to store the path blocking config files
  19. }
  20. type BlockingPath struct {
  21. UUID string
  22. MatchingPath string
  23. ExactMatch bool
  24. StatusCode int
  25. CustomHeaders http.Header
  26. CustomHTML []byte
  27. Enabled bool
  28. CaseSenitive bool
  29. }
  30. type Handler struct {
  31. Options *Options
  32. BlockingPaths []*BlockingPath
  33. }
  34. // Create a new path blocker handler
  35. func NewPathRuleHandler(options *Options) *Handler {
  36. //Create folder if not exists
  37. if !utils.FileExists(options.ConfigFolder) {
  38. os.Mkdir(options.ConfigFolder, 0775)
  39. }
  40. //Load the configs from file
  41. //TODO
  42. return &Handler{
  43. Options: options,
  44. BlockingPaths: []*BlockingPath{},
  45. }
  46. }
  47. func (h *Handler) ListBlockingPath() []*BlockingPath {
  48. return h.BlockingPaths
  49. }
  50. // Get the blocker from matching path (path match, ignore tailing slash)
  51. func (h *Handler) GetPathBlockerFromMatchingPath(matchingPath string) *BlockingPath {
  52. for _, blocker := range h.BlockingPaths {
  53. if blocker.MatchingPath == matchingPath {
  54. return blocker
  55. } else if strings.TrimSuffix(blocker.MatchingPath, "/") == strings.TrimSuffix(matchingPath, "/") {
  56. return blocker
  57. }
  58. }
  59. return nil
  60. }
  61. func (h *Handler) GetPathBlockerFromUUID(UUID string) *BlockingPath {
  62. for _, blocker := range h.BlockingPaths {
  63. if blocker.UUID == UUID {
  64. return blocker
  65. }
  66. }
  67. return nil
  68. }
  69. func (h *Handler) AddBlockingPath(pathBlocker *BlockingPath) error {
  70. //Check if the blocker exists
  71. blockerPath := pathBlocker.MatchingPath
  72. targetBlocker := h.GetPathBlockerFromMatchingPath(blockerPath)
  73. if targetBlocker != nil {
  74. //Blocker with the same matching path already exists
  75. return errors.New("path blocker with the same path already exists")
  76. }
  77. h.BlockingPaths = append(h.BlockingPaths, pathBlocker)
  78. //Write the new config to file
  79. return h.SaveBlockerToFile(pathBlocker)
  80. }
  81. func (h *Handler) RemoveBlockingPathByUUID(uuid string) error {
  82. newBlockingList := []*BlockingPath{}
  83. for _, thisBlocker := range h.BlockingPaths {
  84. if thisBlocker.UUID != uuid {
  85. newBlockingList = append(newBlockingList, thisBlocker)
  86. }
  87. }
  88. if len(h.BlockingPaths) == len(newBlockingList) {
  89. //Nothing is removed
  90. return errors.New("given matching path blocker not exists")
  91. }
  92. h.BlockingPaths = newBlockingList
  93. return h.RemoveBlockerFromFile(uuid)
  94. }
  95. func (h *Handler) SaveBlockerToFile(pathBlocker *BlockingPath) error {
  96. saveFilename := filepath.Join(h.Options.ConfigFolder, pathBlocker.UUID)
  97. js, _ := json.MarshalIndent(pathBlocker, "", " ")
  98. return os.WriteFile(saveFilename, js, 0775)
  99. }
  100. func (h *Handler) RemoveBlockerFromFile(uuid string) error {
  101. expectedConfigFile := filepath.Join(h.Options.ConfigFolder, uuid)
  102. if !utils.FileExists(expectedConfigFile) {
  103. return errors.New("config file not found on disk")
  104. }
  105. return os.Remove(expectedConfigFile)
  106. }
  107. // Get all the matching blockers for the given URL path
  108. // return all the path blockers and the max length matching rule
  109. func (h *Handler) GetMatchingBlockers(urlPath string) ([]*BlockingPath, *BlockingPath) {
  110. urlPath = strings.TrimSuffix(urlPath, "/")
  111. matchingBlockers := []*BlockingPath{}
  112. var longestMatchingPrefix *BlockingPath = nil
  113. for _, thisBlocker := range h.BlockingPaths {
  114. if thisBlocker.Enabled == false {
  115. //This blocker is not enabled. Ignore this
  116. continue
  117. }
  118. incomingURLPath := urlPath
  119. matchingPath := strings.TrimSuffix(thisBlocker.MatchingPath, "/")
  120. if !thisBlocker.CaseSenitive {
  121. //This is not case sensitive
  122. incomingURLPath = strings.ToLower(incomingURLPath)
  123. matchingPath = strings.ToLower(matchingPath)
  124. }
  125. if matchingPath == incomingURLPath {
  126. //This blocker have exact url path match
  127. matchingBlockers = append(matchingBlockers, thisBlocker)
  128. if longestMatchingPrefix == nil || len(thisBlocker.MatchingPath) > len(longestMatchingPrefix.MatchingPath) {
  129. longestMatchingPrefix = thisBlocker
  130. }
  131. continue
  132. }
  133. if !thisBlocker.ExactMatch && strings.HasPrefix(incomingURLPath, matchingPath) {
  134. //This blocker have prefix url match
  135. matchingBlockers = append(matchingBlockers, thisBlocker)
  136. if longestMatchingPrefix == nil || len(thisBlocker.MatchingPath) > len(longestMatchingPrefix.MatchingPath) {
  137. longestMatchingPrefix = thisBlocker
  138. }
  139. continue
  140. }
  141. }
  142. return matchingBlockers, longestMatchingPrefix
  143. }