| skipped 1 lines |
2 | 2 | | |
3 | 3 | | import ( |
4 | 4 | | "context" |
5 | | - | "io" |
6 | 5 | | "net" |
7 | | - | "net/http" |
8 | 6 | | "net/netip" |
9 | 7 | | "net/url" |
10 | 8 | | "os" |
11 | 9 | | "os/user" |
12 | | - | "path/filepath" |
13 | 10 | | "strings" |
14 | 11 | | "time" |
15 | 12 | | |
| skipped 21 lines |
37 | 34 | | F "github.com/sagernet/sing/common/format" |
38 | 35 | | M "github.com/sagernet/sing/common/metadata" |
39 | 36 | | N "github.com/sagernet/sing/common/network" |
40 | | - | "github.com/sagernet/sing/common/rw" |
41 | 37 | | "github.com/sagernet/sing/common/uot" |
42 | 38 | | ) |
43 | 39 | | |
| skipped 28 lines |
72 | 68 | | outbounds []adapter.Outbound |
73 | 69 | | outboundByTag map[string]adapter.Outbound |
74 | 70 | | rules []adapter.Rule |
| 71 | + | ipRules []adapter.IPRule |
75 | 72 | | defaultDetour string |
76 | 73 | | defaultOutboundForConnection adapter.Outbound |
77 | 74 | | defaultOutboundForPacketConnection adapter.Outbound |
| skipped 50 lines |
128 | 125 | | dnsLogger: logFactory.NewLogger("dns"), |
129 | 126 | | outboundByTag: make(map[string]adapter.Outbound), |
130 | 127 | | rules: make([]adapter.Rule, 0, len(options.Rules)), |
| 128 | + | ipRules: make([]adapter.IPRule, 0, len(options.IPRules)), |
131 | 129 | | dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)), |
132 | 130 | | needGeoIPDatabase: hasRule(options.Rules, isGeoIPRule) || hasDNSRule(dnsOptions.Rules, isGeoIPDNSRule), |
133 | 131 | | needGeositeDatabase: hasRule(options.Rules, isGeositeRule) || hasDNSRule(dnsOptions.Rules, isGeositeDNSRule), |
| skipped 15 lines |
149 | 147 | | } |
150 | 148 | | router.rules = append(router.rules, routeRule) |
151 | 149 | | } |
| 150 | + | for i, ipRuleOptions := range options.IPRules { |
| 151 | + | ipRule, err := NewIPRule(router, router.logger, ipRuleOptions) |
| 152 | + | if err != nil { |
| 153 | + | return nil, E.Cause(err, "parse ip rule[", i, "]") |
| 154 | + | } |
| 155 | + | router.ipRules = append(router.ipRules, ipRule) |
| 156 | + | } |
152 | 157 | | for i, dnsRuleOptions := range dnsOptions.Rules { |
153 | 158 | | dnsRule, err := NewDNSRule(router, router.logger, dnsRuleOptions) |
154 | 159 | | if err != nil { |
| skipped 1 lines |
156 | 161 | | } |
157 | 162 | | router.dnsRules = append(router.dnsRules, dnsRule) |
158 | 163 | | } |
| 164 | + | |
159 | 165 | | transports := make([]dns.Transport, len(dnsOptions.Servers)) |
160 | 166 | | dummyTransportMap := make(map[string]dns.Transport) |
161 | 167 | | transportMap := make(map[string]dns.Transport) |
| skipped 354 lines |
516 | 522 | | ) |
517 | 523 | | } |
518 | 524 | | |
519 | | - | func (r *Router) GeoIPReader() *geoip.Reader { |
520 | | - | return r.geoIPReader |
521 | | - | } |
522 | | - | |
523 | | - | func (r *Router) LoadGeosite(code string) (adapter.Rule, error) { |
524 | | - | rule, cached := r.geositeCache[code] |
525 | | - | if cached { |
526 | | - | return rule, nil |
527 | | - | } |
528 | | - | items, err := r.geositeReader.Read(code) |
529 | | - | if err != nil { |
530 | | - | return nil, err |
531 | | - | } |
532 | | - | rule, err = NewDefaultRule(r, nil, geosite.Compile(items)) |
533 | | - | if err != nil { |
534 | | - | return nil, err |
535 | | - | } |
536 | | - | r.geositeCache[code] = rule |
537 | | - | return rule, nil |
538 | | - | } |
539 | | - | |
540 | 525 | | func (r *Router) Outbound(tag string) (adapter.Outbound, bool) { |
541 | 526 | | outbound, loaded := r.outboundByTag[tag] |
542 | 527 | | return outbound, loaded |
| skipped 268 lines |
811 | 796 | | return r.rules |
812 | 797 | | } |
813 | 798 | | |
| 799 | + | func (r *Router) IPRules() []adapter.IPRule { |
| 800 | + | return r.ipRules |
| 801 | + | } |
| 802 | + | |
814 | 803 | | func (r *Router) NetworkMonitor() tun.NetworkUpdateMonitor { |
815 | 804 | | return r.networkMonitor |
816 | 805 | | } |
| skipped 27 lines |
844 | 833 | | |
845 | 834 | | func (r *Router) SetV2RayServer(server adapter.V2RayServer) { |
846 | 835 | | r.v2rayServer = server |
847 | | - | } |
848 | | - | |
849 | | - | func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool { |
850 | | - | for _, rule := range rules { |
851 | | - | switch rule.Type { |
852 | | - | case C.RuleTypeDefault: |
853 | | - | if cond(rule.DefaultOptions) { |
854 | | - | return true |
855 | | - | } |
856 | | - | case C.RuleTypeLogical: |
857 | | - | for _, subRule := range rule.LogicalOptions.Rules { |
858 | | - | if cond(subRule) { |
859 | | - | return true |
860 | | - | } |
861 | | - | } |
862 | | - | } |
863 | | - | } |
864 | | - | return false |
865 | | - | } |
866 | | - | |
867 | | - | func hasDNSRule(rules []option.DNSRule, cond func(rule option.DefaultDNSRule) bool) bool { |
868 | | - | for _, rule := range rules { |
869 | | - | switch rule.Type { |
870 | | - | case C.RuleTypeDefault: |
871 | | - | if cond(rule.DefaultOptions) { |
872 | | - | return true |
873 | | - | } |
874 | | - | case C.RuleTypeLogical: |
875 | | - | for _, subRule := range rule.LogicalOptions.Rules { |
876 | | - | if cond(subRule) { |
877 | | - | return true |
878 | | - | } |
879 | | - | } |
880 | | - | } |
881 | | - | } |
882 | | - | return false |
883 | | - | } |
884 | | - | |
885 | | - | func isGeoIPRule(rule option.DefaultRule) bool { |
886 | | - | return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) || len(rule.GeoIP) > 0 && common.Any(rule.GeoIP, notPrivateNode) |
887 | | - | } |
888 | | - | |
889 | | - | func isGeoIPDNSRule(rule option.DefaultDNSRule) bool { |
890 | | - | return len(rule.SourceGeoIP) > 0 && common.Any(rule.SourceGeoIP, notPrivateNode) |
891 | | - | } |
892 | | - | |
893 | | - | func isGeositeRule(rule option.DefaultRule) bool { |
894 | | - | return len(rule.Geosite) > 0 |
895 | | - | } |
896 | | - | |
897 | | - | func isGeositeDNSRule(rule option.DefaultDNSRule) bool { |
898 | | - | return len(rule.Geosite) > 0 |
899 | | - | } |
900 | | - | |
901 | | - | func isProcessRule(rule option.DefaultRule) bool { |
902 | | - | return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0 |
903 | | - | } |
904 | | - | |
905 | | - | func isProcessDNSRule(rule option.DefaultDNSRule) bool { |
906 | | - | return len(rule.ProcessName) > 0 || len(rule.ProcessPath) > 0 || len(rule.PackageName) > 0 || len(rule.User) > 0 || len(rule.UserID) > 0 |
907 | | - | } |
908 | | - | |
909 | | - | func notPrivateNode(code string) bool { |
910 | | - | return code != "private" |
911 | | - | } |
912 | | - | |
913 | | - | func (r *Router) prepareGeoIPDatabase() error { |
914 | | - | var geoPath string |
915 | | - | if r.geoIPOptions.Path != "" { |
916 | | - | geoPath = r.geoIPOptions.Path |
917 | | - | } else { |
918 | | - | geoPath = "geoip.db" |
919 | | - | if foundPath, loaded := C.FindPath(geoPath); loaded { |
920 | | - | geoPath = foundPath |
921 | | - | } |
922 | | - | } |
923 | | - | geoPath = C.BasePath(geoPath) |
924 | | - | if !rw.FileExists(geoPath) { |
925 | | - | r.logger.Warn("geoip database not exists: ", geoPath) |
926 | | - | var err error |
927 | | - | for attempts := 0; attempts < 3; attempts++ { |
928 | | - | err = r.downloadGeoIPDatabase(geoPath) |
929 | | - | if err == nil { |
930 | | - | break |
931 | | - | } |
932 | | - | r.logger.Error("download geoip database: ", err) |
933 | | - | os.Remove(geoPath) |
934 | | - | // time.Sleep(10 * time.Second) |
935 | | - | } |
936 | | - | if err != nil { |
937 | | - | return err |
938 | | - | } |
939 | | - | } |
940 | | - | geoReader, codes, err := geoip.Open(geoPath) |
941 | | - | if err != nil { |
942 | | - | return E.Cause(err, "open geoip database") |
943 | | - | } |
944 | | - | r.logger.Info("loaded geoip database: ", len(codes), " codes") |
945 | | - | r.geoIPReader = geoReader |
946 | | - | return nil |
947 | | - | } |
948 | | - | |
949 | | - | func (r *Router) prepareGeositeDatabase() error { |
950 | | - | var geoPath string |
951 | | - | if r.geositeOptions.Path != "" { |
952 | | - | geoPath = r.geositeOptions.Path |
953 | | - | } else { |
954 | | - | geoPath = "geosite.db" |
955 | | - | if foundPath, loaded := C.FindPath(geoPath); loaded { |
956 | | - | geoPath = foundPath |
957 | | - | } |
958 | | - | } |
959 | | - | geoPath = C.BasePath(geoPath) |
960 | | - | if !rw.FileExists(geoPath) { |
961 | | - | r.logger.Warn("geosite database not exists: ", geoPath) |
962 | | - | var err error |
963 | | - | for attempts := 0; attempts < 3; attempts++ { |
964 | | - | err = r.downloadGeositeDatabase(geoPath) |
965 | | - | if err == nil { |
966 | | - | break |
967 | | - | } |
968 | | - | r.logger.Error("download geosite database: ", err) |
969 | | - | os.Remove(geoPath) |
970 | | - | // time.Sleep(10 * time.Second) |
971 | | - | } |
972 | | - | if err != nil { |
973 | | - | return err |
974 | | - | } |
975 | | - | } |
976 | | - | geoReader, codes, err := geosite.Open(geoPath) |
977 | | - | if err == nil { |
978 | | - | r.logger.Info("loaded geosite database: ", len(codes), " codes") |
979 | | - | r.geositeReader = geoReader |
980 | | - | } else { |
981 | | - | return E.Cause(err, "open geosite database") |
982 | | - | } |
983 | | - | return nil |
984 | | - | } |
985 | | - | |
986 | | - | func (r *Router) downloadGeoIPDatabase(savePath string) error { |
987 | | - | var downloadURL string |
988 | | - | if r.geoIPOptions.DownloadURL != "" { |
989 | | - | downloadURL = r.geoIPOptions.DownloadURL |
990 | | - | } else { |
991 | | - | downloadURL = "https://github.com/SagerNet/sing-geoip/releases/latest/download/geoip.db" |
992 | | - | } |
993 | | - | r.logger.Info("downloading geoip database") |
994 | | - | var detour adapter.Outbound |
995 | | - | if r.geoIPOptions.DownloadDetour != "" { |
996 | | - | outbound, loaded := r.Outbound(r.geoIPOptions.DownloadDetour) |
997 | | - | if !loaded { |
998 | | - | return E.New("detour outbound not found: ", r.geoIPOptions.DownloadDetour) |
999 | | - | } |
1000 | | - | detour = outbound |
1001 | | - | } else { |
1002 | | - | detour = r.defaultOutboundForConnection |
1003 | | - | } |
1004 | | - | |
1005 | | - | if parentDir := filepath.Dir(savePath); parentDir != "" { |
1006 | | - | os.MkdirAll(parentDir, 0o755) |
1007 | | - | } |
1008 | | - | |
1009 | | - | saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644) |
1010 | | - | if err != nil { |
1011 | | - | return E.Cause(err, "open output file: ", downloadURL) |
1012 | | - | } |
1013 | | - | defer saveFile.Close() |
1014 | | - | |
1015 | | - | httpClient := &http.Client{ |
1016 | | - | Transport: &http.Transport{ |
1017 | | - | ForceAttemptHTTP2: true, |
1018 | | - | TLSHandshakeTimeout: 5 * time.Second, |
1019 | | - | DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { |
1020 | | - | return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) |
1021 | | - | }, |
1022 | | - | }, |
1023 | | - | } |
1024 | | - | defer httpClient.CloseIdleConnections() |
1025 | | - | response, err := httpClient.Get(downloadURL) |
1026 | | - | if err != nil { |
1027 | | - | return err |
1028 | | - | } |
1029 | | - | defer response.Body.Close() |
1030 | | - | _, err = io.Copy(saveFile, response.Body) |
1031 | | - | return err |
1032 | | - | } |
1033 | | - | |
1034 | | - | func (r *Router) downloadGeositeDatabase(savePath string) error { |
1035 | | - | var downloadURL string |
1036 | | - | if r.geositeOptions.DownloadURL != "" { |
1037 | | - | downloadURL = r.geositeOptions.DownloadURL |
1038 | | - | } else { |
1039 | | - | downloadURL = "https://github.com/SagerNet/sing-geosite/releases/latest/download/geosite.db" |
1040 | | - | } |
1041 | | - | r.logger.Info("downloading geosite database") |
1042 | | - | var detour adapter.Outbound |
1043 | | - | if r.geositeOptions.DownloadDetour != "" { |
1044 | | - | outbound, loaded := r.Outbound(r.geositeOptions.DownloadDetour) |
1045 | | - | if !loaded { |
1046 | | - | return E.New("detour outbound not found: ", r.geositeOptions.DownloadDetour) |
1047 | | - | } |
1048 | | - | detour = outbound |
1049 | | - | } else { |
1050 | | - | detour = r.defaultOutboundForConnection |
1051 | | - | } |
1052 | | - | |
1053 | | - | if parentDir := filepath.Dir(savePath); parentDir != "" { |
1054 | | - | os.MkdirAll(parentDir, 0o755) |
1055 | | - | } |
1056 | | - | |
1057 | | - | saveFile, err := os.OpenFile(savePath, os.O_CREATE|os.O_WRONLY, 0o644) |
1058 | | - | if err != nil { |
1059 | | - | return E.Cause(err, "open output file: ", downloadURL) |
1060 | | - | } |
1061 | | - | defer saveFile.Close() |
1062 | | - | |
1063 | | - | httpClient := &http.Client{ |
1064 | | - | Transport: &http.Transport{ |
1065 | | - | ForceAttemptHTTP2: true, |
1066 | | - | TLSHandshakeTimeout: 5 * time.Second, |
1067 | | - | DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { |
1068 | | - | return detour.DialContext(ctx, network, M.ParseSocksaddr(addr)) |
1069 | | - | }, |
1070 | | - | }, |
1071 | | - | } |
1072 | | - | defer httpClient.CloseIdleConnections() |
1073 | | - | response, err := httpClient.Get(downloadURL) |
1074 | | - | if err != nil { |
1075 | | - | return err |
1076 | | - | } |
1077 | | - | defer response.Body.Close() |
1078 | | - | _, err = io.Copy(saveFile, response.Body) |
1079 | | - | return err |
1080 | 836 | | } |
1081 | 837 | | |
1082 | 838 | | func (r *Router) OnPackagesUpdated(packages int, sharedUsers int) { |
| skipped 37 lines |