pathrule.go 4.5 KB

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