github.com/metacubex/mihomo@v1.18.5/rules/common/geoip.go (about)

     1  package common
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/metacubex/mihomo/component/geodata"
     8  	"github.com/metacubex/mihomo/component/geodata/router"
     9  	"github.com/metacubex/mihomo/component/mmdb"
    10  	"github.com/metacubex/mihomo/component/resolver"
    11  	C "github.com/metacubex/mihomo/constant"
    12  	"github.com/metacubex/mihomo/log"
    13  )
    14  
    15  type GEOIP struct {
    16  	*Base
    17  	country      string
    18  	adapter      string
    19  	noResolveIP  bool
    20  	isSourceIP   bool
    21  	geoIPMatcher *router.GeoIPMatcher
    22  	recodeSize   int
    23  }
    24  
    25  var _ C.Rule = (*GEOIP)(nil)
    26  
    27  func (g *GEOIP) RuleType() C.RuleType {
    28  	if g.isSourceIP {
    29  		return C.SrcGEOIP
    30  	}
    31  	return C.GEOIP
    32  }
    33  
    34  func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) {
    35  	ip := metadata.DstIP
    36  	if g.isSourceIP {
    37  		ip = metadata.SrcIP
    38  	}
    39  	if !ip.IsValid() {
    40  		return false, ""
    41  	}
    42  
    43  	if g.country == "lan" {
    44  		return ip.IsPrivate() ||
    45  			ip.IsUnspecified() ||
    46  			ip.IsLoopback() ||
    47  			ip.IsMulticast() ||
    48  			ip.IsLinkLocalUnicast() ||
    49  			resolver.IsFakeBroadcastIP(ip), g.adapter
    50  	}
    51  
    52  	for _, code := range metadata.DstGeoIP {
    53  		if g.country == code {
    54  			return true, g.adapter
    55  		}
    56  	}
    57  
    58  	if !C.GeodataMode {
    59  		if g.isSourceIP {
    60  			codes := mmdb.IPInstance().LookupCode(ip.AsSlice())
    61  			for _, code := range codes {
    62  				if g.country == code {
    63  					return true, g.adapter
    64  				}
    65  			}
    66  			return false, g.adapter
    67  		}
    68  
    69  		if metadata.DstGeoIP != nil {
    70  			return false, g.adapter
    71  		}
    72  		metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice())
    73  		for _, code := range metadata.DstGeoIP {
    74  			if g.country == code {
    75  				return true, g.adapter
    76  			}
    77  		}
    78  		return false, g.adapter
    79  	}
    80  
    81  	match := g.geoIPMatcher.Match(ip)
    82  	if match && !g.isSourceIP {
    83  		metadata.DstGeoIP = append(metadata.DstGeoIP, g.country)
    84  	}
    85  	return match, g.adapter
    86  }
    87  
    88  func (g *GEOIP) Adapter() string {
    89  	return g.adapter
    90  }
    91  
    92  func (g *GEOIP) Payload() string {
    93  	return g.country
    94  }
    95  
    96  func (g *GEOIP) ShouldResolveIP() bool {
    97  	return !g.noResolveIP
    98  }
    99  
   100  func (g *GEOIP) GetCountry() string {
   101  	return g.country
   102  }
   103  
   104  func (g *GEOIP) GetIPMatcher() *router.GeoIPMatcher {
   105  	return g.geoIPMatcher
   106  }
   107  
   108  func (g *GEOIP) GetRecodeSize() int {
   109  	return g.recodeSize
   110  }
   111  
   112  func NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, error) {
   113  	if err := geodata.InitGeoIP(); err != nil {
   114  		log.Errorln("can't initial GeoIP: %s", err)
   115  		return nil, err
   116  	}
   117  	country = strings.ToLower(country)
   118  
   119  	if !C.GeodataMode || country == "lan" {
   120  		geoip := &GEOIP{
   121  			Base:        &Base{},
   122  			country:     country,
   123  			adapter:     adapter,
   124  			noResolveIP: noResolveIP,
   125  			isSourceIP:  isSrc,
   126  		}
   127  		return geoip, nil
   128  	}
   129  
   130  	geoIPMatcher, size, err := geodata.LoadGeoIPMatcher(country)
   131  	if err != nil {
   132  		return nil, fmt.Errorf("[GeoIP] %w", err)
   133  	}
   134  
   135  	log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, size)
   136  	geoip := &GEOIP{
   137  		Base:         &Base{},
   138  		country:      country,
   139  		adapter:      adapter,
   140  		noResolveIP:  noResolveIP,
   141  		isSourceIP:   isSrc,
   142  		geoIPMatcher: geoIPMatcher,
   143  		recodeSize:   size,
   144  	}
   145  	return geoip, nil
   146  }