|
@@ -0,0 +1,648 @@
|
|
|
+//go:build linux
|
|
|
+
|
|
|
+package resolved
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "net/netip"
|
|
|
+ "os"
|
|
|
+ "os/user"
|
|
|
+ "path/filepath"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "syscall"
|
|
|
+
|
|
|
+ "github.com/sagernet/sing-box/adapter"
|
|
|
+ "github.com/sagernet/sing-box/common/process"
|
|
|
+ C "github.com/sagernet/sing-box/constant"
|
|
|
+ "github.com/sagernet/sing-box/dns"
|
|
|
+ "github.com/sagernet/sing-box/log"
|
|
|
+ "github.com/sagernet/sing/common"
|
|
|
+ E "github.com/sagernet/sing/common/exceptions"
|
|
|
+ F "github.com/sagernet/sing/common/format"
|
|
|
+ M "github.com/sagernet/sing/common/metadata"
|
|
|
+
|
|
|
+ "github.com/godbus/dbus/v5"
|
|
|
+ mDNS "github.com/miekg/dns"
|
|
|
+)
|
|
|
+
|
|
|
+type resolve1Manager Service
|
|
|
+
|
|
|
+type Address struct {
|
|
|
+ IfIndex int32
|
|
|
+ Family int32
|
|
|
+ Address []byte
|
|
|
+}
|
|
|
+
|
|
|
+type Name struct {
|
|
|
+ IfIndex int32
|
|
|
+ Hostname string
|
|
|
+}
|
|
|
+
|
|
|
+type ResourceRecord struct {
|
|
|
+ IfIndex int32
|
|
|
+ Type uint16
|
|
|
+ Class uint16
|
|
|
+ Data []byte
|
|
|
+}
|
|
|
+
|
|
|
+type SRVRecord struct {
|
|
|
+ Priority uint16
|
|
|
+ Weight uint16
|
|
|
+ Port uint16
|
|
|
+ Hostname string
|
|
|
+ Addresses []Address
|
|
|
+ CNAME string
|
|
|
+}
|
|
|
+
|
|
|
+type TXTRecord []byte
|
|
|
+
|
|
|
+type LinkDNS struct {
|
|
|
+ Family int32
|
|
|
+ Address []byte
|
|
|
+}
|
|
|
+
|
|
|
+type LinkDNSEx struct {
|
|
|
+ Family int32
|
|
|
+ Address []byte
|
|
|
+ Port uint16
|
|
|
+ Name string
|
|
|
+}
|
|
|
+
|
|
|
+type LinkDomain struct {
|
|
|
+ Domain string
|
|
|
+ RoutingOnly bool
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) getLink(ifIndex int32) (*TransportLink, *dbus.Error) {
|
|
|
+ link, loaded := t.links[ifIndex]
|
|
|
+ if !loaded {
|
|
|
+ link = &TransportLink{}
|
|
|
+ t.links[ifIndex] = link
|
|
|
+ iif, err := t.network.InterfaceFinder().ByIndex(int(ifIndex))
|
|
|
+ if err != nil {
|
|
|
+ return nil, wrapError(err)
|
|
|
+ }
|
|
|
+ link.iif = iif
|
|
|
+ }
|
|
|
+ return link, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) getSenderProcess(sender dbus.Sender) (int32, error) {
|
|
|
+ var senderPid int32
|
|
|
+ dbusObject := t.systemBus.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
|
|
+ if dbusObject == nil {
|
|
|
+ return 0, E.New("missing dbus object")
|
|
|
+ }
|
|
|
+ err := dbusObject.Call("org.freedesktop.DBus.GetConnectionUnixProcessID", 0, string(sender)).Store(&senderPid)
|
|
|
+ if err != nil {
|
|
|
+ return 0, E.Cause(err, "GetConnectionUnixProcessID")
|
|
|
+ }
|
|
|
+ return senderPid, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) createMetadata(sender dbus.Sender) adapter.InboundContext {
|
|
|
+ var metadata adapter.InboundContext
|
|
|
+ metadata.Inbound = t.Tag()
|
|
|
+ metadata.InboundType = C.TypeResolved
|
|
|
+ senderPid, err := t.getSenderProcess(sender)
|
|
|
+ if err != nil {
|
|
|
+ return metadata
|
|
|
+ }
|
|
|
+ var processInfo process.Info
|
|
|
+ metadata.ProcessInfo = &processInfo
|
|
|
+ processInfo.ProcessID = uint32(senderPid)
|
|
|
+
|
|
|
+ processPath, err := os.Readlink(F.ToString("/proc/", senderPid, "/exe"))
|
|
|
+ if err == nil {
|
|
|
+ processInfo.ProcessPath = processPath
|
|
|
+ } else {
|
|
|
+ processPath, err = os.Readlink(F.ToString("/proc/", senderPid, "/comm"))
|
|
|
+ if err == nil {
|
|
|
+ processInfo.ProcessPath = processPath
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var uidFound bool
|
|
|
+ statusContent, err := os.ReadFile(F.ToString("/proc/", senderPid, "/status"))
|
|
|
+ if err == nil {
|
|
|
+ for _, line := range strings.Split(string(statusContent), "\n") {
|
|
|
+ line = strings.TrimSpace(line)
|
|
|
+ if strings.HasPrefix(line, "Uid:") {
|
|
|
+ fields := strings.Fields(line)
|
|
|
+ if len(fields) >= 2 {
|
|
|
+ uid, parseErr := strconv.ParseUint(fields[1], 10, 32)
|
|
|
+ if parseErr != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ processInfo.UserId = int32(uid)
|
|
|
+ uidFound = true
|
|
|
+ if osUser, _ := user.LookupId(F.ToString(uid)); osUser != nil {
|
|
|
+ processInfo.User = osUser.Username
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if !uidFound {
|
|
|
+ metadata.ProcessInfo.UserId = -1
|
|
|
+ }
|
|
|
+ return metadata
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) log(sender dbus.Sender, message ...any) {
|
|
|
+ metadata := t.createMetadata(sender)
|
|
|
+ if metadata.ProcessInfo != nil {
|
|
|
+ var prefix string
|
|
|
+ if metadata.ProcessInfo.ProcessPath != "" {
|
|
|
+ prefix = filepath.Base(metadata.ProcessInfo.ProcessPath)
|
|
|
+ } else if metadata.ProcessInfo.User != "" {
|
|
|
+ prefix = F.ToString("user:", metadata.ProcessInfo.User)
|
|
|
+ } else if metadata.ProcessInfo.UserId != 0 {
|
|
|
+ prefix = F.ToString("uid:", metadata.ProcessInfo.UserId)
|
|
|
+ }
|
|
|
+ t.logger.Info("(", prefix, ") ", F.ToString(message...))
|
|
|
+ } else {
|
|
|
+ t.logger.Info(F.ToString(message...))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) logRequest(sender dbus.Sender, message ...any) context.Context {
|
|
|
+ ctx := log.ContextWithNewID(t.ctx)
|
|
|
+ metadata := t.createMetadata(sender)
|
|
|
+ if metadata.ProcessInfo != nil {
|
|
|
+ var prefix string
|
|
|
+ if metadata.ProcessInfo.ProcessPath != "" {
|
|
|
+ prefix = filepath.Base(metadata.ProcessInfo.ProcessPath)
|
|
|
+ } else if metadata.ProcessInfo.User != "" {
|
|
|
+ prefix = F.ToString("user:", metadata.ProcessInfo.User)
|
|
|
+ } else if metadata.ProcessInfo.UserId != 0 {
|
|
|
+ prefix = F.ToString("uid:", metadata.ProcessInfo.UserId)
|
|
|
+ }
|
|
|
+ t.logger.InfoContext(ctx, "(", prefix, ") ", F.ToString(message...))
|
|
|
+ } else {
|
|
|
+ t.logger.InfoContext(ctx, F.ToString(message...))
|
|
|
+ }
|
|
|
+ return adapter.WithContext(ctx, &metadata)
|
|
|
+}
|
|
|
+
|
|
|
+func familyToString(family int32) string {
|
|
|
+ switch family {
|
|
|
+ case syscall.AF_UNSPEC:
|
|
|
+ return "AF_UNSPEC"
|
|
|
+ case syscall.AF_INET:
|
|
|
+ return "AF_INET"
|
|
|
+ case syscall.AF_INET6:
|
|
|
+ return "AF_INET6"
|
|
|
+ default:
|
|
|
+ return F.ToString(family)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) ResolveHostname(sender dbus.Sender, ifIndex int32, hostname string, family int32, flags uint64) (addresses []Address, canonical string, outflags uint64, err *dbus.Error) {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ t.linkAccess.Unlock()
|
|
|
+ var strategy C.DomainStrategy
|
|
|
+ switch family {
|
|
|
+ case syscall.AF_UNSPEC:
|
|
|
+ strategy = C.DomainStrategyAsIS
|
|
|
+ case syscall.AF_INET:
|
|
|
+ strategy = C.DomainStrategyIPv4Only
|
|
|
+ case syscall.AF_INET6:
|
|
|
+ strategy = C.DomainStrategyIPv6Only
|
|
|
+ }
|
|
|
+ ctx := t.logRequest(sender, "ResolveHostname ", link.iif.Name, " ", hostname, " ", familyToString(family), " ", flags)
|
|
|
+ responseAddresses, lookupErr := t.dnsRouter.Lookup(ctx, hostname, adapter.DNSQueryOptions{
|
|
|
+ LookupStrategy: strategy,
|
|
|
+ })
|
|
|
+ if lookupErr != nil {
|
|
|
+ err = wrapError(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ addresses = common.Map(responseAddresses, func(it netip.Addr) Address {
|
|
|
+ var addrFamily int32
|
|
|
+ if it.Is4() {
|
|
|
+ addrFamily = syscall.AF_INET
|
|
|
+ } else {
|
|
|
+ addrFamily = syscall.AF_INET6
|
|
|
+ }
|
|
|
+ return Address{
|
|
|
+ IfIndex: ifIndex,
|
|
|
+ Family: addrFamily,
|
|
|
+ Address: it.AsSlice(),
|
|
|
+ }
|
|
|
+ })
|
|
|
+ canonical = mDNS.CanonicalName(hostname)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) ResolveAddress(sender dbus.Sender, ifIndex int32, family int32, address []byte, flags uint64) (names []Name, outflags uint64, err *dbus.Error) {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ t.linkAccess.Unlock()
|
|
|
+ addr, ok := netip.AddrFromSlice(address)
|
|
|
+ if !ok {
|
|
|
+ err = wrapError(E.New("invalid address"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ var nibbles []string
|
|
|
+ for i := len(address) - 1; i >= 0; i-- {
|
|
|
+ b := address[i]
|
|
|
+ nibbles = append(nibbles, fmt.Sprintf("%x", b&0x0F))
|
|
|
+ nibbles = append(nibbles, fmt.Sprintf("%x", b>>4))
|
|
|
+ }
|
|
|
+ var ptrDomain string
|
|
|
+ if addr.Is4() {
|
|
|
+ ptrDomain = strings.Join(nibbles, ".") + ".in-addr.arpa."
|
|
|
+ } else {
|
|
|
+ ptrDomain = strings.Join(nibbles, ".") + ".ip6.arpa."
|
|
|
+ }
|
|
|
+ request := &mDNS.Msg{
|
|
|
+ MsgHdr: mDNS.MsgHdr{
|
|
|
+ RecursionDesired: true,
|
|
|
+ },
|
|
|
+ Question: []mDNS.Question{
|
|
|
+ {
|
|
|
+ Name: mDNS.Fqdn(ptrDomain),
|
|
|
+ Qtype: mDNS.TypePTR,
|
|
|
+ Qclass: mDNS.ClassINET,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+ ctx := t.logRequest(sender, "ResolveAddress ", link.iif.Name, familyToString(family), addr, flags)
|
|
|
+ response, lookupErr := t.dnsRouter.Exchange(ctx, request, adapter.DNSQueryOptions{})
|
|
|
+ if lookupErr != nil {
|
|
|
+ err = wrapError(err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if response.Rcode != mDNS.RcodeSuccess {
|
|
|
+ err = rcodeError(response.Rcode)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, rawRR := range response.Answer {
|
|
|
+ switch rr := rawRR.(type) {
|
|
|
+ case *mDNS.PTR:
|
|
|
+ names = append(names, Name{
|
|
|
+ IfIndex: ifIndex,
|
|
|
+ Hostname: rr.Ptr,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) ResolveRecord(sender dbus.Sender, ifIndex int32, family int32, hostname string, qClass uint16, qType uint16, flags uint64) (records []ResourceRecord, outflags uint64, err *dbus.Error) {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ t.linkAccess.Unlock()
|
|
|
+ request := &mDNS.Msg{
|
|
|
+ MsgHdr: mDNS.MsgHdr{
|
|
|
+ RecursionDesired: true,
|
|
|
+ },
|
|
|
+ Question: []mDNS.Question{
|
|
|
+ {
|
|
|
+ Name: mDNS.Fqdn(hostname),
|
|
|
+ Qtype: qType,
|
|
|
+ Qclass: qClass,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+ ctx := t.logRequest(sender, "ResolveRecord ", link.iif.Name, familyToString(family), hostname, mDNS.Class(qClass), mDNS.Type(qType), flags)
|
|
|
+ response, exchangeErr := t.dnsRouter.Exchange(ctx, request, adapter.DNSQueryOptions{})
|
|
|
+ if exchangeErr != nil {
|
|
|
+ err = wrapError(exchangeErr)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if response.Rcode != mDNS.RcodeSuccess {
|
|
|
+ err = rcodeError(response.Rcode)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for _, rr := range response.Answer {
|
|
|
+ var record ResourceRecord
|
|
|
+ record.IfIndex = ifIndex
|
|
|
+ record.Type = rr.Header().Rrtype
|
|
|
+ record.Class = rr.Header().Class
|
|
|
+ data := make([]byte, mDNS.Len(rr))
|
|
|
+ _, unpackErr := mDNS.PackRR(rr, data, 0, nil, false)
|
|
|
+ if unpackErr != nil {
|
|
|
+ err = wrapError(unpackErr)
|
|
|
+ }
|
|
|
+ record.Data = data
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) ResolveService(sender dbus.Sender, ifIndex int32, hostname string, sType string, domain string, family int32, flags uint64) (srvData []SRVRecord, txtData []TXTRecord, canonicalName string, canonicalType string, canonicalDomain string, outflags uint64, err *dbus.Error) {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ t.linkAccess.Unlock()
|
|
|
+
|
|
|
+ serviceName := hostname
|
|
|
+ if hostname != "" && !strings.HasSuffix(hostname, ".") {
|
|
|
+ serviceName += "."
|
|
|
+ }
|
|
|
+ serviceName += sType
|
|
|
+ if !strings.HasSuffix(serviceName, ".") {
|
|
|
+ serviceName += "."
|
|
|
+ }
|
|
|
+ serviceName += domain
|
|
|
+ if !strings.HasSuffix(serviceName, ".") {
|
|
|
+ serviceName += "."
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx := t.logRequest(sender, "ResolveService ", link.iif.Name, " ", hostname, " ", sType, " ", domain, " ", familyToString(family), " ", flags)
|
|
|
+
|
|
|
+ srvRequest := &mDNS.Msg{
|
|
|
+ MsgHdr: mDNS.MsgHdr{
|
|
|
+ RecursionDesired: true,
|
|
|
+ },
|
|
|
+ Question: []mDNS.Question{
|
|
|
+ {
|
|
|
+ Name: serviceName,
|
|
|
+ Qtype: mDNS.TypeSRV,
|
|
|
+ Qclass: mDNS.ClassINET,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ srvResponse, exchangeErr := t.dnsRouter.Exchange(ctx, srvRequest, adapter.DNSQueryOptions{})
|
|
|
+ if exchangeErr != nil {
|
|
|
+ err = wrapError(exchangeErr)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if srvResponse.Rcode != mDNS.RcodeSuccess {
|
|
|
+ err = rcodeError(srvResponse.Rcode)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ txtRequest := &mDNS.Msg{
|
|
|
+ MsgHdr: mDNS.MsgHdr{
|
|
|
+ RecursionDesired: true,
|
|
|
+ },
|
|
|
+ Question: []mDNS.Question{
|
|
|
+ {
|
|
|
+ Name: serviceName,
|
|
|
+ Qtype: mDNS.TypeTXT,
|
|
|
+ Qclass: mDNS.ClassINET,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ txtResponse, exchangeErr := t.dnsRouter.Exchange(ctx, txtRequest, adapter.DNSQueryOptions{})
|
|
|
+ if exchangeErr != nil {
|
|
|
+ err = wrapError(exchangeErr)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, rawRR := range srvResponse.Answer {
|
|
|
+ switch rr := rawRR.(type) {
|
|
|
+ case *mDNS.SRV:
|
|
|
+ var srvRecord SRVRecord
|
|
|
+ srvRecord.Priority = rr.Priority
|
|
|
+ srvRecord.Weight = rr.Weight
|
|
|
+ srvRecord.Port = rr.Port
|
|
|
+ srvRecord.Hostname = rr.Target
|
|
|
+
|
|
|
+ var strategy C.DomainStrategy
|
|
|
+ switch family {
|
|
|
+ case syscall.AF_UNSPEC:
|
|
|
+ strategy = C.DomainStrategyAsIS
|
|
|
+ case syscall.AF_INET:
|
|
|
+ strategy = C.DomainStrategyIPv4Only
|
|
|
+ case syscall.AF_INET6:
|
|
|
+ strategy = C.DomainStrategyIPv6Only
|
|
|
+ }
|
|
|
+
|
|
|
+ addrs, lookupErr := t.dnsRouter.Lookup(ctx, rr.Target, adapter.DNSQueryOptions{
|
|
|
+ LookupStrategy: strategy,
|
|
|
+ })
|
|
|
+ if lookupErr == nil {
|
|
|
+ srvRecord.Addresses = common.Map(addrs, func(it netip.Addr) Address {
|
|
|
+ var addrFamily int32
|
|
|
+ if it.Is4() {
|
|
|
+ addrFamily = syscall.AF_INET
|
|
|
+ } else {
|
|
|
+ addrFamily = syscall.AF_INET6
|
|
|
+ }
|
|
|
+ return Address{
|
|
|
+ IfIndex: ifIndex,
|
|
|
+ Family: addrFamily,
|
|
|
+ Address: it.AsSlice(),
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ for _, a := range srvResponse.Answer {
|
|
|
+ if cname, ok := a.(*mDNS.CNAME); ok && cname.Header().Name == rr.Target {
|
|
|
+ srvRecord.CNAME = cname.Target
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ srvData = append(srvData, srvRecord)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for _, rawRR := range txtResponse.Answer {
|
|
|
+ switch rr := rawRR.(type) {
|
|
|
+ case *mDNS.TXT:
|
|
|
+ data := make([]byte, mDNS.Len(rr))
|
|
|
+ _, packErr := mDNS.PackRR(rr, data, 0, nil, false)
|
|
|
+ if packErr == nil {
|
|
|
+ txtData = append(txtData, data)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ canonicalName = mDNS.CanonicalName(hostname)
|
|
|
+ canonicalType = mDNS.CanonicalName(sType)
|
|
|
+ canonicalDomain = mDNS.CanonicalName(domain)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDNS(sender dbus.Sender, ifIndex int32, addresses []LinkDNS) *dbus.Error {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ defer t.linkAccess.Unlock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return wrapError(err)
|
|
|
+ }
|
|
|
+ link.address = addresses
|
|
|
+ if len(addresses) > 0 {
|
|
|
+ t.log(sender, "SetLinkDNS ", link.iif.Name, " ", strings.Join(common.Map(addresses, func(it LinkDNS) string {
|
|
|
+ return M.AddrFromIP(it.Address).String()
|
|
|
+ }), ", "))
|
|
|
+ } else {
|
|
|
+ t.log(sender, "SetLinkDNS ", link.iif.Name, " (empty)")
|
|
|
+ }
|
|
|
+ return t.postUpdate(link)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDNSEx(sender dbus.Sender, ifIndex int32, addresses []LinkDNSEx) *dbus.Error {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ defer t.linkAccess.Unlock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return wrapError(err)
|
|
|
+ }
|
|
|
+ link.addressEx = addresses
|
|
|
+ if len(addresses) > 0 {
|
|
|
+ t.log(sender, "SetLinkDNSEx ", link.iif.Name, " ", strings.Join(common.Map(addresses, func(it LinkDNSEx) string {
|
|
|
+ return M.SocksaddrFrom(M.AddrFromIP(it.Address), it.Port).String()
|
|
|
+ }), ", "))
|
|
|
+ } else {
|
|
|
+ t.log(sender, "SetLinkDNSEx ", link.iif.Name, " (empty)")
|
|
|
+ }
|
|
|
+ return t.postUpdate(link)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDomains(sender dbus.Sender, ifIndex int32, domains []LinkDomain) *dbus.Error {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ defer t.linkAccess.Unlock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return wrapError(err)
|
|
|
+ }
|
|
|
+ link.domain = domains
|
|
|
+ if len(domains) > 0 {
|
|
|
+ t.log(sender, "SetLinkDomains ", link.iif.Name, " ", strings.Join(common.Map(domains, func(domain LinkDomain) string {
|
|
|
+ if !domain.RoutingOnly {
|
|
|
+ return domain.Domain
|
|
|
+ } else {
|
|
|
+ return "~" + domain.Domain
|
|
|
+ }
|
|
|
+ }), ", "))
|
|
|
+ } else {
|
|
|
+ t.log(sender, "SetLinkDomains ", link.iif.Name, " (empty)")
|
|
|
+ }
|
|
|
+ return t.postUpdate(link)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDefaultRoute(sender dbus.Sender, ifIndex int32, defaultRoute bool) *dbus.Error {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ defer t.linkAccess.Unlock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ link.defaultRoute = defaultRoute
|
|
|
+ if defaultRoute {
|
|
|
+ t.defaultRouteSequence = append(common.Filter(t.defaultRouteSequence, func(it int32) bool { return it != ifIndex }), ifIndex)
|
|
|
+ } else {
|
|
|
+ t.defaultRouteSequence = common.Filter(t.defaultRouteSequence, func(it int32) bool { return it != ifIndex })
|
|
|
+ }
|
|
|
+ var defaultRouteString string
|
|
|
+ if defaultRoute {
|
|
|
+ defaultRouteString = "yes"
|
|
|
+ } else {
|
|
|
+ defaultRouteString = "no"
|
|
|
+ }
|
|
|
+ t.log(sender, "SetLinkDefaultRoute ", link.iif.Name, " ", defaultRouteString)
|
|
|
+ return t.postUpdate(link)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkLLMNR(ifIndex int32, llmnrMode string) *dbus.Error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkMulticastDNS(ifIndex int32, mdnsMode string) *dbus.Error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDNSOverTLS(sender dbus.Sender, ifIndex int32, dotMode string) *dbus.Error {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ defer t.linkAccess.Unlock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return wrapError(err)
|
|
|
+ }
|
|
|
+ switch dotMode {
|
|
|
+ case "yes":
|
|
|
+ link.dnsOverTLS = true
|
|
|
+ case "":
|
|
|
+ dotMode = "no"
|
|
|
+ fallthrough
|
|
|
+ case "opportunistic", "no":
|
|
|
+ link.dnsOverTLS = false
|
|
|
+ }
|
|
|
+ t.log(sender, "SetLinkDNSOverTLS ", link.iif.Name, " ", dotMode)
|
|
|
+ return t.postUpdate(link)
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDNSSEC(ifIndex int32, dnssecMode string) *dbus.Error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) SetLinkDNSSECNegativeTrustAnchors(ifIndex int32, domains []string) *dbus.Error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) RevertLink(sender dbus.Sender, ifIndex int32) *dbus.Error {
|
|
|
+ t.linkAccess.Lock()
|
|
|
+ defer t.linkAccess.Unlock()
|
|
|
+ link, err := t.getLink(ifIndex)
|
|
|
+ if err != nil {
|
|
|
+ return wrapError(err)
|
|
|
+ }
|
|
|
+ delete(t.links, ifIndex)
|
|
|
+ t.log(sender, "RevertLink ", link.iif.Name)
|
|
|
+ return t.postUpdate(link)
|
|
|
+}
|
|
|
+
|
|
|
+// TODO: implement RegisterService, UnregisterService
|
|
|
+
|
|
|
+func (t *resolve1Manager) RegisterService(sender dbus.Sender, identifier string, nameTemplate string, serviceType string, port uint16, priority uint16, weight uint16, txtRecords []TXTRecord) (objectPath dbus.ObjectPath, dbusErr *dbus.Error) {
|
|
|
+ return "", wrapError(E.New("not implemented"))
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) UnregisterService(sender dbus.Sender, servicePath dbus.ObjectPath) error {
|
|
|
+ return wrapError(E.New("not implemented"))
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) ResetStatistics() *dbus.Error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) FlushCaches(sender dbus.Sender) *dbus.Error {
|
|
|
+ t.dnsRouter.ClearCache()
|
|
|
+ t.log(sender, "FlushCaches")
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) ResetServerFeatures() *dbus.Error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (t *resolve1Manager) postUpdate(link *TransportLink) *dbus.Error {
|
|
|
+ if t.updateCallback != nil {
|
|
|
+ return wrapError(t.updateCallback(link))
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func rcodeError(rcode int) *dbus.Error {
|
|
|
+ return dbus.NewError("org.freedesktop.resolve1.DnsError."+mDNS.RcodeToString[rcode], []any{mDNS.RcodeToString[rcode]})
|
|
|
+}
|
|
|
+
|
|
|
+func wrapError(err error) *dbus.Error {
|
|
|
+ if err == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ var rcode dns.RcodeError
|
|
|
+ if errors.As(err, &rcode) {
|
|
|
+ return rcodeError(int(rcode))
|
|
|
+ }
|
|
|
+ return dbus.MakeFailedError(err)
|
|
|
+}
|