Projects STRLCPY sing-box Commits de752a79
🤬
  • ■ ■ ■ ■ ■ ■
    adapter/experimental.go
    skipped 12 lines
    13 13   PreStarter
    14 14   Mode() string
    15 15   StoreSelected() bool
     16 + StoreFakeIP() bool
    16 17   CacheFile() ClashCacheFile
    17 18   HistoryStorage() *urltest.HistoryStorage
    18 19   RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
    skipped 3 lines
    22 23  type ClashCacheFile interface {
    23 24   LoadSelected(group string) string
    24 25   StoreSelected(group string, selected string) error
     26 + FakeIPStorage
    25 27  }
    26 28   
    27 29  type Tracker interface {
    skipped 25 lines
  • ■ ■ ■ ■ ■ ■
    adapter/fakeip.go
     1 +package adapter
     2 + 
     3 +import (
     4 + "net/netip"
     5 + 
     6 + "github.com/sagernet/sing-dns"
     7 +)
     8 + 
     9 +type FakeIPStore interface {
     10 + Service
     11 + Contains(address netip.Addr) bool
     12 + Create(domain string, strategy dns.DomainStrategy) (netip.Addr, error)
     13 + Lookup(address netip.Addr) (string, bool)
     14 + Reset() error
     15 +}
     16 + 
     17 +type FakeIPStorage interface {
     18 + FakeIPMetadata() *FakeIPMetadata
     19 + FakeIPSaveMetadata(metadata *FakeIPMetadata) error
     20 + FakeIPStore(address netip.Addr, domain string) error
     21 + FakeIPLoad(address netip.Addr) (string, bool)
     22 + FakeIPReset() error
     23 +}
     24 + 
  • ■ ■ ■ ■ ■ ■
    adapter/fakeip_metadata.go
     1 +package adapter
     2 + 
     3 +import (
     4 + "bytes"
     5 + "encoding"
     6 + "encoding/binary"
     7 + "io"
     8 + "net/netip"
     9 + 
     10 + "github.com/sagernet/sing/common"
     11 +)
     12 + 
     13 +type FakeIPMetadata struct {
     14 + Inet4Range netip.Prefix
     15 + Inet6Range netip.Prefix
     16 + Inet4Current netip.Addr
     17 + Inet6Current netip.Addr
     18 +}
     19 + 
     20 +func (m *FakeIPMetadata) MarshalBinary() (data []byte, err error) {
     21 + var buffer bytes.Buffer
     22 + for _, marshaler := range []encoding.BinaryMarshaler{m.Inet4Range, m.Inet6Range, m.Inet4Current, m.Inet6Current} {
     23 + data, err = marshaler.MarshalBinary()
     24 + if err != nil {
     25 + return
     26 + }
     27 + common.Must(binary.Write(&buffer, binary.BigEndian, uint16(len(data))))
     28 + buffer.Write(data)
     29 + }
     30 + data = buffer.Bytes()
     31 + return
     32 +}
     33 + 
     34 +func (m *FakeIPMetadata) UnmarshalBinary(data []byte) error {
     35 + reader := bytes.NewReader(data)
     36 + for _, unmarshaler := range []encoding.BinaryUnmarshaler{&m.Inet4Range, &m.Inet6Range, &m.Inet4Current, &m.Inet6Current} {
     37 + var length uint16
     38 + common.Must(binary.Read(reader, binary.BigEndian, &length))
     39 + element := make([]byte, length)
     40 + _, err := io.ReadFull(reader, element)
     41 + if err != nil {
     42 + return err
     43 + }
     44 + err = unmarshaler.UnmarshalBinary(element)
     45 + if err != nil {
     46 + return err
     47 + }
     48 + }
     49 + return nil
     50 +}
     51 + 
  • ■ ■ ■ ■ ■ ■
    adapter/router.go
    skipped 20 lines
    21 21   Outbound(tag string) (Outbound, bool)
    22 22   DefaultOutbound(network string) Outbound
    23 23   
     24 + FakeIPStore() FakeIPStore
     25 + 
    24 26   RouteConnection(ctx context.Context, conn net.Conn, metadata InboundContext) error
    25 27   RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
    26 28   RouteIPConnection(ctx context.Context, conn tun.RouteContext, metadata InboundContext) tun.RouteAction
    skipped 69 lines
  • ■ ■ ■ ■ ■ ■
    experimental/clashapi/cache.go
    skipped 2 lines
    3 3  import (
    4 4   "net/http"
    5 5   
     6 + "github.com/sagernet/sing-box/adapter"
     7 + 
    6 8   "github.com/go-chi/chi/v5"
    7 9   "github.com/go-chi/render"
    8 10  )
    9 11   
    10  -func cacheRouter() http.Handler {
     12 +func cacheRouter(router adapter.Router) http.Handler {
    11 13   r := chi.NewRouter()
    12  - r.Post("/fakeip/flush", flushFakeip)
     14 + r.Post("/fakeip/flush", flushFakeip(router))
    13 15   return r
    14 16  }
    15 17   
    16  -func flushFakeip(w http.ResponseWriter, r *http.Request) {
    17  - /*if err := cachefile.Cache().FlushFakeip(); err != nil {
    18  - render.Status(r, http.StatusInternalServerError)
    19  - render.JSON(w, r, newError(err.Error()))
    20  - return
    21  - }*/
    22  - render.NoContent(w, r)
     18 +func flushFakeip(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
     19 + return func(w http.ResponseWriter, r *http.Request) {
     20 + if cacheFile := router.ClashServer().CacheFile(); cacheFile != nil {
     21 + err := cacheFile.FakeIPReset()
     22 + if err != nil {
     23 + render.Status(r, http.StatusInternalServerError)
     24 + render.JSON(w, r, newError(err.Error()))
     25 + return
     26 + }
     27 + }
     28 + render.NoContent(w, r)
     29 + }
    23 30  }
    24 31   
  • ■ ■ ■ ■ ■ ■
    experimental/clashapi/cachefile/fakeip.go
     1 +package cachefile
     2 + 
     3 +import (
     4 + "net/netip"
     5 + "os"
     6 + 
     7 + "github.com/sagernet/sing-box/adapter"
     8 + 
     9 + "go.etcd.io/bbolt"
     10 +)
     11 + 
     12 +var (
     13 + bucketFakeIP = []byte("fakeip")
     14 + keyMetadata = []byte("metadata")
     15 +)
     16 + 
     17 +func (c *CacheFile) FakeIPMetadata() *adapter.FakeIPMetadata {
     18 + var metadata adapter.FakeIPMetadata
     19 + err := c.DB.View(func(tx *bbolt.Tx) error {
     20 + bucket := tx.Bucket(bucketFakeIP)
     21 + if bucket == nil {
     22 + return nil
     23 + }
     24 + metadataBinary := bucket.Get(keyMetadata)
     25 + if len(metadataBinary) == 0 {
     26 + return os.ErrInvalid
     27 + }
     28 + return metadata.UnmarshalBinary(metadataBinary)
     29 + })
     30 + if err != nil {
     31 + return nil
     32 + }
     33 + return &metadata
     34 +}
     35 + 
     36 +func (c *CacheFile) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
     37 + return c.DB.Batch(func(tx *bbolt.Tx) error {
     38 + bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
     39 + if err != nil {
     40 + return err
     41 + }
     42 + metadataBinary, err := metadata.MarshalBinary()
     43 + if err != nil {
     44 + return err
     45 + }
     46 + return bucket.Put(keyMetadata, metadataBinary)
     47 + })
     48 +}
     49 + 
     50 +func (c *CacheFile) FakeIPStore(address netip.Addr, domain string) error {
     51 + return c.DB.Batch(func(tx *bbolt.Tx) error {
     52 + bucket, err := tx.CreateBucketIfNotExists(bucketFakeIP)
     53 + if err != nil {
     54 + return err
     55 + }
     56 + return bucket.Put(address.AsSlice(), []byte(domain))
     57 + })
     58 +}
     59 + 
     60 +func (c *CacheFile) FakeIPLoad(address netip.Addr) (string, bool) {
     61 + var domain string
     62 + _ = c.DB.View(func(tx *bbolt.Tx) error {
     63 + bucket := tx.Bucket(bucketFakeIP)
     64 + if bucket == nil {
     65 + return nil
     66 + }
     67 + domain = string(bucket.Get(address.AsSlice()))
     68 + return nil
     69 + })
     70 + return domain, domain != ""
     71 +}
     72 + 
     73 +func (c *CacheFile) FakeIPReset() error {
     74 + return c.DB.Batch(func(tx *bbolt.Tx) error {
     75 + return tx.DeleteBucket(bucketFakeIP)
     76 + })
     77 +}
     78 + 
  • ■ ■ ■ ■ ■
    experimental/clashapi/server.go
    skipped 43 lines
    44 44   urlTestHistory *urltest.HistoryStorage
    45 45   mode string
    46 46   storeSelected bool
     47 + storeFakeIP bool
    47 48   cacheFilePath string
    48 49   cacheFile adapter.ClashCacheFile
    49 50  }
    skipped 11 lines
    61 62   trafficManager: trafficManager,
    62 63   urlTestHistory: urltest.NewHistoryStorage(),
    63 64   mode: strings.ToLower(options.DefaultMode),
     65 + storeSelected: options.StoreSelected,
     66 + storeFakeIP: options.StoreFakeIP,
    64 67   }
    65 68   if server.mode == "" {
    66 69   server.mode = "rule"
    67 70   }
    68  - if options.StoreSelected {
    69  - server.storeSelected = true
     71 + if options.StoreSelected || options.StoreFakeIP {
    70 72   cachePath := os.ExpandEnv(options.CacheFile)
    71 73   if cachePath == "" {
    72 74   cachePath = "cache.db"
    skipped 26 lines
    99 101   r.Mount("/providers/rules", ruleProviderRouter())
    100 102   r.Mount("/script", scriptRouter())
    101 103   r.Mount("/profile", profileRouter())
    102  - r.Mount("/cache", cacheRouter())
     104 + r.Mount("/cache", cacheRouter(router))
    103 105   r.Mount("/dns", dnsRouter(router))
    104 106   })
    105 107   if options.ExternalUI != "" {
    skipped 48 lines
    154 156   
    155 157  func (s *Server) StoreSelected() bool {
    156 158   return s.storeSelected
     159 +}
     160 + 
     161 +func (s *Server) StoreFakeIP() bool {
     162 + return s.storeFakeIP
    157 163  }
    158 164   
    159 165  func (s *Server) CacheFile() adapter.ClashCacheFile {
    skipped 238 lines
  • ■ ■ ■ ■
    go.mod
    skipped 25 lines
    26 26   github.com/sagernet/quic-go v0.0.0-20230202071646-a8c8afb18b32
    27 27   github.com/sagernet/reality v0.0.0-20230323230523-5fa25e693e7f
    28 28   github.com/sagernet/sing v0.2.1
    29  - github.com/sagernet/sing-dns v0.1.5-0.20230324014656-cc070d645ee7
     29 + github.com/sagernet/sing-dns v0.1.5-0.20230325044541-1b3c42d0e1b7
    30 30   github.com/sagernet/sing-shadowsocks v0.2.0
    31 31   github.com/sagernet/sing-shadowtls v0.1.0
    32 32   github.com/sagernet/sing-tun v0.1.4-0.20230324082220-ca53ccf346b5
    skipped 63 lines
  • ■ ■ ■ ■ ■ ■
    go.sum
    skipped 112 lines
    113 113  github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
    114 114  github.com/sagernet/sing v0.2.1 h1:r0STYeyfKBBtoAHsBtW1dQonxG+3Qidde7/1VAMhdn8=
    115 115  github.com/sagernet/sing v0.2.1/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw=
    116  -github.com/sagernet/sing-dns v0.1.5-0.20230324014656-cc070d645ee7 h1:+Ym0IAbyJKZ6fV+PBEXXjC+RyZF5/bI69qe2b0C5i0g=
    117  -github.com/sagernet/sing-dns v0.1.5-0.20230324014656-cc070d645ee7/go.mod h1:Ym+EQTT0AWzMkbXQSO+R4Qxvo4d5AZPv02R+qYlu/Fg=
     116 +github.com/sagernet/sing-dns v0.1.5-0.20230325044541-1b3c42d0e1b7 h1:1wWzexJJ4vnB44yHlT3bo8hLsRgPcCg5H8Coe59UYGo=
     117 +github.com/sagernet/sing-dns v0.1.5-0.20230325044541-1b3c42d0e1b7/go.mod h1:Ym+EQTT0AWzMkbXQSO+R4Qxvo4d5AZPv02R+qYlu/Fg=
    118 118  github.com/sagernet/sing-shadowsocks v0.2.0 h1:ILDWL7pwWfkPLEbviE/MyCgfjaBmJY/JVVY+5jhSb58=
    119 119  github.com/sagernet/sing-shadowsocks v0.2.0/go.mod h1:ysYzszRLpNzJSorvlWRMuzU6Vchsp7sd52q+JNY4axw=
    120 120  github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ=
    skipped 123 lines
  • ■ ■ ■ ■ ■
    option/clash.go
    skipped 5 lines
    6 6   Secret string `json:"secret,omitempty"`
    7 7   DefaultMode string `json:"default_mode,omitempty"`
    8 8   StoreSelected bool `json:"store_selected,omitempty"`
     9 + StoreFakeIP bool `json:"store_fakeip,omitempty"`
    9 10   CacheFile string `json:"cache_file,omitempty"`
    10 11  }
    11 12   
    skipped 12 lines
  • ■ ■ ■ ■ ■ ■
    option/dns.go
    skipped 4 lines
    5 5   Rules []DNSRule `json:"rules,omitempty"`
    6 6   Final string `json:"final,omitempty"`
    7 7   ReverseMapping bool `json:"reverse_mapping,omitempty"`
     8 + FakeIP *DNSFakeIPOptions `json:"fakeip,omitempty"`
    8 9   DNSClientOptions
    9  -}
    10  - 
    11  -type DNSClientOptions struct {
    12  - Strategy DomainStrategy `json:"strategy,omitempty"`
    13  - DisableCache bool `json:"disable_cache,omitempty"`
    14  - DisableExpire bool `json:"disable_expire,omitempty"`
    15 10  }
    16 11   
    17 12  type DNSServerOptions struct {
    skipped 6 lines
    24 19   Detour string `json:"detour,omitempty"`
    25 20  }
    26 21   
     22 +type DNSClientOptions struct {
     23 + Strategy DomainStrategy `json:"strategy,omitempty"`
     24 + DisableCache bool `json:"disable_cache,omitempty"`
     25 + DisableExpire bool `json:"disable_expire,omitempty"`
     26 +}
     27 + 
     28 +type DNSFakeIPOptions struct {
     29 + Enabled bool `json:"enabled,omitempty"`
     30 + Inet4Range *ListenPrefix `json:"inet4_range,omitempty"`
     31 + Inet6Range *ListenPrefix `json:"inet6_range,omitempty"`
     32 +}
     33 + 
  • ■ ■ ■ ■ ■
    route/router.go
    skipped 22 lines
    23 23   "github.com/sagernet/sing-box/log"
    24 24   "github.com/sagernet/sing-box/ntp"
    25 25   "github.com/sagernet/sing-box/option"
     26 + "github.com/sagernet/sing-box/transport/fakeip"
    26 27   "github.com/sagernet/sing-dns"
    27 28   "github.com/sagernet/sing-tun"
    28 29   "github.com/sagernet/sing-vmess"
    skipped 58 lines
    87 88   transportMap map[string]dns.Transport
    88 89   transportDomainStrategy map[dns.Transport]dns.DomainStrategy
    89 90   dnsReverseMapping *DNSReverseMapping
     91 + fakeIPStore adapter.FakeIPStore
    90 92   interfaceFinder myInterfaceFinder
    91 93   autoDetectInterface bool
    92 94   defaultInterface string
    skipped 124 lines
    217 219   } else {
    218 220   continue
    219 221   }
    220  - } else if notIpAddress != nil {
    221  - switch serverURL.Scheme {
    222  - case "rcode", "dhcp":
    223  - default:
    224  - return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
    225  - }
     222 + } else if notIpAddress != nil && strings.Contains(server.Address, ".") {
     223 + return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
    226 224   }
    227 225   }
    228 226   transport, err := dns.CreateTransport(ctx, logFactory.NewLogger(F.ToString("dns/transport[", tag, "]")), detour, server.Address)
    skipped 49 lines
    278 276   router.dnsReverseMapping = NewDNSReverseMapping()
    279 277   }
    280 278   
     279 + if fakeIPOptions := dnsOptions.FakeIP; fakeIPOptions != nil && dnsOptions.FakeIP.Enabled {
     280 + var inet4Range netip.Prefix
     281 + var inet6Range netip.Prefix
     282 + if fakeIPOptions.Inet4Range != nil {
     283 + inet4Range = fakeIPOptions.Inet4Range.Build()
     284 + }
     285 + if fakeIPOptions.Inet6Range != nil {
     286 + inet6Range = fakeIPOptions.Inet6Range.Build()
     287 + }
     288 + router.fakeIPStore = fakeip.NewStore(router, inet4Range, inet6Range)
     289 + }
     290 + 
    281 291   needInterfaceMonitor := platformInterface == nil && (options.AutoDetectInterface || common.Any(inbounds, func(inbound option.Inbound) bool {
    282 292   return inbound.HTTPOptions.SetSystemProxy || inbound.MixedOptions.SetSystemProxy || inbound.TunOptions.AutoRoute
    283 293   }))
    skipped 200 lines
    484 494   return E.Cause(err, "initialize DNS rule[", i, "]")
    485 495   }
    486 496   }
     497 + if r.fakeIPStore != nil {
     498 + err := r.fakeIPStore.Start()
     499 + if err != nil {
     500 + return err
     501 + }
     502 + }
    487 503   for i, transport := range r.transports {
    488 504   err := transport.Start()
    489 505   if err != nil {
    skipped 34 lines
    524 540   r.networkMonitor,
    525 541   r.packageManager,
    526 542   r.timeService,
     543 + r.fakeIPStore,
    527 544   )
    528 545  }
    529 546   
    skipped 10 lines
    540 557   }
    541 558  }
    542 559   
     560 +func (r *Router) FakeIPStore() adapter.FakeIPStore {
     561 + return r.fakeIPStore
     562 +}
     563 + 
    543 564  func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
    544 565   if metadata.InboundDetour != "" {
    545 566   if metadata.LastInbound == metadata.InboundDetour {
    skipped 46 lines
    592 613   metadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()}
    593 614   return r.RoutePacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata)
    594 615   }
     616 + 
     617 + if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
     618 + domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr)
     619 + if !loaded {
     620 + return E.New("missing fakeip context")
     621 + }
     622 + metadata.Destination = M.Socksaddr{
     623 + Fqdn: domain,
     624 + Port: metadata.Destination.Port,
     625 + }
     626 + r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
     627 + }
     628 + 
    595 629   if metadata.InboundOptions.SniffEnabled {
    596 630   buffer := buf.NewPacket()
    597 631   buffer.FullReset()
    skipped 80 lines
    678 712   return nil
    679 713   }
    680 714   metadata.Network = N.NetworkUDP
     715 + 
     716 + var originAddress M.Socksaddr
     717 + if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
     718 + domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr)
     719 + if !loaded {
     720 + return E.New("missing fakeip context")
     721 + }
     722 + originAddress = metadata.Destination
     723 + metadata.Destination = M.Socksaddr{
     724 + Fqdn: domain,
     725 + Port: metadata.Destination.Port,
     726 + }
     727 + r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
     728 + }
     729 + 
    681 730   if metadata.InboundOptions.SniffEnabled {
    682 731   buffer := buf.NewPacket()
    683 732   buffer.FullReset()
    skipped 49 lines
    733 782   if statsService := r.v2rayServer.StatsService(); statsService != nil {
    734 783   conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
    735 784   }
     785 + }
     786 + if originAddress.IsValid() {
     787 + conn = fakeip.NewNATPacketConn(conn, originAddress, metadata.Destination)
    736 788   }
    737 789   return detour.NewPacketConnection(ctx, conn, metadata)
    738 790  }
    skipped 158 lines
  • ■ ■ ■ ■ ■ ■
    route/router_ip.go
    skipped 7 lines
    8 8   "github.com/sagernet/sing-dns"
    9 9   "github.com/sagernet/sing-tun"
    10 10   F "github.com/sagernet/sing/common/format"
     11 + M "github.com/sagernet/sing/common/metadata"
    11 12  )
    12 13   
    13 14  func (r *Router) RouteIPConnection(ctx context.Context, conn tun.RouteContext, metadata adapter.InboundContext) tun.RouteAction {
     15 + if r.fakeIPStore != nil && r.fakeIPStore.Contains(metadata.Destination.Addr) {
     16 + domain, loaded := r.fakeIPStore.Lookup(metadata.Destination.Addr)
     17 + if !loaded {
     18 + r.logger.ErrorContext(ctx, "missing fakeip context")
     19 + return (*tun.ActionReturn)(nil)
     20 + }
     21 + metadata.Destination = M.Socksaddr{
     22 + Fqdn: domain,
     23 + Port: metadata.Destination.Port,
     24 + }
     25 + r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
     26 + }
    14 27   if r.dnsReverseMapping != nil && metadata.Domain == "" {
    15 28   domain, loaded := r.dnsReverseMapping.Query(metadata.Destination.Addr)
    16 29   if loaded {
    skipped 51 lines
  • ■ ■ ■ ■ ■ ■
    route/rule_abstract.go
    skipped 56 lines
    57 57  }
    58 58   
    59 59  func (r *abstractDefaultRule) Match(metadata *adapter.InboundContext) bool {
     60 + if len(r.allItems) == 0 {
     61 + return true
     62 + }
     63 + 
    60 64   for _, item := range r.items {
    61 65   if !item.Match(metadata) {
    62 66   return r.invert
    skipped 138 lines
  • ■ ■ ■ ■ ■ ■
    transport/fakeip/memory.go
     1 +package fakeip
     2 + 
     3 +import (
     4 + "net/netip"
     5 + 
     6 + "github.com/sagernet/sing-box/adapter"
     7 + "github.com/sagernet/sing/common/cache"
     8 +)
     9 + 
     10 +var _ adapter.FakeIPStorage = (*MemoryStorage)(nil)
     11 + 
     12 +type MemoryStorage struct {
     13 + metadata *adapter.FakeIPMetadata
     14 + domainCache *cache.LruCache[netip.Addr, string]
     15 +}
     16 + 
     17 +func NewMemoryStorage() *MemoryStorage {
     18 + return &MemoryStorage{
     19 + domainCache: cache.New[netip.Addr, string](),
     20 + }
     21 +}
     22 + 
     23 +func (s *MemoryStorage) FakeIPMetadata() *adapter.FakeIPMetadata {
     24 + return s.metadata
     25 +}
     26 + 
     27 +func (s *MemoryStorage) FakeIPSaveMetadata(metadata *adapter.FakeIPMetadata) error {
     28 + s.metadata = metadata
     29 + return nil
     30 +}
     31 + 
     32 +func (s *MemoryStorage) FakeIPStore(address netip.Addr, domain string) error {
     33 + s.domainCache.Store(address, domain)
     34 + return nil
     35 +}
     36 + 
     37 +func (s *MemoryStorage) FakeIPLoad(address netip.Addr) (string, bool) {
     38 + return s.domainCache.Load(address)
     39 +}
     40 + 
     41 +func (s *MemoryStorage) FakeIPReset() error {
     42 + s.domainCache = cache.New[netip.Addr, string]()
     43 + return nil
     44 +}
     45 + 
  • ■ ■ ■ ■ ■ ■
    transport/fakeip/packet.go
     1 +package fakeip
     2 + 
     3 +import (
     4 + "github.com/sagernet/sing/common/buf"
     5 + M "github.com/sagernet/sing/common/metadata"
     6 + N "github.com/sagernet/sing/common/network"
     7 +)
     8 + 
     9 +var _ N.PacketConn = (*NATPacketConn)(nil)
     10 + 
     11 +type NATPacketConn struct {
     12 + N.PacketConn
     13 + origin M.Socksaddr
     14 + destination M.Socksaddr
     15 +}
     16 + 
     17 +func NewNATPacketConn(conn N.PacketConn, origin M.Socksaddr, destination M.Socksaddr) *NATPacketConn {
     18 + return &NATPacketConn{
     19 + PacketConn: conn,
     20 + origin: origin,
     21 + destination: destination,
     22 + }
     23 +}
     24 + 
     25 +func (c *NATPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
     26 + destination, err = c.PacketConn.ReadPacket(buffer)
     27 + if destination == c.destination {
     28 + destination = c.origin
     29 + }
     30 + return
     31 +}
     32 + 
     33 +func (c *NATPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
     34 + if destination == c.origin {
     35 + destination = c.destination
     36 + }
     37 + return c.PacketConn.WritePacket(buffer, destination)
     38 +}
     39 + 
     40 +func (c *NATPacketConn) Upstream() any {
     41 + return c.PacketConn
     42 +}
     43 + 
  • ■ ■ ■ ■ ■ ■
    transport/fakeip/server.go
     1 +package fakeip
     2 + 
     3 +import (
     4 + "context"
     5 + "net/netip"
     6 + "os"
     7 + 
     8 + "github.com/sagernet/sing-box/adapter"
     9 + "github.com/sagernet/sing-dns"
     10 + E "github.com/sagernet/sing/common/exceptions"
     11 + "github.com/sagernet/sing/common/logger"
     12 + N "github.com/sagernet/sing/common/network"
     13 + 
     14 + mDNS "github.com/miekg/dns"
     15 +)
     16 + 
     17 +var _ dns.Transport = (*Server)(nil)
     18 + 
     19 +func init() {
     20 + dns.RegisterTransport([]string{"fakeip"}, NewTransport)
     21 +}
     22 + 
     23 +type Server struct {
     24 + router adapter.Router
     25 + store adapter.FakeIPStore
     26 + logger logger.ContextLogger
     27 +}
     28 + 
     29 +func NewTransport(ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
     30 + router := adapter.RouterFromContext(ctx)
     31 + if router == nil {
     32 + return nil, E.New("missing router in context")
     33 + }
     34 + return &Server{
     35 + router: router,
     36 + logger: logger,
     37 + }, nil
     38 +}
     39 + 
     40 +func (s *Server) Start() error {
     41 + s.store = s.router.FakeIPStore()
     42 + if s.store == nil {
     43 + return E.New("fakeip not enabled")
     44 + }
     45 + return nil
     46 +}
     47 + 
     48 +func (s *Server) Close() error {
     49 + return nil
     50 +}
     51 + 
     52 +func (s *Server) Raw() bool {
     53 + return false
     54 +}
     55 + 
     56 +func (s *Server) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
     57 + return nil, os.ErrInvalid
     58 +}
     59 + 
     60 +func (s *Server) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
     61 + var addresses []netip.Addr
     62 + if strategy != dns.DomainStrategyUseIPv6 {
     63 + inet4Address, err := s.store.Create(domain, dns.DomainStrategyUseIPv4)
     64 + if err != nil {
     65 + return nil, err
     66 + }
     67 + addresses = append(addresses, inet4Address)
     68 + }
     69 + if strategy != dns.DomainStrategyUseIPv4 {
     70 + inet6Address, err := s.store.Create(domain, dns.DomainStrategyUseIPv6)
     71 + if err != nil {
     72 + return nil, err
     73 + }
     74 + addresses = append(addresses, inet6Address)
     75 + }
     76 + return addresses, nil
     77 +}
     78 + 
  • ■ ■ ■ ■ ■ ■
    transport/fakeip/store.go
     1 +package fakeip
     2 + 
     3 +import (
     4 + "net/netip"
     5 + 
     6 + "github.com/sagernet/sing-box/adapter"
     7 + "github.com/sagernet/sing-dns"
     8 + E "github.com/sagernet/sing/common/exceptions"
     9 +)
     10 + 
     11 +var _ adapter.FakeIPStore = (*Store)(nil)
     12 + 
     13 +type Store struct {
     14 + router adapter.Router
     15 + inet4Range netip.Prefix
     16 + inet6Range netip.Prefix
     17 + storage adapter.FakeIPStorage
     18 + inet4Current netip.Addr
     19 + inet6Current netip.Addr
     20 +}
     21 + 
     22 +func NewStore(router adapter.Router, inet4Range netip.Prefix, inet6Range netip.Prefix) *Store {
     23 + return &Store{
     24 + router: router,
     25 + inet4Range: inet4Range,
     26 + inet6Range: inet6Range,
     27 + }
     28 +}
     29 + 
     30 +func (s *Store) Start() error {
     31 + var storage adapter.FakeIPStorage
     32 + if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreFakeIP() {
     33 + if cacheFile := clashServer.CacheFile(); cacheFile != nil {
     34 + storage = cacheFile
     35 + }
     36 + }
     37 + if storage == nil {
     38 + storage = NewMemoryStorage()
     39 + }
     40 + metadata := storage.FakeIPMetadata()
     41 + if metadata != nil && metadata.Inet4Range == s.inet4Range && metadata.Inet6Range == s.inet6Range {
     42 + s.inet4Current = metadata.Inet4Current
     43 + s.inet6Current = metadata.Inet6Current
     44 + } else {
     45 + if s.inet4Range.IsValid() {
     46 + s.inet4Current = s.inet4Range.Addr().Next().Next()
     47 + }
     48 + if s.inet6Range.IsValid() {
     49 + s.inet6Current = s.inet6Range.Addr().Next().Next()
     50 + }
     51 + }
     52 + s.storage = storage
     53 + return nil
     54 +}
     55 + 
     56 +func (s *Store) Contains(address netip.Addr) bool {
     57 + return s.inet4Range.Contains(address) || s.inet6Range.Contains(address)
     58 +}
     59 + 
     60 +func (s *Store) Close() error {
     61 + if s.storage == nil {
     62 + return nil
     63 + }
     64 + return s.storage.FakeIPSaveMetadata(&adapter.FakeIPMetadata{
     65 + Inet4Range: s.inet4Range,
     66 + Inet6Range: s.inet6Range,
     67 + Inet4Current: s.inet4Current,
     68 + Inet6Current: s.inet6Current,
     69 + })
     70 +}
     71 + 
     72 +func (s *Store) Create(domain string, strategy dns.DomainStrategy) (netip.Addr, error) {
     73 + var address netip.Addr
     74 + if strategy == dns.DomainStrategyUseIPv4 {
     75 + if !s.inet4Current.IsValid() {
     76 + return netip.Addr{}, E.New("missing IPv4 fakeip address range")
     77 + }
     78 + nextAddress := s.inet4Current.Next()
     79 + if !s.inet4Range.Contains(nextAddress) {
     80 + nextAddress = s.inet4Range.Addr().Next().Next()
     81 + }
     82 + s.inet4Current = nextAddress
     83 + address = nextAddress
     84 + } else {
     85 + if !s.inet6Current.IsValid() {
     86 + return netip.Addr{}, E.New("missing IPv6 fakeip address range")
     87 + }
     88 + nextAddress := s.inet6Current.Next()
     89 + if !s.inet6Range.Contains(nextAddress) {
     90 + nextAddress = s.inet6Range.Addr().Next().Next()
     91 + }
     92 + s.inet6Current = nextAddress
     93 + address = nextAddress
     94 + }
     95 + err := s.storage.FakeIPStore(address, domain)
     96 + if err != nil {
     97 + return netip.Addr{}, err
     98 + }
     99 + return address, nil
     100 +}
     101 + 
     102 +func (s *Store) Lookup(address netip.Addr) (string, bool) {
     103 + return s.storage.FakeIPLoad(address)
     104 +}
     105 + 
     106 +func (s *Store) Reset() error {
     107 + return s.storage.FakeIPReset()
     108 +}
     109 + 
Please wait...
Page is in error, reload to recover