router_geo_resources.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. package route
  2. import (
  3. "context"
  4. "io"
  5. "net"
  6. "net/http"
  7. "os"
  8. "path/filepath"
  9. "github.com/sagernet/sing-box/adapter"
  10. "github.com/sagernet/sing-box/common/geoip"
  11. "github.com/sagernet/sing-box/common/geosite"
  12. C "github.com/sagernet/sing-box/constant"
  13. E "github.com/sagernet/sing/common/exceptions"
  14. M "github.com/sagernet/sing/common/metadata"
  15. "github.com/sagernet/sing/common/rw"
  16. "github.com/sagernet/sing/service/filemanager"
  17. )
  18. func (r *Router) GeoIPReader() *geoip.Reader {
  19. return r.geoIPReader
  20. }
  21. func (r *Router) LoadGeosite(code string) (adapter.Rule, error) {
  22. rule, cached := r.geositeCache[code]
  23. if cached {
  24. return rule, nil
  25. }
  26. items, err := r.geositeReader.Read(code)
  27. if err != nil {
  28. return nil, err
  29. }
  30. rule, err = NewDefaultRule(r, nil, geosite.Compile(items))
  31. if err != nil {
  32. return nil, err
  33. }
  34. r.geositeCache[code] = rule
  35. return rule, nil
  36. }
  37. func (r *Router) prepareGeoIPDatabase() error {
  38. var geoPath string
  39. if r.geoIPOptions.Path != "" {
  40. geoPath = r.geoIPOptions.Path
  41. } else {
  42. geoPath = "geoip.db"
  43. if foundPath, loaded := C.FindPath(geoPath); loaded {
  44. geoPath = foundPath
  45. }
  46. }
  47. if !rw.IsFile(geoPath) {
  48. geoPath = filemanager.BasePath(r.ctx, geoPath)
  49. }
  50. if stat, err := os.Stat(geoPath); err == nil {
  51. if stat.IsDir() {
  52. return E.New("geoip path is a directory: ", geoPath)
  53. }
  54. if stat.Size() == 0 {
  55. os.Remove(geoPath)
  56. }
  57. }
  58. if !rw.IsFile(geoPath) {
  59. r.logger.Warn("geoip database not exists: ", geoPath)
  60. var err error
  61. for attempts := 0; attempts < 3; attempts++ {
  62. err = r.downloadGeoIPDatabase(geoPath)
  63. if err == nil {
  64. break
  65. }
  66. r.logger.Error("download geoip database: ", err)
  67. os.Remove(geoPath)
  68. // time.Sleep(10 * time.Second)
  69. }
  70. if err != nil {
  71. return err
  72. }
  73. }
  74. geoReader, codes, err := geoip.Open(geoPath)
  75. if err != nil {
  76. return E.Cause(err, "open geoip database")
  77. }
  78. r.logger.Info("loaded geoip database: ", len(codes), " codes")
  79. r.geoIPReader = geoReader
  80. return nil
  81. }
  82. func (r *Router) prepareGeositeDatabase() error {
  83. var geoPath string
  84. if r.geositeOptions.Path != "" {
  85. geoPath = r.geositeOptions.Path
  86. } else {
  87. geoPath = "geosite.db"
  88. if foundPath, loaded := C.FindPath(geoPath); loaded {
  89. geoPath = foundPath
  90. }
  91. }
  92. if !rw.IsFile(geoPath) {
  93. geoPath = filemanager.BasePath(r.ctx, geoPath)
  94. }
  95. if stat, err := os.Stat(geoPath); err == nil {
  96. if stat.IsDir() {
  97. return E.New("geoip path is a directory: ", geoPath)
  98. }
  99. if stat.Size() == 0 {
  100. os.Remove(geoPath)
  101. }
  102. }
  103. if !rw.IsFile(geoPath) {
  104. r.logger.Warn("geosite database not exists: ", geoPath)
  105. var err error
  106. for attempts := 0; attempts < 3; attempts++ {
  107. err = r.downloadGeositeDatabase(geoPath)
  108. if err == nil {
  109. break
  110. }
  111. r.logger.Error("download geosite database: ", err)
  112. os.Remove(geoPath)
  113. }
  114. if err != nil {
  115. return err
  116. }
  117. }
  118. geoReader, codes, err := geosite.Open(geoPath)
  119. if err == nil {
  120. r.logger.Info("loaded geosite database: ", len(codes), " codes")
  121. r.geositeReader = geoReader
  122. } else {
  123. return E.Cause(err, "open geosite database")
  124. }
  125. return nil
  126. }
  127. func (r *Router) downloadGeoIPDatabase(savePath string) error {
  128. var downloadURL string
  129. if r.geoIPOptions.DownloadURL != "" {
  130. downloadURL = r.geoIPOptions.DownloadURL
  131. } else {
  132. downloadURL = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db"
  133. }
  134. r.logger.Info("downloading geoip database")
  135. var detour adapter.Outbound
  136. if r.geoIPOptions.DownloadDetour != "" {
  137. outbound, loaded := r.Outbound(r.geoIPOptions.DownloadDetour)
  138. if !loaded {
  139. return E.New("detour outbound not found: ", r.geoIPOptions.DownloadDetour)
  140. }
  141. detour = outbound
  142. } else {
  143. detour = r.defaultOutboundForConnection
  144. }
  145. if parentDir := filepath.Dir(savePath); parentDir != "" {
  146. filemanager.MkdirAll(r.ctx, parentDir, 0o755)
  147. }
  148. httpClient := &http.Client{
  149. Transport: &http.Transport{
  150. ForceAttemptHTTP2: true,
  151. TLSHandshakeTimeout: C.TCPTimeout,
  152. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  153. return detour.DialContext(ctx, network, M.ParseSocksaddr(addr))
  154. },
  155. },
  156. }
  157. defer httpClient.CloseIdleConnections()
  158. request, err := http.NewRequest("GET", downloadURL, nil)
  159. if err != nil {
  160. return err
  161. }
  162. response, err := httpClient.Do(request.WithContext(r.ctx))
  163. if err != nil {
  164. return err
  165. }
  166. defer response.Body.Close()
  167. saveFile, err := filemanager.Create(r.ctx, savePath)
  168. if err != nil {
  169. return E.Cause(err, "open output file: ", downloadURL)
  170. }
  171. _, err = io.Copy(saveFile, response.Body)
  172. saveFile.Close()
  173. if err != nil {
  174. filemanager.Remove(r.ctx, savePath)
  175. }
  176. return err
  177. }
  178. func (r *Router) downloadGeositeDatabase(savePath string) error {
  179. var downloadURL string
  180. if r.geositeOptions.DownloadURL != "" {
  181. downloadURL = r.geositeOptions.DownloadURL
  182. } else {
  183. downloadURL = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db"
  184. }
  185. r.logger.Info("downloading geosite database")
  186. var detour adapter.Outbound
  187. if r.geositeOptions.DownloadDetour != "" {
  188. outbound, loaded := r.Outbound(r.geositeOptions.DownloadDetour)
  189. if !loaded {
  190. return E.New("detour outbound not found: ", r.geositeOptions.DownloadDetour)
  191. }
  192. detour = outbound
  193. } else {
  194. detour = r.defaultOutboundForConnection
  195. }
  196. if parentDir := filepath.Dir(savePath); parentDir != "" {
  197. filemanager.MkdirAll(r.ctx, parentDir, 0o755)
  198. }
  199. httpClient := &http.Client{
  200. Transport: &http.Transport{
  201. ForceAttemptHTTP2: true,
  202. TLSHandshakeTimeout: C.TCPTimeout,
  203. DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
  204. return detour.DialContext(ctx, network, M.ParseSocksaddr(addr))
  205. },
  206. },
  207. }
  208. defer httpClient.CloseIdleConnections()
  209. request, err := http.NewRequest("GET", downloadURL, nil)
  210. if err != nil {
  211. return err
  212. }
  213. response, err := httpClient.Do(request.WithContext(r.ctx))
  214. if err != nil {
  215. return err
  216. }
  217. defer response.Body.Close()
  218. saveFile, err := filemanager.Create(r.ctx, savePath)
  219. if err != nil {
  220. return E.Cause(err, "open output file: ", downloadURL)
  221. }
  222. _, err = io.Copy(saveFile, response.Body)
  223. saveFile.Close()
  224. if err != nil {
  225. filemanager.Remove(r.ctx, savePath)
  226. }
  227. return err
  228. }