github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/route/router_geo_resources.go (about) 1 package route 2 3 import ( 4 "context" 5 "io" 6 "net" 7 "net/http" 8 "os" 9 "path/filepath" 10 "time" 11 12 "github.com/inazumav/sing-box/adapter" 13 "github.com/inazumav/sing-box/common/geoip" 14 "github.com/inazumav/sing-box/common/geosite" 15 C "github.com/inazumav/sing-box/constant" 16 "github.com/inazumav/sing-box/option" 17 "github.com/sagernet/sing/common" 18 E "github.com/sagernet/sing/common/exceptions" 19 M "github.com/sagernet/sing/common/metadata" 20 "github.com/sagernet/sing/common/rw" 21 "github.com/sagernet/sing/service/filemanager" 22 ) 23 24 func (r *Router) GeoIPReader() *geoip.Reader { 25 return r.geoIPReader 26 } 27 28 func (r *Router) LoadGeosite(code string) (adapter.Rule, error) { 29 rule, cached := r.geositeCache[code] 30 if cached { 31 return rule, nil 32 } 33 items, err := r.geositeReader.Read(code) 34 if err != nil { 35 return nil, err 36 } 37 rule, err = NewDefaultRule(r, nil, geosite.Compile(items)) 38 if err != nil { 39 return nil, err 40 } 41 r.geositeCache[code] = rule 42 return rule, nil 43 } 44 45 func (r *Router) prepareGeoIPDatabase() error { 46 var geoPath string 47 if r.geoIPOptions.Path != "" { 48 geoPath = r.geoIPOptions.Path 49 } else { 50 geoPath = "geoip.db" 51 if foundPath, loaded := C.FindPath(geoPath); loaded { 52 geoPath = foundPath 53 } 54 } 55 if !rw.FileExists(geoPath) { 56 geoPath = filemanager.BasePath(r.ctx, geoPath) 57 } 58 if stat, err := os.Stat(geoPath); err == nil { 59 if stat.IsDir() { 60 return E.New("geoip path is a directory: ", geoPath) 61 } 62 if stat.Size() == 0 { 63 os.Remove(geoPath) 64 } 65 } 66 if !rw.FileExists(geoPath) { 67 r.logger.Warn("geoip database not exists: ", geoPath) 68 var err error 69 for attempts := 0; attempts < 3; attempts++ { 70 err = r.downloadGeoIPDatabase(geoPath) 71 if err == nil { 72 break 73 } 74 r.logger.Error("download geoip database: ", err) 75 os.Remove(geoPath) 76 // time.Sleep(10 * time.Second) 77 } 78 if err != nil { 79 return err 80 } 81 } 82 geoReader, codes, err := geoip.Open(geoPath) 83 if err != nil { 84 return E.Cause(err, "open geoip database") 85 } 86 r.logger.Info("loaded geoip database: ", len(codes), " codes") 87 r.geoIPReader = geoReader 88 return nil 89 } 90 91 func (r *Router) prepareGeositeDatabase() error { 92 var geoPath string 93 if r.geositeOptions.Path != "" { 94 geoPath = r.geositeOptions.Path 95 } else { 96 geoPath = "geosite.db" 97 if foundPath, loaded := C.FindPath(geoPath); loaded { 98 geoPath = foundPath 99 } 100 } 101 if !rw.FileExists(geoPath) { 102 geoPath = filemanager.BasePath(r.ctx, geoPath) 103 } 104 if stat, err := os.Stat(geoPath); err == nil { 105 if stat.IsDir() { 106 return E.New("geoip path is a directory: ", geoPath) 107 } 108 if stat.Size() == 0 { 109 os.Remove(geoPath) 110 } 111 } 112 if !rw.FileExists(geoPath) { 113 r.logger.Warn("geosite database not exists: ", geoPath) 114 var err error 115 for attempts := 0; attempts < 3; attempts++ { 116 err = r.downloadGeositeDatabase(geoPath) 117 if err == nil { 118 break 119 } 120 r.logger.Error("download geosite database: ", err) 121 os.Remove(geoPath) 122 } 123 if err != nil { 124 return err 125 } 126 } 127 geoReader, codes, err := geosite.Open(geoPath) 128 if err == nil { 129 r.logger.Info("loaded geosite database: ", len(codes), " codes") 130 r.geositeReader = geoReader 131 } else { 132 return E.Cause(err, "open geosite database") 133 } 134 return nil 135 } 136 137 func (r *Router) downloadGeoIPDatabase(savePath string) error { 138 var downloadURL string 139 if r.geoIPOptions.DownloadURL != "" { 140 downloadURL = r.geoIPOptions.DownloadURL 141 } else { 142 downloadURL = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db" 143 } 144 r.logger.Info("downloading geoip database") 145 var detour adapter.Outbound 146 if r.geoIPOptions.DownloadDetour != "" { 147 outbound, loaded := r.Outbound(r.geoIPOptions.DownloadDetour) 148 if !loaded { 149 return E.New("detour outbound not found: ", r.geoIPOptions.DownloadDetour) 150 } 151 detour = outbound 152 } else { 153 detour = r.defaultOutboundForConnection 154 } 155 156 if parentDir := filepath.Dir(savePath); parentDir != "" { 157 filemanager.MkdirAll(r.ctx, parentDir, 0o755) 158 } 159 160 saveFile, err := filemanager.Create(r.ctx, savePath) 161 if err != nil { 162 return E.Cause(err, "open output file: ", downloadURL) 163 } 164 defer saveFile.Close() 165 166 httpClient := &http.Client{ 167 Transport: &http.Transport{ 168 ForceAttemptHTTP2: true, 169 TLSHandshakeTimeout: 5 * time.Second, 170 DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 171 return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) 172 }, 173 }, 174 } 175 defer httpClient.CloseIdleConnections() 176 response, err := httpClient.Get(downloadURL) 177 if err != nil { 178 return err 179 } 180 defer response.Body.Close() 181 _, err = io.Copy(saveFile, response.Body) 182 return err 183 } 184 185 func (r *Router) downloadGeositeDatabase(savePath string) error { 186 var downloadURL string 187 if r.geositeOptions.DownloadURL != "" { 188 downloadURL = r.geositeOptions.DownloadURL 189 } else { 190 downloadURL = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db" 191 } 192 r.logger.Info("downloading geosite database") 193 var detour adapter.Outbound 194 if r.geositeOptions.DownloadDetour != "" { 195 outbound, loaded := r.Outbound(r.geositeOptions.DownloadDetour) 196 if !loaded { 197 return E.New("detour outbound not found: ", r.geositeOptions.DownloadDetour) 198 } 199 detour = outbound 200 } else { 201 detour = r.defaultOutboundForConnection 202 } 203 204 if parentDir := filepath.Dir(savePath); parentDir != "" { 205 filemanager.MkdirAll(r.ctx, parentDir, 0o755) 206 } 207 208 saveFile, err := filemanager.Create(r.ctx, savePath) 209 if err != nil { 210 return E.Cause(err, "open output file: ", downloadURL) 211 } 212 defer saveFile.Close() 213 214 httpClient := &http.Client{ 215 Transport: &http.Transport{ 216 ForceAttemptHTTP2: true, 217 TLSHandshakeTimeout: 5 * time.Second, 218 DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { 219 return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) 220 }, 221 }, 222 } 223 defer httpClient.CloseIdleConnections() 224 response, err := httpClient.Get(downloadURL) 225 if err != nil { 226 return err 227 } 228 defer response.Body.Close() 229 _, err = io.Copy(saveFile, response.Body) 230 return err 231 } 232 233 func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool { 234 for _, rule := range rules { 235 switch rule.Type { 236 case C.RuleTypeDefault: 237 if cond(rule.DefaultOptions) { 238 return true 239 } 240 case C.RuleTypeLogical: 241 for _, subRule := range rule.LogicalOptions.Rules { 242 if cond(subRule) { 243 return true 244 } 245 } 246 } 247 } 248 return false 249 } 250 251 func hasDNSRule(rules []option.DNSRule, cond func(rule option.DefaultDNSRule) bool) bool { 252 for _, rule := range rules { 253 switch rule.Type { 254 case C.RuleTypeDefault: 255 if cond(rule.DefaultOptions) { 256 return true 257 } 258 case C.RuleTypeLogical: 259 for _, subRule := range rule.LogicalOptions.Rules { 260 if cond(subRule) { 261 return true 262 } 263 } 264 } 265 } 266 return false 267 } 268 269 func isGeoIPRule(rule option.DefaultRule) bool { 270 return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode) 271 } 272 273 func isGeoIPDNSRule(rule option.DefaultDNSRule) bool { 274 return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) 275 } 276 277 func isGeositeRule(rule option.DefaultRule) bool { 278 return len(rule.Geosite) > 0 279 } 280 281 func isGeositeDNSRule(rule option.DefaultDNSRule) bool { 282 return len(rule.Geosite) > 0 283 } 284 285 func isProcessRule(rule option.DefaultRule) bool { 286 return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0 287 } 288 289 func isProcessDNSRule(rule option.DefaultDNSRule) bool { 290 return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0 291 } 292 293 func notPrivateNode(code string) bool { 294 return code != "private" 295 }