package dns import ( "net/netip" "slices" "github.com/sagernet/sing/common" "github.com/miekg/dns" ) func SetClientSubnet(message *dns.Msg, clientSubnet netip.Prefix) *dns.Msg { return setClientSubnet(message, clientSubnet, true) } func clientSubnetFromMessage(message *dns.Msg) netip.Prefix { for _, record := range message.Extra { optRecord, isOPTRecord := record.(*dns.OPT) if !isOPTRecord { continue } for _, option := range optRecord.Option { subnetOption, isEDNS0Subnet := option.(*dns.EDNS0_SUBNET) if !isEDNS0Subnet { continue } address, addressLoaded := netip.AddrFromSlice(subnetOption.Address) if !addressLoaded { return netip.Prefix{} } return netip.PrefixFrom(address.Unmap(), int(subnetOption.SourceNetmask)) } } return netip.Prefix{} } func removeClientSubnet(message *dns.Msg) *dns.Msg { if !slices.ContainsFunc(message.Extra, func(record dns.RR) bool { optRecord, isOPTRecord := record.(*dns.OPT) if !isOPTRecord { return false } return slices.ContainsFunc(optRecord.Option, func(option dns.EDNS0) bool { return option.Option() == dns.EDNS0SUBNET }) }) { return message } message = message.Copy() for _, record := range message.Extra { optRecord, isOPTRecord := record.(*dns.OPT) if !isOPTRecord { continue } optRecord.Option = common.Filter(optRecord.Option, func(option dns.EDNS0) bool { return option.Option() != dns.EDNS0SUBNET }) } return message } func setClientSubnet(message *dns.Msg, clientSubnet netip.Prefix, clone bool) *dns.Msg { var ( optRecord *dns.OPT subnetOption *dns.EDNS0_SUBNET ) findExists: for _, record := range message.Extra { var isOPTRecord bool if optRecord, isOPTRecord = record.(*dns.OPT); isOPTRecord { for _, option := range optRecord.Option { var isEDNS0Subnet bool subnetOption, isEDNS0Subnet = option.(*dns.EDNS0_SUBNET) if isEDNS0Subnet { break findExists } } } } if optRecord == nil { exMessage := *message message = &exMessage optRecord = &dns.OPT{ Hdr: dns.RR_Header{ Name: ".", Rrtype: dns.TypeOPT, }, } message.Extra = append(message.Extra, optRecord) } else if clone { return setClientSubnet(message.Copy(), clientSubnet, false) } if subnetOption == nil { subnetOption = new(dns.EDNS0_SUBNET) subnetOption.Code = dns.EDNS0SUBNET optRecord.Option = append(optRecord.Option, subnetOption) } if clientSubnet.Addr().Is4() { subnetOption.Family = 1 } else { subnetOption.Family = 2 } subnetOption.SourceNetmask = uint8(clientSubnet.Bits()) subnetOption.Address = clientSubnet.Addr().AsSlice() return message }