123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- //go:build linux
- package resolved
- import (
- "context"
- "net"
- "strings"
- "sync"
- "time"
- "github.com/sagernet/sing-box/adapter"
- boxService "github.com/sagernet/sing-box/adapter/service"
- "github.com/sagernet/sing-box/common/listener"
- C "github.com/sagernet/sing-box/constant"
- "github.com/sagernet/sing-box/dns"
- "github.com/sagernet/sing-box/log"
- "github.com/sagernet/sing-box/option"
- dnsOutbound "github.com/sagernet/sing-box/protocol/dns"
- tun "github.com/sagernet/sing-tun"
- "github.com/sagernet/sing/common"
- "github.com/sagernet/sing/common/buf"
- "github.com/sagernet/sing/common/control"
- E "github.com/sagernet/sing/common/exceptions"
- M "github.com/sagernet/sing/common/metadata"
- N "github.com/sagernet/sing/common/network"
- "github.com/sagernet/sing/common/x/list"
- "github.com/sagernet/sing/service"
- "github.com/godbus/dbus/v5"
- mDNS "github.com/miekg/dns"
- )
- func RegisterService(registry *boxService.Registry) {
- boxService.Register[option.ResolvedServiceOptions](registry, C.TypeResolved, NewService)
- }
- type Service struct {
- boxService.Adapter
- ctx context.Context
- logger log.ContextLogger
- network adapter.NetworkManager
- dnsRouter adapter.DNSRouter
- listener *listener.Listener
- systemBus *dbus.Conn
- linkAccess sync.RWMutex
- links map[int32]*TransportLink
- defaultRouteSequence []int32
- networkUpdateCallback *list.Element[tun.NetworkUpdateCallback]
- updateCallback func(*TransportLink) error
- deleteCallback func(*TransportLink)
- }
- type TransportLink struct {
- iif *control.Interface
- address []LinkDNS
- addressEx []LinkDNSEx
- domain []LinkDomain
- defaultRoute bool
- dnsOverTLS bool
- // dnsOverTLSFallback bool
- }
- func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.ResolvedServiceOptions) (adapter.Service, error) {
- inbound := &Service{
- Adapter: boxService.NewAdapter(C.TypeResolved, tag),
- ctx: ctx,
- logger: logger,
- network: service.FromContext[adapter.NetworkManager](ctx),
- dnsRouter: service.FromContext[adapter.DNSRouter](ctx),
- links: make(map[int32]*TransportLink),
- }
- inbound.listener = listener.New(listener.Options{
- Context: ctx,
- Logger: logger,
- Network: []string{N.NetworkTCP, N.NetworkUDP},
- Listen: options.ListenOptions,
- ConnectionHandler: inbound,
- OOBPacketHandler: inbound,
- ThreadUnsafePacketWriter: true,
- })
- return inbound, nil
- }
- func (i *Service) Start(stage adapter.StartStage) error {
- switch stage {
- case adapter.StartStateInitialize:
- inboundManager := service.FromContext[adapter.ServiceManager](i.ctx)
- for _, transport := range inboundManager.Services() {
- if transport.Type() == C.TypeResolved && transport != i {
- return E.New("multiple resolved service are not supported")
- }
- }
- case adapter.StartStateStart:
- err := i.listener.Start()
- if err != nil {
- return err
- }
- systemBus, err := dbus.SystemBus()
- if err != nil {
- return err
- }
- i.systemBus = systemBus
- err = systemBus.Export((*resolve1Manager)(i), "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager")
- if err != nil {
- return err
- }
- reply, err := systemBus.RequestName("org.freedesktop.resolve1", dbus.NameFlagDoNotQueue)
- if err != nil {
- return err
- }
- switch reply {
- case dbus.RequestNameReplyPrimaryOwner:
- case dbus.RequestNameReplyExists:
- return E.New("D-Bus object already exists, maybe real resolved is running")
- default:
- return E.New("unknown request name reply: ", reply)
- }
- i.networkUpdateCallback = i.network.NetworkMonitor().RegisterCallback(i.onNetworkUpdate)
- }
- return nil
- }
- func (i *Service) Close() error {
- if i.networkUpdateCallback != nil {
- i.network.NetworkMonitor().UnregisterCallback(i.networkUpdateCallback)
- }
- if i.systemBus != nil {
- i.systemBus.ReleaseName("org.freedesktop.resolve1")
- i.systemBus.Close()
- }
- return i.listener.Close()
- }
- func (i *Service) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
- metadata.Inbound = i.Tag()
- metadata.InboundType = i.Type()
- metadata.Destination = M.Socksaddr{}
- for {
- conn.SetReadDeadline(time.Now().Add(C.DNSTimeout))
- err := dnsOutbound.HandleStreamDNSRequest(ctx, i.dnsRouter, conn, metadata)
- if err != nil {
- N.CloseOnHandshakeFailure(conn, onClose, err)
- return
- }
- }
- }
- func (i *Service) NewPacketEx(buffer *buf.Buffer, oob []byte, source M.Socksaddr) {
- go i.exchangePacket(buffer, oob, source)
- }
- func (i *Service) exchangePacket(buffer *buf.Buffer, oob []byte, source M.Socksaddr) {
- ctx := log.ContextWithNewID(i.ctx)
- err := i.exchangePacket0(ctx, buffer, oob, source)
- if err != nil {
- i.logger.ErrorContext(ctx, "process DNS packet: ", err)
- }
- }
- func (i *Service) exchangePacket0(ctx context.Context, buffer *buf.Buffer, oob []byte, source M.Socksaddr) error {
- var message mDNS.Msg
- err := message.Unpack(buffer.Bytes())
- buffer.Release()
- if err != nil {
- return E.Cause(err, "unpack request")
- }
- var metadata adapter.InboundContext
- metadata.Source = source
- response, err := i.dnsRouter.Exchange(adapter.WithContext(ctx, &metadata), &message, adapter.DNSQueryOptions{})
- if err != nil {
- return err
- }
- responseBuffer, err := dns.TruncateDNSMessage(&message, response, 0)
- if err != nil {
- return err
- }
- defer responseBuffer.Release()
- _, _, err = i.listener.UDPConn().WriteMsgUDPAddrPort(responseBuffer.Bytes(), oob, source.AddrPort())
- return err
- }
- func (i *Service) onNetworkUpdate() {
- i.linkAccess.Lock()
- defer i.linkAccess.Unlock()
- var deleteIfIndex []int
- for ifIndex, link := range i.links {
- iif, err := i.network.InterfaceFinder().ByIndex(int(ifIndex))
- if err != nil || iif != link.iif {
- deleteIfIndex = append(deleteIfIndex, int(ifIndex))
- }
- i.defaultRouteSequence = common.Filter(i.defaultRouteSequence, func(it int32) bool {
- return it != ifIndex
- })
- if i.deleteCallback != nil {
- i.deleteCallback(link)
- }
- }
- for _, ifIndex := range deleteIfIndex {
- delete(i.links, int32(ifIndex))
- }
- }
- func (conf *TransportLink) nameList(ndots int, name string) []string {
- search := common.Map(common.Filter(conf.domain, func(it LinkDomain) bool {
- return !it.RoutingOnly
- }), func(it LinkDomain) string {
- return it.Domain
- })
- l := len(name)
- rooted := l > 0 && name[l-1] == '.'
- if l > 254 || l == 254 && !rooted {
- return nil
- }
- if rooted {
- if avoidDNS(name) {
- return nil
- }
- return []string{name}
- }
- hasNdots := strings.Count(name, ".") >= ndots
- name += "."
- // l++
- names := make([]string, 0, 1+len(search))
- if hasNdots && !avoidDNS(name) {
- names = append(names, name)
- }
- for _, suffix := range search {
- fqdn := name + suffix
- if !avoidDNS(fqdn) && len(fqdn) <= 254 {
- names = append(names, fqdn)
- }
- }
- if !hasNdots && !avoidDNS(name) {
- names = append(names, name)
- }
- return names
- }
- func avoidDNS(name string) bool {
- if name == "" {
- return true
- }
- if name[len(name)-1] == '.' {
- name = name[:len(name)-1]
- }
- return strings.HasSuffix(name, ".onion")
- }
|