1
0

cache.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. package cachefile
  2. import (
  3. "errors"
  4. "net/netip"
  5. "os"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/sagernet/bbolt"
  10. bboltErrors "github.com/sagernet/bbolt/errors"
  11. "github.com/sagernet/sing-box/adapter"
  12. "github.com/sagernet/sing/common"
  13. E "github.com/sagernet/sing/common/exceptions"
  14. )
  15. var (
  16. bucketSelected = []byte("selected")
  17. bucketExpand = []byte("group_expand")
  18. bucketMode = []byte("clash_mode")
  19. bucketNameList = []string{
  20. string(bucketSelected),
  21. string(bucketExpand),
  22. string(bucketMode),
  23. }
  24. cacheIDDefault = []byte("default")
  25. )
  26. var _ adapter.ClashCacheFile = (*CacheFile)(nil)
  27. type CacheFile struct {
  28. DB *bbolt.DB
  29. cacheID []byte
  30. saveAccess sync.RWMutex
  31. saveDomain map[netip.Addr]string
  32. saveAddress4 map[string]netip.Addr
  33. saveAddress6 map[string]netip.Addr
  34. saveMetadataTimer *time.Timer
  35. }
  36. func Open(path string, cacheID string) (*CacheFile, error) {
  37. const fileMode = 0o666
  38. options := bbolt.Options{Timeout: time.Second}
  39. var (
  40. db *bbolt.DB
  41. err error
  42. )
  43. for i := 0; i < 10; i++ {
  44. db, err = bbolt.Open(path, fileMode, &options)
  45. if err == nil {
  46. break
  47. }
  48. if errors.Is(err, bboltErrors.ErrTimeout) {
  49. continue
  50. }
  51. if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
  52. rmErr := os.Remove(path)
  53. if rmErr != nil {
  54. return nil, err
  55. }
  56. }
  57. time.Sleep(100 * time.Millisecond)
  58. }
  59. if err != nil {
  60. return nil, err
  61. }
  62. var cacheIDBytes []byte
  63. if cacheID != "" {
  64. cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
  65. }
  66. err = db.Batch(func(tx *bbolt.Tx) error {
  67. return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
  68. if name[0] == 0 {
  69. return b.ForEachBucket(func(k []byte) error {
  70. bucketName := string(k)
  71. if !(common.Contains(bucketNameList, bucketName)) {
  72. _ = b.DeleteBucket(name)
  73. }
  74. return nil
  75. })
  76. } else {
  77. bucketName := string(name)
  78. if !(common.Contains(bucketNameList, bucketName) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
  79. _ = tx.DeleteBucket(name)
  80. }
  81. }
  82. return nil
  83. })
  84. })
  85. if err != nil {
  86. return nil, err
  87. }
  88. return &CacheFile{
  89. DB: db,
  90. cacheID: cacheIDBytes,
  91. saveDomain: make(map[netip.Addr]string),
  92. saveAddress4: make(map[string]netip.Addr),
  93. saveAddress6: make(map[string]netip.Addr),
  94. }, nil
  95. }
  96. func (c *CacheFile) LoadMode() string {
  97. var mode string
  98. c.DB.View(func(t *bbolt.Tx) error {
  99. bucket := t.Bucket(bucketMode)
  100. if bucket == nil {
  101. return nil
  102. }
  103. var modeBytes []byte
  104. if len(c.cacheID) > 0 {
  105. modeBytes = bucket.Get(c.cacheID)
  106. } else {
  107. modeBytes = bucket.Get(cacheIDDefault)
  108. }
  109. mode = string(modeBytes)
  110. return nil
  111. })
  112. return mode
  113. }
  114. func (c *CacheFile) StoreMode(mode string) error {
  115. return c.DB.Batch(func(t *bbolt.Tx) error {
  116. bucket, err := t.CreateBucketIfNotExists(bucketMode)
  117. if err != nil {
  118. return err
  119. }
  120. if len(c.cacheID) > 0 {
  121. return bucket.Put(c.cacheID, []byte(mode))
  122. } else {
  123. return bucket.Put(cacheIDDefault, []byte(mode))
  124. }
  125. })
  126. }
  127. func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket {
  128. if c.cacheID == nil {
  129. return t.Bucket(key)
  130. }
  131. bucket := t.Bucket(c.cacheID)
  132. if bucket == nil {
  133. return nil
  134. }
  135. return bucket.Bucket(key)
  136. }
  137. func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error) {
  138. if c.cacheID == nil {
  139. return t.CreateBucketIfNotExists(key)
  140. }
  141. bucket, err := t.CreateBucketIfNotExists(c.cacheID)
  142. if bucket == nil {
  143. return nil, err
  144. }
  145. return bucket.CreateBucketIfNotExists(key)
  146. }
  147. func (c *CacheFile) LoadSelected(group string) string {
  148. var selected string
  149. c.DB.View(func(t *bbolt.Tx) error {
  150. bucket := c.bucket(t, bucketSelected)
  151. if bucket == nil {
  152. return nil
  153. }
  154. selectedBytes := bucket.Get([]byte(group))
  155. if len(selectedBytes) > 0 {
  156. selected = string(selectedBytes)
  157. }
  158. return nil
  159. })
  160. return selected
  161. }
  162. func (c *CacheFile) StoreSelected(group, selected string) error {
  163. return c.DB.Batch(func(t *bbolt.Tx) error {
  164. bucket, err := c.createBucket(t, bucketSelected)
  165. if err != nil {
  166. return err
  167. }
  168. return bucket.Put([]byte(group), []byte(selected))
  169. })
  170. }
  171. func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
  172. c.DB.View(func(t *bbolt.Tx) error {
  173. bucket := c.bucket(t, bucketExpand)
  174. if bucket == nil {
  175. return nil
  176. }
  177. expandBytes := bucket.Get([]byte(group))
  178. if len(expandBytes) == 1 {
  179. isExpand = expandBytes[0] == 1
  180. loaded = true
  181. }
  182. return nil
  183. })
  184. return
  185. }
  186. func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
  187. return c.DB.Batch(func(t *bbolt.Tx) error {
  188. bucket, err := c.createBucket(t, bucketExpand)
  189. if err != nil {
  190. return err
  191. }
  192. if isExpand {
  193. return bucket.Put([]byte(group), []byte{1})
  194. } else {
  195. return bucket.Put([]byte(group), []byte{0})
  196. }
  197. })
  198. }
  199. func (c *CacheFile) Close() error {
  200. return c.DB.Close()
  201. }