From b4c8fe6e2cadc13227abdd4c2efaf56c1050aa44 Mon Sep 17 00:00:00 2001 From: arash <arash.cordi@gmail.com> Date: Thu, 4 Apr 2019 14:33:10 +0430 Subject: [PATCH] apply gofmt --- handler/dns_types.go | 279 ++-- handler/dnssec.go | 84 +- handler/dnssec_test.go | 603 ++++---- handler/geoip.go | 364 ++--- handler/geoip_test.go | 391 +++-- handler/handler.go | 1570 ++++++++++---------- handler/handler_test.go | 2807 ++++++++++++++++++----------------- handler/healthcheck.go | 808 +++++----- handler/healthcheck_test.go | 855 ++++++----- handler/limiter.go | 144 +- handler/limiter_test.go | 132 +- handler/server.go | 31 +- handler/subnet_test.go | 83 +- handler/upstream.go | 122 +- handler/upstream_test.go | 134 +- perf/bulk.go | 100 +- perf/gen.go | 12 +- redins.go | 364 ++--- 18 files changed, 4444 insertions(+), 4439 deletions(-) diff --git a/handler/dns_types.go b/handler/dns_types.go index 28f2610..3d25bf5 100644 --- a/handler/dns_types.go +++ b/handler/dns_types.go @@ -1,225 +1,224 @@ package handler import ( - "net" - "crypto" - "github.com/miekg/dns" - "encoding/json" - "github.com/pkg/errors" + "crypto" + "encoding/json" + "github.com/miekg/dns" + "github.com/pkg/errors" + "net" ) type RRSets struct { - A IP_RRSet `json:"a,omitempty"` - AAAA IP_RRSet `json:"aaaa,omitempty"` - TXT TXT_RRSet `json:"txt,omitempty"` - CNAME *CNAME_RRSet `json:"cname,omitempty"` - NS NS_RRSet `json:"ns,omitempty"` - MX MX_RRSet `json:"mx,omitempty"` - SRV SRV_RRSet `json:"srv,omitempty"` - CAA CAA_RRSet `json:"caa,omitempty"` - PTR *PTR_RRSet `json:"ptr,omitempty"` - TLSA TLSA_RRSet `json:"tlsa,omitempty"` - ANAME *ANAME_Record `json:"aname,omitempty"` + A IP_RRSet `json:"a,omitempty"` + AAAA IP_RRSet `json:"aaaa,omitempty"` + TXT TXT_RRSet `json:"txt,omitempty"` + CNAME *CNAME_RRSet `json:"cname,omitempty"` + NS NS_RRSet `json:"ns,omitempty"` + MX MX_RRSet `json:"mx,omitempty"` + SRV SRV_RRSet `json:"srv,omitempty"` + CAA CAA_RRSet `json:"caa,omitempty"` + PTR *PTR_RRSet `json:"ptr,omitempty"` + TLSA TLSA_RRSet `json:"tlsa,omitempty"` + ANAME *ANAME_Record `json:"aname,omitempty"` } type Record struct { - RRSets - Zone *Zone `json:"-"` - Name string `json:"-"` + RRSets + Zone *Zone `json:"-"` + Name string `json:"-"` } type ZoneKey struct { - PublicKey string `json:"public_key,omitmpty"` - PrivateKey string `json:"private_key,omitempty"` - Algorithm uint8 `json:"algorithm,omitempty"` + PublicKey string `json:"public_key,omitmpty"` + PrivateKey string `json:"private_key,omitempty"` + Algorithm uint8 `json:"algorithm,omitempty"` } type ZoneConfig struct { - DomainId string `json:"domain_id,omitempty"` - SOA *SOA_RRSet `json:"soa,omitempty"` - DnsSec bool `json:"dnssec,omitempty"` - CnameFlattening bool `json:"cname_flattening,omitempty"` + DomainId string `json:"domain_id,omitempty"` + SOA *SOA_RRSet `json:"soa,omitempty"` + DnsSec bool `json:"dnssec,omitempty"` + CnameFlattening bool `json:"cname_flattening,omitempty"` } type Zone struct { - Name string - Config ZoneConfig - Locations map[string]struct{} - DnsKey *dns.DNSKEY - DnsKeySig dns.RR - PrivateKey crypto.PrivateKey - KeyInception uint32 - KeyExpiration uint32 + Name string + Config ZoneConfig + Locations map[string]struct{} + DnsKey *dns.DNSKEY + DnsKeySig dns.RR + PrivateKey crypto.PrivateKey + KeyInception uint32 + KeyExpiration uint32 } type IP_RRSet struct { - FilterConfig IpFilterConfig `json:"filter,omitempty"` - HealthCheckConfig IpHealthCheckConfig `json:"health_check,omitempty"` - Ttl uint32 `json:"ttl,omitempty"` - Data []IP_RR `json:"records,omitempty"` + FilterConfig IpFilterConfig `json:"filter,omitempty"` + HealthCheckConfig IpHealthCheckConfig `json:"health_check,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []IP_RR `json:"records,omitempty"` } type IP_RR struct { - Weight int `json:"weight,omitempty"` - Ip net.IP `json:"ip"` - Country []string `json:"country,omitempty"` - ASN []uint `json:"asn,omitempty"` + Weight int `json:"weight,omitempty"` + Ip net.IP `json:"ip"` + Country []string `json:"country,omitempty"` + ASN []uint `json:"asn,omitempty"` } type _IP_RR struct { - Country interface{} `json:"country,omitempty"` - ASN interface{} `json:"asn,omitempty"` - Weight int `json:"weight,omitempty"` - Ip net.IP `json:"ip"` + Country interface{} `json:"country,omitempty"` + ASN interface{} `json:"asn,omitempty"` + Weight int `json:"weight,omitempty"` + Ip net.IP `json:"ip"` } func (iprr *IP_RR) UnmarshalJSON(data []byte) error { - var _ip_rr _IP_RR - if err := json.Unmarshal(data, &_ip_rr); err != nil { - return err - } - - iprr.Ip = _ip_rr.Ip - iprr.Weight = _ip_rr.Weight - - switch v := _ip_rr.Country.(type) { - case nil: - case string: - iprr.Country = []string{v} - case []interface{}: - for _, x := range v { - switch x.(type) { - case string: - iprr.Country = append(iprr.Country, x.(string)) - default: - return errors.Errorf("string expected got %T:%v", x, x) - } - } - default: - return errors.Errorf("cannot parse country value: %v type: %T", v, v) - } - switch v := _ip_rr.ASN.(type) { - case nil: - case float64: - iprr.ASN = []uint{uint(v)} - case []interface{}: - for _, x := range v { - switch x.(type) { - case float64: - iprr.ASN = append(iprr.ASN, uint(x.(float64))) - default: - return errors.Errorf("invalid type:%T:%v", x, x) - } - - } - default: - return errors.Errorf("cannot parse asn value: %v type: %T", v, v) - } - return nil + var _ip_rr _IP_RR + if err := json.Unmarshal(data, &_ip_rr); err != nil { + return err + } + + iprr.Ip = _ip_rr.Ip + iprr.Weight = _ip_rr.Weight + + switch v := _ip_rr.Country.(type) { + case nil: + case string: + iprr.Country = []string{v} + case []interface{}: + for _, x := range v { + switch x.(type) { + case string: + iprr.Country = append(iprr.Country, x.(string)) + default: + return errors.Errorf("string expected got %T:%v", x, x) + } + } + default: + return errors.Errorf("cannot parse country value: %v type: %T", v, v) + } + switch v := _ip_rr.ASN.(type) { + case nil: + case float64: + iprr.ASN = []uint{uint(v)} + case []interface{}: + for _, x := range v { + switch x.(type) { + case float64: + iprr.ASN = append(iprr.ASN, uint(x.(float64))) + default: + return errors.Errorf("invalid type:%T:%v", x, x) + } + + } + default: + return errors.Errorf("cannot parse asn value: %v type: %T", v, v) + } + return nil } type IpHealthCheckConfig struct { - Protocol string `json:"protocol,omitempty"` - Uri string `json:"uri,omitempty"` - Port int `json:"port,omitempty"` - Timeout int `json:"timeout,omitempty"` - UpCount int `json:"up_count,omitempty"` - DownCount int `json:"down_count,omitempty"` - Enable bool `json:"enable,omitempty"` + Protocol string `json:"protocol,omitempty"` + Uri string `json:"uri,omitempty"` + Port int `json:"port,omitempty"` + Timeout int `json:"timeout,omitempty"` + UpCount int `json:"up_count,omitempty"` + DownCount int `json:"down_count,omitempty"` + Enable bool `json:"enable,omitempty"` } type IpFilterConfig struct { - Count string `json:"count,omitempty"` // "multi", "single" - Order string `json:"order,omitmpty"` // "weighted", "rr", "none" - GeoFilter string `json:"geo_filter,omitempty"` // "country", "location", "asn", "asn+country", "none" + Count string `json:"count,omitempty"` // "multi", "single" + Order string `json:"order,omitmpty"` // "weighted", "rr", "none" + GeoFilter string `json:"geo_filter,omitempty"` // "country", "location", "asn", "asn+country", "none" } type CNAME_RRSet struct { - Host string `json:"host"` - Ttl uint32 `json:"ttl,omitempty"` + Host string `json:"host"` + Ttl uint32 `json:"ttl,omitempty"` } type TXT_RRSet struct { - Ttl uint32 `json:"ttl,omitempty"` - Data []TXT_RR `json:"records,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []TXT_RR `json:"records,omitempty"` } type TXT_RR struct { - Text string `json:"text"` + Text string `json:"text"` } type NS_RRSet struct { - Ttl uint32 `json:"ttl,omitempty"` - Data []NS_RR `json:"records,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []NS_RR `json:"records,omitempty"` } type NS_RR struct { - Host string `json:"host"` + Host string `json:"host"` } type MX_RRSet struct { - Ttl uint32 `json:"ttl,omitempty"` - Data []MX_RR `json:"records,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []MX_RR `json:"records,omitempty"` } type MX_RR struct { - Host string `json:"host"` - Preference uint16 `json:"preference"` + Host string `json:"host"` + Preference uint16 `json:"preference"` } type SRV_RRSet struct { - Ttl uint32 `json:"ttl,omitempty"` - Data []SRV_RR `json:"records,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []SRV_RR `json:"records,omitempty"` } type SRV_RR struct { - Target string `json:"target"` - Priority uint16 `json:"priority"` - Weight uint16 `json:"weight"` - Port uint16 `json:"port"` + Target string `json:"target"` + Priority uint16 `json:"priority"` + Weight uint16 `json:"weight"` + Port uint16 `json:"port"` } type CAA_RRSet struct { - Ttl uint32 `json:"ttl,omitempty"` - Data []CAA_RR `json:"records,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []CAA_RR `json:"records,omitempty"` } type CAA_RR struct { - Tag string `json:"tag"` - Value string `json:"value"` - Flag uint8 `json:"flag"` + Tag string `json:"tag"` + Value string `json:"value"` + Flag uint8 `json:"flag"` } type PTR_RRSet struct { - Domain string `json:"domain"` - Ttl uint32 `json:"ttl,omitempty"` + Domain string `json:"domain"` + Ttl uint32 `json:"ttl,omitempty"` } type TLSA_RRSet struct { - Ttl uint32 `json:"ttl,omitempty"` - Data []TLSA_RR `json:"records,omitempty"` + Ttl uint32 `json:"ttl,omitempty"` + Data []TLSA_RR `json:"records,omitempty"` } type TLSA_RR struct { - Usage uint8 `json:"usage"` - Selector uint8 `json:"selector"` - MatchingType uint8 `json:"matching_type"` - Certificate string `json:"certificate"` + Usage uint8 `json:"usage"` + Selector uint8 `json:"selector"` + MatchingType uint8 `json:"matching_type"` + Certificate string `json:"certificate"` } type SOA_RRSet struct { - Ns string `json:"ns"` - MBox string `json:"MBox"` - Data *dns.SOA `json:"-"` - Ttl uint32 `json:"ttl,omitempty"` - Refresh uint32 `json:"refresh"` - Retry uint32 `json:"retry"` - Expire uint32 `json:"expire"` - MinTtl uint32 `json:"minttl"` - Serial uint32 `json:"serial"` + Ns string `json:"ns"` + MBox string `json:"MBox"` + Data *dns.SOA `json:"-"` + Ttl uint32 `json:"ttl,omitempty"` + Refresh uint32 `json:"refresh"` + Retry uint32 `json:"retry"` + Expire uint32 `json:"expire"` + MinTtl uint32 `json:"minttl"` + Serial uint32 `json:"serial"` } type ANAME_Record struct { - Location string `json:"location,omitempty"` + Location string `json:"location,omitempty"` } - diff --git a/handler/dnssec.go b/handler/dnssec.go index b2f6554..0a88c6b 100644 --- a/handler/dnssec.go +++ b/handler/dnssec.go @@ -1,57 +1,57 @@ package handler import ( - "crypto/rsa" - "crypto/ecdsa" - "errors" + "crypto/ecdsa" + "crypto/rsa" + "errors" - "github.com/miekg/dns" - "github.com/hawell/logger" + "github.com/hawell/logger" + "github.com/miekg/dns" ) var ( - NSecTypes = []uint16 { dns.TypeRRSIG, dns.TypeNSEC} + NSecTypes = []uint16{dns.TypeRRSIG, dns.TypeNSEC} ) func Sign(rrs []dns.RR, name string, zone *Zone, ttl uint32) (*dns.RRSIG, error) { - rrsig := &dns.RRSIG { - Hdr: dns.RR_Header { name, dns.TypeRRSIG, dns.ClassINET,ttl, 0}, - Inception:zone.KeyInception, - Expiration:zone.KeyExpiration, - KeyTag:zone.DnsKey.KeyTag(), - SignerName:zone.DnsKey.Hdr.Name, - Algorithm: zone.DnsKey.Algorithm, - } - switch rrsig.Algorithm { - case dns.RSAMD5, dns.RSASHA1, dns.RSASHA1NSEC3SHA1, dns.RSASHA256, dns.RSASHA512: - if err := rrsig.Sign(zone.PrivateKey.(*rsa.PrivateKey), rrs); err != nil { - logger.Default.Errorf("sign failed : %s", err) - return nil, err - } - case dns.ECDSAP256SHA256, dns.ECDSAP384SHA384: - if err := rrsig.Sign(zone.PrivateKey.(*ecdsa.PrivateKey), rrs); err != nil { - logger.Default.Errorf("sign failed : %s", err) - return nil, err - } - case dns.DSA, dns.DSANSEC3SHA1: - //rrsig.Sign(zone.PrivateKey.(*dsa.PrivateKey), rrs) - fallthrough - default: - return nil, errors.New("invalid or not supported algorithm") - } - return rrsig, nil + rrsig := &dns.RRSIG{ + Hdr: dns.RR_Header{name, dns.TypeRRSIG, dns.ClassINET, ttl, 0}, + Inception: zone.KeyInception, + Expiration: zone.KeyExpiration, + KeyTag: zone.DnsKey.KeyTag(), + SignerName: zone.DnsKey.Hdr.Name, + Algorithm: zone.DnsKey.Algorithm, + } + switch rrsig.Algorithm { + case dns.RSAMD5, dns.RSASHA1, dns.RSASHA1NSEC3SHA1, dns.RSASHA256, dns.RSASHA512: + if err := rrsig.Sign(zone.PrivateKey.(*rsa.PrivateKey), rrs); err != nil { + logger.Default.Errorf("sign failed : %s", err) + return nil, err + } + case dns.ECDSAP256SHA256, dns.ECDSAP384SHA384: + if err := rrsig.Sign(zone.PrivateKey.(*ecdsa.PrivateKey), rrs); err != nil { + logger.Default.Errorf("sign failed : %s", err) + return nil, err + } + case dns.DSA, dns.DSANSEC3SHA1: + //rrsig.Sign(zone.PrivateKey.(*dsa.PrivateKey), rrs) + fallthrough + default: + return nil, errors.New("invalid or not supported algorithm") + } + return rrsig, nil } func NSec(name string, zone *Zone) ([]dns.RR, error) { - nsec := &dns.NSEC{ - Hdr: dns.RR_Header{name, dns.TypeNSEC, dns.ClassINET, zone.Config.SOA.MinTtl, 0}, - NextDomain: "\\000." + name, - TypeBitMap: NSecTypes, - } - sigs, err := Sign([]dns.RR{nsec}, name, zone, zone.Config.SOA.MinTtl) - if err != nil { - return nil, err - } + nsec := &dns.NSEC{ + Hdr: dns.RR_Header{name, dns.TypeNSEC, dns.ClassINET, zone.Config.SOA.MinTtl, 0}, + NextDomain: "\\000." + name, + TypeBitMap: NSecTypes, + } + sigs, err := Sign([]dns.RR{nsec}, name, zone, zone.Config.SOA.MinTtl) + if err != nil { + return nil, err + } - return []dns.RR{nsec, sigs}, nil + return []dns.RR{nsec, sigs}, nil } diff --git a/handler/dnssec_test.go b/handler/dnssec_test.go index 3dd5ec6..07b4782 100644 --- a/handler/dnssec_test.go +++ b/handler/dnssec_test.go @@ -1,27 +1,26 @@ package handler import ( - "testing" - "sort" - "fmt" - "log" - "github.com/miekg/dns" - "github.com/hawell/logger" - "github.com/hawell/uperdis" - "arvancloud/redins/test" - "github.com/coredns/coredns/request" + "arvancloud/redins/test" + "fmt" + "github.com/coredns/coredns/request" + "github.com/hawell/logger" + "github.com/hawell/uperdis" + "github.com/miekg/dns" + "log" + "sort" + "testing" ) var dnssecZone = string("dnssec_test.com.") - var dnssecConfig = `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.dnssec_test.com.","ns":"ns1.dnssec_test.com.","refresh":44,"retry":55,"expire":66},"dnssec": true}` -var dnssecEntries = [][]string { - {"@", - `{"ns":{"ttl":300,"records":[{"host":"a.dnssec_test.com."}]}}`, - }, - {"x", - `{ +var dnssecEntries = [][]string{ + {"@", + `{"ns":{"ttl":300,"records":[{"host":"a.dnssec_test.com."}]}}`, + }, + {"x", + `{ "a":{"ttl":300, "records":[{"ip":"1.2.3.4", "country":"ES"},{"ip":"5.6.7.8", "country":""}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}, "txt":{"ttl":300, "records":[{"text":"foo"},{"text":"bar"}]}, @@ -29,41 +28,41 @@ var dnssecEntries = [][]string { "mx":{"ttl":300, "records":[{"host":"mx1.dnssec_test.com.", "preference":10},{"host":"mx2.dnssec_test.com.", "preference":10}]}, "srv":{"ttl":300, "records":[{"target":"sip.dnssec_test.com.","port":555,"priority":10,"weight":100}]} }`, - }, - {"*", - `{"txt":{"ttl":300,"records":[{"text":"wildcard text"}]}}`, - }, - {"a", - `{"a":{"ttl":300,"records":[{"ip":"129.0.2.1"}]},"txt":{"ttl":300,"records":[{"text":"a text"}]}}`, - }, - {"d", - `{"a":{"ttl":300,"records":[{"ip":"129.0.2.1"}]},"txt":{"ttl":300,"records":[{"text":"d text"}]}}`, - }, - {"c1", - `{"cname":{"ttl":300, "host":"c2.dnssec_test.com."}}`, - }, - {"c2", - `{"cname":{"ttl":300, "host":"c3.dnssec_test.com."}}`, - }, - {"c3", - `{"cname":{"ttl":300, "host":"a.dnssec_test.com."}}`, - }, - {"w", - `{"cname":{"ttl":300, "host":"w.a.dnssec_test.com."}}`, - }, - {"*.a", - `{"cname":{"ttl":300, "host":"w.b.dnssec_test.com."}}`, - }, - {"*.b", - `{"cname":{"ttl":300, "host":"w.c.dnssec_test.com."}}`, - }, - {"*.c", - `{"a":{"ttl":300, "records":[{"ip":"129.0.2.1"}]}}`, - }, + }, + {"*", + `{"txt":{"ttl":300,"records":[{"text":"wildcard text"}]}}`, + }, + {"a", + `{"a":{"ttl":300,"records":[{"ip":"129.0.2.1"}]},"txt":{"ttl":300,"records":[{"text":"a text"}]}}`, + }, + {"d", + `{"a":{"ttl":300,"records":[{"ip":"129.0.2.1"}]},"txt":{"ttl":300,"records":[{"text":"d text"}]}}`, + }, + {"c1", + `{"cname":{"ttl":300, "host":"c2.dnssec_test.com."}}`, + }, + {"c2", + `{"cname":{"ttl":300, "host":"c3.dnssec_test.com."}}`, + }, + {"c3", + `{"cname":{"ttl":300, "host":"a.dnssec_test.com."}}`, + }, + {"w", + `{"cname":{"ttl":300, "host":"w.a.dnssec_test.com."}}`, + }, + {"*.a", + `{"cname":{"ttl":300, "host":"w.b.dnssec_test.com."}}`, + }, + {"*.b", + `{"cname":{"ttl":300, "host":"w.c.dnssec_test.com."}}`, + }, + {"*.c", + `{"a":{"ttl":300, "records":[{"ip":"129.0.2.1"}]}}`, + }, } var dnssecKeyPriv = string( -`Private-key-format: v1.3 + `Private-key-format: v1.3 Algorithm: 5 (RSASHA1) Modulus: oqwXm/EF8q6p5Rrj66Bbft+0Vk7Kj6TuvZp4nNl0htiT/8/92kIcri5gbxnV2v+p6jXYQI1Vx/vqP5cB0kPzjUQuJFVpm14fxOp89D6N0fPXR7xJ+SHs5nigHBIJdaP5 PublicExponent: AQAB @@ -80,271 +79,271 @@ Activate: 20180717134704 var dnssecKeyPub = string("dnssec_test.com. IN DNSKEY 256 3 5 AwEAAaKsF5vxBfKuqeUa4+ugW37ftFZOyo+k7r2aeJzZdIbYk//P/dpC HK4uYG8Z1dr/qeo12ECNVcf76j+XAdJD841ELiRVaZteH8TqfPQ+jdHz 10e8Sfkh7OZ4oBwSCXWj+Q==") -var dnskeyQuery = test.Case { - Do: true, - Qname: "dnssec_test.com", Qtype: dns.TypeDNSKEY, +var dnskeyQuery = test.Case{ + Do: true, + Qname: "dnssec_test.com", Qtype: dns.TypeDNSKEY, } var dnssecTestCases = []test.Case{ - { - Qname: "x.dnssec_test.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("x.dnssec_test.com. 300 IN A 1.2.3.4"), - test.A("x.dnssec_test.com. 300 IN A 5.6.7.8"), - test.RRSIG("x.dnssec_test.com. 300 IN RRSIG A 5 3 300 20180726080503 20180718050503 22548 dnssec_test.com. b/rdGOnMQzKX4K9c3CLvJYb2ErFlrShy8vBh86Y28t1RRnN9OCj7L1AGhr+5xEge3mpuRNd2djXFh7CwZmAOm6R0/acRP1mw1RnlSANhaVt1Enr57c6+5grPgn7e45X3"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - { - Qname: "x.dnssec_test.com.", Qtype: dns.TypeAAAA, - Answer: []dns.RR{ - test.AAAA("x.dnssec_test.com. 300 IN AAAA ::1"), - test.RRSIG("x.dnssec_test.com. 300 IN RRSIG AAAA 5 3 300 20180726102716 20180718072716 22548 dnssec_test.com. Bl6GjbEY2jXyWhVuQzehQs4RVvrIRvLz72eXjvRKXTg6BGmcZF7CyZo1+R2w3p83gAA0yhs6UnSD/GMC5zmLeR5/8LiTzWa0S5f5xZNHwWNEUtrtnS7nGCCFDXfLUI3n"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // TXT Test - { - Qname: "x.dnssec_test.com.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("x.dnssec_test.com. 300 IN TXT bar"), - test.TXT("x.dnssec_test.com. 300 IN TXT foo"), - test.RRSIG("x.dnssec_test.com. 300 IN RRSIG TXT 5 3 300 20180726102908 20180718072908 22548 dnssec_test.com. NND6mWXgQ1CY/KTsgPcjvty7FdLCFQdoHQ6Rmyv2hpPg12xTmAokB/TScTeL+zhvtt+9ktYnErspZc3LVoyPqZ8TYppHHoEXDR8OpyqmVcTPx/fzRuW5zmuUpofnhlV6"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // NS Test - { - Qname: "x.dnssec_test.com.", Qtype: dns.TypeNS, - Answer: []dns.RR{ - test.NS("x.dnssec_test.com. 300 IN NS ns1.dnssec_test.com."), - test.NS("x.dnssec_test.com. 300 IN NS ns2.dnssec_test.com."), - test.RRSIG("x.dnssec_test.com. 300 IN RRSIG NS 5 3 300 20180726104727 20180718074727 22548 dnssec_test.com. NTYiqJBR8hFjYQcHeuUUWH2zIEqpF5xfFeHBb24icTbd5kg7VU9QHkzc/odnAFu80SfDJVnxX9OTV7re8Epp06CBT7m8VpUUv6+qnn6ma2qukWa8wyvFPg/PXJLA8cpG"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // MX Test - { - Qname: "x.dnssec_test.com.", Qtype: dns.TypeMX, - Answer: []dns.RR{ - test.MX("x.dnssec_test.com. 300 IN MX 10 mx1.dnssec_test.com."), - test.MX("x.dnssec_test.com. 300 IN MX 10 mx2.dnssec_test.com."), - test.RRSIG("x.dnssec_test.com. 300 IN RRSIG MX 5 3 300 20180726104823 20180718074823 22548 dnssec_test.com. I0il28K7OmjA/hRwV/uPyieeg+EnpxRQmcUvZ1JsijIAqf6FVqDbysgrZfzZBheizMuLsEjPmmVTJrl34Y1ZEHxwD9oxgxWSDQ4L7kHLUeOSTRA73maHOtr+Sypygw6E"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // SRV Test - { - Qname: "x.dnssec_test.com.", Qtype: dns.TypeSRV, - Answer: []dns.RR{ - test.SRV("x.dnssec_test.com. 300 IN SRV 10 100 555 sip.dnssec_test.com."), - test.RRSIG("x.dnssec_test.com. 300 IN RRSIG SRV 5 3 300 20180726104916 20180718074916 22548 dnssec_test.com. hwyeNmMQ6K6Ja/ogepGQvGEyEiBeCd7Suhb6CL/uEREuREq1wcr9QhS2s3yKy9ZhjO9xs2x38vSSZHvRBvTjVxMIpPaQuxcWI02s/NgVLkRA5H0LpBPE5pyXDxTmtavV"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // SOA Test - { - Qname: "dnssec_test.com.", Qtype: dns.TypeSOA, - Answer: []dns.RR{ - test.SOA("dnssec_test.com. 300 IN SOA ns1.dnssec_test.com. hostmaster.dnssec_test.com. 1533107401 44 55 66 100"), - test.RRSIG("dnssec_test.com. 300 IN RRSIG SOA 5 2 300 20180809071001 20180801041001 22548 dnssec_test.com. O4+6kPz9sr26RDZLy9MUoQRFweEzVZJ8JvQAJ+3mcZ/xO8z4KKNRb3Gpf7sWyoQk6Bd476VkZHbkbEf9SRptDqDHPV5MxMDUa3AtbdwUkRaVDidL95B4KDcno5FOU55I"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // NXDomain Test - { - Qname: "nxdomain.x.dnssec_test.com.", Qtype: dns.TypeAAAA, - Ns: []dns.RR{ - test.SOA("dnssec_test.com. 300 IN SOA ns1.dnssec_test.com. hostmaster.dnssec_test.com. 1533107621 44 55 66 100"), - test.RRSIG("dnssec_test.com. 300 IN RRSIG SOA 5 2 300 20180809071341 20180801041341 22548 dnssec_test.com. hJ6GxQo46z5hxBV48hs5Ab1tdfCJ1S7wxIIoI3cksCtf+dqv/eLmlxGH0KuEabAPWhp9VqyjjQYxvSP/0gH0Z/BwYxoghxrROuqHqiIbkbM8wvgLHBwNv+vA4xXUN/Ej"), - test.NSEC("nxdomain.x.dnssec_test.com. 100 IN NSEC \\000.nxdomain.x.dnssec_test.com. RRSIG NSEC"), - test.RRSIG("nxdomain.x.dnssec_test.com. 100 IN RRSIG NSEC 5 4 100 20180809115341 20180801085341 22548 dnssec_test.com. cHqIhWUalUAib9cpVd+4XLLzxrm6zKiQKLWs1/2T4dNhaS/CAkIXY6so0YDpsm0wgS2McpVd/GL+2fPDEb0MXJYyTfX8mzn5i49riQjEiHbmlL7oZfXCUKxKTRYczxjf"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // wildcard Test - { - Qname: "z.dnssec_test.com.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("z.dnssec_test.com. 300 IN TXT \"wildcard text\""), - test.RRSIG("z.dnssec_test.com. 300 IN RRSIG TXT 5 3 300 20180731095235 20180723065235 22548 dnssec_test.com. YCmkNMLkg6qtey+9+Yt+Jq0V1itDF9Gw8rodPk82b486jE22xxleLq8zcwne8Xekp57H/9Sk5mmTzczWTZQAUauUQF+o2QzLkgiI5vr0gtC5Y3fraRCDclo9/8IQ2yEs"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // cname flattening test - { - Qname:"c1.dnssec_test.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.CNAME("c1.dnssec_test.com. 300 IN CNAME c2.dnssec_test.com."), - test.RRSIG("c1.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. lvcR8ruQHs3qnQd+SZEr8LsTfIbcPQr7G6xHprp0vgcjstnb+0egDgJNJfJZHanwn3Ya/72Bqww3cDpIFV/8/kSVlSYz4cMb9hJR8Cq+ttFsRAFgSEA0cFxX4fG6WG85"), - test.CNAME("c2.dnssec_test.com. 300 IN CNAME c3.dnssec_test.com."), - test.RRSIG("c2.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. YNSfNKSz5LOhhoeGmZ77aLE/Z/QZEnkz5UD8g9fxalAkogVKR/bAEYcNkxMh5u5wjTH9/HnWMBLkK56FjmXIrI5KeY3paXWJ85QJJGeTAcwj/uLgF0Qq+nVCqldudmN+"), - test.CNAME("c3.dnssec_test.com. 300 IN CNAME a.dnssec_test.com."), - test.RRSIG("c3.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. FFE4WsYh2sAsYlewm1/1/GSo0oeFwJPt+35C2k/6nB+w+9/rBcRXwS8kfEvCuJS4GxcYV/vCLncQxNY5OI7Q5Vaxyo1OV+xWYY7OKTS7MBivUdlNvquMMkgIqZwqYdFl"), - test.A("a.dnssec_test.com. 300 IN A 129.0.2.1"), - test.RRSIG("a.dnssec_test.com. 300 IN RRSIG A 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. fKHuZTJgweFmBmASxDiZYr8r300CtAmJ03ICKAHS8FkATjLvUyZxWqjI/fExZz277pZ0FMGRiwIb7o6aI31fpAahtU1E0Mo7J0sXjVATCBhME0S88DDuPXgrOMzu8f7K"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, - // CNAME flattening + wildcard Test - { - Qname:"w.dnssec_test.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.CNAME("w.a.dnssec_test.com. 300 IN CNAME w.b.dnssec_test.com."), - test.RRSIG("w.a.dnssec_test.com. 300 IN RRSIG CNAME 5 4 300 20180801064612 20180724034612 22548 dnssec_test.com. OZlpQZTJH6KjNJPDuB/YPQORgwRfPpGz5FR0AReqRizAJMOjPSNjcmzpjpFXi7N5Hg+x+15RD0pnE8yL6XXSrg5pNsQo7p9XJa/6H9AL9OGMgYcOJe5FRJwHN9XXGrVr"), - test.CNAME("w.b.dnssec_test.com. 300 IN CNAME w.c.dnssec_test.com."), - test.RRSIG("w.b.dnssec_test.com. 300 IN RRSIG CNAME 5 4 300 20180801064612 20180724034612 22548 dnssec_test.com. VMs35joPFxyRrWtz1gyGRKju9j6p7MrQihOwU8m7cmCKmNT/6e58qS3OYYnp6tH34IxJnf+DZGapL07pMwSe+JyaOpsSirTmmytKU6NRQoidijKa7QkMXtXpY1l70Fga"), - test.A("w.c.dnssec_test.com. 300 IN A 129.0.2.1"), - test.RRSIG("w.c.dnssec_test.com. 300 IN RRSIG A 5 4 300 20180801064612 20180724034612 22548 dnssec_test.com. LrrMYhyADHnznyVFx/DKqpteVrRqqOIgkrWzpOO3AI8Mx1xTfNqy6xMi/ngZPRfUuLHqkp9dyYhJN1qHrRwu2rJw1P+X3n7oD3hDL982ppB3hYAWPzTcwYO0C5848AQD"), - test.CNAME("w.dnssec_test.com. 300 IN CNAME w.a.dnssec_test.com."), - test.RRSIG("w.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180801064612 20180724034612 22548 dnssec_test.com. fgaoAooAffMg2apxMqmQBKgVVTGx+PaOo7ik61DvsG9UP7EeBQ7K0bNGxYlcQHDv7aZdLwtTU5OpLk2UCbZPhVAr69Irdr0RYOc+/Jzgw0u+iWU2o0ERxUG9ICiB+Ix8"), - }, - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - }, + { + Qname: "x.dnssec_test.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("x.dnssec_test.com. 300 IN A 1.2.3.4"), + test.A("x.dnssec_test.com. 300 IN A 5.6.7.8"), + test.RRSIG("x.dnssec_test.com. 300 IN RRSIG A 5 3 300 20180726080503 20180718050503 22548 dnssec_test.com. b/rdGOnMQzKX4K9c3CLvJYb2ErFlrShy8vBh86Y28t1RRnN9OCj7L1AGhr+5xEge3mpuRNd2djXFh7CwZmAOm6R0/acRP1mw1RnlSANhaVt1Enr57c6+5grPgn7e45X3"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + { + Qname: "x.dnssec_test.com.", Qtype: dns.TypeAAAA, + Answer: []dns.RR{ + test.AAAA("x.dnssec_test.com. 300 IN AAAA ::1"), + test.RRSIG("x.dnssec_test.com. 300 IN RRSIG AAAA 5 3 300 20180726102716 20180718072716 22548 dnssec_test.com. Bl6GjbEY2jXyWhVuQzehQs4RVvrIRvLz72eXjvRKXTg6BGmcZF7CyZo1+R2w3p83gAA0yhs6UnSD/GMC5zmLeR5/8LiTzWa0S5f5xZNHwWNEUtrtnS7nGCCFDXfLUI3n"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // TXT Test + { + Qname: "x.dnssec_test.com.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("x.dnssec_test.com. 300 IN TXT bar"), + test.TXT("x.dnssec_test.com. 300 IN TXT foo"), + test.RRSIG("x.dnssec_test.com. 300 IN RRSIG TXT 5 3 300 20180726102908 20180718072908 22548 dnssec_test.com. NND6mWXgQ1CY/KTsgPcjvty7FdLCFQdoHQ6Rmyv2hpPg12xTmAokB/TScTeL+zhvtt+9ktYnErspZc3LVoyPqZ8TYppHHoEXDR8OpyqmVcTPx/fzRuW5zmuUpofnhlV6"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // NS Test + { + Qname: "x.dnssec_test.com.", Qtype: dns.TypeNS, + Answer: []dns.RR{ + test.NS("x.dnssec_test.com. 300 IN NS ns1.dnssec_test.com."), + test.NS("x.dnssec_test.com. 300 IN NS ns2.dnssec_test.com."), + test.RRSIG("x.dnssec_test.com. 300 IN RRSIG NS 5 3 300 20180726104727 20180718074727 22548 dnssec_test.com. NTYiqJBR8hFjYQcHeuUUWH2zIEqpF5xfFeHBb24icTbd5kg7VU9QHkzc/odnAFu80SfDJVnxX9OTV7re8Epp06CBT7m8VpUUv6+qnn6ma2qukWa8wyvFPg/PXJLA8cpG"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // MX Test + { + Qname: "x.dnssec_test.com.", Qtype: dns.TypeMX, + Answer: []dns.RR{ + test.MX("x.dnssec_test.com. 300 IN MX 10 mx1.dnssec_test.com."), + test.MX("x.dnssec_test.com. 300 IN MX 10 mx2.dnssec_test.com."), + test.RRSIG("x.dnssec_test.com. 300 IN RRSIG MX 5 3 300 20180726104823 20180718074823 22548 dnssec_test.com. I0il28K7OmjA/hRwV/uPyieeg+EnpxRQmcUvZ1JsijIAqf6FVqDbysgrZfzZBheizMuLsEjPmmVTJrl34Y1ZEHxwD9oxgxWSDQ4L7kHLUeOSTRA73maHOtr+Sypygw6E"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // SRV Test + { + Qname: "x.dnssec_test.com.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.SRV("x.dnssec_test.com. 300 IN SRV 10 100 555 sip.dnssec_test.com."), + test.RRSIG("x.dnssec_test.com. 300 IN RRSIG SRV 5 3 300 20180726104916 20180718074916 22548 dnssec_test.com. hwyeNmMQ6K6Ja/ogepGQvGEyEiBeCd7Suhb6CL/uEREuREq1wcr9QhS2s3yKy9ZhjO9xs2x38vSSZHvRBvTjVxMIpPaQuxcWI02s/NgVLkRA5H0LpBPE5pyXDxTmtavV"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // SOA Test + { + Qname: "dnssec_test.com.", Qtype: dns.TypeSOA, + Answer: []dns.RR{ + test.SOA("dnssec_test.com. 300 IN SOA ns1.dnssec_test.com. hostmaster.dnssec_test.com. 1533107401 44 55 66 100"), + test.RRSIG("dnssec_test.com. 300 IN RRSIG SOA 5 2 300 20180809071001 20180801041001 22548 dnssec_test.com. O4+6kPz9sr26RDZLy9MUoQRFweEzVZJ8JvQAJ+3mcZ/xO8z4KKNRb3Gpf7sWyoQk6Bd476VkZHbkbEf9SRptDqDHPV5MxMDUa3AtbdwUkRaVDidL95B4KDcno5FOU55I"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // NXDomain Test + { + Qname: "nxdomain.x.dnssec_test.com.", Qtype: dns.TypeAAAA, + Ns: []dns.RR{ + test.SOA("dnssec_test.com. 300 IN SOA ns1.dnssec_test.com. hostmaster.dnssec_test.com. 1533107621 44 55 66 100"), + test.RRSIG("dnssec_test.com. 300 IN RRSIG SOA 5 2 300 20180809071341 20180801041341 22548 dnssec_test.com. hJ6GxQo46z5hxBV48hs5Ab1tdfCJ1S7wxIIoI3cksCtf+dqv/eLmlxGH0KuEabAPWhp9VqyjjQYxvSP/0gH0Z/BwYxoghxrROuqHqiIbkbM8wvgLHBwNv+vA4xXUN/Ej"), + test.NSEC("nxdomain.x.dnssec_test.com. 100 IN NSEC \\000.nxdomain.x.dnssec_test.com. RRSIG NSEC"), + test.RRSIG("nxdomain.x.dnssec_test.com. 100 IN RRSIG NSEC 5 4 100 20180809115341 20180801085341 22548 dnssec_test.com. cHqIhWUalUAib9cpVd+4XLLzxrm6zKiQKLWs1/2T4dNhaS/CAkIXY6so0YDpsm0wgS2McpVd/GL+2fPDEb0MXJYyTfX8mzn5i49riQjEiHbmlL7oZfXCUKxKTRYczxjf"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // wildcard Test + { + Qname: "z.dnssec_test.com.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("z.dnssec_test.com. 300 IN TXT \"wildcard text\""), + test.RRSIG("z.dnssec_test.com. 300 IN RRSIG TXT 5 3 300 20180731095235 20180723065235 22548 dnssec_test.com. YCmkNMLkg6qtey+9+Yt+Jq0V1itDF9Gw8rodPk82b486jE22xxleLq8zcwne8Xekp57H/9Sk5mmTzczWTZQAUauUQF+o2QzLkgiI5vr0gtC5Y3fraRCDclo9/8IQ2yEs"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // cname flattening test + { + Qname: "c1.dnssec_test.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("c1.dnssec_test.com. 300 IN CNAME c2.dnssec_test.com."), + test.RRSIG("c1.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. lvcR8ruQHs3qnQd+SZEr8LsTfIbcPQr7G6xHprp0vgcjstnb+0egDgJNJfJZHanwn3Ya/72Bqww3cDpIFV/8/kSVlSYz4cMb9hJR8Cq+ttFsRAFgSEA0cFxX4fG6WG85"), + test.CNAME("c2.dnssec_test.com. 300 IN CNAME c3.dnssec_test.com."), + test.RRSIG("c2.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. YNSfNKSz5LOhhoeGmZ77aLE/Z/QZEnkz5UD8g9fxalAkogVKR/bAEYcNkxMh5u5wjTH9/HnWMBLkK56FjmXIrI5KeY3paXWJ85QJJGeTAcwj/uLgF0Qq+nVCqldudmN+"), + test.CNAME("c3.dnssec_test.com. 300 IN CNAME a.dnssec_test.com."), + test.RRSIG("c3.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. FFE4WsYh2sAsYlewm1/1/GSo0oeFwJPt+35C2k/6nB+w+9/rBcRXwS8kfEvCuJS4GxcYV/vCLncQxNY5OI7Q5Vaxyo1OV+xWYY7OKTS7MBivUdlNvquMMkgIqZwqYdFl"), + test.A("a.dnssec_test.com. 300 IN A 129.0.2.1"), + test.RRSIG("a.dnssec_test.com. 300 IN RRSIG A 5 3 300 20180731105909 20180723075909 22548 dnssec_test.com. fKHuZTJgweFmBmASxDiZYr8r300CtAmJ03ICKAHS8FkATjLvUyZxWqjI/fExZz277pZ0FMGRiwIb7o6aI31fpAahtU1E0Mo7J0sXjVATCBhME0S88DDuPXgrOMzu8f7K"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, + // CNAME flattening + wildcard Test + { + Qname: "w.dnssec_test.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("w.a.dnssec_test.com. 300 IN CNAME w.b.dnssec_test.com."), + test.RRSIG("w.a.dnssec_test.com. 300 IN RRSIG CNAME 5 4 300 20180801064612 20180724034612 22548 dnssec_test.com. OZlpQZTJH6KjNJPDuB/YPQORgwRfPpGz5FR0AReqRizAJMOjPSNjcmzpjpFXi7N5Hg+x+15RD0pnE8yL6XXSrg5pNsQo7p9XJa/6H9AL9OGMgYcOJe5FRJwHN9XXGrVr"), + test.CNAME("w.b.dnssec_test.com. 300 IN CNAME w.c.dnssec_test.com."), + test.RRSIG("w.b.dnssec_test.com. 300 IN RRSIG CNAME 5 4 300 20180801064612 20180724034612 22548 dnssec_test.com. VMs35joPFxyRrWtz1gyGRKju9j6p7MrQihOwU8m7cmCKmNT/6e58qS3OYYnp6tH34IxJnf+DZGapL07pMwSe+JyaOpsSirTmmytKU6NRQoidijKa7QkMXtXpY1l70Fga"), + test.A("w.c.dnssec_test.com. 300 IN A 129.0.2.1"), + test.RRSIG("w.c.dnssec_test.com. 300 IN RRSIG A 5 4 300 20180801064612 20180724034612 22548 dnssec_test.com. LrrMYhyADHnznyVFx/DKqpteVrRqqOIgkrWzpOO3AI8Mx1xTfNqy6xMi/ngZPRfUuLHqkp9dyYhJN1qHrRwu2rJw1P+X3n7oD3hDL982ppB3hYAWPzTcwYO0C5848AQD"), + test.CNAME("w.dnssec_test.com. 300 IN CNAME w.a.dnssec_test.com."), + test.RRSIG("w.dnssec_test.com. 300 IN RRSIG CNAME 5 3 300 20180801064612 20180724034612 22548 dnssec_test.com. fgaoAooAffMg2apxMqmQBKgVVTGx+PaOo7ik61DvsG9UP7EeBQ7K0bNGxYlcQHDv7aZdLwtTU5OpLk2UCbZPhVAr69Irdr0RYOc+/Jzgw0u+iWU2o0ERxUG9ICiB+Ix8"), + }, + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + }, } -var dnssecTestConfig = HandlerConfig { - MaxTtl: 300, - CacheTimeout: 60, - ZoneReload: 600, - Redis: uperdis.RedisConfig { - Ip: "redis", - Port: 6379, - DB: 0, - Password: "", - Prefix: "test_", - Suffix: "_test", - ConnectTimeout: 0, - ReadTimeout: 0, - }, - Log: logger.LogConfig { - Enable: false, - }, - Upstream: []UpstreamConfig { - { - Ip: "1.1.1.1", - Port: 53, - Protocol: "udp", - Timeout: 1000, - }, - }, - GeoIp: GeoIpConfig { - Enable: true, - CountryDB: "../geoCity.mmdb", - }, +var dnssecTestConfig = HandlerConfig{ + MaxTtl: 300, + CacheTimeout: 60, + ZoneReload: 600, + Redis: uperdis.RedisConfig{ + Ip: "redis", + Port: 6379, + DB: 0, + Password: "", + Prefix: "test_", + Suffix: "_test", + ConnectTimeout: 0, + ReadTimeout: 0, + }, + Log: logger.LogConfig{ + Enable: false, + }, + Upstream: []UpstreamConfig{ + { + Ip: "1.1.1.1", + Port: 53, + Protocol: "udp", + Timeout: 1000, + }, + }, + GeoIp: GeoIpConfig{ + Enable: true, + CountryDB: "../geoCity.mmdb", + }, } func TestDNSSEC(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) + logger.Default = logger.NewLogger(&logger.LogConfig{}) - h := NewHandler(&dnssecTestConfig) + h := NewHandler(&dnssecTestConfig) - h.Redis.Del(dnssecZone) - for _, cmd := range dnssecEntries { - err := h.Redis.HSet("redins:zones:" + dnssecZone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - t.Fail() - } - } - h.Redis.Set("redins:zones:" + dnssecZone + ":config", dnssecConfig) - h.Redis.Set("redins:zones:" + dnssecZone + ":pub", dnssecKeyPub) - h.Redis.Set("redins:zones:" + dnssecZone + ":priv", dnssecKeyPriv) - h.Redis.SAdd("redins:zones", dnssecZone) - h.LoadZones() + h.Redis.Del(dnssecZone) + for _, cmd := range dnssecEntries { + err := h.Redis.HSet("redins:zones:"+dnssecZone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + t.Fail() + } + } + h.Redis.Set("redins:zones:"+dnssecZone+":config", dnssecConfig) + h.Redis.Set("redins:zones:"+dnssecZone+":pub", dnssecKeyPub) + h.Redis.Set("redins:zones:"+dnssecZone+":priv", dnssecKeyPriv) + h.Redis.SAdd("redins:zones", dnssecZone) + h.LoadZones() - var dnskey dns.RR - { - r := dnskeyQuery.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - resp := w.Msg - fmt.Println(resp.Answer) - dnskey = resp.Answer[0] - } + var dnskey dns.RR + { + r := dnskeyQuery.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + resp := w.Msg + fmt.Println(resp.Answer) + dnskey = resp.Answer[0] + } - for i, tc0 := range dnssecTestCases { - tc := test.Case{ - Qname: dnssecTestCases[i].Qname, Qtype: dnssecTestCases[i].Qtype, - Answer: make([]dns.RR, len(dnssecTestCases[i].Answer)), - Ns: make([]dns.RR, len(dnssecTestCases[i].Ns)), - Do: true, - Extra: []dns.RR{ - test.OPT(4096, true), - }, - } - copy(tc.Answer, dnssecTestCases[i].Answer) - copy(tc.Ns, dnssecTestCases[i].Ns) - sort.Sort(test.RRSet(tc.Answer)) - sort.Sort(test.RRSet(tc.Ns)) + for i, tc0 := range dnssecTestCases { + tc := test.Case{ + Qname: dnssecTestCases[i].Qname, Qtype: dnssecTestCases[i].Qtype, + Answer: make([]dns.RR, len(dnssecTestCases[i].Answer)), + Ns: make([]dns.RR, len(dnssecTestCases[i].Ns)), + Do: true, + Extra: []dns.RR{ + test.OPT(4096, true), + }, + } + copy(tc.Answer, dnssecTestCases[i].Answer) + copy(tc.Ns, dnssecTestCases[i].Ns) + sort.Sort(test.RRSet(tc.Answer)) + sort.Sort(test.RRSet(tc.Ns)) - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - resp := w.Msg - if i == 7 { - fmt.Println("here") - } - for zz, rrs := range ([][]dns.RR{tc0.Answer, tc0.Ns, resp.Answer, resp.Ns}) { - fmt.Println(zz) - s := 0 - e := 1 - for { - if s >= len(rrs) || e >= len(rrs) { - break - } - if rrsig, ok := rrs[e].(*dns.RRSIG); ok { - fmt.Printf("s = %d, e = %d\n", s, e) - if rrsig.Verify(dnskey.(*dns.DNSKEY), rrs[s:e]) != nil { - fmt.Println("fail") - t.Fail() - } - s = e+1 - e = s+1 - } else { - e++ - } - } - } - fmt.Println("dddd") - if test.SortAndCheck(resp, tc) != nil { - t.Fail() - } - fmt.Println("xxxx") - } + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + resp := w.Msg + if i == 7 { + fmt.Println("here") + } + for zz, rrs := range [][]dns.RR{tc0.Answer, tc0.Ns, resp.Answer, resp.Ns} { + fmt.Println(zz) + s := 0 + e := 1 + for { + if s >= len(rrs) || e >= len(rrs) { + break + } + if rrsig, ok := rrs[e].(*dns.RRSIG); ok { + fmt.Printf("s = %d, e = %d\n", s, e) + if rrsig.Verify(dnskey.(*dns.DNSKEY), rrs[s:e]) != nil { + fmt.Println("fail") + t.Fail() + } + s = e + 1 + e = s + 1 + } else { + e++ + } + } + } + fmt.Println("dddd") + if test.SortAndCheck(resp, tc) != nil { + t.Fail() + } + fmt.Println("xxxx") + } } diff --git a/handler/geoip.go b/handler/geoip.go index 4333e0e..56c5cd0 100644 --- a/handler/geoip.go +++ b/handler/geoip.go @@ -1,217 +1,217 @@ package handler import ( - "math" - "net" + "math" + "net" - "github.com/oschwald/maxminddb-golang" - "github.com/hawell/logger" + "github.com/hawell/logger" + "github.com/oschwald/maxminddb-golang" ) type GeoIp struct { - Enable bool - CountryDB *maxminddb.Reader - ASNDB *maxminddb.Reader + Enable bool + CountryDB *maxminddb.Reader + ASNDB *maxminddb.Reader } type GeoIpConfig struct { - Enable bool `json:"enable,omitempty"` - CountryDB string `json:"country_db,omitempty"` - ASNDB string `json:"asn_db,omitempty"` + Enable bool `json:"enable,omitempty"` + CountryDB string `json:"country_db,omitempty"` + ASNDB string `json:"asn_db,omitempty"` } func NewGeoIp(config *GeoIpConfig) *GeoIp { - g := &GeoIp { - Enable: config.Enable, - } - var err error - if g.Enable { - g.CountryDB, err = maxminddb.Open(config.CountryDB) - if err != nil { - logger.Default.Errorf("cannot open maxminddb file %s: %s", config.CountryDB, err) - } - g.ASNDB, err = maxminddb.Open(config.ASNDB) - if err != nil { - logger.Default.Errorf("cannot open maxminddb file %s: %s", config.ASNDB, err) - } - } - // defer g.db.Close() - return g + g := &GeoIp{ + Enable: config.Enable, + } + var err error + if g.Enable { + g.CountryDB, err = maxminddb.Open(config.CountryDB) + if err != nil { + logger.Default.Errorf("cannot open maxminddb file %s: %s", config.CountryDB, err) + } + g.ASNDB, err = maxminddb.Open(config.ASNDB) + if err != nil { + logger.Default.Errorf("cannot open maxminddb file %s: %s", config.ASNDB, err) + } + } + // defer g.db.Close() + return g } func (g *GeoIp) GetSameCountry(sourceIp net.IP, ips []IP_RR, logData map[string]interface{}) []IP_RR { - if !g.Enable || g.CountryDB == nil { - return ips - } - _, _, sourceCountry, err := g.GetGeoLocation(sourceIp) - if err != nil { - logger.Default.Error("getSameCountry failed") - return ips - } - logData["SourceCountry"] = sourceCountry - - var result []IP_RR - if sourceCountry != "" { - for _, ip := range ips { - for _, country := range ip.Country { - if country == sourceCountry { - result = append(result, ip) - break - } - } - } - } - if len(result) > 0 { - return result - } - - for _, ip := range ips { - if ip.Country == nil || len(ip.Country) == 0 { - result = append(result, ip) - } else { - for _, country := range ip.Country { - if country == "" { - result = append(result, ip) - break - } - } - } - } - if len(result) > 0 { - return result - } - - return ips + if !g.Enable || g.CountryDB == nil { + return ips + } + _, _, sourceCountry, err := g.GetGeoLocation(sourceIp) + if err != nil { + logger.Default.Error("getSameCountry failed") + return ips + } + logData["SourceCountry"] = sourceCountry + + var result []IP_RR + if sourceCountry != "" { + for _, ip := range ips { + for _, country := range ip.Country { + if country == sourceCountry { + result = append(result, ip) + break + } + } + } + } + if len(result) > 0 { + return result + } + + for _, ip := range ips { + if ip.Country == nil || len(ip.Country) == 0 { + result = append(result, ip) + } else { + for _, country := range ip.Country { + if country == "" { + result = append(result, ip) + break + } + } + } + } + if len(result) > 0 { + return result + } + + return ips } func (g *GeoIp) GetSameASN(sourceIp net.IP, ips []IP_RR, logData map[string]interface{}) []IP_RR { - if !g.Enable || g.ASNDB == nil { - return ips - } - sourceASN, err := g.GetASN(sourceIp) - if err != nil { - logger.Default.Error("getSameASN failed") - return ips - } - logData["SourceASN"] = sourceASN - - var result []IP_RR - if sourceASN != 0 { - for _, ip := range ips { - for _, asn := range ip.ASN { - if asn == sourceASN { - result = append(result, ip) - break - } - } - } - } - if len(result) > 0 { - return result - } - - for _, ip := range ips { - if ip.ASN == nil || len(ip.ASN) == 0 { - result = append(result, ip) - } else { - for _, asn := range ip.ASN { - if asn == 0 { - result = append(result, ip) - break - } - } - } - } - if len(result) > 0 { - return result - } - - return ips + if !g.Enable || g.ASNDB == nil { + return ips + } + sourceASN, err := g.GetASN(sourceIp) + if err != nil { + logger.Default.Error("getSameASN failed") + return ips + } + logData["SourceASN"] = sourceASN + + var result []IP_RR + if sourceASN != 0 { + for _, ip := range ips { + for _, asn := range ip.ASN { + if asn == sourceASN { + result = append(result, ip) + break + } + } + } + } + if len(result) > 0 { + return result + } + + for _, ip := range ips { + if ip.ASN == nil || len(ip.ASN) == 0 { + result = append(result, ip) + } else { + for _, asn := range ip.ASN { + if asn == 0 { + result = append(result, ip) + break + } + } + } + } + if len(result) > 0 { + return result + } + + return ips } func (g *GeoIp) GetMinimumDistance(sourceIp net.IP, ips []IP_RR, logData map[string]interface{}) []IP_RR { - if !g.Enable || g.CountryDB == nil { - return ips - } - minDistance := 1000.0 - var dists []float64 - var result []IP_RR - slat, slong, _, err := g.GetGeoLocation(sourceIp) - if err != nil { - logger.Default.Error("getMinimumDistance failed") - return ips - } - for _, ip := range ips { - destinationIp := ip.Ip - dlat, dlong, _, err := g.GetGeoLocation(destinationIp) - d, err := g.getDistance(slat, slong, dlat, dlong) - if err != nil { - d = 1000.0 - } - if d < minDistance { - minDistance = d - } - dists = append(dists, d) - } - for i, ip := range ips { - if dists[i] == minDistance { - result = append(result, ip) - } - } - if len(result) > 0 { - return result - } - return ips + if !g.Enable || g.CountryDB == nil { + return ips + } + minDistance := 1000.0 + var dists []float64 + var result []IP_RR + slat, slong, _, err := g.GetGeoLocation(sourceIp) + if err != nil { + logger.Default.Error("getMinimumDistance failed") + return ips + } + for _, ip := range ips { + destinationIp := ip.Ip + dlat, dlong, _, err := g.GetGeoLocation(destinationIp) + d, err := g.getDistance(slat, slong, dlat, dlong) + if err != nil { + d = 1000.0 + } + if d < minDistance { + minDistance = d + } + dists = append(dists, d) + } + for i, ip := range ips { + if dists[i] == minDistance { + result = append(result, ip) + } + } + if len(result) > 0 { + return result + } + return ips } func (g *GeoIp) getDistance(slat, slong, dlat, dlong float64) (float64, error) { - deltaLat := (dlat - slat) * math.Pi / 180.0 - deltaLong := (dlong - slong) * math.Pi / 180.0 - slat = slat * math.Pi / 180.0 - dlat = dlat * math.Pi / 180.0 + deltaLat := (dlat - slat) * math.Pi / 180.0 + deltaLong := (dlong - slong) * math.Pi / 180.0 + slat = slat * math.Pi / 180.0 + dlat = dlat * math.Pi / 180.0 - a := math.Sin(deltaLat/2.0)*math.Sin(deltaLat/2.0) + - math.Cos(slat)*math.Cos(dlat)* math.Sin(deltaLong/2.0)*math.Sin(deltaLong/2.0) - c := 2.0 * math.Atan2(math.Sqrt(a), math.Sqrt(1.0-a)) + a := math.Sin(deltaLat/2.0)*math.Sin(deltaLat/2.0) + + math.Cos(slat)*math.Cos(dlat)*math.Sin(deltaLong/2.0)*math.Sin(deltaLong/2.0) + c := 2.0 * math.Atan2(math.Sqrt(a), math.Sqrt(1.0-a)) - logger.Default.Debugf("distance = %f", c) + logger.Default.Debugf("distance = %f", c) - return c, nil + return c, nil } func (g *GeoIp) GetGeoLocation(ip net.IP) (latitude float64, longitude float64, country string, err error) { - if !g.Enable || g.CountryDB == nil { - return - } - var record struct { - Location struct { - Latitude float64 `maxminddb:"latitude"` - LongitudeOffset uintptr `maxminddb:"longitude"` - } `maxminddb:"location"` - Country struct { - ISOCode string `maxminddb:"iso_code"` - } `maxminddb:"country"` - } - logger.Default.Debugf("ip : %s", ip) - err = g.CountryDB.Lookup(ip, &record) - if err != nil { - logger.Default.Errorf("lookup failed : %s", err) - return 0, 0, "", err - } - g.CountryDB.Decode(record.Location.LongitudeOffset, &longitude) - logger.Default.Debug("lat = ", record.Location.Latitude, " lang = ", longitude, " country = ", record.Country.ISOCode) - return record.Location.Latitude, longitude, record.Country.ISOCode, nil + if !g.Enable || g.CountryDB == nil { + return + } + var record struct { + Location struct { + Latitude float64 `maxminddb:"latitude"` + LongitudeOffset uintptr `maxminddb:"longitude"` + } `maxminddb:"location"` + Country struct { + ISOCode string `maxminddb:"iso_code"` + } `maxminddb:"country"` + } + logger.Default.Debugf("ip : %s", ip) + err = g.CountryDB.Lookup(ip, &record) + if err != nil { + logger.Default.Errorf("lookup failed : %s", err) + return 0, 0, "", err + } + g.CountryDB.Decode(record.Location.LongitudeOffset, &longitude) + logger.Default.Debug("lat = ", record.Location.Latitude, " lang = ", longitude, " country = ", record.Country.ISOCode) + return record.Location.Latitude, longitude, record.Country.ISOCode, nil } func (g *GeoIp) GetASN(ip net.IP) (uint, error) { - var record struct { - AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` - } - err := g.ASNDB.Lookup(ip, &record) - if err != nil { - logger.Default.Errorf("lookup failed : %s", err) - return 0, err - } - logger.Default.Debug("asn = ", record.AutonomousSystemNumber) - return record.AutonomousSystemNumber, nil + var record struct { + AutonomousSystemNumber uint `maxminddb:"autonomous_system_number"` + } + err := g.ASNDB.Lookup(ip, &record) + if err != nil { + logger.Default.Errorf("lookup failed : %s", err) + return 0, err + } + logger.Default.Debug("asn = ", record.AutonomousSystemNumber) + return record.AutonomousSystemNumber, nil } diff --git a/handler/geoip_test.go b/handler/geoip_test.go index af107b2..891362e 100644 --- a/handler/geoip_test.go +++ b/handler/geoip_test.go @@ -1,161 +1,160 @@ package handler import ( - "testing" - "net" - "log" + "log" + "net" + "testing" - "github.com/hawell/logger" - "strconv" - "fmt" + "fmt" + "github.com/hawell/logger" + "strconv" ) func TestGeoIpAutomatic(t *testing.T) { - sip := [][]string { - {"212.83.32.45", "DE", "213.95.10.76"}, - {"80.67.163.250", "FR", "62.240.228.4"}, - {"178.18.89.144", "NL", "46.19.36.12"}, - {"206.108.0.43", "CA", "154.11.253.242"}, - {"185.70.144.117", "DE", "213.95.10.76"}, - {"62.220.128.73", "CH", "82.220.3.51"}, - } - - dip := [][]string { - {"82.220.3.51", "CH"}, - {"192.30.252.225", "US"}, - {"213.95.10.76", "DE"}, - {"94.76.229.204", "GB"}, - {"46.19.36.12", "NL"}, - {"46.30.209.1", "DK"}, - {"91.239.97.26", "SI"}, - {"14.1.44.230", "NZ"}, - {"52.76.214.87", "SG"}, - {"103.31.84.12", "MV"}, - {"212.63.210.241", "SE"}, - {"154.11.253.242", "CA"}, - {"128.139.197.81", "IL"}, - {"194.190.198.13", "RU"}, - {"84.88.14.229", "ES"}, - {"79.110.197.36", "PL"}, - {"175.45.73.66", "AU"}, - {"62.240.228.4", "FR"}, - {"200.238.130.54", "BR"}, - {"13.113.70.195", "JP"}, - {"37.252.235.214", "AT"}, - {"185.87.111.13", "FI"}, - {"52.66.51.117", "IN"}, - {"193.198.233.217", "HR"}, - {"118.67.200.190", "KH"}, - {"103.6.84.107", "HK"}, - {"78.128.211.50", "CZ"}, - {"87.238.39.42", "NO"}, - {"37.148.176.54", "BE"}, - } - - cfg := GeoIpConfig { - Enable: true, - CountryDB: "../geoCity.mmdb", - } - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - g := NewGeoIp(&cfg) - - for i := range sip { - dest := new(IP_RRSet) - for j := range dip { - _, _, cc, _ := g.GetGeoLocation(net.ParseIP(dip[j][0])) - if cc != dip[j][1] { - t.Fail() - } - r := IP_RR { - Ip: net.ParseIP(dip[j][0]), - } - dest.Data = append(dest.Data, r) - } - dest.Ttl = 100 - ips := g.GetMinimumDistance(net.ParseIP(sip[i][0]), dest.Data, map[string]interface{}{}) - log.Println("[DEBUG]", sip[i][0], " ", ips[0].Ip.String(), " ", len(ips)) - if sip[i][2] != ips[0].Ip.String() { - t.Fail() - } - } + sip := [][]string{ + {"212.83.32.45", "DE", "213.95.10.76"}, + {"80.67.163.250", "FR", "62.240.228.4"}, + {"178.18.89.144", "NL", "46.19.36.12"}, + {"206.108.0.43", "CA", "154.11.253.242"}, + {"185.70.144.117", "DE", "213.95.10.76"}, + {"62.220.128.73", "CH", "82.220.3.51"}, + } + + dip := [][]string{ + {"82.220.3.51", "CH"}, + {"192.30.252.225", "US"}, + {"213.95.10.76", "DE"}, + {"94.76.229.204", "GB"}, + {"46.19.36.12", "NL"}, + {"46.30.209.1", "DK"}, + {"91.239.97.26", "SI"}, + {"14.1.44.230", "NZ"}, + {"52.76.214.87", "SG"}, + {"103.31.84.12", "MV"}, + {"212.63.210.241", "SE"}, + {"154.11.253.242", "CA"}, + {"128.139.197.81", "IL"}, + {"194.190.198.13", "RU"}, + {"84.88.14.229", "ES"}, + {"79.110.197.36", "PL"}, + {"175.45.73.66", "AU"}, + {"62.240.228.4", "FR"}, + {"200.238.130.54", "BR"}, + {"13.113.70.195", "JP"}, + {"37.252.235.214", "AT"}, + {"185.87.111.13", "FI"}, + {"52.66.51.117", "IN"}, + {"193.198.233.217", "HR"}, + {"118.67.200.190", "KH"}, + {"103.6.84.107", "HK"}, + {"78.128.211.50", "CZ"}, + {"87.238.39.42", "NO"}, + {"37.148.176.54", "BE"}, + } + + cfg := GeoIpConfig{ + Enable: true, + CountryDB: "../geoCity.mmdb", + } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + g := NewGeoIp(&cfg) + + for i := range sip { + dest := new(IP_RRSet) + for j := range dip { + _, _, cc, _ := g.GetGeoLocation(net.ParseIP(dip[j][0])) + if cc != dip[j][1] { + t.Fail() + } + r := IP_RR{ + Ip: net.ParseIP(dip[j][0]), + } + dest.Data = append(dest.Data, r) + } + dest.Ttl = 100 + ips := g.GetMinimumDistance(net.ParseIP(sip[i][0]), dest.Data, map[string]interface{}{}) + log.Println("[DEBUG]", sip[i][0], " ", ips[0].Ip.String(), " ", len(ips)) + if sip[i][2] != ips[0].Ip.String() { + t.Fail() + } + } } func TestGetSameCountry(t *testing.T) { - sip := [][]string{ - {"212.83.32.45", "DE", "1.2.3.4"}, - {"80.67.163.250", "FR", "2.3.4.5"}, - {"154.11.253.242", "", "3.4.5.6"}, - {"127.0.0.1", "", "3.4.5.6"}, - } - - cfg := GeoIpConfig { - Enable: true, - CountryDB: "../geoCity.mmdb", - } - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - g := NewGeoIp(&cfg) - - - for i := range sip { - var dest IP_RRSet - dest.Data = []IP_RR { - { Ip: net.ParseIP("1.2.3.4"), Country: []string{"DE"}}, - { Ip: net.ParseIP("2.3.4.5"), Country: []string{"FR"}}, - { Ip: net.ParseIP("3.4.5.6"), Country: []string{""}}, - } - ips := g.GetSameCountry(net.ParseIP(sip[i][0]), dest.Data, map[string]interface{}{}) - if len(ips) != 1 { - t.Fail() - } - log.Println("[DEBUG]", sip[i][1], sip[i][2], ips[0].Country, ips[0].Ip.String()) - if ips[0].Country[0] != sip[i][1] || ips[0].Ip.String() != sip[i][2] { - t.Fail() - } - } + sip := [][]string{ + {"212.83.32.45", "DE", "1.2.3.4"}, + {"80.67.163.250", "FR", "2.3.4.5"}, + {"154.11.253.242", "", "3.4.5.6"}, + {"127.0.0.1", "", "3.4.5.6"}, + } + + cfg := GeoIpConfig{ + Enable: true, + CountryDB: "../geoCity.mmdb", + } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + g := NewGeoIp(&cfg) + + for i := range sip { + var dest IP_RRSet + dest.Data = []IP_RR{ + {Ip: net.ParseIP("1.2.3.4"), Country: []string{"DE"}}, + {Ip: net.ParseIP("2.3.4.5"), Country: []string{"FR"}}, + {Ip: net.ParseIP("3.4.5.6"), Country: []string{""}}, + } + ips := g.GetSameCountry(net.ParseIP(sip[i][0]), dest.Data, map[string]interface{}{}) + if len(ips) != 1 { + t.Fail() + } + log.Println("[DEBUG]", sip[i][1], sip[i][2], ips[0].Country, ips[0].Ip.String()) + if ips[0].Country[0] != sip[i][1] || ips[0].Ip.String() != sip[i][2] { + t.Fail() + } + } } func TestGetSameASN(t *testing.T) { - sip := []string{ - "212.83.32.45", - "80.67.163.250", - "154.11.253.242", - "127.0.0.1", - } - - dip := IP_RRSet{ - Data: []IP_RR{ - {Ip: net.ParseIP("1.2.3.4"), ASN: []uint{47447}}, - {Ip: net.ParseIP("2.3.4.5"), ASN: []uint{20766}}, - {Ip: net.ParseIP("3.4.5.6"), ASN: []uint{852}}, - {Ip: net.ParseIP("4.5.6.7"), ASN: []uint{0}}, - }, - } - - res := [][]string { - {"47447", "1.2.3.4"}, - {"20766", "2.3.4.5"}, - {"852", "3.4.5.6"}, - {"0", "4.5.6.7"}, - } - cfg := GeoIpConfig { - Enable: true, - ASNDB: "../geoIsp.mmdb", - } - - g := NewGeoIp(&cfg) - - for i := range sip { - ips := g.GetSameASN(net.ParseIP(sip[i]), dip.Data, map[string]interface{}{}) - if len(ips) != 1 { - t.Fail() - } - if strconv.Itoa(int(ips[0].ASN[0])) != res[i][0] || ips[0].Ip.String() != res[i][1] { - t.Fail() - } - } + sip := []string{ + "212.83.32.45", + "80.67.163.250", + "154.11.253.242", + "127.0.0.1", + } + + dip := IP_RRSet{ + Data: []IP_RR{ + {Ip: net.ParseIP("1.2.3.4"), ASN: []uint{47447}}, + {Ip: net.ParseIP("2.3.4.5"), ASN: []uint{20766}}, + {Ip: net.ParseIP("3.4.5.6"), ASN: []uint{852}}, + {Ip: net.ParseIP("4.5.6.7"), ASN: []uint{0}}, + }, + } + + res := [][]string{ + {"47447", "1.2.3.4"}, + {"20766", "2.3.4.5"}, + {"852", "3.4.5.6"}, + {"0", "4.5.6.7"}, + } + cfg := GeoIpConfig{ + Enable: true, + ASNDB: "../geoIsp.mmdb", + } + + g := NewGeoIp(&cfg) + + for i := range sip { + ips := g.GetSameASN(net.ParseIP(sip[i]), dip.Data, map[string]interface{}{}) + if len(ips) != 1 { + t.Fail() + } + if strconv.Itoa(int(ips[0].ASN[0])) != res[i][0] || ips[0].Ip.String() != res[i][1] { + t.Fail() + } + } } @@ -195,56 +194,56 @@ func TestGetSameASN(t *testing.T) { 206.108.0.43 393424 CA 185.70.144.117 200567 DE 62.220.128.73 6893 CH - */ +*/ func printCountryASN() { - ips := []string{ - "82.220.3.51", - "192.30.252.225", - "213.95.10.76", - "94.76.229.204", - "46.19.36.12", - "46.30.209.1", - "91.239.97.26", - "14.1.44.230", - "52.76.214.87", - "103.31.84.12", - "212.63.210.241", - "154.11.253.242", - "128.139.197.81", - "194.190.198.13", - "84.88.14.229", - "79.110.197.36", - "175.45.73.66", - "62.240.228.4", - "200.238.130.54", - "13.113.70.195", - "37.252.235.214", - "185.87.111.13", - "52.66.51.117", - "193.198.233.217", - "118.67.200.190", - "103.6.84.107", - "78.128.211.50", - "87.238.39.42", - "37.148.176.54", - "212.83.32.45", - "80.67.163.250", - "178.18.89.144", - "206.108.0.43", - "185.70.144.117", - "62.220.128.73", - } - cfg := GeoIpConfig { - Enable: true, - ASNDB: "../geoIsp.mmdb", - CountryDB: "../geoCity.mmdb", - } - - g := NewGeoIp(&cfg) - - for _, ip := range ips { - asn, _ := g.GetASN(net.ParseIP(ip)) - _, _, c, _ := g.GetGeoLocation(net.ParseIP(ip)) - fmt.Println(ip, asn, c) - } -} \ No newline at end of file + ips := []string{ + "82.220.3.51", + "192.30.252.225", + "213.95.10.76", + "94.76.229.204", + "46.19.36.12", + "46.30.209.1", + "91.239.97.26", + "14.1.44.230", + "52.76.214.87", + "103.31.84.12", + "212.63.210.241", + "154.11.253.242", + "128.139.197.81", + "194.190.198.13", + "84.88.14.229", + "79.110.197.36", + "175.45.73.66", + "62.240.228.4", + "200.238.130.54", + "13.113.70.195", + "37.252.235.214", + "185.87.111.13", + "52.66.51.117", + "193.198.233.217", + "118.67.200.190", + "103.6.84.107", + "78.128.211.50", + "87.238.39.42", + "37.148.176.54", + "212.83.32.45", + "80.67.163.250", + "178.18.89.144", + "206.108.0.43", + "185.70.144.117", + "62.220.128.73", + } + cfg := GeoIpConfig{ + Enable: true, + ASNDB: "../geoIsp.mmdb", + CountryDB: "../geoCity.mmdb", + } + + g := NewGeoIp(&cfg) + + for _, ip := range ips { + asn, _ := g.GetASN(net.ParseIP(ip)) + _, _, c, _ := g.GetGeoLocation(net.ParseIP(ip)) + fmt.Println(ip, asn, c) + } +} diff --git a/handler/handler.go b/handler/handler.go index a58ff42..a01c842 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -1,916 +1,914 @@ package handler import ( - "encoding/json" - "strings" - "time" - "math/rand" - "sync" - "net" - - "github.com/miekg/dns" - "github.com/patrickmn/go-cache" - "github.com/coredns/coredns/request" - "github.com/hawell/logger" - "github.com/hawell/uperdis" - "github.com/hashicorp/go-immutable-radix" + "encoding/json" + "math/rand" + "net" + "strings" + "sync" + "time" + + "github.com/coredns/coredns/request" + "github.com/hashicorp/go-immutable-radix" + "github.com/hawell/logger" + "github.com/hawell/uperdis" + "github.com/miekg/dns" + "github.com/patrickmn/go-cache" ) type DnsRequestHandler struct { - Config *HandlerConfig - Zones *iradix.Tree - LastZoneUpdate time.Time - Redis *uperdis.Redis - Logger *logger.EventLogger - RecordCache *cache.Cache - ZoneCache *cache.Cache - geoip *GeoIp - healthcheck *Healthcheck - upstream *Upstream - quit chan struct{} - quitWG sync.WaitGroup - numRoutines int - + Config *HandlerConfig + Zones *iradix.Tree + LastZoneUpdate time.Time + Redis *uperdis.Redis + Logger *logger.EventLogger + RecordCache *cache.Cache + ZoneCache *cache.Cache + geoip *GeoIp + healthcheck *Healthcheck + upstream *Upstream + quit chan struct{} + quitWG sync.WaitGroup + numRoutines int } type HandlerConfig struct { - Upstream []UpstreamConfig `json:"upstream,omitempty"` - GeoIp GeoIpConfig `json:"geoip,omitempty"` - HealthCheck HealthcheckConfig `json:"healthcheck,omitempty"` - MaxTtl int `json:"max_ttl,omitempty"` - CacheTimeout int `json:"cache_timeout,omitempty"` - ZoneReload int `json:"zone_reload,omitempty"` - LogSourceLocation bool `json:"log_source_location,omitempty"` - UpstreamFallback bool `json:"upstream_fallback,omitempty"` - Redis uperdis.RedisConfig `json:"redis,omitempty"` - Log logger.LogConfig `json:"log,omitempty"` + Upstream []UpstreamConfig `json:"upstream,omitempty"` + GeoIp GeoIpConfig `json:"geoip,omitempty"` + HealthCheck HealthcheckConfig `json:"healthcheck,omitempty"` + MaxTtl int `json:"max_ttl,omitempty"` + CacheTimeout int `json:"cache_timeout,omitempty"` + ZoneReload int `json:"zone_reload,omitempty"` + LogSourceLocation bool `json:"log_source_location,omitempty"` + UpstreamFallback bool `json:"upstream_fallback,omitempty"` + Redis uperdis.RedisConfig `json:"redis,omitempty"` + Log logger.LogConfig `json:"log,omitempty"` } func NewHandler(config *HandlerConfig) *DnsRequestHandler { - h := &DnsRequestHandler { - Config: config, - } - - h.Redis = uperdis.NewRedis(&config.Redis) - h.Logger = logger.NewLogger(&config.Log) - h.geoip = NewGeoIp(&config.GeoIp) - h.healthcheck = NewHealthcheck(&config.HealthCheck, h.Redis) - h.upstream = NewUpstream(config.Upstream) - h.Zones = iradix.New() - h.quit = make(chan struct{}, 1) - - h.LoadZones() - - h.RecordCache = cache.New(time.Second * time.Duration(h.Config.CacheTimeout), time.Duration(h.Config.CacheTimeout) * time.Second * 10) - h.ZoneCache = cache.New(time.Second * time.Duration(h.Config.CacheTimeout), time.Duration(h.Config.CacheTimeout) * time.Second * 10) - - go h.healthcheck.Start() - - if h.Redis.SubscribeEvent("redins:zones", func(channel string, event string){ - logger.Default.Debug("loading zones") - h.LoadZones() - }) != nil { - logger.Default.Warning("event notification is not available, adding/removing zones will not be instant") - go func() { - h.numRoutines++ - for { - select { - case <-h.quit: - // fmt.Println("updateZone : quit") - h.quitWG.Done() - return - case <-time.After(time.Duration(h.Config.ZoneReload) * time.Second): - logger.Default.Debugf("%v", h.Zones) - logger.Default.Debug("loading zones") - h.LoadZones() - } - } - }() - } - - return h + h := &DnsRequestHandler{ + Config: config, + } + + h.Redis = uperdis.NewRedis(&config.Redis) + h.Logger = logger.NewLogger(&config.Log) + h.geoip = NewGeoIp(&config.GeoIp) + h.healthcheck = NewHealthcheck(&config.HealthCheck, h.Redis) + h.upstream = NewUpstream(config.Upstream) + h.Zones = iradix.New() + h.quit = make(chan struct{}, 1) + + h.LoadZones() + + h.RecordCache = cache.New(time.Second*time.Duration(h.Config.CacheTimeout), time.Duration(h.Config.CacheTimeout)*time.Second*10) + h.ZoneCache = cache.New(time.Second*time.Duration(h.Config.CacheTimeout), time.Duration(h.Config.CacheTimeout)*time.Second*10) + + go h.healthcheck.Start() + + if h.Redis.SubscribeEvent("redins:zones", func(channel string, event string) { + logger.Default.Debug("loading zones") + h.LoadZones() + }) != nil { + logger.Default.Warning("event notification is not available, adding/removing zones will not be instant") + go func() { + h.numRoutines++ + for { + select { + case <-h.quit: + // fmt.Println("updateZone : quit") + h.quitWG.Done() + return + case <-time.After(time.Duration(h.Config.ZoneReload) * time.Second): + logger.Default.Debugf("%v", h.Zones) + logger.Default.Debug("loading zones") + h.LoadZones() + } + } + }() + } + + return h } func (h *DnsRequestHandler) ShutDown() { - // fmt.Println("handler : stopping") - h.healthcheck.ShutDown() - h.quitWG.Add(h.numRoutines) - close(h.quit) - h.quitWG.Wait() - // fmt.Println("handler : stopped") + // fmt.Println("handler : stopping") + h.healthcheck.ShutDown() + h.quitWG.Add(h.numRoutines) + close(h.quit) + h.quitWG.Wait() + // fmt.Println("handler : stopped") } func (h *DnsRequestHandler) HandleRequest(state *request.Request) { - qname := state.Name() - qtype := state.QType() - - logger.Default.Debugf("name : %s", state.Name()) - logger.Default.Debugf("type : %s", state.Type()) - - requestStartTime := time.Now() - - logData := map[string]interface{} { - "SourceIP": state.IP(), - "Record": state.Name(), - "Type": state.Type(), - } - logData["ClientSubnet"] = GetSourceSubnet(state) - - if h.Config.LogSourceLocation { - sourceIP := GetSourceIp(state) - _, _, sourceCountry, _ := h.geoip.GetGeoLocation(sourceIP) - logData["SourceCountry"] = sourceCountry - sourceASN, _ := h.geoip.GetASN(sourceIP) - logData["SourceASN"] = sourceASN - } - - auth := true - - var record *Record - var localRes int - var res int - var answers []dns.RR - var authority []dns.RR - record, localRes = h.FetchRecord(qname, logData) - originalRecord := record - secured := state.Do() && record != nil && record.Zone.Config.DnsSec - if record != nil { - logData["DomainId"] = record.Zone.Config.DomainId - if qtype != dns.TypeCNAME { - // TODO: check for cname loop - for { - if localRes != dns.RcodeSuccess { - break - } - if record.CNAME == nil { - break - } - if !record.Zone.Config.CnameFlattening { - answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) - qname = record.CNAME.Host - } - record, localRes = h.FetchRecord(record.CNAME.Host, logData) - } - } - } - - res = localRes - if localRes == dns.RcodeSuccess { - switch qtype { - case dns.TypeA: - if len(record.A.Data) == 0 { - if record.ANAME != nil { - anameAnswer, anameRes := h.FetchRecord(record.ANAME.Location, logData) - if anameRes == dns.RcodeSuccess { - ips := h.Filter(state, &anameAnswer.A, logData) - answers = AppendRR(answers, h.A(qname, anameAnswer, ips), qname, record, secured) - } else { - upstreamAnswers, upstreamRes := h.upstream.Query(record.ANAME.Location, dns.TypeA) - if upstreamRes == dns.RcodeSuccess { - var anameRecord []dns.RR - for _, r := range upstreamAnswers { - if r.Header().Name == record.ANAME.Location && r.Header().Rrtype == dns.TypeA { - a := r.(*dns.A) - anameRecord = append(anameRecord, &dns.A{A:a.A, Hdr:dns.RR_Header{Rrtype:dns.TypeA, Name:qname,Ttl:a.Hdr.Ttl,Class:dns.ClassINET, Rdlength:0}}) - } - } - answers = AppendRR(answers, anameRecord, qname, record, secured) - } - res = upstreamRes - } - } - } else { - ips := h.Filter(state, &record.A, logData) - answers = AppendRR(answers, h.A(qname, record, ips), qname, record, secured) - } - case dns.TypeAAAA: - if len(record.AAAA.Data) == 0 { - if record.ANAME != nil { - anameAnswer, anameRes := h.FetchRecord(record.ANAME.Location, logData) - if anameRes == dns.RcodeSuccess { - ips := h.Filter(state, &anameAnswer.AAAA, logData) - answers = AppendRR(answers, h.AAAA(qname, anameAnswer, ips), qname, record, secured) - } else { - upstreamAnswers, upstreamRes := h.upstream.Query(record.ANAME.Location, dns.TypeAAAA) - if upstreamRes == dns.RcodeSuccess { - var anameRecord []dns.RR - for _, r := range upstreamAnswers { - if r.Header().Name == record.ANAME.Location && r.Header().Rrtype == dns.TypeAAAA { - a := r.(*dns.AAAA) - anameRecord = append(anameRecord, &dns.AAAA{AAAA:a.AAAA, Hdr:dns.RR_Header{Rrtype:dns.TypeAAAA, Name:qname,Ttl:a.Hdr.Ttl,Class:dns.ClassINET, Rdlength:0}}) - } - } - answers = AppendRR(answers, anameRecord, qname, record, secured) - } - res = upstreamRes - } - } - } else { - ips := h.Filter(state, &record.AAAA, logData) - answers = AppendRR(answers, h.AAAA(qname, record, ips), qname, record, secured) - } - case dns.TypeCNAME: - answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) - case dns.TypeTXT: - answers = AppendRR(answers, h.TXT(qname, record), qname, record, secured) - case dns.TypeNS: - answers = AppendRR(answers, h.NS(qname, record), qname, record, secured) - case dns.TypeMX: - answers = AppendRR(answers, h.MX(qname, record), qname, record, secured) - case dns.TypeSRV: - answers = AppendRR(answers, h.SRV(qname, record), qname, record, secured) - case dns.TypeCAA: - caaRecord := h.FindCAA(record) - if caaRecord != nil { - answers = AppendRR(answers, h.CAA(qname, caaRecord), qname, caaRecord, secured) - } - case dns.TypePTR: - answers = AppendRR(answers, h.PTR(qname, record), qname, record, secured) - case dns.TypeTLSA: - answers = AppendRR(answers, h.TLSA(qname, record), qname, record, secured) - case dns.TypeSOA: - answers = AppendSOA(answers, record.Zone, secured) - case dns.TypeDNSKEY: - if secured { - answers = []dns.RR{record.Zone.DnsKey, record.Zone.DnsKeySig} - } - default: - answers = []dns.RR{} - authority = []dns.RR{} - res = dns.RcodeNotImplemented - } - if len(answers) == 0 { - if originalRecord.CNAME != nil { - answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) - } else { - authority = AppendSOA(authority, originalRecord.Zone, secured) - authority = AppendNSEC(authority, originalRecord.Zone, qname, secured) - } - } - } else if localRes == dns.RcodeNameError { - answers = []dns.RR{} - authority = AppendSOA(authority, originalRecord.Zone, secured) - if secured { - authority = AppendNSEC(authority, record.Zone, qname, secured) - res = dns.RcodeSuccess - } - } else if localRes == dns.RcodeNotAuth { - if h.Config.UpstreamFallback { - upstreamAnswers, upstreamRes := h.upstream.Query(dns.Fqdn(qname), qtype) - if upstreamRes == dns.RcodeSuccess { - answers = append(answers, upstreamAnswers...) - auth = false - } - res = upstreamRes - } else if originalRecord != nil && originalRecord.CNAME != nil { - if len(answers) == 0 { - answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) - } - res = dns.RcodeSuccess - } - } - - h.LogRequest(logData, requestStartTime, res) - m := new(dns.Msg) - m.SetReply(state.Req) - m.Authoritative, m.RecursionAvailable, m.Compress = auth, h.Config.UpstreamFallback, true - m.SetRcode(state.Req, res) - m.Answer = append(m.Answer, answers...) - m.Ns = append(m.Ns, authority...) - - state.SizeAndDo(m) - m = state.Scrub(m) - state.W.WriteMsg(m) + qname := state.Name() + qtype := state.QType() + + logger.Default.Debugf("name : %s", state.Name()) + logger.Default.Debugf("type : %s", state.Type()) + + requestStartTime := time.Now() + + logData := map[string]interface{}{ + "SourceIP": state.IP(), + "Record": state.Name(), + "Type": state.Type(), + } + logData["ClientSubnet"] = GetSourceSubnet(state) + + if h.Config.LogSourceLocation { + sourceIP := GetSourceIp(state) + _, _, sourceCountry, _ := h.geoip.GetGeoLocation(sourceIP) + logData["SourceCountry"] = sourceCountry + sourceASN, _ := h.geoip.GetASN(sourceIP) + logData["SourceASN"] = sourceASN + } + + auth := true + + var record *Record + var localRes int + var res int + var answers []dns.RR + var authority []dns.RR + record, localRes = h.FetchRecord(qname, logData) + originalRecord := record + secured := state.Do() && record != nil && record.Zone.Config.DnsSec + if record != nil { + logData["DomainId"] = record.Zone.Config.DomainId + if qtype != dns.TypeCNAME { + // TODO: check for cname loop + for { + if localRes != dns.RcodeSuccess { + break + } + if record.CNAME == nil { + break + } + if !record.Zone.Config.CnameFlattening { + answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) + qname = record.CNAME.Host + } + record, localRes = h.FetchRecord(record.CNAME.Host, logData) + } + } + } + + res = localRes + if localRes == dns.RcodeSuccess { + switch qtype { + case dns.TypeA: + if len(record.A.Data) == 0 { + if record.ANAME != nil { + anameAnswer, anameRes := h.FetchRecord(record.ANAME.Location, logData) + if anameRes == dns.RcodeSuccess { + ips := h.Filter(state, &anameAnswer.A, logData) + answers = AppendRR(answers, h.A(qname, anameAnswer, ips), qname, record, secured) + } else { + upstreamAnswers, upstreamRes := h.upstream.Query(record.ANAME.Location, dns.TypeA) + if upstreamRes == dns.RcodeSuccess { + var anameRecord []dns.RR + for _, r := range upstreamAnswers { + if r.Header().Name == record.ANAME.Location && r.Header().Rrtype == dns.TypeA { + a := r.(*dns.A) + anameRecord = append(anameRecord, &dns.A{A: a.A, Hdr: dns.RR_Header{Rrtype: dns.TypeA, Name: qname, Ttl: a.Hdr.Ttl, Class: dns.ClassINET, Rdlength: 0}}) + } + } + answers = AppendRR(answers, anameRecord, qname, record, secured) + } + res = upstreamRes + } + } + } else { + ips := h.Filter(state, &record.A, logData) + answers = AppendRR(answers, h.A(qname, record, ips), qname, record, secured) + } + case dns.TypeAAAA: + if len(record.AAAA.Data) == 0 { + if record.ANAME != nil { + anameAnswer, anameRes := h.FetchRecord(record.ANAME.Location, logData) + if anameRes == dns.RcodeSuccess { + ips := h.Filter(state, &anameAnswer.AAAA, logData) + answers = AppendRR(answers, h.AAAA(qname, anameAnswer, ips), qname, record, secured) + } else { + upstreamAnswers, upstreamRes := h.upstream.Query(record.ANAME.Location, dns.TypeAAAA) + if upstreamRes == dns.RcodeSuccess { + var anameRecord []dns.RR + for _, r := range upstreamAnswers { + if r.Header().Name == record.ANAME.Location && r.Header().Rrtype == dns.TypeAAAA { + a := r.(*dns.AAAA) + anameRecord = append(anameRecord, &dns.AAAA{AAAA: a.AAAA, Hdr: dns.RR_Header{Rrtype: dns.TypeAAAA, Name: qname, Ttl: a.Hdr.Ttl, Class: dns.ClassINET, Rdlength: 0}}) + } + } + answers = AppendRR(answers, anameRecord, qname, record, secured) + } + res = upstreamRes + } + } + } else { + ips := h.Filter(state, &record.AAAA, logData) + answers = AppendRR(answers, h.AAAA(qname, record, ips), qname, record, secured) + } + case dns.TypeCNAME: + answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) + case dns.TypeTXT: + answers = AppendRR(answers, h.TXT(qname, record), qname, record, secured) + case dns.TypeNS: + answers = AppendRR(answers, h.NS(qname, record), qname, record, secured) + case dns.TypeMX: + answers = AppendRR(answers, h.MX(qname, record), qname, record, secured) + case dns.TypeSRV: + answers = AppendRR(answers, h.SRV(qname, record), qname, record, secured) + case dns.TypeCAA: + caaRecord := h.FindCAA(record) + if caaRecord != nil { + answers = AppendRR(answers, h.CAA(qname, caaRecord), qname, caaRecord, secured) + } + case dns.TypePTR: + answers = AppendRR(answers, h.PTR(qname, record), qname, record, secured) + case dns.TypeTLSA: + answers = AppendRR(answers, h.TLSA(qname, record), qname, record, secured) + case dns.TypeSOA: + answers = AppendSOA(answers, record.Zone, secured) + case dns.TypeDNSKEY: + if secured { + answers = []dns.RR{record.Zone.DnsKey, record.Zone.DnsKeySig} + } + default: + answers = []dns.RR{} + authority = []dns.RR{} + res = dns.RcodeNotImplemented + } + if len(answers) == 0 { + if originalRecord.CNAME != nil { + answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) + } else { + authority = AppendSOA(authority, originalRecord.Zone, secured) + authority = AppendNSEC(authority, originalRecord.Zone, qname, secured) + } + } + } else if localRes == dns.RcodeNameError { + answers = []dns.RR{} + authority = AppendSOA(authority, originalRecord.Zone, secured) + if secured { + authority = AppendNSEC(authority, record.Zone, qname, secured) + res = dns.RcodeSuccess + } + } else if localRes == dns.RcodeNotAuth { + if h.Config.UpstreamFallback { + upstreamAnswers, upstreamRes := h.upstream.Query(dns.Fqdn(qname), qtype) + if upstreamRes == dns.RcodeSuccess { + answers = append(answers, upstreamAnswers...) + auth = false + } + res = upstreamRes + } else if originalRecord != nil && originalRecord.CNAME != nil { + if len(answers) == 0 { + answers = AppendRR(answers, h.CNAME(qname, record), qname, record, secured) + } + res = dns.RcodeSuccess + } + } + + h.LogRequest(logData, requestStartTime, res) + m := new(dns.Msg) + m.SetReply(state.Req) + m.Authoritative, m.RecursionAvailable, m.Compress = auth, h.Config.UpstreamFallback, true + m.SetRcode(state.Req, res) + m.Answer = append(m.Answer, answers...) + m.Ns = append(m.Ns, authority...) + + state.SizeAndDo(m) + m = state.Scrub(m) + state.W.WriteMsg(m) } func (h *DnsRequestHandler) Filter(request *request.Request, rrset *IP_RRSet, logData map[string]interface{}) []IP_RR { - ips := h.healthcheck.FilterHealthcheck(request.Name(), rrset) - switch rrset.FilterConfig.GeoFilter { - case "asn": - ips = h.geoip.GetSameASN(GetSourceIp(request), ips, logData) - case "country": - ips = h.geoip.GetSameCountry(GetSourceIp(request), ips, logData) - case "asn+country": - ips = h.geoip.GetSameASN(GetSourceIp(request), ips, logData) - ips = h.geoip.GetSameCountry(GetSourceIp(request), ips, logData) - case "location": - ips = h.geoip.GetMinimumDistance(GetSourceIp(request), ips, logData) - default: - } - if len(ips) <= 1 { - return ips - } - - switch rrset.FilterConfig.Count { - case "single": - index := 0 - switch rrset.FilterConfig.Order { - case "weighted": - index = ChooseIp(ips, true) - case "rr": - index = ChooseIp(ips, false) - default: - index = 0 - } - logData["DestinationIp"] = ips[index].Ip.String() - logData["DestinationCountry"] = ips[index].Country - return []IP_RR{ips[index]} - - case "multi": - fallthrough - default: - index := 0 - switch rrset.FilterConfig.Order { - case "weighted": - index = ChooseIp(ips, true) - case "rr": - index = ChooseIp(ips,false) - default: - index = 0 - } - return append(ips[index:], ips[:index]...) - } - return ips + ips := h.healthcheck.FilterHealthcheck(request.Name(), rrset) + switch rrset.FilterConfig.GeoFilter { + case "asn": + ips = h.geoip.GetSameASN(GetSourceIp(request), ips, logData) + case "country": + ips = h.geoip.GetSameCountry(GetSourceIp(request), ips, logData) + case "asn+country": + ips = h.geoip.GetSameASN(GetSourceIp(request), ips, logData) + ips = h.geoip.GetSameCountry(GetSourceIp(request), ips, logData) + case "location": + ips = h.geoip.GetMinimumDistance(GetSourceIp(request), ips, logData) + default: + } + if len(ips) <= 1 { + return ips + } + + switch rrset.FilterConfig.Count { + case "single": + index := 0 + switch rrset.FilterConfig.Order { + case "weighted": + index = ChooseIp(ips, true) + case "rr": + index = ChooseIp(ips, false) + default: + index = 0 + } + logData["DestinationIp"] = ips[index].Ip.String() + logData["DestinationCountry"] = ips[index].Country + return []IP_RR{ips[index]} + + case "multi": + fallthrough + default: + index := 0 + switch rrset.FilterConfig.Order { + case "weighted": + index = ChooseIp(ips, true) + case "rr": + index = ChooseIp(ips, false) + default: + index = 0 + } + return append(ips[index:], ips[:index]...) + } + return ips } func (h *DnsRequestHandler) LogRequest(data map[string]interface{}, startTime time.Time, responseCode int) { - data["ProcessTime"] = time.Since(startTime).Nanoseconds() / 1000000 - data["ResponseCode"] = responseCode - h.Logger.Log(data, "ar_dns_request") + data["ProcessTime"] = time.Since(startTime).Nanoseconds() / 1000000 + data["ResponseCode"] = responseCode + h.Logger.Log(data, "ar_dns_request") } func GetSourceIp(request *request.Request) net.IP { - opt := request.Req.IsEdns0() - if opt != nil && len(opt.Option) != 0 { - for _, o := range opt.Option { - switch v := o.(type) { - case *dns.EDNS0_SUBNET: - return v.Address - } - } - } - return net.ParseIP(request.IP()) + opt := request.Req.IsEdns0() + if opt != nil && len(opt.Option) != 0 { + for _, o := range opt.Option { + switch v := o.(type) { + case *dns.EDNS0_SUBNET: + return v.Address + } + } + } + return net.ParseIP(request.IP()) } func GetSourceSubnet(request *request.Request) string { - opt := request.Req.IsEdns0() - if opt != nil && len(opt.Option) != 0 { - for _, o := range opt.Option { - switch o.(type) { - case *dns.EDNS0_SUBNET: - return o.String() - } - } - } - return "" + opt := request.Req.IsEdns0() + if opt != nil && len(opt.Option) != 0 { + for _, o := range opt.Option { + switch o.(type) { + case *dns.EDNS0_SUBNET: + return o.String() + } + } + } + return "" } func reverseZone(zone string) string { - x := strings.Split(zone, ".") - var y string - for i := len(x)-1; i > 0; i-- { - y += x[i] + "." - } - y += x[0] - return y + x := strings.Split(zone, ".") + var y string + for i := len(x) - 1; i > 0; i-- { + y += x[i] + "." + } + y += x[0] + return y } func (h *DnsRequestHandler) LoadZones() { - h.LastZoneUpdate = time.Now() - zones, err := h.Redis.SMembers("redins:zones") - if err != nil { - logger.Default.Error("cannot load zones : ", err) - } - newZones := iradix.New() - for _, zone := range zones { - newZones, _, _ = newZones.Insert([]byte(reverseZone(zone)), zone) - } - h.Zones = newZones + h.LastZoneUpdate = time.Now() + zones, err := h.Redis.SMembers("redins:zones") + if err != nil { + logger.Default.Error("cannot load zones : ", err) + } + newZones := iradix.New() + for _, zone := range zones { + newZones, _, _ = newZones.Insert([]byte(reverseZone(zone)), zone) + } + h.Zones = newZones } func (h *DnsRequestHandler) FetchRecord(qname string, logData map[string]interface{}) (*Record, int) { - cachedRecord, found := h.RecordCache.Get(qname) - if found { - logger.Default.Debug("cached") - logData["Cache"] = "HIT" - return cachedRecord.(*Record), dns.RcodeSuccess - } else { - logData["Cache"] = "MISS" - record, res := h.GetRecord(qname) - if res == dns.RcodeSuccess { - h.RecordCache.Set(qname, record, time.Duration(h.Config.CacheTimeout)*time.Second) - } - return record, res - } + cachedRecord, found := h.RecordCache.Get(qname) + if found { + logger.Default.Debug("cached") + logData["Cache"] = "HIT" + return cachedRecord.(*Record), dns.RcodeSuccess + } else { + logData["Cache"] = "MISS" + record, res := h.GetRecord(qname) + if res == dns.RcodeSuccess { + h.RecordCache.Set(qname, record, time.Duration(h.Config.CacheTimeout)*time.Second) + } + return record, res + } } func (h *DnsRequestHandler) A(name string, record *Record, ips []IP_RR) (answers []dns.RR) { - for _, ip := range ips { - if ip.Ip == nil { - continue - } - r := new(dns.A) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, - Class: dns.ClassINET, Ttl: h.getTtl(record.A.Ttl)} - r.A = ip.Ip - answers = append(answers, r) - } - return + for _, ip := range ips { + if ip.Ip == nil { + continue + } + r := new(dns.A) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: h.getTtl(record.A.Ttl)} + r.A = ip.Ip + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) AAAA(name string, record *Record, ips []IP_RR) (answers []dns.RR) { - for _, ip := range ips { - if ip.Ip == nil { - continue - } - r := new(dns.AAAA) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, - Class: dns.ClassINET, Ttl: h.getTtl(record.AAAA.Ttl)} - r.AAAA = ip.Ip - answers = append(answers, r) - } - return + for _, ip := range ips { + if ip.Ip == nil { + continue + } + r := new(dns.AAAA) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, Ttl: h.getTtl(record.AAAA.Ttl)} + r.AAAA = ip.Ip + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) CNAME(name string, record *Record) (answers []dns.RR) { - if record.CNAME == nil { - return - } - r := new(dns.CNAME) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, - Class: dns.ClassINET, Ttl: h.getTtl(record.CNAME.Ttl)} - r.Target = dns.Fqdn(record.CNAME.Host) - answers = append(answers, r) - return + if record.CNAME == nil { + return + } + r := new(dns.CNAME) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCNAME, + Class: dns.ClassINET, Ttl: h.getTtl(record.CNAME.Ttl)} + r.Target = dns.Fqdn(record.CNAME.Host) + answers = append(answers, r) + return } func (h *DnsRequestHandler) TXT(name string, record *Record) (answers []dns.RR) { - for _, txt := range record.TXT.Data { - if len(txt.Text) == 0 { - continue - } - r := new(dns.TXT) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, - Class: dns.ClassINET, Ttl: h.getTtl(record.TXT.Ttl)} - r.Txt = split255(txt.Text) - answers = append(answers, r) - } - return + for _, txt := range record.TXT.Data { + if len(txt.Text) == 0 { + continue + } + r := new(dns.TXT) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeTXT, + Class: dns.ClassINET, Ttl: h.getTtl(record.TXT.Ttl)} + r.Txt = split255(txt.Text) + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) NS(name string, record *Record) (answers []dns.RR) { - for _, ns := range record.NS.Data { - if len(ns.Host) == 0 { - continue - } - r := new(dns.NS) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeNS, - Class: dns.ClassINET, Ttl: h.getTtl(record.NS.Ttl)} - r.Ns = ns.Host - answers = append(answers, r) - } - return + for _, ns := range record.NS.Data { + if len(ns.Host) == 0 { + continue + } + r := new(dns.NS) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeNS, + Class: dns.ClassINET, Ttl: h.getTtl(record.NS.Ttl)} + r.Ns = ns.Host + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) MX(name string, record *Record) (answers []dns.RR) { - for _, mx := range record.MX.Data { - if len(mx.Host) == 0 { - continue - } - r := new(dns.MX) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeMX, - Class: dns.ClassINET, Ttl: h.getTtl(record.MX.Ttl)} - r.Mx = mx.Host - r.Preference = mx.Preference - answers = append(answers, r) - } - return + for _, mx := range record.MX.Data { + if len(mx.Host) == 0 { + continue + } + r := new(dns.MX) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeMX, + Class: dns.ClassINET, Ttl: h.getTtl(record.MX.Ttl)} + r.Mx = mx.Host + r.Preference = mx.Preference + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) SRV(name string, record *Record) (answers []dns.RR) { - for _, srv := range record.SRV.Data { - if len(srv.Target) == 0 { - continue - } - r := new(dns.SRV) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, - Class: dns.ClassINET, Ttl: h.getTtl(record.SRV.Ttl)} - r.Target = srv.Target - r.Weight = srv.Weight - r.Port = srv.Port - r.Priority = srv.Priority - answers = append(answers, r) - } - return + for _, srv := range record.SRV.Data { + if len(srv.Target) == 0 { + continue + } + r := new(dns.SRV) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeSRV, + Class: dns.ClassINET, Ttl: h.getTtl(record.SRV.Ttl)} + r.Target = srv.Target + r.Weight = srv.Weight + r.Port = srv.Port + r.Priority = srv.Priority + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) CAA(name string, record *Record) (answers []dns.RR) { - for _, caa := range record.CAA.Data { - r := new(dns.CAA) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCAA, - Class: dns.ClassINET, Ttl: h.getTtl(record.CAA.Ttl)} - r.Value = caa.Value - r.Flag = caa.Flag - r.Tag = caa.Tag - answers = append(answers, r) - } - return + for _, caa := range record.CAA.Data { + r := new(dns.CAA) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeCAA, + Class: dns.ClassINET, Ttl: h.getTtl(record.CAA.Ttl)} + r.Value = caa.Value + r.Flag = caa.Flag + r.Tag = caa.Tag + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) PTR(name string, record *Record) (answers []dns.RR) { - if record.PTR == nil { - return - } - r := new(dns.PTR) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypePTR, - Class: dns.ClassINET, Ttl: h.getTtl(record.PTR.Ttl)} - r.Ptr = dns.Fqdn(record.PTR.Domain) - answers = append(answers, r) - return + if record.PTR == nil { + return + } + r := new(dns.PTR) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypePTR, + Class: dns.ClassINET, Ttl: h.getTtl(record.PTR.Ttl)} + r.Ptr = dns.Fqdn(record.PTR.Domain) + answers = append(answers, r) + return } func (h *DnsRequestHandler) TLSA(name string, record *Record) (answers []dns.RR) { - for _, tlsa := range record.TLSA.Data { - r := new(dns.TLSA) - r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeTLSA, - Class:dns.ClassNONE, Ttl: h.getTtl(record.TLSA.Ttl)} - r.Usage = tlsa.Usage - r.Selector = tlsa.Selector - r.MatchingType = tlsa.MatchingType - r.Certificate = tlsa.Certificate - answers = append(answers, r) - } - return + for _, tlsa := range record.TLSA.Data { + r := new(dns.TLSA) + r.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeTLSA, + Class: dns.ClassNONE, Ttl: h.getTtl(record.TLSA.Ttl)} + r.Usage = tlsa.Usage + r.Selector = tlsa.Selector + r.MatchingType = tlsa.MatchingType + r.Certificate = tlsa.Certificate + answers = append(answers, r) + } + return } func (h *DnsRequestHandler) getTtl(ttl uint32) uint32 { - maxTtl := uint32(h.Config.MaxTtl) - if ttl == 0 { - return maxTtl - } - if maxTtl == 0 { - return ttl - } - if ttl > maxTtl { - return maxTtl - } - return ttl + maxTtl := uint32(h.Config.MaxTtl) + if ttl == 0 { + return maxTtl + } + if maxTtl == 0 { + return ttl + } + if ttl > maxTtl { + return maxTtl + } + return ttl } func (h *DnsRequestHandler) findLocation(query string, z *Zone) string { - var ( - ok bool - closestEncloser string - sourceOfSynthesis string - ) - - // request for zone records - if query == z.Name { - return query - } - - query = strings.TrimSuffix(query, "."+z.Name) - - if _, ok = z.Locations[query]; ok { - return query - } - - closestEncloser, sourceOfSynthesis, ok = splitQuery(query) - for ok { - ceExists := keyMatches(closestEncloser, z) || keyExists(closestEncloser, z) - ssExists := keyExists(sourceOfSynthesis, z) - if ceExists { - if ssExists { - return sourceOfSynthesis - } else { - return "" - } - } else { - closestEncloser, sourceOfSynthesis, ok = splitQuery(closestEncloser) - } - } - return "" + var ( + ok bool + closestEncloser string + sourceOfSynthesis string + ) + + // request for zone records + if query == z.Name { + return query + } + + query = strings.TrimSuffix(query, "."+z.Name) + + if _, ok = z.Locations[query]; ok { + return query + } + + closestEncloser, sourceOfSynthesis, ok = splitQuery(query) + for ok { + ceExists := keyMatches(closestEncloser, z) || keyExists(closestEncloser, z) + ssExists := keyExists(sourceOfSynthesis, z) + if ceExists { + if ssExists { + return sourceOfSynthesis + } else { + return "" + } + } else { + closestEncloser, sourceOfSynthesis, ok = splitQuery(closestEncloser) + } + } + return "" } func keyExists(key string, z *Zone) bool { - _, ok := z.Locations[key] - return ok + _, ok := z.Locations[key] + return ok } func keyMatches(key string, z *Zone) bool { - for value := range z.Locations { - if strings.HasSuffix(value, key) { - return true - } - } - return false + for value := range z.Locations { + if strings.HasSuffix(value, key) { + return true + } + } + return false } func splitQuery(query string) (string, string, bool) { - if query == "" { - return "", "", false - } - var ( - splits []string - closestEncloser string - sourceOfSynthesis string - ) - splits = strings.SplitAfterN(query, ".", 2) - if len(splits) == 2 { - closestEncloser = splits[1] - sourceOfSynthesis = "*." + closestEncloser - } else { - closestEncloser = "" - sourceOfSynthesis = "*" - } - return closestEncloser, sourceOfSynthesis, true + if query == "" { + return "", "", false + } + var ( + splits []string + closestEncloser string + sourceOfSynthesis string + ) + splits = strings.SplitAfterN(query, ".", 2) + if len(splits) == 2 { + closestEncloser = splits[1] + sourceOfSynthesis = "*." + closestEncloser + } else { + closestEncloser = "" + sourceOfSynthesis = "*" + } + return closestEncloser, sourceOfSynthesis, true } func split255(s string) []string { - if len(s) < 255 { - return []string{s} - } - var sx []string - p, i := 0, 255 - for { - if i <= len(s) { - sx = append(sx, s[p:i]) - } else { - sx = append(sx, s[p:]) - break + if len(s) < 255 { + return []string{s} + } + var sx []string + p, i := 0, 255 + for { + if i <= len(s) { + sx = append(sx, s[p:i]) + } else { + sx = append(sx, s[p:]) + break - } - p, i = p+255, i+255 - } + } + p, i = p+255, i+255 + } - return sx + return sx } func (h *DnsRequestHandler) Matches(qname string) string { - rzname := reverseZone(qname) - _, zname, ok := h.Zones.Root().LongestPrefix([]byte(rzname)) - if ok { - return zname.(string) - } - return "" + rzname := reverseZone(qname) + _, zname, ok := h.Zones.Root().LongestPrefix([]byte(rzname)) + if ok { + return zname.(string) + } + return "" } func (h *DnsRequestHandler) GetRecord(qname string) (record *Record, rcode int) { - logger.Default.Debug("GetRecord") + logger.Default.Debug("GetRecord") - zone := h.Matches(qname) - logger.Default.Debugf("zone : %s", zone) - if zone == "" { - logger.Default.Debugf("no matching zone found for %s", qname) - return nil, dns.RcodeNotAuth - } + zone := h.Matches(qname) + logger.Default.Debugf("zone : %s", zone) + if zone == "" { + logger.Default.Debugf("no matching zone found for %s", qname) + return nil, dns.RcodeNotAuth + } - z := h.LoadZone(zone) - if z == nil { - logger.Default.Errorf("empty zone : %s", zone) - return nil, dns.RcodeServerFailure - } + z := h.LoadZone(zone) + if z == nil { + logger.Default.Errorf("empty zone : %s", zone) + return nil, dns.RcodeServerFailure + } - location := h.findLocation(qname, z) - if len(location) == 0 { // empty, no results - logger.Default.Errorf("location not exists : %s", qname) - return &Record{Name:qname, Zone: z}, dns.RcodeNameError - } - logger.Default.Debugf("location : %s", location) + location := h.findLocation(qname, z) + if len(location) == 0 { // empty, no results + logger.Default.Errorf("location not exists : %s", qname) + return &Record{Name: qname, Zone: z}, dns.RcodeNameError + } + logger.Default.Debugf("location : %s", location) - record = h.LoadLocation(location, z) - if record == nil { - return nil, dns.RcodeServerFailure - } + record = h.LoadLocation(location, z) + if record == nil { + return nil, dns.RcodeServerFailure + } - return record, dns.RcodeSuccess + return record, dns.RcodeSuccess } func (h *DnsRequestHandler) LoadZone(zone string) *Zone { - cachedZone, found := h.ZoneCache.Get(zone) - if found { - return cachedZone.(*Zone) - } - - z := new(Zone) - z.Name = zone - vals, err := h.Redis.GetHKeys("redins:zones:" + zone) - if err != nil { - logger.Default.Errorf("cannot load zone %s locations : %s", zone, err) - } - z.Locations = make(map[string]struct{}) - for _, val := range vals { - z.Locations[val] = struct{}{} - } - - z.Config = ZoneConfig { - DnsSec: false, - CnameFlattening: false, - SOA: &SOA_RRSet { - Ns: "ns1." + z.Name, - MinTtl: 300, - Refresh: 86400, - Retry: 7200, - Expire: 3600, - MBox: "hostmaster." + z.Name, - Serial: uint32(time.Now().Unix()), - Ttl: 300, - }, - } - val, err := h.Redis.Get("redins:zones:" + zone + ":config") - if err != nil { - logger.Default.Errorf("cannot load zone %s config : %s", zone, err) - } - if len(val) > 0 { - err := json.Unmarshal([]byte(val), &z.Config) - if err != nil { - logger.Default.Errorf("cannot parse zone config : %s", err) - } - } - z.Config.SOA.Ns = dns.Fqdn(z.Config.SOA.Ns) - z.Config.SOA.Data = &dns.SOA { - Hdr: dns.RR_Header { Name: z.Name, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: z.Config.SOA.Ttl, Rdlength:0}, - Ns: z.Config.SOA.Ns, - Mbox: z.Config.SOA.MBox, - Refresh: z.Config.SOA.Refresh, - Retry: z.Config.SOA.Retry, - Expire: z.Config.SOA.Expire, - Minttl: z.Config.SOA.MinTtl, - Serial: z.Config.SOA.Serial, - } - - z = func()*Zone{ - if z.Config.DnsSec { - pubStr, _ := h.Redis.Get("redins:zones:" + z.Name + ":pub") - privStr, _ := h.Redis.Get("redins:zones:" + z.Name + ":priv") - privStr = strings.Replace(privStr, "\\n", "\n", -1) - if pubStr == "" || privStr == "" { - logger.Default.Errorf("key is not set for zone %s", z.Name) - z.Config.DnsSec = false - return z - } - if rr, err := dns.NewRR(pubStr); err == nil { - z.DnsKey = rr.(*dns.DNSKEY) - } else { - logger.Default.Errorf("cannot parse zone key : %s", err) - z.Config.DnsSec = false - return z - } - if pk, err := z.DnsKey.NewPrivateKey(privStr); err == nil { - z.PrivateKey = pk - } else { - logger.Default.Errorf("cannot create private key : %s", err) - z.Config.DnsSec = false - return z - } - now := time.Now() - z.KeyInception = uint32(now.Add(-3 * time.Hour).Unix()) - z.KeyExpiration = uint32(now.Add(8 * 24 * time.Hour).Unix()) - if rrsig, err := Sign([]dns.RR{z.DnsKey}, z.Name, z, 300); err == nil { - z.DnsKeySig = rrsig - } else { - logger.Default.Errorf("cannot create RRSIG for DNSKEY : %s", err) - z.Config.DnsSec = false - return z - } - } - return z - }() - - h.ZoneCache.Set(zone, z, time.Duration(h.Config.CacheTimeout) * time.Second) - return z + cachedZone, found := h.ZoneCache.Get(zone) + if found { + return cachedZone.(*Zone) + } + + z := new(Zone) + z.Name = zone + vals, err := h.Redis.GetHKeys("redins:zones:" + zone) + if err != nil { + logger.Default.Errorf("cannot load zone %s locations : %s", zone, err) + } + z.Locations = make(map[string]struct{}) + for _, val := range vals { + z.Locations[val] = struct{}{} + } + + z.Config = ZoneConfig{ + DnsSec: false, + CnameFlattening: false, + SOA: &SOA_RRSet{ + Ns: "ns1." + z.Name, + MinTtl: 300, + Refresh: 86400, + Retry: 7200, + Expire: 3600, + MBox: "hostmaster." + z.Name, + Serial: uint32(time.Now().Unix()), + Ttl: 300, + }, + } + val, err := h.Redis.Get("redins:zones:" + zone + ":config") + if err != nil { + logger.Default.Errorf("cannot load zone %s config : %s", zone, err) + } + if len(val) > 0 { + err := json.Unmarshal([]byte(val), &z.Config) + if err != nil { + logger.Default.Errorf("cannot parse zone config : %s", err) + } + } + z.Config.SOA.Ns = dns.Fqdn(z.Config.SOA.Ns) + z.Config.SOA.Data = &dns.SOA{ + Hdr: dns.RR_Header{Name: z.Name, Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: z.Config.SOA.Ttl, Rdlength: 0}, + Ns: z.Config.SOA.Ns, + Mbox: z.Config.SOA.MBox, + Refresh: z.Config.SOA.Refresh, + Retry: z.Config.SOA.Retry, + Expire: z.Config.SOA.Expire, + Minttl: z.Config.SOA.MinTtl, + Serial: z.Config.SOA.Serial, + } + + z = func() *Zone { + if z.Config.DnsSec { + pubStr, _ := h.Redis.Get("redins:zones:" + z.Name + ":pub") + privStr, _ := h.Redis.Get("redins:zones:" + z.Name + ":priv") + privStr = strings.Replace(privStr, "\\n", "\n", -1) + if pubStr == "" || privStr == "" { + logger.Default.Errorf("key is not set for zone %s", z.Name) + z.Config.DnsSec = false + return z + } + if rr, err := dns.NewRR(pubStr); err == nil { + z.DnsKey = rr.(*dns.DNSKEY) + } else { + logger.Default.Errorf("cannot parse zone key : %s", err) + z.Config.DnsSec = false + return z + } + if pk, err := z.DnsKey.NewPrivateKey(privStr); err == nil { + z.PrivateKey = pk + } else { + logger.Default.Errorf("cannot create private key : %s", err) + z.Config.DnsSec = false + return z + } + now := time.Now() + z.KeyInception = uint32(now.Add(-3 * time.Hour).Unix()) + z.KeyExpiration = uint32(now.Add(8 * 24 * time.Hour).Unix()) + if rrsig, err := Sign([]dns.RR{z.DnsKey}, z.Name, z, 300); err == nil { + z.DnsKeySig = rrsig + } else { + logger.Default.Errorf("cannot create RRSIG for DNSKEY : %s", err) + z.Config.DnsSec = false + return z + } + } + return z + }() + + h.ZoneCache.Set(zone, z, time.Duration(h.Config.CacheTimeout)*time.Second) + return z } - func (h *DnsRequestHandler) LoadLocation(location string, z *Zone) *Record { - var label, name string - if location == z.Name { - name = z.Name - label = "@" - } else { - name = location + "." + z.Name - label = location - } - r := new(Record) - r.A = IP_RRSet{ - FilterConfig: IpFilterConfig { - Count: "multi", - Order: "none", - GeoFilter: "none", - }, - HealthCheckConfig: IpHealthCheckConfig { - Enable: false, - }, - } - r.AAAA = r.A - r.Zone = z - r.Name = name - - val, _ := h.Redis.HGet("redins:zones:" + z.Name, label) - if val == "" && name == z.Name { - return r - } - err := json.Unmarshal([]byte(val), r) - if err != nil { - logger.Default.Errorf("cannot parse json : zone -> %s, location -> %s, \"%s\" -> %s", z.Name, location, val, err) - return nil - } - - return r + var label, name string + if location == z.Name { + name = z.Name + label = "@" + } else { + name = location + "." + z.Name + label = location + } + r := new(Record) + r.A = IP_RRSet{ + FilterConfig: IpFilterConfig{ + Count: "multi", + Order: "none", + GeoFilter: "none", + }, + HealthCheckConfig: IpHealthCheckConfig{ + Enable: false, + }, + } + r.AAAA = r.A + r.Zone = z + r.Name = name + + val, _ := h.Redis.HGet("redins:zones:"+z.Name, label) + if val == "" && name == z.Name { + return r + } + err := json.Unmarshal([]byte(val), r) + if err != nil { + logger.Default.Errorf("cannot parse json : zone -> %s, location -> %s, \"%s\" -> %s", z.Name, location, val, err) + return nil + } + + return r } func (h *DnsRequestHandler) SetLocation(location string, z *Zone, val *Record) { - jsonValue, err := json.Marshal(val) - if err != nil { - logger.Default.Errorf("cannot encode to json : %s", err) - return - } - var label string - if location == z.Name { - label = "@" - } else { - label = location - } - h.Redis.HSet(z.Name, label, string(jsonValue)) + jsonValue, err := json.Marshal(val) + if err != nil { + logger.Default.Errorf("cannot encode to json : %s", err) + return + } + var label string + if location == z.Name { + label = "@" + } else { + label = location + } + h.Redis.HSet(z.Name, label, string(jsonValue)) } func ChooseIp(ips []IP_RR, weighted bool) int { - sum := 0 + sum := 0 - if !weighted { - return rand.Intn(len(ips)) - } + if !weighted { + return rand.Intn(len(ips)) + } - for _, ip := range ips { - sum += ip.Weight - } - index := 0 + for _, ip := range ips { + sum += ip.Weight + } + index := 0 - // all Ips have 0 weight, choosing a random one - if sum == 0 { - return rand.Intn(len(ips)) - } + // all Ips have 0 weight, choosing a random one + if sum == 0 { + return rand.Intn(len(ips)) + } - x := rand.Intn(sum) - for ; index < len(ips); index++ { - // skip Ips with 0 weight - x -= ips[index].Weight - if x < 0 { - break - } - } - if index >= len(ips) { - index-- - } + x := rand.Intn(sum) + for ; index < len(ips); index++ { + // skip Ips with 0 weight + x -= ips[index].Weight + if x < 0 { + break + } + } + if index >= len(ips) { + index-- + } - return index + return index } func AppendRR(answers []dns.RR, rrs []dns.RR, qname string, record *Record, secured bool) []dns.RR { - if len(rrs) == 0 { - return answers - } - answers = append(answers, rrs...) - if secured { - if rrsig, err := Sign(rrs, qname, record.Zone, rrs[0].Header().Ttl); err == nil { - answers = append(answers, rrsig) - } - } - return answers + if len(rrs) == 0 { + return answers + } + answers = append(answers, rrs...) + if secured { + if rrsig, err := Sign(rrs, qname, record.Zone, rrs[0].Header().Ttl); err == nil { + answers = append(answers, rrsig) + } + } + return answers } func AppendSOA(target []dns.RR, zone *Zone, secured bool) []dns.RR { - target = append(target, zone.Config.SOA.Data) - if secured { - if rrsig, err := Sign([]dns.RR{zone.Config.SOA.Data}, zone.Name, zone, zone.Config.SOA.Ttl); err == nil { - target = append(target, rrsig) - } - } - return target + target = append(target, zone.Config.SOA.Data) + if secured { + if rrsig, err := Sign([]dns.RR{zone.Config.SOA.Data}, zone.Name, zone, zone.Config.SOA.Ttl); err == nil { + target = append(target, rrsig) + } + } + return target } func AppendNSEC(target []dns.RR, zone *Zone, qname string, secured bool) []dns.RR { - if !secured { - return target - } - if nsec, err := NSec(qname, zone); err == nil { - target = append(target, nsec...) - } - return target + if !secured { + return target + } + if nsec, err := NSec(qname, zone); err == nil { + target = append(target, nsec...) + } + return target } func (h *DnsRequestHandler) FindCAA(record *Record) *Record { - zone := record.Zone - currentRecord := record - for currentRecord != nil && strings.HasSuffix(currentRecord.Name, zone.Name) { - if len(currentRecord.CAA.Data) != 0 { - return currentRecord - } - splits := strings.SplitAfterN(currentRecord.Name, ".", 2) - if len(splits) != 2 { - return nil - } - currentRecord, _ = h.FetchRecord(splits[1], map[string]interface{}{}) - } - return nil + zone := record.Zone + currentRecord := record + for currentRecord != nil && strings.HasSuffix(currentRecord.Name, zone.Name) { + if len(currentRecord.CAA.Data) != 0 { + return currentRecord + } + splits := strings.SplitAfterN(currentRecord.Name, ".", 2) + if len(splits) != 2 { + return nil + } + currentRecord, _ = h.FetchRecord(splits[1], map[string]interface{}{}) + } + return nil } diff --git a/handler/handler_test.go b/handler/handler_test.go index 31da31c..7403d4d 100644 --- a/handler/handler_test.go +++ b/handler/handler_test.go @@ -1,38 +1,38 @@ package handler import ( - "net" - "testing" - "log" - - "github.com/miekg/dns" - "github.com/coredns/coredns/request" - "github.com/hawell/logger" - "github.com/hawell/uperdis" - "fmt" - "time" - "arvancloud/redins/test" + "log" + "net" + "testing" + + "arvancloud/redins/test" + "fmt" + "github.com/coredns/coredns/request" + "github.com/hawell/logger" + "github.com/hawell/uperdis" + "github.com/miekg/dns" + "time" ) -var lookupZones = []string { - "example.com.", "example.net.", "example.aaa.", "example.bbb.", "example.ccc.", "example.ddd.", "example.caa.", "0.0.127.in-addr.arpa.", "20.127.10.in-addr.arpa.", +var lookupZones = []string{ + "example.com.", "example.net.", "example.aaa.", "example.bbb.", "example.ccc.", "example.ddd.", "example.caa.", "0.0.127.in-addr.arpa.", "20.127.10.in-addr.arpa.", } -var lookupConfig = []string { - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.com.","ns":"ns1.example.com.","refresh":44,"retry":55,"expire":66}}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.net.","ns":"ns1.example.net.","refresh":44,"retry":55,"expire":66}}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.aaa.","ns":"ns1.example.aaa.","refresh":44,"retry":55,"expire":66}}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.bbb.","ns":"ns1.example.bbb.","refresh":44,"retry":55,"expire":66}}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.ccc.","ns":"ns1.example.ccc.","refresh":44,"retry":55,"expire":66}}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.ddd.","ns":"ns1.example.ddd.","refresh":44,"retry":55,"expire":66},"cname_flattening":true}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.caa.","ns":"ns1.example.caa.","refresh":44,"retry":55,"expire":66}}`, - "", - "", +var lookupConfig = []string{ + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.com.","ns":"ns1.example.com.","refresh":44,"retry":55,"expire":66}}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.net.","ns":"ns1.example.net.","refresh":44,"retry":55,"expire":66}}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.aaa.","ns":"ns1.example.aaa.","refresh":44,"retry":55,"expire":66}}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.bbb.","ns":"ns1.example.bbb.","refresh":44,"retry":55,"expire":66}}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.ccc.","ns":"ns1.example.ccc.","refresh":44,"retry":55,"expire":66}}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.ddd.","ns":"ns1.example.ddd.","refresh":44,"retry":55,"expire":66},"cname_flattening":true}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.example.caa.","ns":"ns1.example.caa.","refresh":44,"retry":55,"expire":66}}`, + "", + "", } -var lookupEntries = [][][]string { - { - {"x", - `{ +var lookupEntries = [][][]string{ + { + {"x", + `{ "a":{"ttl":300, "records":[{"ip":"1.2.3.4", "country":"ES"},{"ip":"5.6.7.8", "country":""}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}, "txt":{"ttl":300, "records":[{"text":"foo"},{"text":"bar"}]}, @@ -40,843 +40,864 @@ var lookupEntries = [][][]string { "mx":{"ttl":300, "records":[{"host":"mx1.example.com.", "preference":10},{"host":"mx2.example.com.", "preference":10}]}, "srv":{"ttl":300, "records":[{"target":"sip.example.com.","port":555,"priority":10,"weight":100}]} }`, - }, - {"y", - `{"cname":{"ttl":300, "host":"x.example.com."}}`, - }, - {"ns1", - `{"a":{"ttl":300, "records":[{"ip":"2.2.2.2"}]}}`, - }, - {"ns2", - `{"a":{"ttl":300, "records":[{"ip":"3.3.3.3"}]}}`, - }, - {"_sip._tcp", - `{"srv":{"ttl":300, "records":[{"target":"sip.example.com.","port":555,"priority":10,"weight":100}]}}`, - }, - {"_443._tcp.www", - `{"tlsa":{"ttl":300, "records":[{"usage":0, "selector":0, "matching_type":1, "certificate":"d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971"}]}}`, - }, - {"_990._tcp", - `{ + }, + {"y", + `{"cname":{"ttl":300, "host":"x.example.com."}}`, + }, + {"ns1", + `{"a":{"ttl":300, "records":[{"ip":"2.2.2.2"}]}}`, + }, + {"ns2", + `{"a":{"ttl":300, "records":[{"ip":"3.3.3.3"}]}}`, + }, + {"_sip._tcp", + `{"srv":{"ttl":300, "records":[{"target":"sip.example.com.","port":555,"priority":10,"weight":100}]}}`, + }, + {"_443._tcp.www", + `{"tlsa":{"ttl":300, "records":[{"usage":0, "selector":0, "matching_type":1, "certificate":"d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971"}]}}`, + }, + {"_990._tcp", + `{ "tlsa":{"ttl":300, "records":[ {"usage":1, "selector":1, "matching_type":1, "certificate":"1CFC98A706BCF3683015"}, {"usage":1, "selector":1, "matching_type":1, "certificate":"62D5414CD1CC657E3D30"} ]}}`, - }, - {"sip", - `{"a":{"ttl":300, "records":[{"ip":"7.7.7.7"}]}, + }, + {"sip", + `{"a":{"ttl":300, "records":[{"ip":"7.7.7.7"}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}}`, - }, - {"t.u.v.w", - `{"a":{"ttl":300, "records":[{"ip":"9.9.9.9"}]}}`, - }, - }, - { - {"@", - `{"ns":{"ttl":300, "records":[{"host":"ns1.example.net."},{"host":"ns2.example.net."}]}}`, - }, - {"sub.*", - `{"txt":{"ttl":300, "records":[{"text":"this is not a wildcard"}]}}`, - }, - {"host1", - `{"a":{"ttl":300, "records":[{"ip":"5.5.5.5"}]}}`, - }, - {"subdel", - `{"ns":{"ttl":300, "records":[{"host":"ns1.subdel.example.net."},{"host":"ns2.subdel.example.net."}]}}`, - }, - {"*", - `{"txt":{"ttl":300, "records":[{"text":"this is a wildcard"}]}, + }, + {"t.u.v.w", + `{"a":{"ttl":300, "records":[{"ip":"9.9.9.9"}]}}`, + }, + }, + { + {"@", + `{"ns":{"ttl":300, "records":[{"host":"ns1.example.net."},{"host":"ns2.example.net."}]}}`, + }, + {"sub.*", + `{"txt":{"ttl":300, "records":[{"text":"this is not a wildcard"}]}}`, + }, + {"host1", + `{"a":{"ttl":300, "records":[{"ip":"5.5.5.5"}]}}`, + }, + {"subdel", + `{"ns":{"ttl":300, "records":[{"host":"ns1.subdel.example.net."},{"host":"ns2.subdel.example.net."}]}}`, + }, + {"*", + `{"txt":{"ttl":300, "records":[{"text":"this is a wildcard"}]}, "mx":{"ttl":300, "records":[{"host":"host1.example.net.","preference": 10}]}}`, - }, - {"_ssh._tcp.host1", - `{"srv":{"ttl":300, "records":[{"target":"tcp.example.com.","port":123,"priority":10,"weight":100}]}}`, - }, - {"_ssh._tcp.host2", - `{"srv":{"ttl":300, "records":[{"target":"tcp.example.com.","port":123,"priority":10,"weight":100}]}}`, - }, - }, - { - {"x", - `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}, + }, + {"_ssh._tcp.host1", + `{"srv":{"ttl":300, "records":[{"target":"tcp.example.com.","port":123,"priority":10,"weight":100}]}}`, + }, + {"_ssh._tcp.host2", + `{"srv":{"ttl":300, "records":[{"target":"tcp.example.com.","port":123,"priority":10,"weight":100}]}}`, + }, + }, + { + {"x", + `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}, "txt":{"ttl":300, "records":[{"text":"foo"},{"text":"bar"}]}, "ns":{"ttl":300, "records":[{"host":"ns1.example.aaa."},{"ttl":300, "host":"ns2.example.aaa."}]}, "mx":{"ttl":300, "records":[{"host":"mx1.example.aaa.", "preference":10},{"host":"mx2.example.aaa.", "preference":10}]}, "srv":{"ttl":300, "records":[{"target":"sip.example.aaa.","port":555,"priority":10,"weight":100}]}}`, - }, - {"y", - `{"cname":{"ttl":300, "host":"x.example.aaa."}}`, - }, - {"z", - `{"cname":{"ttl":300, "host":"y.example.aaa."}}`, - }, - }, - { - {"x", - `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, - }, - {"y", - `{"cname":{"ttl":300, "host":"x.example.bbb."}}`, - }, - {"z", - `{}`, - }, - }, - { - {"x", - `{"txt":{"ttl":300, "records":[{"text":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}]}}`, - }, - }, - { - {"a", - `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}, + }, + {"y", + `{"cname":{"ttl":300, "host":"x.example.aaa."}}`, + }, + {"z", + `{"cname":{"ttl":300, "host":"y.example.aaa."}}`, + }, + }, + { + {"x", + `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, + }, + {"y", + `{"cname":{"ttl":300, "host":"x.example.bbb."}}`, + }, + {"z", + `{}`, + }, + }, + { + {"x", + `{"txt":{"ttl":300, "records":[{"text":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}]}}`, + }, + }, + { + {"a", + `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}, "txt":{"ttl":300, "records":[{"text":"foo"},{"text":"bar"}]}, "ns":{"ttl":300, "records":[{"host":"ns1.example.ddd."},{"ttl":300, "host":"ns2.example.ddd."}]}, "mx":{"ttl":300, "records":[{"host":"mx1.example.ddd.", "preference":10},{"host":"mx2.example.ddd.", "preference":10}]}, "srv":{"ttl":300, "records":[{"target":"sip.example.ddd.","port":555,"priority":10,"weight":100}]}}`, - }, - {"b", - `{"cname":{"ttl":300, "host":"a.example.ddd."}}`, - }, - {"c", - `{"cname":{"ttl":300, "host":"b.example.ddd."}}`, - }, - {"d", - `{"cname":{"ttl":300, "host":"c.example.ddd."}}`, - }, - {"e", - `{"cname":{"ttl":300, "host":"d.example.ddd."}}`, - }, - }, - { - {"@", - `{"caa":{"ttl":300, "records":[{"tag":"issue", "value":"godaddy.com;", "flag":0}]}}`, - }, - {"a.b.c.d", - `{"cname":{"ttl":300, "host":"b.c.d.example.caa."}}`, - }, - {"b.c.d", - `{"cname":{"ttl":300, "host":"c.d.example.caa."}}`, - }, - {"c.d", - `{"cname":{"ttl":300, "host":"d.example.caa."}}`, - }, - {"d", - `{"cname":{"ttl":300, "host":"example.caa."}}`, - }, - {"x.y.z", - `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, - }, - {"y.z", - `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, - }, - {"z", - `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, - }, - }, - { - {"1", - `{"ptr":{"ttl":300, "domain":"localhost"}}`, - }, - }, - { - {"54", - `{"ptr":{"ttl":300, "domain":"example.fff"}}`, - }, - }, + }, + {"b", + `{"cname":{"ttl":300, "host":"a.example.ddd."}}`, + }, + {"c", + `{"cname":{"ttl":300, "host":"b.example.ddd."}}`, + }, + {"d", + `{"cname":{"ttl":300, "host":"c.example.ddd."}}`, + }, + {"e", + `{"cname":{"ttl":300, "host":"d.example.ddd."}}`, + }, + }, + { + {"@", + `{"caa":{"ttl":300, "records":[{"tag":"issue", "value":"godaddy.com;", "flag":0}]}}`, + }, + {"a.b.c.d", + `{"cname":{"ttl":300, "host":"b.c.d.example.caa."}}`, + }, + {"b.c.d", + `{"cname":{"ttl":300, "host":"c.d.example.caa."}}`, + }, + {"c.d", + `{"cname":{"ttl":300, "host":"d.example.caa."}}`, + }, + {"d", + `{"cname":{"ttl":300, "host":"example.caa."}}`, + }, + {"x.y.z", + `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, + }, + {"y.z", + `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, + }, + {"z", + `{"a":{"ttl":300, "records":[{"ip":"1.2.3.4"}]}}`, + }, + }, + { + {"1", + `{"ptr":{"ttl":300, "domain":"localhost"}}`, + }, + }, + { + {"54", + `{"ptr":{"ttl":300, "domain":"example.fff"}}`, + }, + }, } var lookupTestCases = [][]test.Case{ - // basic tests - { - // NOAUTH Test - { - Qname: "dsdsd.sdf.dfd.", Qtype: dns.TypeA, - Rcode: dns.RcodeNotAuth, - }, - // A Test - { - Qname: "x.example.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("x.example.com. 300 IN A 1.2.3.4"), - test.A("x.example.com. 300 IN A 5.6.7.8"), - }, - }, - // AAAA Test - { - Qname: "x.example.com.", Qtype: dns.TypeAAAA, - Answer: []dns.RR{ - test.AAAA("x.example.com. 300 IN AAAA ::1"), - }, - }, - // TXT Test - { - Qname: "x.example.com.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("x.example.com. 300 IN TXT bar"), - test.TXT("x.example.com. 300 IN TXT foo"), - }, - }, - // CNAME Test - { - Qname: "y.example.com.", Qtype: dns.TypeCNAME, - Answer: []dns.RR{ - test.CNAME("y.example.com. 300 IN CNAME x.example.com."), - }, - }, - // NS Test - { - Qname: "x.example.com.", Qtype: dns.TypeNS, - Answer: []dns.RR{ - test.NS("x.example.com. 300 IN NS ns1.example.com."), - test.NS("x.example.com. 300 IN NS ns2.example.com."), - }, - }, - // MX Test - { - Qname: "x.example.com.", Qtype: dns.TypeMX, - Answer: []dns.RR{ - test.MX("x.example.com. 300 IN MX 10 mx1.example.com."), - test.MX("x.example.com. 300 IN MX 10 mx2.example.com."), - }, - }, - // SRV Test - { - Qname: "_sip._tcp.example.com.", Qtype: dns.TypeSRV, - Answer: []dns.RR{ - test.SRV("_sip._tcp.example.com. 300 IN SRV 10 100 555 sip.example.com."), - }, - }, - // TLSA Test - { - Qname: "_443._tcp.www.example.com.", Qtype: dns.TypeTLSA, - Answer: []dns.RR{ - test.TLSA("_443._tcp.www.example.com. 300 IN TLSA 0 0 1 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971"), - }, - }, - { - Qname: "_990._tcp.example.com.", Qtype: dns.TypeTLSA, - Answer: []dns.RR{ - test.TLSA("_990._tcp.example.com. 300 IN TLSA 1 1 1 1CFC98A706BCF3683015"), - test.TLSA("_990._tcp.example.com. 300 IN TLSA 1 1 1 62D5414CD1CC657E3D30"), - }, - }, - // NXDOMAIN Test - { - Qname: "notexists.example.com.", Qtype: dns.TypeA, - Rcode: dns.RcodeNameError, - Ns: []dns.RR{ - test.SOA("example.com. 300 IN SOA ns1.example.com. hostmaster.example.com. 1460498836 44 55 66 100"), - }, - }, - // SOA Test - { - Qname: "example.com.", Qtype: dns.TypeSOA, - Answer: []dns.RR{ - test.SOA("example.com. 300 IN SOA ns1.example.com. hostmaster.example.com. 1460498836 44 55 66 100"), - }, - }, - // not implemented - { - Qname: "example.com.", Qtype: dns.TypeUNSPEC, - Rcode: dns.RcodeNotImplemented, - Ns: []dns.RR{ - test.SOA("example.com. 300 IN SOA ns1.example.com. hostmaster.example.com. 1460498836 44 55 66 100"), - }, - }, - // Empty non-terminal Test - // FIXME: should return NOERROR instead of NXDOMAIN - /* - { - Qname:"v.w.example.com.", Qtype: dns.TypeA, - }, - */ - }, - // Wildcard Tests - { - { - Qname: "host3.example.net.", Qtype: dns.TypeMX, - Answer: []dns.RR{ - test.MX("host3.example.net. 300 IN MX 10 host1.example.net."), - }, - }, - { - Qname: "host3.example.net.", Qtype: dns.TypeA, - Ns: []dns.RR{ - test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), - }, - }, - { - Qname: "foo.bar.example.net.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("foo.bar.example.net. 300 IN TXT \"this is a wildcard\""), - }, - }, - { - Qname: "host1.example.net.", Qtype: dns.TypeMX, - Ns: []dns.RR{ - test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), - }, - }, - { - Qname: "sub.*.example.net.", Qtype: dns.TypeMX, - Ns: []dns.RR{ - test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), - }, - }, - { - Qname: "host.subdel.example.net.", Qtype: dns.TypeA, - Rcode: dns.RcodeNameError, - Ns: []dns.RR{ - test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), - }, - }, - { - Qname: "ghost.*.example.net.", Qtype: dns.TypeMX, - Rcode: dns.RcodeNameError, - Ns: []dns.RR{ - test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), - }, - }, - { - Qname: "f.h.g.f.t.r.e.example.net.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("f.h.g.f.t.r.e.example.net. 300 IN TXT \"this is a wildcard\""), - }, - }, - }, - // CNAME tests - { - { - Qname: "y.example.aaa.", Qtype: dns.TypeCNAME, - Answer: []dns.RR{ - test.CNAME("y.example.aaa. 300 IN CNAME x.example.aaa."), - }, - }, - { - Qname: "z.example.aaa.", Qtype: dns.TypeCNAME, - Answer: []dns.RR{ - test.CNAME("z.example.aaa. 300 IN CNAME y.example.aaa."), - }, - }, - { - Qname: "z.example.aaa.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("x.example.aaa. 300 IN A 1.2.3.4"), - test.CNAME("y.example.aaa. 300 IN CNAME x.example.aaa."), - test.CNAME("z.example.aaa. 300 IN CNAME y.example.aaa."), - }, - }, - }, - // empty values tests - { - // empty A test - { - Qname: "z.example.bbb.", Qtype: dns.TypeA, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty AAAA test - { - Qname: "z.example.bbb.", Qtype: dns.TypeAAAA, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty TXT test - { - Qname: "z.example.bbb.", Qtype: dns.TypeTXT, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty NS test - { - Qname: "z.example.bbb.", Qtype: dns.TypeNS, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty MX test - { - Qname: "z.example.bbb.", Qtype: dns.TypeMX, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty SRV test - { - Qname: "z.example.bbb.", Qtype: dns.TypeSRV, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty CNAME test - { - Qname: "x.example.bbb.", Qtype: dns.TypeCNAME, - Ns: []dns.RR{ - test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), - }, - }, - // empty A test with cname - { - Qname: "y.example.bbb.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("x.example.bbb. 300 IN A 1.2.3.4"), - test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), - }, - }, - // empty AAAA test with cname - { - Qname: "y.example.bbb.", Qtype: dns.TypeAAAA, - Answer: []dns.RR{ - test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), - }, - }, - // empty TXT test with cname - { - Qname: "y.example.bbb.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), - }, - }, - // empty NS test with cname - { - Qname: "y.example.bbb.", Qtype: dns.TypeNS, - Answer: []dns.RR{ - test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), - }, - }, - // empty MX test with cname - { - Qname: "y.example.bbb.", Qtype: dns.TypeMX, - Answer: []dns.RR{ - test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), - }, - }, - // empty SRV test with cname - { - Qname: "y.example.bbb.", Qtype: dns.TypeSRV, - Answer: []dns.RR{ - test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), - }, - }, - }, - // long text - { - { - Qname: "x.example.ccc.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("x.example.ccc. 300 IN TXT \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\""), - }, - }, - }, - // CNAME flattening - { - { - Qname: "e.example.ddd.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("e.example.ddd. 300 IN A 1.2.3.4"), - }, - }, - { - Qname: "e.example.ddd.", Qtype: dns.TypeAAAA, - Answer: []dns.RR{ - test.AAAA("e.example.ddd. 300 IN AAAA ::1"), - }, - }, - { - Qname: "e.example.ddd.", Qtype: dns.TypeTXT, - Answer: []dns.RR{ - test.TXT("e.example.ddd. 300 IN TXT \"bar\""), - test.TXT("e.example.ddd. 300 IN TXT \"foo\""), - }, - }, - { - Qname: "e.example.ddd.", Qtype: dns.TypeNS, - Answer: []dns.RR{ - test.NS("e.example.ddd. 300 IN NS ns1.example.ddd."), - test.NS("e.example.ddd. 300 IN NS ns2.example.ddd."), - }, - }, - // MX Test - { - Qname: "e.example.ddd.", Qtype: dns.TypeMX, - Answer: []dns.RR{ - test.MX("e.example.ddd. 300 IN MX 10 mx1.example.ddd."), - test.MX("e.example.ddd. 300 IN MX 10 mx2.example.ddd."), - }, - }, - // SRV Test - { - Qname: "e.example.ddd.", Qtype: dns.TypeSRV, - Answer: []dns.RR{ - test.SRV("e.example.ddd. 300 IN SRV 10 100 555 sip.example.ddd."), - }, - }, - { - Qname: "e.example.ddd.", Qtype: dns.TypeCNAME, - Answer: []dns.RR{ - test.CNAME("e.example.ddd. 300 IN CNAME d.example.ddd."), - }, - }, - }, - // CAA Test - { - { - Qname: "example.caa.", Qtype: dns.TypeCAA, - Answer: []dns.RR{ - test.CAA("example.caa. 300 IN CAA 0 issue \"godaddy.com;\""), - }, - }, - { - Qname: "a.b.c.d.example.caa.", Qtype: dns.TypeCAA, - Answer: []dns.RR{ - test.CNAME("a.b.c.d.example.caa. 300 IN CNAME b.c.d.example.caa."), - test.CNAME("b.c.d.example.caa. 300 IN CNAME c.d.example.caa."), - test.CNAME("c.d.example.caa. 300 IN CNAME d.example.caa."), - test.CNAME("d.example.caa. 300 IN CNAME example.caa."), - test.CAA("example.caa. 300 IN CAA 0 issue \"godaddy.com;\""), - }, - }, - { - Qname: "x.y.z.example.caa.", Qtype: dns.TypeCAA, - Answer: []dns.RR{ - test.CAA("x.y.z.example.caa. 300 IN CAA 0 issue \"godaddy.com;\""), - }, - }, - }, - // PTR Test - { - { - Qname: "1.0.0.127.in-addr.arpa.", Qtype:dns.TypePTR, - Answer: []dns.RR{ - test.PTR("1.0.0.127.in-addr.arpa. 300 IN PTR localhost."), - }, - }, - }, - { - { - Qname: "54.20.127.10.in-addr.arpa.", Qtype:dns.TypePTR, - Answer: []dns.RR{ - test.PTR("54.20.127.10.in-addr.arpa. 300 IN PTR example.fff."), - }, - }, - }, + // basic tests + { + // NOAUTH Test + { + Qname: "dsdsd.sdf.dfd.", Qtype: dns.TypeA, + Rcode: dns.RcodeNotAuth, + }, + // A Test + { + Qname: "x.example.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("x.example.com. 300 IN A 1.2.3.4"), + test.A("x.example.com. 300 IN A 5.6.7.8"), + }, + }, + // AAAA Test + { + Qname: "x.example.com.", Qtype: dns.TypeAAAA, + Answer: []dns.RR{ + test.AAAA("x.example.com. 300 IN AAAA ::1"), + }, + }, + // TXT Test + { + Qname: "x.example.com.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("x.example.com. 300 IN TXT bar"), + test.TXT("x.example.com. 300 IN TXT foo"), + }, + }, + // CNAME Test + { + Qname: "y.example.com.", Qtype: dns.TypeCNAME, + Answer: []dns.RR{ + test.CNAME("y.example.com. 300 IN CNAME x.example.com."), + }, + }, + // NS Test + { + Qname: "x.example.com.", Qtype: dns.TypeNS, + Answer: []dns.RR{ + test.NS("x.example.com. 300 IN NS ns1.example.com."), + test.NS("x.example.com. 300 IN NS ns2.example.com."), + }, + }, + // MX Test + { + Qname: "x.example.com.", Qtype: dns.TypeMX, + Answer: []dns.RR{ + test.MX("x.example.com. 300 IN MX 10 mx1.example.com."), + test.MX("x.example.com. 300 IN MX 10 mx2.example.com."), + }, + }, + // SRV Test + { + Qname: "_sip._tcp.example.com.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.SRV("_sip._tcp.example.com. 300 IN SRV 10 100 555 sip.example.com."), + }, + }, + // TLSA Test + { + Qname: "_443._tcp.www.example.com.", Qtype: dns.TypeTLSA, + Answer: []dns.RR{ + test.TLSA("_443._tcp.www.example.com. 300 IN TLSA 0 0 1 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971"), + }, + }, + { + Qname: "_990._tcp.example.com.", Qtype: dns.TypeTLSA, + Answer: []dns.RR{ + test.TLSA("_990._tcp.example.com. 300 IN TLSA 1 1 1 1CFC98A706BCF3683015"), + test.TLSA("_990._tcp.example.com. 300 IN TLSA 1 1 1 62D5414CD1CC657E3D30"), + }, + }, + // NXDOMAIN Test + { + Qname: "notexists.example.com.", Qtype: dns.TypeA, + Rcode: dns.RcodeNameError, + Ns: []dns.RR{ + test.SOA("example.com. 300 IN SOA ns1.example.com. hostmaster.example.com. 1460498836 44 55 66 100"), + }, + }, + // SOA Test + { + Qname: "example.com.", Qtype: dns.TypeSOA, + Answer: []dns.RR{ + test.SOA("example.com. 300 IN SOA ns1.example.com. hostmaster.example.com. 1460498836 44 55 66 100"), + }, + }, + // not implemented + { + Qname: "example.com.", Qtype: dns.TypeUNSPEC, + Rcode: dns.RcodeNotImplemented, + Ns: []dns.RR{ + test.SOA("example.com. 300 IN SOA ns1.example.com. hostmaster.example.com. 1460498836 44 55 66 100"), + }, + }, + // Empty non-terminal Test + // FIXME: should return NOERROR instead of NXDOMAIN + /* + { + Qname:"v.w.example.com.", Qtype: dns.TypeA, + }, + */ + }, + // Wildcard Tests + { + { + Qname: "host3.example.net.", Qtype: dns.TypeMX, + Answer: []dns.RR{ + test.MX("host3.example.net. 300 IN MX 10 host1.example.net."), + }, + }, + { + Qname: "host3.example.net.", Qtype: dns.TypeA, + Ns: []dns.RR{ + test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), + }, + }, + { + Qname: "foo.bar.example.net.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("foo.bar.example.net. 300 IN TXT \"this is a wildcard\""), + }, + }, + { + Qname: "host1.example.net.", Qtype: dns.TypeMX, + Ns: []dns.RR{ + test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), + }, + }, + { + Qname: "sub.*.example.net.", Qtype: dns.TypeMX, + Ns: []dns.RR{ + test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), + }, + }, + { + Qname: "host.subdel.example.net.", Qtype: dns.TypeA, + Rcode: dns.RcodeNameError, + Ns: []dns.RR{ + test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), + }, + }, + { + Qname: "ghost.*.example.net.", Qtype: dns.TypeMX, + Rcode: dns.RcodeNameError, + Ns: []dns.RR{ + test.SOA("example.net. 300 IN SOA ns1.example.net. hostmaster.example.net. 1460498836 44 55 66 100"), + }, + }, + { + Qname: "f.h.g.f.t.r.e.example.net.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("f.h.g.f.t.r.e.example.net. 300 IN TXT \"this is a wildcard\""), + }, + }, + }, + // CNAME tests + { + { + Qname: "y.example.aaa.", Qtype: dns.TypeCNAME, + Answer: []dns.RR{ + test.CNAME("y.example.aaa. 300 IN CNAME x.example.aaa."), + }, + }, + { + Qname: "z.example.aaa.", Qtype: dns.TypeCNAME, + Answer: []dns.RR{ + test.CNAME("z.example.aaa. 300 IN CNAME y.example.aaa."), + }, + }, + { + Qname: "z.example.aaa.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("x.example.aaa. 300 IN A 1.2.3.4"), + test.CNAME("y.example.aaa. 300 IN CNAME x.example.aaa."), + test.CNAME("z.example.aaa. 300 IN CNAME y.example.aaa."), + }, + }, + }, + // empty values tests + { + // empty A test + { + Qname: "z.example.bbb.", Qtype: dns.TypeA, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty AAAA test + { + Qname: "z.example.bbb.", Qtype: dns.TypeAAAA, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty TXT test + { + Qname: "z.example.bbb.", Qtype: dns.TypeTXT, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty NS test + { + Qname: "z.example.bbb.", Qtype: dns.TypeNS, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty MX test + { + Qname: "z.example.bbb.", Qtype: dns.TypeMX, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty SRV test + { + Qname: "z.example.bbb.", Qtype: dns.TypeSRV, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty CNAME test + { + Qname: "x.example.bbb.", Qtype: dns.TypeCNAME, + Ns: []dns.RR{ + test.SOA("example.bbb. 300 IN SOA ns1.example.bbb. hostmaster.example.bbb. 1460498836 44 55 66 100"), + }, + }, + // empty A test with cname + { + Qname: "y.example.bbb.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("x.example.bbb. 300 IN A 1.2.3.4"), + test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), + }, + }, + // empty AAAA test with cname + { + Qname: "y.example.bbb.", Qtype: dns.TypeAAAA, + Answer: []dns.RR{ + test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), + }, + }, + // empty TXT test with cname + { + Qname: "y.example.bbb.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), + }, + }, + // empty NS test with cname + { + Qname: "y.example.bbb.", Qtype: dns.TypeNS, + Answer: []dns.RR{ + test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), + }, + }, + // empty MX test with cname + { + Qname: "y.example.bbb.", Qtype: dns.TypeMX, + Answer: []dns.RR{ + test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), + }, + }, + // empty SRV test with cname + { + Qname: "y.example.bbb.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.CNAME("y.example.bbb. 300 IN CNAME x.example.bbb."), + }, + }, + }, + // long text + { + { + Qname: "x.example.ccc.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("x.example.ccc. 300 IN TXT \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\""), + }, + }, + }, + // CNAME flattening + { + { + Qname: "e.example.ddd.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("e.example.ddd. 300 IN A 1.2.3.4"), + }, + }, + { + Qname: "e.example.ddd.", Qtype: dns.TypeAAAA, + Answer: []dns.RR{ + test.AAAA("e.example.ddd. 300 IN AAAA ::1"), + }, + }, + { + Qname: "e.example.ddd.", Qtype: dns.TypeTXT, + Answer: []dns.RR{ + test.TXT("e.example.ddd. 300 IN TXT \"bar\""), + test.TXT("e.example.ddd. 300 IN TXT \"foo\""), + }, + }, + { + Qname: "e.example.ddd.", Qtype: dns.TypeNS, + Answer: []dns.RR{ + test.NS("e.example.ddd. 300 IN NS ns1.example.ddd."), + test.NS("e.example.ddd. 300 IN NS ns2.example.ddd."), + }, + }, + // MX Test + { + Qname: "e.example.ddd.", Qtype: dns.TypeMX, + Answer: []dns.RR{ + test.MX("e.example.ddd. 300 IN MX 10 mx1.example.ddd."), + test.MX("e.example.ddd. 300 IN MX 10 mx2.example.ddd."), + }, + }, + // SRV Test + { + Qname: "e.example.ddd.", Qtype: dns.TypeSRV, + Answer: []dns.RR{ + test.SRV("e.example.ddd. 300 IN SRV 10 100 555 sip.example.ddd."), + }, + }, + { + Qname: "e.example.ddd.", Qtype: dns.TypeCNAME, + Answer: []dns.RR{ + test.CNAME("e.example.ddd. 300 IN CNAME d.example.ddd."), + }, + }, + }, + // CAA Test + { + { + Qname: "example.caa.", Qtype: dns.TypeCAA, + Answer: []dns.RR{ + test.CAA("example.caa. 300 IN CAA 0 issue \"godaddy.com;\""), + }, + }, + { + Qname: "a.b.c.d.example.caa.", Qtype: dns.TypeCAA, + Answer: []dns.RR{ + test.CNAME("a.b.c.d.example.caa. 300 IN CNAME b.c.d.example.caa."), + test.CNAME("b.c.d.example.caa. 300 IN CNAME c.d.example.caa."), + test.CNAME("c.d.example.caa. 300 IN CNAME d.example.caa."), + test.CNAME("d.example.caa. 300 IN CNAME example.caa."), + test.CAA("example.caa. 300 IN CAA 0 issue \"godaddy.com;\""), + }, + }, + { + Qname: "x.y.z.example.caa.", Qtype: dns.TypeCAA, + Answer: []dns.RR{ + test.CAA("x.y.z.example.caa. 300 IN CAA 0 issue \"godaddy.com;\""), + }, + }, + }, + // PTR Test + { + { + Qname: "1.0.0.127.in-addr.arpa.", Qtype: dns.TypePTR, + Answer: []dns.RR{ + test.PTR("1.0.0.127.in-addr.arpa. 300 IN PTR localhost."), + }, + }, + }, + { + { + Qname: "54.20.127.10.in-addr.arpa.", Qtype: dns.TypePTR, + Answer: []dns.RR{ + test.PTR("54.20.127.10.in-addr.arpa. 300 IN PTR example.fff."), + }, + }, + }, } -var handlerTestConfig = HandlerConfig { - MaxTtl: 300, - CacheTimeout: 60, - ZoneReload: 600, - Redis: uperdis.RedisConfig { - Ip: "redis", - Port: 6379, - DB: 0, - Password: "", - Prefix: "test_", - Suffix: "_test", - ConnectTimeout: 0, - ReadTimeout: 0, - }, - Log: logger.LogConfig { - Enable: false, - }, - Upstream: []UpstreamConfig { - { - Ip: "1.1.1.1", - Port: 53, - Protocol: "udp", - Timeout: 1000, - }, - }, - GeoIp: GeoIpConfig { - Enable: true, - CountryDB: "../geoCity.mmdb", - ASNDB: "../geoIsp.mmdb", - }, +var handlerTestConfig = HandlerConfig{ + MaxTtl: 300, + CacheTimeout: 60, + ZoneReload: 600, + Redis: uperdis.RedisConfig{ + Ip: "redis", + Port: 6379, + DB: 0, + Password: "", + Prefix: "test_", + Suffix: "_test", + ConnectTimeout: 0, + ReadTimeout: 0, + }, + Log: logger.LogConfig{ + Enable: false, + }, + Upstream: []UpstreamConfig{ + { + Ip: "1.1.1.1", + Port: 53, + Protocol: "udp", + Timeout: 1000, + }, + }, + GeoIp: GeoIpConfig{ + Enable: true, + CountryDB: "../geoCity.mmdb", + ASNDB: "../geoIsp.mmdb", + }, } func TestLookup(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - for i, zone := range lookupZones { - h.Redis.SAdd("redins:zones", zone) - for _, cmd := range lookupEntries[i] { - err := h.Redis.HSet("redins:zones:" + zone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - t.Fail() - } - } - h.Redis.Set("redins:zones:" + zone + ":config", lookupConfig[i]) - h.LoadZones() - for j, tc := range lookupTestCases[i] { - - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - - if err := test.SortAndCheck(resp, tc); err != nil { - fmt.Println(i, j, err, tc.Qname, tc.Answer, resp.Answer) - t.Fail() - } - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + for i, zone := range lookupZones { + h.Redis.SAdd("redins:zones", zone) + for _, cmd := range lookupEntries[i] { + err := h.Redis.HSet("redins:zones:"+zone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + t.Fail() + } + } + h.Redis.Set("redins:zones:"+zone+":config", lookupConfig[i]) + h.LoadZones() + for j, tc := range lookupTestCases[i] { + + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + + if err := test.SortAndCheck(resp, tc); err != nil { + fmt.Println(i, j, err, tc.Qname, tc.Answer, resp.Answer) + t.Fail() + } + } + } } func TestWeight(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - // distribution - ips := []IP_RR { - { Ip:net.ParseIP("1.2.3.4"), Weight: 4}, - { Ip:net.ParseIP("2.3.4.5"), Weight: 1}, - { Ip:net.ParseIP("3.4.5.6"), Weight: 5}, - { Ip:net.ParseIP("4.5.6.7"), Weight: 10}, - } - n := make([]int, 4) - for i:= 0; i < 100000; i++ { - x := ChooseIp(ips, true) - switch ips[x].Ip.String() { - case "1.2.3.4": n[0]++ - case "2.3.4.5": n[1]++ - case "3.4.5.6": n[2]++ - case "4.5.6.7": n[3]++ - } - } - if n[0] > n[2] || n[2] > n[3] || n[1] > n[0] { - t.Fail() - } - - // all zero - for i := range ips { - ips[i].Weight = 0 - } - n[0], n[1], n[2], n[3] = 0, 0, 0, 0 - for i:= 0; i < 100000; i++ { - x := ChooseIp(ips, true) - switch ips[x].Ip.String() { - case "1.2.3.4": n[0]++ - case "2.3.4.5": n[1]++ - case "3.4.5.6": n[2]++ - case "4.5.6.7": n[3]++ - } - } - for i := 0; i < 4; i++ { - if n[i] < 2000 && n[i] > 3000 { - t.Fail() - } - } - - // some zero - n[0], n[1], n[2], n[3] = 0, 0, 0, 0 - ips[0].Weight, ips[1].Weight, ips[2].Weight, ips[3].Weight = 0, 5, 7, 0 - for i:= 0; i < 100000; i++ { - x := ChooseIp(ips, true) - switch ips[x].Ip.String() { - case "1.2.3.4": n[0]++ - case "2.3.4.5": n[1]++ - case "3.4.5.6": n[2]++ - case "4.5.6.7": n[3]++ - } - } - log.Println(n) - if n[0] > 0 || n[3] > 0 { - t.Fail() - } - - - // weighted = false - n[0], n[1], n[2], n[3] = 0, 0, 0, 0 - ips[0].Weight, ips[1].Weight, ips[2].Weight, ips[3].Weight = 0, 5, 7, 0 - for i:= 0; i < 100000; i++ { - x := ChooseIp(ips, false) - switch ips[x].Ip.String() { - case "1.2.3.4": n[0]++ - case "2.3.4.5": n[1]++ - case "3.4.5.6": n[2]++ - case "4.5.6.7": n[3]++ - } - } - log.Println(n) - for i := 0; i < 4; i++ { - if n[i] < 2000 && n[i] > 3000 { - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + // distribution + ips := []IP_RR{ + {Ip: net.ParseIP("1.2.3.4"), Weight: 4}, + {Ip: net.ParseIP("2.3.4.5"), Weight: 1}, + {Ip: net.ParseIP("3.4.5.6"), Weight: 5}, + {Ip: net.ParseIP("4.5.6.7"), Weight: 10}, + } + n := make([]int, 4) + for i := 0; i < 100000; i++ { + x := ChooseIp(ips, true) + switch ips[x].Ip.String() { + case "1.2.3.4": + n[0]++ + case "2.3.4.5": + n[1]++ + case "3.4.5.6": + n[2]++ + case "4.5.6.7": + n[3]++ + } + } + if n[0] > n[2] || n[2] > n[3] || n[1] > n[0] { + t.Fail() + } + + // all zero + for i := range ips { + ips[i].Weight = 0 + } + n[0], n[1], n[2], n[3] = 0, 0, 0, 0 + for i := 0; i < 100000; i++ { + x := ChooseIp(ips, true) + switch ips[x].Ip.String() { + case "1.2.3.4": + n[0]++ + case "2.3.4.5": + n[1]++ + case "3.4.5.6": + n[2]++ + case "4.5.6.7": + n[3]++ + } + } + for i := 0; i < 4; i++ { + if n[i] < 2000 && n[i] > 3000 { + t.Fail() + } + } + + // some zero + n[0], n[1], n[2], n[3] = 0, 0, 0, 0 + ips[0].Weight, ips[1].Weight, ips[2].Weight, ips[3].Weight = 0, 5, 7, 0 + for i := 0; i < 100000; i++ { + x := ChooseIp(ips, true) + switch ips[x].Ip.String() { + case "1.2.3.4": + n[0]++ + case "2.3.4.5": + n[1]++ + case "3.4.5.6": + n[2]++ + case "4.5.6.7": + n[3]++ + } + } + log.Println(n) + if n[0] > 0 || n[3] > 0 { + t.Fail() + } + + // weighted = false + n[0], n[1], n[2], n[3] = 0, 0, 0, 0 + ips[0].Weight, ips[1].Weight, ips[2].Weight, ips[3].Weight = 0, 5, 7, 0 + for i := 0; i < 100000; i++ { + x := ChooseIp(ips, false) + switch ips[x].Ip.String() { + case "1.2.3.4": + n[0]++ + case "2.3.4.5": + n[1]++ + case "3.4.5.6": + n[2]++ + case "4.5.6.7": + n[3]++ + } + } + log.Println(n) + for i := 0; i < 4; i++ { + if n[i] < 2000 && n[i] > 3000 { + t.Fail() + } + } } -var anameZones = []string { - "arvancloud.com.", "arvan.an.", +var anameZones = []string{ + "arvancloud.com.", "arvan.an.", } -var anameConfig = []string { - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.arvancloud.com.","ns":"ns1.arvancloud.com.","refresh":44,"retry":55,"expire":66}}`, - `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.arvan.an.","ns":"ns1.arvan.an.","refresh":44,"retry":55,"expire":66}}`, +var anameConfig = []string{ + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.arvancloud.com.","ns":"ns1.arvancloud.com.","refresh":44,"retry":55,"expire":66}}`, + `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.arvan.an.","ns":"ns1.arvan.an.","refresh":44,"retry":55,"expire":66}}`, } var anameEntries = [][][]string{ - { - {"@", - `{"aname":{"location":"aname.arvan.an."}}`, - }, - {"upstream", - `{"aname":{"location":"google.com."}}`, - }, - {"upstream2", - `{"aname":{"location":"aname2.arvan.an."}}`, - }, - }, - { - {"aname", - `{"a":{"ttl":300, "records":[{"ip":"6.5.6.5"}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}}`, - }, - {"aname2", - `{ + { + {"@", + `{"aname":{"location":"aname.arvan.an."}}`, + }, + {"upstream", + `{"aname":{"location":"google.com."}}`, + }, + {"upstream2", + `{"aname":{"location":"aname2.arvan.an."}}`, + }, + }, + { + {"aname", + `{"a":{"ttl":300, "records":[{"ip":"6.5.6.5"}]}, "aaaa":{"ttl":300, "records":[{"ip":"::1"}]}}`, + }, + {"aname2", + `{ "a":{"ttl":300, "filter": {"count":"single", "order": "weighted", "geo_filter":"none"}, "records":[{"ip":"1.1.1.1", "weight":1},{"ip":"2.2.2.2", "weight":5},{"ip":"3.3.3.3", "weight":10}]}, "aaaa":{"ttl":300, "filter": {"count":"single", "order": "weighted", "geo_filter":"none"}, "records":[{"ip":"2001:db8::1", "weight":1},{"ip":"2001:db8::2", "weight":5},{"ip":"2001:db8::3", "weight":10}]} }`, - }, - }, + }, + }, } -var anameTestCases = []test.Case { - { - Qname: "arvancloud.com.", Qtype: dns.TypeA, - Answer: []dns.RR { - test.A("arvancloud.com. 300 IN A 6.5.6.5"), - }, - }, - { - Qname: "arvancloud.com.", Qtype: dns.TypeAAAA, - Answer: []dns.RR { - test.AAAA("arvancloud.com. 300 IN AAAA ::1"), - }, - }, +var anameTestCases = []test.Case{ + { + Qname: "arvancloud.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("arvancloud.com. 300 IN A 6.5.6.5"), + }, + }, + { + Qname: "arvancloud.com.", Qtype: dns.TypeAAAA, + Answer: []dns.RR{ + test.AAAA("arvancloud.com. 300 IN AAAA ::1"), + }, + }, } func TestANAME(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - for i, zone := range anameZones { - h.Redis.SAdd("redins:zones", zone) - for _, cmd := range anameEntries[i] { - err := h.Redis.HSet("redins:zones:" + zone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - t.Fail() - } - } - h.Redis.Set("redins:zones:" + zone + ":config", anameConfig[i]) - } - h.LoadZones() - - for _, tc := range anameTestCases { - - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - - if test.SortAndCheck(resp, tc) != nil { - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + for i, zone := range anameZones { + h.Redis.SAdd("redins:zones", zone) + for _, cmd := range anameEntries[i] { + err := h.Redis.HSet("redins:zones:"+zone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + t.Fail() + } + } + h.Redis.Set("redins:zones:"+zone+":config", anameConfig[i]) + } + h.LoadZones() + + for _, tc := range anameTestCases { + + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + + if test.SortAndCheck(resp, tc) != nil { + t.Fail() + } + } } func TestWeightedANAME(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - for i, zone := range anameZones { - h.Redis.SAdd("redins:zones", zone) - for _, cmd := range anameEntries[i] { - err := h.Redis.HSet("redins:zones:" + zone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - t.Fail() - } - } - h.Redis.Set("redins:zones:" + zone + ":config", anameConfig[i]) - } - h.LoadZones() - - tc := test.Case { - Qname: "upstream2.arvancloud.com.", Qtype: dns.TypeA, - } - tc2 := test.Case { - Qname: "upstream2.arvancloud.com.", Qtype: dns.TypeAAAA, - } - ip1 := 0 - ip2 := 0 - ip3 := 0 - for i := 0; i < 1000; i++ { - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if resp.Rcode != dns.RcodeSuccess { - t.Fail() - } - if len(resp.Answer) == 0 { - t.Fail() - } - a := resp.Answer[0].(*dns.A) - switch a.A.String() { - case "1.1.1.1":ip1++ - case "2.2.2.2":ip2++ - case "3.3.3.3":ip3++ - default: - t.Fail() - } - } - if !(ip1<ip2 && ip2<ip3) { - t.Fail() - } - ip61 := 0 - ip62 := 0 - ip63 := 0 - for i := 0; i < 1000; i++ { - r := tc2.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if resp.Rcode != dns.RcodeSuccess { - t.Fail() - } - if len(resp.Answer) == 0 { - t.Fail() - } - aaaa := resp.Answer[0].(*dns.AAAA) - switch aaaa.AAAA.String() { - case "2001:db8::1":ip61++ - case "2001:db8::2":ip62++ - case "2001:db8::3":ip63++ - default: - t.Fail() - } - } - if !(ip61<ip62 && ip62<ip63) { - t.Fail() - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + for i, zone := range anameZones { + h.Redis.SAdd("redins:zones", zone) + for _, cmd := range anameEntries[i] { + err := h.Redis.HSet("redins:zones:"+zone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + t.Fail() + } + } + h.Redis.Set("redins:zones:"+zone+":config", anameConfig[i]) + } + h.LoadZones() + + tc := test.Case{ + Qname: "upstream2.arvancloud.com.", Qtype: dns.TypeA, + } + tc2 := test.Case{ + Qname: "upstream2.arvancloud.com.", Qtype: dns.TypeAAAA, + } + ip1 := 0 + ip2 := 0 + ip3 := 0 + for i := 0; i < 1000; i++ { + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if resp.Rcode != dns.RcodeSuccess { + t.Fail() + } + if len(resp.Answer) == 0 { + t.Fail() + } + a := resp.Answer[0].(*dns.A) + switch a.A.String() { + case "1.1.1.1": + ip1++ + case "2.2.2.2": + ip2++ + case "3.3.3.3": + ip3++ + default: + t.Fail() + } + } + if !(ip1 < ip2 && ip2 < ip3) { + t.Fail() + } + ip61 := 0 + ip62 := 0 + ip63 := 0 + for i := 0; i < 1000; i++ { + r := tc2.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if resp.Rcode != dns.RcodeSuccess { + t.Fail() + } + if len(resp.Answer) == 0 { + t.Fail() + } + aaaa := resp.Answer[0].(*dns.AAAA) + switch aaaa.AAAA.String() { + case "2001:db8::1": + ip61++ + case "2001:db8::2": + ip62++ + case "2001:db8::3": + ip63++ + default: + t.Fail() + } + } + if !(ip61 < ip62 && ip62 < ip63) { + t.Fail() + } } var filterGeoZone = "filtergeo.com." @@ -884,8 +905,8 @@ var filterGeoZone = "filtergeo.com." var filterGeoConfig = `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.filter.com.","ns":"ns1.filter.com.","refresh":44,"retry":55,"expire":66}}` var filterGeoEntries = [][]string{ - {"ww1", - `{"a":{"ttl":300, "records":[ + {"ww1", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":""}, {"ip":"127.0.0.2", "country":""}, {"ip":"127.0.0.3", "country":""}, @@ -893,9 +914,9 @@ var filterGeoEntries = [][]string{ {"ip":"127.0.0.5", "country":""}, {"ip":"127.0.0.6", "country":""}], "filter":{"count":"multi","order":"none","geo_filter":"none"}}}`, - }, - {"ww2", - `{"a":{"ttl":300, "records":[ + }, + {"ww2", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":"US"}, {"ip":"127.0.0.2", "country":"GB"}, {"ip":"127.0.0.3", "country":"ES"}, @@ -903,17 +924,17 @@ var filterGeoEntries = [][]string{ {"ip":"127.0.0.5", "country":""}, {"ip":"127.0.0.6", "country":""}], "filter":{"count":"multi","order":"none","geo_filter":"country"}}}`, - }, - {"ww3", - `{"a":{"ttl":300, "records":[ + }, + {"ww3", + `{"a":{"ttl":300, "records":[ {"ip":"192.30.252.225"}, {"ip":"94.76.229.204"}, {"ip":"84.88.14.229"}, {"ip":"192.168.0.1"}], "filter":{"count":"multi","order":"none","geo_filter":"location"}}}`, - }, - {"ww4", - `{"a":{"ttl":300, "records":[ + }, + {"ww4", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "asn":47447}, {"ip":"127.0.0.2", "asn":20776}, {"ip":"127.0.0.3", "asn":35470}, @@ -921,9 +942,9 @@ var filterGeoEntries = [][]string{ {"ip":"127.0.0.5", "asn":0}, {"ip":"127.0.0.6", "asn":0}], "filter":{"count":"multi", "order":"none","geo_filter":"asn"}}}`, - }, - {"ww5", - `{"a":{"ttl":300, "records":[ + }, + {"ww5", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":"DE", "asn":47447}, {"ip":"127.0.0.2", "country":"DE", "asn":20776}, {"ip":"127.0.0.3", "country":"DE", "asn":35470}, @@ -931,9 +952,9 @@ var filterGeoEntries = [][]string{ {"ip":"127.0.0.5", "country":"", "asn":0}, {"ip":"127.0.0.6", "country":"", "asn":0}], "filter":{"count":"multi", "order":"none","geo_filter":"asn+country"}}}`, - }, - {"ww6", - `{"a":{"ttl":300, "records":[ + }, + {"ww6", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "asn":[47447,20776]}, {"ip":"127.0.0.2", "asn":[0,35470]}, {"ip":"127.0.0.3", "asn":35470}, @@ -941,9 +962,9 @@ var filterGeoEntries = [][]string{ {"ip":"127.0.0.5", "asn":[]}, {"ip":"127.0.0.6"}], "filter":{"count":"multi", "order":"none","geo_filter":"asn"}}}`, - }, - {"ww7", - `{"a":{"ttl":300, "records":[ + }, + {"ww7", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":["DE", "GB"]}, {"ip":"127.0.0.2", "country":["", "DE"]}, {"ip":"127.0.0.3", "country":"DE"}, @@ -952,189 +973,188 @@ var filterGeoEntries = [][]string{ {"ip":"127.0.0.6", "country": []}, {"ip":"127.0.0.7"}], "filter":{"count":"multi", "order":"none","geo_filter":"country"}}}`, - }, + }, } -var filterGeoSourceIps = []string { - "127.0.0.1", - "127.0.0.1", - "127.0.0.1", - "127.0.0.1", - "127.0.0.1", - "94.76.229.204", // country = GB - "154.11.253.242", // location = CA near US - "212.83.32.45", // ASN = 47447 - "212.83.32.45", // country = DE, ASN = 47447 - "212.83.32.45", - "178.18.89.144", - "127.0.0.1", - "213.95.10.76", // DE - "94.76.229.204", // GB - "154.11.253.242", // CA - "127.0.0.1", +var filterGeoSourceIps = []string{ + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "127.0.0.1", + "94.76.229.204", // country = GB + "154.11.253.242", // location = CA near US + "212.83.32.45", // ASN = 47447 + "212.83.32.45", // country = DE, ASN = 47447 + "212.83.32.45", + "178.18.89.144", + "127.0.0.1", + "213.95.10.76", // DE + "94.76.229.204", // GB + "154.11.253.242", // CA + "127.0.0.1", } var filterGeoTestCases = []test.Case{ - { - Qname: "ww1.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww1.filtergeo.com. 300 IN A 127.0.0.1"), - test.A("ww1.filtergeo.com. 300 IN A 127.0.0.2"), - test.A("ww1.filtergeo.com. 300 IN A 127.0.0.3"), - test.A("ww1.filtergeo.com. 300 IN A 127.0.0.4"), - test.A("ww1.filtergeo.com. 300 IN A 127.0.0.5"), - test.A("ww1.filtergeo.com. 300 IN A 127.0.0.6"), - }, - }, - { - Qname: "ww2.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww2.filtergeo.com. 300 IN A 127.0.0.4"), - test.A("ww2.filtergeo.com. 300 IN A 127.0.0.5"), - test.A("ww2.filtergeo.com. 300 IN A 127.0.0.6"), - }, - }, - { - Qname: "ww3.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww3.filtergeo.com. 300 IN A 192.168.0.1"), - }, - }, - { - Qname: "ww4.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww4.filtergeo.com. 300 IN A 127.0.0.4"), - test.A("ww4.filtergeo.com. 300 IN A 127.0.0.5"), - test.A("ww4.filtergeo.com. 300 IN A 127.0.0.6"), - }, - }, - { - Qname: "ww5.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww5.filtergeo.com. 300 IN A 127.0.0.5"), - test.A("ww5.filtergeo.com. 300 IN A 127.0.0.6"), - }, - }, - { - Qname: "ww2.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww2.filtergeo.com. 300 IN A 127.0.0.2"), - }, - }, - { - Qname: "ww3.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww3.filtergeo.com. 300 IN A 192.30.252.225"), - }, - }, - { - Qname: "ww4.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww4.filtergeo.com. 300 IN A 127.0.0.1"), - }, - }, - { - Qname: "ww5.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww5.filtergeo.com. 300 IN A 127.0.0.1"), - }, - }, - { - Qname: "ww6.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.1"), - }, - }, - { - Qname: "ww6.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.2"), - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.3"), - }, - }, - { - Qname: "ww6.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.2"), - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.4"), - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.5"), - test.A("ww6.filtergeo.com. 300 IN A 127.0.0.6"), - }, - }, - { - Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.1"), - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.2"), - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.3"), - }, - }, - { - Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.1"), - }, - }, - { - Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.4"), - }, - }, - { - Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.2"), - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.5"), - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.6"), - test.A("ww7.filtergeo.com. 300 IN A 127.0.0.7"), - }, - }, - + { + Qname: "ww1.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww1.filtergeo.com. 300 IN A 127.0.0.1"), + test.A("ww1.filtergeo.com. 300 IN A 127.0.0.2"), + test.A("ww1.filtergeo.com. 300 IN A 127.0.0.3"), + test.A("ww1.filtergeo.com. 300 IN A 127.0.0.4"), + test.A("ww1.filtergeo.com. 300 IN A 127.0.0.5"), + test.A("ww1.filtergeo.com. 300 IN A 127.0.0.6"), + }, + }, + { + Qname: "ww2.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww2.filtergeo.com. 300 IN A 127.0.0.4"), + test.A("ww2.filtergeo.com. 300 IN A 127.0.0.5"), + test.A("ww2.filtergeo.com. 300 IN A 127.0.0.6"), + }, + }, + { + Qname: "ww3.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww3.filtergeo.com. 300 IN A 192.168.0.1"), + }, + }, + { + Qname: "ww4.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww4.filtergeo.com. 300 IN A 127.0.0.4"), + test.A("ww4.filtergeo.com. 300 IN A 127.0.0.5"), + test.A("ww4.filtergeo.com. 300 IN A 127.0.0.6"), + }, + }, + { + Qname: "ww5.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww5.filtergeo.com. 300 IN A 127.0.0.5"), + test.A("ww5.filtergeo.com. 300 IN A 127.0.0.6"), + }, + }, + { + Qname: "ww2.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww2.filtergeo.com. 300 IN A 127.0.0.2"), + }, + }, + { + Qname: "ww3.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww3.filtergeo.com. 300 IN A 192.30.252.225"), + }, + }, + { + Qname: "ww4.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww4.filtergeo.com. 300 IN A 127.0.0.1"), + }, + }, + { + Qname: "ww5.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww5.filtergeo.com. 300 IN A 127.0.0.1"), + }, + }, + { + Qname: "ww6.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.1"), + }, + }, + { + Qname: "ww6.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.2"), + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.3"), + }, + }, + { + Qname: "ww6.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.2"), + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.4"), + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.5"), + test.A("ww6.filtergeo.com. 300 IN A 127.0.0.6"), + }, + }, + { + Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.1"), + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.2"), + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.3"), + }, + }, + { + Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.1"), + }, + }, + { + Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.4"), + }, + }, + { + Qname: "ww7.filtergeo.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.2"), + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.5"), + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.6"), + test.A("ww7.filtergeo.com. 300 IN A 127.0.0.7"), + }, + }, } func TestGeoFilter(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{Target:"file", Enable:true, Path:"/tmp/rtest.log", Format:"txt"}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - h.Redis.SAdd("redins:zones", filterGeoZone) - for _, cmd := range filterGeoEntries { - err := h.Redis.HSet("redins:zones:" + filterGeoZone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - t.Fail() - } - } - h.Redis.Set("redins:zones:" + filterGeoZone + ":config", filterGeoConfig) - h.LoadZones() - for i, tc := range filterGeoTestCases { - sa := filterGeoSourceIps[i] - opt := &dns.OPT { - Hdr: dns.RR_Header{Name:".", Rrtype:dns.TypeOPT,Class:dns.ClassANY, Rdlength:0, Ttl: 300,}, - Option: []dns.EDNS0 { - &dns.EDNS0_SUBNET{ - Address:net.ParseIP(sa), - Code:dns.EDNS0SUBNET, - Family: 1, - SourceNetmask:32, - SourceScope:0, - }, - }, - } - r := tc.Msg() - r.Extra = append(r.Extra, opt) - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - resp.Extra = nil - - if test.SortAndCheck(resp, tc) != nil { - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{Target: "file", Enable: true, Path: "/tmp/rtest.log", Format: "txt"}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + h.Redis.SAdd("redins:zones", filterGeoZone) + for _, cmd := range filterGeoEntries { + err := h.Redis.HSet("redins:zones:"+filterGeoZone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + t.Fail() + } + } + h.Redis.Set("redins:zones:"+filterGeoZone+":config", filterGeoConfig) + h.LoadZones() + for i, tc := range filterGeoTestCases { + sa := filterGeoSourceIps[i] + opt := &dns.OPT{ + Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeOPT, Class: dns.ClassANY, Rdlength: 0, Ttl: 300}, + Option: []dns.EDNS0{ + &dns.EDNS0_SUBNET{ + Address: net.ParseIP(sa), + Code: dns.EDNS0SUBNET, + Family: 1, + SourceNetmask: 32, + SourceScope: 0, + }, + }, + } + r := tc.Msg() + r.Extra = append(r.Extra, opt) + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + resp.Extra = nil + + if test.SortAndCheck(resp, tc) != nil { + t.Fail() + } + } } var filterMultiZone = "filtermulti.com." @@ -1142,8 +1162,8 @@ var filterMultiZone = "filtermulti.com." var filterMultiConfig = `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.filtermulti.com.","ns":"ns1.filter.com.","refresh":44,"retry":55,"expire":66}}` var filterMultiEntries = [][]string{ - {"ww1", - `{"a":{"ttl":300, "records":[ + {"ww1", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":""}, {"ip":"127.0.0.2", "country":""}, {"ip":"127.0.0.3", "country":""}, @@ -1151,160 +1171,158 @@ var filterMultiEntries = [][]string{ {"ip":"127.0.0.5", "country":""}, {"ip":"127.0.0.6", "country":""}], "filter":{"count":"multi","order":"none","geo_filter":"none"}}}`, - }, - {"ww2", - `{"a":{"ttl":300, "records":[ + }, + {"ww2", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":"", "weight":1}, {"ip":"127.0.0.2", "country":"", "weight":4}, {"ip":"127.0.0.3", "country":"", "weight":10}, {"ip":"127.0.0.4", "country":"", "weight":2}, {"ip":"127.0.0.5", "country":"", "weight":20}], "filter":{"count":"multi","order":"weighted","geo_filter":"none"}}}`, - }, - {"ww3", - `{"a":{"ttl":300, "records":[ + }, + {"ww3", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":""}, {"ip":"127.0.0.2", "country":""}, {"ip":"127.0.0.3", "country":""}, {"ip":"127.0.0.4", "country":""}, {"ip":"127.0.0.5", "country":""}], "filter":{"count":"multi","order":"rr","geo_filter":"none"}}}`, - }, + }, } var filterMultiTestCases = []test.Case{ - { - Qname: "ww1.filtermulti.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww1.filtermulti.com. 300 IN A 127.0.0.1"), - test.A("ww1.filtermulti.com. 300 IN A 127.0.0.2"), - test.A("ww1.filtermulti.com. 300 IN A 127.0.0.3"), - test.A("ww1.filtermulti.com. 300 IN A 127.0.0.4"), - test.A("ww1.filtermulti.com. 300 IN A 127.0.0.5"), - test.A("ww1.filtermulti.com. 300 IN A 127.0.0.6"), - }, - }, - { - Qname: "ww2.filtermulti.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - }, - }, - { - Qname: "ww3.filtermulti.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - }, - }, + { + Qname: "ww1.filtermulti.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww1.filtermulti.com. 300 IN A 127.0.0.1"), + test.A("ww1.filtermulti.com. 300 IN A 127.0.0.2"), + test.A("ww1.filtermulti.com. 300 IN A 127.0.0.3"), + test.A("ww1.filtermulti.com. 300 IN A 127.0.0.4"), + test.A("ww1.filtermulti.com. 300 IN A 127.0.0.5"), + test.A("ww1.filtermulti.com. 300 IN A 127.0.0.6"), + }, + }, + { + Qname: "ww2.filtermulti.com.", Qtype: dns.TypeA, + Answer: []dns.RR{}, + }, + { + Qname: "ww3.filtermulti.com.", Qtype: dns.TypeA, + Answer: []dns.RR{}, + }, } func TestMultiFilter(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - h.Redis.SAdd("redins:zones", filterMultiZone) - for _, cmd := range filterMultiEntries { - err := h.Redis.HSet("redins:zones:" + filterMultiZone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - log.Println("1") - t.Fail() - } - } - h.Redis.Set("redins:zones:" + filterMultiZone + ":config", filterMultiConfig) - h.LoadZones() - - for i := 0; i < 10; i++ { - tc := filterMultiTestCases[0] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - - if test.SortAndCheck(resp, tc) != nil { - t.Fail() - } - } - - w1, w4, w10, w2, w20 := 0, 0, 0, 0, 0 - for i := 0; i < 10000; i++ { - tc := filterMultiTestCases[1] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if len(resp.Answer) != 5 { - log.Println("2") - t.Fail() - } - - resa := resp.Answer[0].(*dns.A) - - switch resa.A.String() { - case "127.0.0.1": - w1++ - case "127.0.0.2": - w4++ - case "127.0.0.3": - w10++ - case "127.0.0.4": - w2++ - case "127.0.0.5": - w20++ - } - } - log.Println(w1, w2, w4, w10, w20) - if w1 > w2 || w2 > w4 || w4 > w10 || w10 > w20 { - log.Println("3") - t.Fail() - } - - rr := make([]int, 5) - for i := 0; i < 10000; i++ { - tc := filterMultiTestCases[2] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if len(resp.Answer) != 5 { - log.Println("4") - t.Fail() - } - - resa := resp.Answer[0].(*dns.A) - - switch resa.A.String() { - case "127.0.0.1": - rr[0]++ - case "127.0.0.2": - rr[1]++ - case "127.0.0.3": - rr[2]++ - case "127.0.0.4": - rr[3]++ - case "127.0.0.5": - rr[4]++ - } - } - log.Println(rr) - for i := range rr { - if rr[i] < 1500 || rr[i] > 2500 { - log.Println("5") - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + h.Redis.SAdd("redins:zones", filterMultiZone) + for _, cmd := range filterMultiEntries { + err := h.Redis.HSet("redins:zones:"+filterMultiZone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + log.Println("1") + t.Fail() + } + } + h.Redis.Set("redins:zones:"+filterMultiZone+":config", filterMultiConfig) + h.LoadZones() + + for i := 0; i < 10; i++ { + tc := filterMultiTestCases[0] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + + if test.SortAndCheck(resp, tc) != nil { + t.Fail() + } + } + + w1, w4, w10, w2, w20 := 0, 0, 0, 0, 0 + for i := 0; i < 10000; i++ { + tc := filterMultiTestCases[1] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if len(resp.Answer) != 5 { + log.Println("2") + t.Fail() + } + + resa := resp.Answer[0].(*dns.A) + + switch resa.A.String() { + case "127.0.0.1": + w1++ + case "127.0.0.2": + w4++ + case "127.0.0.3": + w10++ + case "127.0.0.4": + w2++ + case "127.0.0.5": + w20++ + } + } + log.Println(w1, w2, w4, w10, w20) + if w1 > w2 || w2 > w4 || w4 > w10 || w10 > w20 { + log.Println("3") + t.Fail() + } + + rr := make([]int, 5) + for i := 0; i < 10000; i++ { + tc := filterMultiTestCases[2] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if len(resp.Answer) != 5 { + log.Println("4") + t.Fail() + } + + resa := resp.Answer[0].(*dns.A) + + switch resa.A.String() { + case "127.0.0.1": + rr[0]++ + case "127.0.0.2": + rr[1]++ + case "127.0.0.3": + rr[2]++ + case "127.0.0.4": + rr[3]++ + case "127.0.0.5": + rr[4]++ + } + } + log.Println(rr) + for i := range rr { + if rr[i] < 1500 || rr[i] > 2500 { + log.Println("5") + t.Fail() + } + } } var filterSingleZone = "filtersingle.com." var filterSingleConfig = `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.filtersingle.com.","ns":"ns1.filter.com.","refresh":44,"retry":55,"expire":66}}` var filterSingleEntries = [][]string{ - {"ww1", - `{"a":{"ttl":300, "records":[ + {"ww1", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":""}, {"ip":"127.0.0.2", "country":""}, {"ip":"127.0.0.3", "country":""}, @@ -1312,292 +1330,289 @@ var filterSingleEntries = [][]string{ {"ip":"127.0.0.5", "country":""}, {"ip":"127.0.0.6", "country":""}], "filter":{"count":"single","order":"none","geo_filter":"none"}}}`, - }, - {"ww2", - `{"a":{"ttl":300, "records":[ + }, + {"ww2", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":"", "weight":1}, {"ip":"127.0.0.2", "country":"", "weight":4}, {"ip":"127.0.0.3", "country":"", "weight":10}, {"ip":"127.0.0.4", "country":"", "weight":2}, {"ip":"127.0.0.5", "country":"", "weight":20}], "filter":{"count":"single","order":"weighted","geo_filter":"none"}}}`, - }, - {"ww3", - `{"a":{"ttl":300, "records":[ + }, + {"ww3", + `{"a":{"ttl":300, "records":[ {"ip":"127.0.0.1", "country":""}, {"ip":"127.0.0.2", "country":""}, {"ip":"127.0.0.3", "country":""}, {"ip":"127.0.0.4", "country":""}, {"ip":"127.0.0.5", "country":""}], "filter":{"count":"single","order":"rr","geo_filter":"none"}}}`, - }, + }, } var filterSingleTestCases = []test.Case{ - { - Qname: "ww1.filtersingle.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - test.A("ww1.filtersingle.com. 300 IN A 127.0.0.1"), - }, - }, - { - Qname: "ww2.filtersingle.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - }, - }, - { - Qname: "ww3.filtersingle.com.", Qtype: dns.TypeA, - Answer: []dns.RR{ - }, - }, + { + Qname: "ww1.filtersingle.com.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.A("ww1.filtersingle.com. 300 IN A 127.0.0.1"), + }, + }, + { + Qname: "ww2.filtersingle.com.", Qtype: dns.TypeA, + Answer: []dns.RR{}, + }, + { + Qname: "ww3.filtersingle.com.", Qtype: dns.TypeA, + Answer: []dns.RR{}, + }, } func TestSingleFilter(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - h.Redis.SAdd("redins:zones", filterSingleZone) - for _, cmd := range filterSingleEntries { - err := h.Redis.HSet("redins:zones:" + filterSingleZone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - log.Println("1") - t.Fail() - } - } - h.Redis.Set("redins:zones:" + filterSingleZone + ":config", filterSingleConfig) - h.LoadZones() - - for i := 0; i < 10; i++ { - tc := filterSingleTestCases[0] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - - if test.SortAndCheck(resp, tc) != nil { - t.Fail() - } - } - - w1, w4, w10, w2, w20 := 0, 0, 0, 0, 0 - for i := 0; i < 10000; i++ { - tc := filterSingleTestCases[1] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if len(resp.Answer) != 1 { - log.Println("2") - t.Fail() - } - - resa := resp.Answer[0].(*dns.A) - - switch resa.A.String() { - case "127.0.0.1": - w1++ - case "127.0.0.2": - w4++ - case "127.0.0.3": - w10++ - case "127.0.0.4": - w2++ - case "127.0.0.5": - w20++ - } - } - log.Println(w1, w2, w4, w10, w20) - if w1 > w2 || w2 > w4 || w4 > w10 || w10 > w20 { - log.Println("3") - t.Fail() - } - - rr := make([]int, 5) - for i := 0; i < 10000; i++ { - tc := filterSingleTestCases[2] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if len(resp.Answer) != 1 { - log.Println("4") - t.Fail() - } - - resa := resp.Answer[0].(*dns.A) - - switch resa.A.String() { - case "127.0.0.1": - rr[0]++ - case "127.0.0.2": - rr[1]++ - case "127.0.0.3": - rr[2]++ - case "127.0.0.4": - rr[3]++ - case "127.0.0.5": - rr[4]++ - } - } - log.Println(rr) - for i := range rr { - if rr[i] < 1500 || rr[i] > 2500 { - log.Println("5") - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + h.Redis.SAdd("redins:zones", filterSingleZone) + for _, cmd := range filterSingleEntries { + err := h.Redis.HSet("redins:zones:"+filterSingleZone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + log.Println("1") + t.Fail() + } + } + h.Redis.Set("redins:zones:"+filterSingleZone+":config", filterSingleConfig) + h.LoadZones() + + for i := 0; i < 10; i++ { + tc := filterSingleTestCases[0] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + + if test.SortAndCheck(resp, tc) != nil { + t.Fail() + } + } + + w1, w4, w10, w2, w20 := 0, 0, 0, 0, 0 + for i := 0; i < 10000; i++ { + tc := filterSingleTestCases[1] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if len(resp.Answer) != 1 { + log.Println("2") + t.Fail() + } + + resa := resp.Answer[0].(*dns.A) + + switch resa.A.String() { + case "127.0.0.1": + w1++ + case "127.0.0.2": + w4++ + case "127.0.0.3": + w10++ + case "127.0.0.4": + w2++ + case "127.0.0.5": + w20++ + } + } + log.Println(w1, w2, w4, w10, w20) + if w1 > w2 || w2 > w4 || w4 > w10 || w10 > w20 { + log.Println("3") + t.Fail() + } + + rr := make([]int, 5) + for i := 0; i < 10000; i++ { + tc := filterSingleTestCases[2] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if len(resp.Answer) != 1 { + log.Println("4") + t.Fail() + } + + resa := resp.Answer[0].(*dns.A) + + switch resa.A.String() { + case "127.0.0.1": + rr[0]++ + case "127.0.0.2": + rr[1]++ + case "127.0.0.3": + rr[2]++ + case "127.0.0.4": + rr[3]++ + case "127.0.0.5": + rr[4]++ + } + } + log.Println(rr) + for i := range rr { + if rr[i] < 1500 || rr[i] > 2500 { + log.Println("5") + t.Fail() + } + } } var upstreamCNAMEZone = "upstreamcname.com." var upstreamCNAMEConfig = `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.upstreamcname.com.","ns":"ns1.upstreamcname.com.","refresh":44,"retry":55,"expire":66}}` -var upstreamCNAME = [][]string { - {"upstream", - `{"cname":{"ttl":300, "host":"www.google.com"}}`, - }, +var upstreamCNAME = [][]string{ + {"upstream", + `{"cname":{"ttl":300, "host":"www.google.com"}}`, + }, } var upstreamCNAMETestCases = []test.Case{ - { - Qname: "upstream.upstreamcname.com.", Qtype: dns.TypeA, - }, + { + Qname: "upstream.upstreamcname.com.", Qtype: dns.TypeA, + }, } func TestUpstreamCNAME(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - h.Redis.SAdd("redins:zones", upstreamCNAMEZone) - for _, cmd := range upstreamCNAME { - err := h.Redis.HSet("redins:zones:" + upstreamCNAMEZone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - log.Println("1") - t.Fail() - } - } - h.Redis.Set("redins:zones:" + upstreamCNAMEZone + ":config", upstreamCNAMEConfig) - h.LoadZones() - - h.Config.UpstreamFallback = false - { - tc := upstreamCNAMETestCases[0] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - log.Println(resp) - if resp.Rcode != dns.RcodeSuccess { - log.Println("1") - t.Fail() - } - cname := resp.Answer[0].(*dns.CNAME) - if cname.Target != "www.google.com." { - log.Println("2 ", cname) - t.Fail() - } - } - - h.Config.UpstreamFallback = true - { - tc := upstreamCNAMETestCases[0] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - log.Println(resp) - if resp.Rcode != dns.RcodeSuccess { - log.Println("3") - t.Fail() - } - hasCNAME := false - hasA := false - for _, rr := range resp.Answer { - switch rr.(type) { - case *dns.CNAME: - hasCNAME = true - case *dns.A: - hasA = true - } - } - if !hasCNAME || !hasA { - log.Println("4") - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + h.Redis.SAdd("redins:zones", upstreamCNAMEZone) + for _, cmd := range upstreamCNAME { + err := h.Redis.HSet("redins:zones:"+upstreamCNAMEZone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + log.Println("1") + t.Fail() + } + } + h.Redis.Set("redins:zones:"+upstreamCNAMEZone+":config", upstreamCNAMEConfig) + h.LoadZones() + + h.Config.UpstreamFallback = false + { + tc := upstreamCNAMETestCases[0] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + log.Println(resp) + if resp.Rcode != dns.RcodeSuccess { + log.Println("1") + t.Fail() + } + cname := resp.Answer[0].(*dns.CNAME) + if cname.Target != "www.google.com." { + log.Println("2 ", cname) + t.Fail() + } + } + + h.Config.UpstreamFallback = true + { + tc := upstreamCNAMETestCases[0] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + log.Println(resp) + if resp.Rcode != dns.RcodeSuccess { + log.Println("3") + t.Fail() + } + hasCNAME := false + hasA := false + for _, rr := range resp.Answer { + switch rr.(type) { + case *dns.CNAME: + hasCNAME = true + case *dns.A: + hasA = true + } + } + if !hasCNAME || !hasA { + log.Println("4") + t.Fail() + } + } } var subsZone = "zone1.com." -var subsEntries = [][]string { - { - "www", - `{"a":{"ttl":300, "records":[{"ip":"1.1.1.1"}]}}`, - }, +var subsEntries = [][]string{ + { + "www", + `{"a":{"ttl":300, "records":[{"ip":"1.1.1.1"}]}}`, + }, } var subsTestCases = []test.Case{ - { - Qname: "www.zone1.com", Qtype: dns.TypeA, - }, + { + Qname: "www.zone1.com", Qtype: dns.TypeA, + }, } func TestSubscribeZones(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - - handlerTestConfig.CacheTimeout = 1 - h := NewHandler(&handlerTestConfig) - h.Redis.Del("*") - for _, cmd := range subsEntries { - err := h.Redis.HSet("redins:zones:"+subsZone, cmd[0], cmd[1]) - if err != nil { - log.Printf("[ERROR] cannot connect to redis: %s", err) - log.Println("1") - t.Fail() - } - } - - - h.Redis.SAdd("redins:zones", subsZone) - time.Sleep(time.Millisecond * 10) - { - tc := subsTestCases[0] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if resp.Rcode != dns.RcodeSuccess { - fmt.Println("1") - t.Fail() - } - } - h.Redis.SRem("redins:zones", subsZone) - time.Sleep(time.Millisecond * 1500) - { - tc := subsTestCases[0] - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) - - resp := w.Msg - if resp.Rcode != dns.RcodeNotAuth { - fmt.Println("2") - t.Fail() - } - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + + handlerTestConfig.CacheTimeout = 1 + h := NewHandler(&handlerTestConfig) + h.Redis.Del("*") + for _, cmd := range subsEntries { + err := h.Redis.HSet("redins:zones:"+subsZone, cmd[0], cmd[1]) + if err != nil { + log.Printf("[ERROR] cannot connect to redis: %s", err) + log.Println("1") + t.Fail() + } + } + + h.Redis.SAdd("redins:zones", subsZone) + time.Sleep(time.Millisecond * 10) + { + tc := subsTestCases[0] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if resp.Rcode != dns.RcodeSuccess { + fmt.Println("1") + t.Fail() + } + } + h.Redis.SRem("redins:zones", subsZone) + time.Sleep(time.Millisecond * 1500) + { + tc := subsTestCases[0] + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) + + resp := w.Msg + if resp.Rcode != dns.RcodeNotAuth { + fmt.Println("2") + t.Fail() + } + } } diff --git a/handler/healthcheck.go b/handler/healthcheck.go index 819c426..a1a9246 100644 --- a/handler/healthcheck.go +++ b/handler/healthcheck.go @@ -1,464 +1,462 @@ package handler import ( - "fmt" - "crypto/tls" - "time" - "encoding/json" - "strings" - "net" - "net/http" - "sync" - - "github.com/hawell/logger" - "github.com/hawell/uperdis" - "github.com/hawell/workerpool" - "github.com/patrickmn/go-cache" - "github.com/pkg/errors" - "golang.org/x/net/icmp" - "golang.org/x/net/ipv4" - "encoding/binary" + "crypto/tls" + "encoding/json" + "fmt" + "net" + "net/http" + "strings" + "sync" + "time" + + "encoding/binary" + "github.com/hawell/logger" + "github.com/hawell/uperdis" + "github.com/hawell/workerpool" + "github.com/patrickmn/go-cache" + "github.com/pkg/errors" + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" ) type HealthCheckItem struct { - Protocol string `json:"protocol,omitempty"` - Uri string `json:"uri,omitempty"` - Port int `json:"port,omitempty"` - Status int `json:"status,omitempty"` - LastCheck time.Time `json:"lastcheck,omitempty"` - Timeout int `json:"timeout,omitempty"` - UpCount int `json:"up_count,omitempty"` - DownCount int `json:"down_count,omitempty"` - Enable bool `json:"enable,omitempty"` - DomainId string `json:"domain_uuid, omitempty"` - Host string `json:"-"` - Ip string `json:"-"` - Error error `json:"-"` + Protocol string `json:"protocol,omitempty"` + Uri string `json:"uri,omitempty"` + Port int `json:"port,omitempty"` + Status int `json:"status,omitempty"` + LastCheck time.Time `json:"lastcheck,omitempty"` + Timeout int `json:"timeout,omitempty"` + UpCount int `json:"up_count,omitempty"` + DownCount int `json:"down_count,omitempty"` + Enable bool `json:"enable,omitempty"` + DomainId string `json:"domain_uuid, omitempty"` + Host string `json:"-"` + Ip string `json:"-"` + Error error `json:"-"` } type Healthcheck struct { - Enable bool - maxRequests int - maxPendingRequests int - updateInterval time.Duration - checkInterval time.Duration - redisConfigServer *uperdis.Redis - redisStatusServer *uperdis.Redis - logger *logger.EventLogger - cachedItems *cache.Cache - lastUpdate time.Time - dispatcher *workerpool.Dispatcher - quit chan struct{} - quitWG sync.WaitGroup + Enable bool + maxRequests int + maxPendingRequests int + updateInterval time.Duration + checkInterval time.Duration + redisConfigServer *uperdis.Redis + redisStatusServer *uperdis.Redis + logger *logger.EventLogger + cachedItems *cache.Cache + lastUpdate time.Time + dispatcher *workerpool.Dispatcher + quit chan struct{} + quitWG sync.WaitGroup } func HandleHealthCheck(h *Healthcheck) workerpool.JobHandler { - return func(worker *workerpool.Worker, job workerpool.Job) { - item := job.(*HealthCheckItem) - logger.Default.Debugf("item %v received", item) - var err error = nil - switch item.Protocol { - case "http", "https": - timeout := time.Duration(item.Timeout) * time.Millisecond - url := item.Protocol + "://" + item.Ip + item.Uri - err = httpCheck(url, item.Host, timeout) - case "ping", "icmp": - err = pingCheck(item.Ip, time.Duration(item.Timeout) * time.Millisecond) - logger.Default.Error("@@@@@@@@@@@@@@ ", item.Ip, " : result : ", err) - default: - err = errors.New(fmt.Sprintf("invalid protocol : %s used for %s:%d", item.Protocol, item.Ip, item.Port)) - logger.Default.Error(err) - } - item.Error = err - if err == nil { - statusUp(item) - } else { - statusDown(item) - } - item.LastCheck = time.Now() - h.storeItem(item) - h.logHealthcheck(item) - } + return func(worker *workerpool.Worker, job workerpool.Job) { + item := job.(*HealthCheckItem) + logger.Default.Debugf("item %v received", item) + var err error = nil + switch item.Protocol { + case "http", "https": + timeout := time.Duration(item.Timeout) * time.Millisecond + url := item.Protocol + "://" + item.Ip + item.Uri + err = httpCheck(url, item.Host, timeout) + case "ping", "icmp": + err = pingCheck(item.Ip, time.Duration(item.Timeout)*time.Millisecond) + logger.Default.Error("@@@@@@@@@@@@@@ ", item.Ip, " : result : ", err) + default: + err = errors.New(fmt.Sprintf("invalid protocol : %s used for %s:%d", item.Protocol, item.Ip, item.Port)) + logger.Default.Error(err) + } + item.Error = err + if err == nil { + statusUp(item) + } else { + statusDown(item) + } + item.LastCheck = time.Now() + h.storeItem(item) + h.logHealthcheck(item) + } } -func httpCheck(url string,host string, timeout time.Duration) error { - tr := &http.Transport { - MaxIdleConnsPerHost: 1024, - TLSHandshakeTimeout: 0 * time.Second, - TLSClientConfig: &tls.Config { - InsecureSkipVerify: true, - ServerName: host, - }, - } - client := &http.Client { - Timeout: timeout, - Transport: tr, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - - } - req, err := http.NewRequest("HEAD", url, nil) - if err != nil { - logger.Default.Errorf("invalid request, host:%s, url:%s : %s", host, url, err) - return err - } - req.Host = strings.TrimRight(host, ".") - resp, err := client.Do(req) - if err != nil { - logger.Default.Errorf("request failed, host:%s, url:%s : %s", host, url, err) - return err - } - switch resp.StatusCode { - case http.StatusOK, http.StatusFound, http.StatusMovedPermanently: - return nil - default: - return errors.New(fmt.Sprintf("invalid http status code : %d", resp.StatusCode)) - } +func httpCheck(url string, host string, timeout time.Duration) error { + tr := &http.Transport{ + MaxIdleConnsPerHost: 1024, + TLSHandshakeTimeout: 0 * time.Second, + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + ServerName: host, + }, + } + client := &http.Client{ + Timeout: timeout, + Transport: tr, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + logger.Default.Errorf("invalid request, host:%s, url:%s : %s", host, url, err) + return err + } + req.Host = strings.TrimRight(host, ".") + resp, err := client.Do(req) + if err != nil { + logger.Default.Errorf("request failed, host:%s, url:%s : %s", host, url, err) + return err + } + switch resp.StatusCode { + case http.StatusOK, http.StatusFound, http.StatusMovedPermanently: + return nil + default: + return errors.New(fmt.Sprintf("invalid http status code : %d", resp.StatusCode)) + } } // FIXME: ping check is not working properly func pingCheck(ip string, timeout time.Duration) error { - c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") - c.SetDeadline(time.Now().Add(timeout)) - if err != nil { - return err - } - defer c.Close() - - id := int(binary.BigEndian.Uint32(net.ParseIP(ip))) - wm := icmp.Message{ - Type: ipv4.ICMPTypeEcho, Code: 0, - Body: &icmp.Echo{ - ID: id, - Data: []byte("HELLO-R-U-THERE"), - }, - } - wb, err := wm.Marshal(nil) - if err != nil { - return err - } - if _, err := c.WriteTo(wb, &net.IPAddr{IP: net.ParseIP(ip)}); err != nil { - return err - } - - rb := make([]byte, 1500) - n, _, err := c.ReadFrom(rb) - if err != nil { - return err - } - rm, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), rb[:n]) - if err != nil { - return err - } - switch rm.Type { - case ipv4.ICMPTypeEchoReply: - logger.Default.Error("@@@@@@@@@@@@ code = ", rm.Code) - return nil - default: - return errors.New(fmt.Sprintf("got %+v; want echo reply", rm)) - } + c, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") + c.SetDeadline(time.Now().Add(timeout)) + if err != nil { + return err + } + defer c.Close() + + id := int(binary.BigEndian.Uint32(net.ParseIP(ip))) + wm := icmp.Message{ + Type: ipv4.ICMPTypeEcho, Code: 0, + Body: &icmp.Echo{ + ID: id, + Data: []byte("HELLO-R-U-THERE"), + }, + } + wb, err := wm.Marshal(nil) + if err != nil { + return err + } + if _, err := c.WriteTo(wb, &net.IPAddr{IP: net.ParseIP(ip)}); err != nil { + return err + } + + rb := make([]byte, 1500) + n, _, err := c.ReadFrom(rb) + if err != nil { + return err + } + rm, err := icmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), rb[:n]) + if err != nil { + return err + } + switch rm.Type { + case ipv4.ICMPTypeEchoReply: + logger.Default.Error("@@@@@@@@@@@@ code = ", rm.Code) + return nil + default: + return errors.New(fmt.Sprintf("got %+v; want echo reply", rm)) + } } type HealthcheckConfig struct { - Enable bool `json:"enable,omitempty"` - MaxRequests int `json:"max_requests,omitempty"` - MaxPendingRequests int `json:"max_pending_requests,omitempty"` - UpdateInterval int `json:"update_interval,omitempty"` - CheckInterval int `json:"check_interval,omitempty"` - RedisStatusServer uperdis.RedisConfig `json:"redis,omitempty"` - Log logger.LogConfig `json:"log,omitempty"` + Enable bool `json:"enable,omitempty"` + MaxRequests int `json:"max_requests,omitempty"` + MaxPendingRequests int `json:"max_pending_requests,omitempty"` + UpdateInterval int `json:"update_interval,omitempty"` + CheckInterval int `json:"check_interval,omitempty"` + RedisStatusServer uperdis.RedisConfig `json:"redis,omitempty"` + Log logger.LogConfig `json:"log,omitempty"` } func NewHealthcheck(config *HealthcheckConfig, redisConfigServer *uperdis.Redis) *Healthcheck { - h := &Healthcheck { - Enable: config.Enable, - maxRequests: config.MaxRequests, - maxPendingRequests: config.MaxPendingRequests, - updateInterval: time.Duration(config.UpdateInterval) * time.Second, - checkInterval: time.Duration(config.CheckInterval) * time.Second, - } - - if h.Enable { - - h.redisConfigServer = redisConfigServer - h.redisStatusServer = uperdis.NewRedis(&config.RedisStatusServer) - h.cachedItems = cache.New(time.Second * time.Duration(config.CheckInterval), time.Duration(config.CheckInterval) * time.Second * 10) - h.dispatcher = workerpool.NewDispatcher(config.MaxPendingRequests, config.MaxRequests) - for i := 0; i < config.MaxRequests; i++ { - h.dispatcher.AddWorker(HandleHealthCheck(h)) - } - h.logger = logger.NewLogger(&config.Log) - h.quit = make(chan struct{}, 1) - } - - return h + h := &Healthcheck{ + Enable: config.Enable, + maxRequests: config.MaxRequests, + maxPendingRequests: config.MaxPendingRequests, + updateInterval: time.Duration(config.UpdateInterval) * time.Second, + checkInterval: time.Duration(config.CheckInterval) * time.Second, + } + + if h.Enable { + + h.redisConfigServer = redisConfigServer + h.redisStatusServer = uperdis.NewRedis(&config.RedisStatusServer) + h.cachedItems = cache.New(time.Second*time.Duration(config.CheckInterval), time.Duration(config.CheckInterval)*time.Second*10) + h.dispatcher = workerpool.NewDispatcher(config.MaxPendingRequests, config.MaxRequests) + for i := 0; i < config.MaxRequests; i++ { + h.dispatcher.AddWorker(HandleHealthCheck(h)) + } + h.logger = logger.NewLogger(&config.Log) + h.quit = make(chan struct{}, 1) + } + + return h } func (h *Healthcheck) ShutDown() { - if !h.Enable { - return - } - // fmt.Println("healthcheck : stopping") - h.dispatcher.Stop() - h.quitWG.Add(2) // one for h.dispatcher.Start(), another for h.Transfer() - close(h.quit) - h.quitWG.Wait() - // fmt.Println("healthcheck : stopped") + if !h.Enable { + return + } + // fmt.Println("healthcheck : stopping") + h.dispatcher.Stop() + h.quitWG.Add(2) // one for h.dispatcher.Start(), another for h.Transfer() + close(h.quit) + h.quitWG.Wait() + // fmt.Println("healthcheck : stopped") } func (h *Healthcheck) getStatus(host string, ip net.IP) int { - if !h.Enable { - return 0 - } - key := host + ":" + ip.String() - var item *HealthCheckItem - val, found := h.cachedItems.Get(key) - if !found { - item = h.loadItem(key) - if item == nil { - item = new(HealthCheckItem) - } - h.cachedItems.Set(key, item, h.updateInterval) - } else { - item = val.(*HealthCheckItem) - } - return item.Status + if !h.Enable { + return 0 + } + key := host + ":" + ip.String() + var item *HealthCheckItem + val, found := h.cachedItems.Get(key) + if !found { + item = h.loadItem(key) + if item == nil { + item = new(HealthCheckItem) + } + h.cachedItems.Set(key, item, h.updateInterval) + } else { + item = val.(*HealthCheckItem) + } + return item.Status } func (h *Healthcheck) loadItem(key string) *HealthCheckItem { - splits := strings.SplitAfterN(key, ":", 2) - // logger.Default.Error(splits) - if len(splits) != 2 { - logger.Default.Errorf("invalid key: %s", key) - return nil - } - item := new(HealthCheckItem) - item.Host = strings.TrimSuffix(splits[0], ":") - item.Ip = splits[1] - itemStr, err := h.redisStatusServer.Get("redins:healthcheck:" + key) - if err != nil { - logger.Default.Errorf("cannot load item %s : %s", key, err) - return nil - } - json.Unmarshal([]byte(itemStr), item) - if item.DownCount > 0 { - item.DownCount = -item.DownCount - } - return item + splits := strings.SplitAfterN(key, ":", 2) + // logger.Default.Error(splits) + if len(splits) != 2 { + logger.Default.Errorf("invalid key: %s", key) + return nil + } + item := new(HealthCheckItem) + item.Host = strings.TrimSuffix(splits[0], ":") + item.Ip = splits[1] + itemStr, err := h.redisStatusServer.Get("redins:healthcheck:" + key) + if err != nil { + logger.Default.Errorf("cannot load item %s : %s", key, err) + return nil + } + json.Unmarshal([]byte(itemStr), item) + if item.DownCount > 0 { + item.DownCount = -item.DownCount + } + return item } func (h *Healthcheck) storeItem(item *HealthCheckItem) { - key := item.Host + ":" + item.Ip - itemStr, err := json.Marshal(item) - if err != nil { - logger.Default.Errorf("cannot marshal item to json : %s", err) - return - } - logger.Default.Debugf("setting %v in redis : %s", *item, string(itemStr)) - h.redisStatusServer.Set("redins:healthcheck:" + key, string(itemStr)) + key := item.Host + ":" + item.Ip + itemStr, err := json.Marshal(item) + if err != nil { + logger.Default.Errorf("cannot marshal item to json : %s", err) + return + } + logger.Default.Debugf("setting %v in redis : %s", *item, string(itemStr)) + h.redisStatusServer.Set("redins:healthcheck:"+key, string(itemStr)) } func (h *Healthcheck) getDomainId(zone string) string { - var cfg ZoneConfig - val, err := h.redisConfigServer.Get("redins:zones:" + zone + ":config") - if err != nil { - logger.Default.Errorf("cannot load zone %s config : %s", zone, err) - } - if len(val) > 0 { - err := json.Unmarshal([]byte(val), &cfg) - if err != nil { - logger.Default.Errorf("cannot parse zone config : %s", err) - } - } - return cfg.DomainId + var cfg ZoneConfig + val, err := h.redisConfigServer.Get("redins:zones:" + zone + ":config") + if err != nil { + logger.Default.Errorf("cannot load zone %s config : %s", zone, err) + } + if len(val) > 0 { + err := json.Unmarshal([]byte(val), &cfg) + if err != nil { + logger.Default.Errorf("cannot parse zone config : %s", err) + } + } + return cfg.DomainId } func (h *Healthcheck) Start() { - if !h.Enable { - return - } - h.dispatcher.Run() - - go h.Transfer() - - for { - itemKeys, err := h.redisStatusServer.GetKeys("redins:healthcheck:*") - if err != nil { - logger.Default.Errorf("cannot load keys : redins:healthcheck:* : %s", err) - } - select { - case <-h.quit: - h.quitWG.Done() - return - case <-time.After(h.checkInterval): - for i := range itemKeys { - itemKey := strings.TrimLeft(itemKeys[i], "redins:healthcheck:") - item := h.loadItem(itemKey) - if item != nil { - if time.Since(item.LastCheck) > h.checkInterval { - h.dispatcher.Queue(item) - } - } - } - } - } + if !h.Enable { + return + } + h.dispatcher.Run() + + go h.Transfer() + + for { + itemKeys, err := h.redisStatusServer.GetKeys("redins:healthcheck:*") + if err != nil { + logger.Default.Errorf("cannot load keys : redins:healthcheck:* : %s", err) + } + select { + case <-h.quit: + h.quitWG.Done() + return + case <-time.After(h.checkInterval): + for i := range itemKeys { + itemKey := strings.TrimLeft(itemKeys[i], "redins:healthcheck:") + item := h.loadItem(itemKey) + if item != nil { + if time.Since(item.LastCheck) > h.checkInterval { + h.dispatcher.Queue(item) + } + } + } + } + } } func (h *Healthcheck) logHealthcheck(item *HealthCheckItem) { - data := map[string]interface{} { - "ip": item.Ip, - "port": item.Port, - "domain_name": item.Host, - "domain_uuid": item.DomainId, - "uri": item.Uri, - "status": item.Status, - } - if item.Error == nil { - data["error"] = "" - } else { - data["error"] = item.Error.Error() - } - - h.logger.Log(data,"ar_dns_healthcheck") + data := map[string]interface{}{ + "ip": item.Ip, + "port": item.Port, + "domain_name": item.Host, + "domain_uuid": item.DomainId, + "uri": item.Uri, + "status": item.Status, + } + if item.Error == nil { + data["error"] = "" + } else { + data["error"] = item.Error.Error() + } + + h.logger.Log(data, "ar_dns_healthcheck") } func statusDown(item *HealthCheckItem) { - if item.Status <= 0 { - item.Status-- - if item.Status < item.DownCount { - item.Status = item.DownCount - } - } else { - item.Status = -1 - } + if item.Status <= 0 { + item.Status-- + if item.Status < item.DownCount { + item.Status = item.DownCount + } + } else { + item.Status = -1 + } } func statusUp(item *HealthCheckItem) { - if item.Status >= 0 { - item.Status++ - if item.Status > item.UpCount { - item.Status = item.UpCount - } - } else { - item.Status = 1 - } + if item.Status >= 0 { + item.Status++ + if item.Status > item.UpCount { + item.Status = item.UpCount + } + } else { + item.Status = 1 + } } func (h *Healthcheck) FilterHealthcheck(qname string, rrset *IP_RRSet) []IP_RR { - var newIps []IP_RR - if !h.Enable { - newIps = append(newIps, rrset.Data...) - return newIps - } - min := rrset.HealthCheckConfig.DownCount - for _, ip := range rrset.Data { - status := h.getStatus(qname, ip.Ip) - if status > min { - min = status - } - } - logger.Default.Debugf("min = %d", min) - if min < rrset.HealthCheckConfig.UpCount - 1 && min > rrset.HealthCheckConfig.DownCount { - min = rrset.HealthCheckConfig.DownCount + 1 - } - logger.Default.Debugf("min = %d", min) - for _, ip := range rrset.Data { - logger.Default.Debug("qname: ", ip.Ip.String(), " status: ", h.getStatus(qname, ip.Ip)) - if h.getStatus(qname, ip.Ip) < min { - continue - } - newIps = append(newIps, ip) - } - return newIps + var newIps []IP_RR + if !h.Enable { + newIps = append(newIps, rrset.Data...) + return newIps + } + min := rrset.HealthCheckConfig.DownCount + for _, ip := range rrset.Data { + status := h.getStatus(qname, ip.Ip) + if status > min { + min = status + } + } + logger.Default.Debugf("min = %d", min) + if min < rrset.HealthCheckConfig.UpCount-1 && min > rrset.HealthCheckConfig.DownCount { + min = rrset.HealthCheckConfig.DownCount + 1 + } + logger.Default.Debugf("min = %d", min) + for _, ip := range rrset.Data { + logger.Default.Debug("qname: ", ip.Ip.String(), " status: ", h.getStatus(qname, ip.Ip)) + if h.getStatus(qname, ip.Ip) < min { + continue + } + newIps = append(newIps, ip) + } + return newIps } - func (h *Healthcheck) Transfer() { - itemsEqual := func(item1 *HealthCheckItem, item2 *HealthCheckItem) bool { - if item1 == nil || item2 == nil { - return false - } - if item1.Ip != item2.Ip || item1.Uri != item2.Uri || item1.Port != item2.Port || - item1.Protocol != item2.Protocol || item1.Enable != item2.Enable || - item1.UpCount != item2.UpCount || item1.DownCount != item2.DownCount || item1.Timeout != item2.Timeout { - return false - } - return true - } - - limiter := time.Tick(time.Millisecond * 50) - for { - domains,err := h.redisConfigServer.SMembers("redins:zones") - if err !=nil { - logger.Default.Errorf("cannot get members of redins:zones : %s", err) - } - for _, domain := range domains { - domainId := h.getDomainId(domain) - subdomains, err := h.redisConfigServer.GetHKeys("redins:zones:" + domain) - if err != nil { - logger.Default.Errorf("cannot get keys of %s : %s", domain, err) - } - for _, subdomain := range subdomains { - select { - case <-h.quit: - h.quitWG.Done() - return - case <-limiter: - recordStr, err := h.redisConfigServer.HGet("redins:zones:" + domain, subdomain) - if err != nil { - logger.Default.Errorf("cannot get record of %s.%s : %s", subdomain, domain, err) - } - record := new(Record) - record.A.HealthCheckConfig = IpHealthCheckConfig { - Timeout: 1000, - Port: 80, - UpCount: 3, - DownCount: -3, - Protocol: "http", - Uri: "/", - Enable: false, - } - record.AAAA = record.A - err = json.Unmarshal([]byte(recordStr), record) - if err != nil { - logger.Default.Errorf("cannot parse json : zone -> %s, location -> %s, %s -> %s", domain, subdomain, recordStr, err) - continue - } - var host string - if subdomain == "@" { - host = domain - } else { - host = subdomain + "." + domain - } - for _, rrset := range []*IP_RRSet{&record.A, &record.AAAA} { - if !rrset.HealthCheckConfig.Enable { - continue - } - for i := range rrset.Data { - key := host + ":" + rrset.Data[i].Ip.String() - newItem := &HealthCheckItem{ - Ip: rrset.Data[i].Ip.String(), - Port: rrset.HealthCheckConfig.Port, - Host: host, - Enable: rrset.HealthCheckConfig.Enable, - DownCount: rrset.HealthCheckConfig.DownCount, - UpCount: rrset.HealthCheckConfig.UpCount, - Timeout: rrset.HealthCheckConfig.Timeout, - Uri: rrset.HealthCheckConfig.Uri, - Protocol: rrset.HealthCheckConfig.Protocol, - DomainId: domainId, - } - oldItem := h.loadItem(key) - if !itemsEqual(oldItem, newItem) { - h.storeItem(newItem) - } - h.redisStatusServer.Expire("redins:healthcheck:" + key, h.updateInterval) - } - } - } - } - } - } + itemsEqual := func(item1 *HealthCheckItem, item2 *HealthCheckItem) bool { + if item1 == nil || item2 == nil { + return false + } + if item1.Ip != item2.Ip || item1.Uri != item2.Uri || item1.Port != item2.Port || + item1.Protocol != item2.Protocol || item1.Enable != item2.Enable || + item1.UpCount != item2.UpCount || item1.DownCount != item2.DownCount || item1.Timeout != item2.Timeout { + return false + } + return true + } + + limiter := time.Tick(time.Millisecond * 50) + for { + domains, err := h.redisConfigServer.SMembers("redins:zones") + if err != nil { + logger.Default.Errorf("cannot get members of redins:zones : %s", err) + } + for _, domain := range domains { + domainId := h.getDomainId(domain) + subdomains, err := h.redisConfigServer.GetHKeys("redins:zones:" + domain) + if err != nil { + logger.Default.Errorf("cannot get keys of %s : %s", domain, err) + } + for _, subdomain := range subdomains { + select { + case <-h.quit: + h.quitWG.Done() + return + case <-limiter: + recordStr, err := h.redisConfigServer.HGet("redins:zones:"+domain, subdomain) + if err != nil { + logger.Default.Errorf("cannot get record of %s.%s : %s", subdomain, domain, err) + } + record := new(Record) + record.A.HealthCheckConfig = IpHealthCheckConfig{ + Timeout: 1000, + Port: 80, + UpCount: 3, + DownCount: -3, + Protocol: "http", + Uri: "/", + Enable: false, + } + record.AAAA = record.A + err = json.Unmarshal([]byte(recordStr), record) + if err != nil { + logger.Default.Errorf("cannot parse json : zone -> %s, location -> %s, %s -> %s", domain, subdomain, recordStr, err) + continue + } + var host string + if subdomain == "@" { + host = domain + } else { + host = subdomain + "." + domain + } + for _, rrset := range []*IP_RRSet{&record.A, &record.AAAA} { + if !rrset.HealthCheckConfig.Enable { + continue + } + for i := range rrset.Data { + key := host + ":" + rrset.Data[i].Ip.String() + newItem := &HealthCheckItem{ + Ip: rrset.Data[i].Ip.String(), + Port: rrset.HealthCheckConfig.Port, + Host: host, + Enable: rrset.HealthCheckConfig.Enable, + DownCount: rrset.HealthCheckConfig.DownCount, + UpCount: rrset.HealthCheckConfig.UpCount, + Timeout: rrset.HealthCheckConfig.Timeout, + Uri: rrset.HealthCheckConfig.Uri, + Protocol: rrset.HealthCheckConfig.Protocol, + DomainId: domainId, + } + oldItem := h.loadItem(key) + if !itemsEqual(oldItem, newItem) { + h.storeItem(newItem) + } + h.redisStatusServer.Expire("redins:healthcheck:"+key, h.updateInterval) + } + } + } + } + } + } } diff --git a/handler/healthcheck_test.go b/handler/healthcheck_test.go index 74794e3..4807b91 100644 --- a/handler/healthcheck_test.go +++ b/handler/healthcheck_test.go @@ -1,475 +1,474 @@ package handler import ( - "testing" - "log" - "net" - "strings" - "strconv" - "time" - "fmt" - - "github.com/hawell/uperdis" - "github.com/hawell/logger" + "fmt" + "log" + "net" + "strconv" + "strings" + "testing" + "time" + + "github.com/hawell/logger" + "github.com/hawell/uperdis" ) -var healthcheckGetEntries = [][]string { - {"w0.healthcheck.com.:1.2.3.4", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":3}`}, - {"w0.healthcheck.com.:2.3.4.5", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":1}`}, - {"w0.healthcheck.com.:3.4.5.6", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":0}`}, - {"w0.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, - {"w0.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, +var healthcheckGetEntries = [][]string{ + {"w0.healthcheck.com.:1.2.3.4", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":3}`}, + {"w0.healthcheck.com.:2.3.4.5", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":1}`}, + {"w0.healthcheck.com.:3.4.5.6", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":0}`}, + {"w0.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, + {"w0.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, - {"w1.healthcheck.com.:2.3.4.5", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":1}`}, - {"w1.healthcheck.com.:3.4.5.6", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":0}`}, - {"w1.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, - {"w1.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, + {"w1.healthcheck.com.:2.3.4.5", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":1}`}, + {"w1.healthcheck.com.:3.4.5.6", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":0}`}, + {"w1.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, + {"w1.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, - {"w2.healthcheck.com.:3.4.5.6", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":0}`}, - {"w2.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, - {"w2.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, + {"w2.healthcheck.com.:3.4.5.6", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":0}`}, + {"w2.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, + {"w2.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, - {"w3.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, - {"w3.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, + {"w3.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-1}`}, + {"w3.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, - {"w4.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, + {"w4.healthcheck.com.:5.6.7.8", `{"enable":true,"protocol":"http","uri":"/","port":80, "status":-3}`}, } -var stats = []int { 3, 1, 0, -1, -3, 1, 0, -1, -3, 0, -1, -3, -1, -3, -3} -var filterResult = []int { 1, 3, 2, 1, 1} +var stats = []int{3, 1, 0, -1, -3, 1, 0, -1, -3, 0, -1, -3, -1, -3, -3} +var filterResult = []int{1, 3, 2, 1, 1} - -var healthCheckSetEntries = [][]string { - {"@", "185.143.233.2", - `{"enable":true,"protocol":"http","uri":"","port":80, "timeout": 1000}`, - }, - {"www", "185.143.234.50", - `{"enable":true,"protocol":"http","uri":"","port":80, "timeout": 1000}`, - }, +var healthCheckSetEntries = [][]string{ + {"@", "185.143.233.2", + `{"enable":true,"protocol":"http","uri":"","port":80, "timeout": 1000}`, + }, + {"www", "185.143.234.50", + `{"enable":true,"protocol":"http","uri":"","port":80, "timeout": 1000}`, + }, } var healthcheckTransferItems = [][]string{ - {"w0", "1.2.3.4", - `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, - `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":2, "up_count": 3, "down_count": -3, "timeout":1000}`, - }, - {"w1", "2.3.4.5", - `{"enable":true,"protocol":"https","uri":"/uri111","port":8081, "up_count": 3, "down_count": -3, "timeout":1000}`, - `{"enable":true,"protocol":"http","uri":"/uri1","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, - }, - {"w2", "3.4.5.6", - "", - `{"enable":true,"protocol":"http","uri":"/uri2","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, - }, - {"w3", "4.5.6.7", - `{"enable":true,"protocol":"http","uri":"/uri3","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, - ``, - }, + {"w0", "1.2.3.4", + `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, + `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":2, "up_count": 3, "down_count": -3, "timeout":1000}`, + }, + {"w1", "2.3.4.5", + `{"enable":true,"protocol":"https","uri":"/uri111","port":8081, "up_count": 3, "down_count": -3, "timeout":1000}`, + `{"enable":true,"protocol":"http","uri":"/uri1","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, + }, + {"w2", "3.4.5.6", + "", + `{"enable":true,"protocol":"http","uri":"/uri2","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, + }, + {"w3", "4.5.6.7", + `{"enable":true,"protocol":"http","uri":"/uri3","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, + ``, + }, } -var healthCheckTransferResults = [][]string { - {"w0.healthcheck.com.:1.2.3.4", `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":2, "up_count": 3, "down_count": -3, "timeout":1000}`}, - {"w1.healthcheck.com.:2.3.4.5", `{"enable":true,"protocol":"https","uri":"/uri111","port":8081, "status":0, "up_count": 3, "down_count": -3, "timeout":1000}`}, - {"w3.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/uri3","port":80, "status":0, "up_count": 3, "down_count": -3, "timeout":1000}`}, +var healthCheckTransferResults = [][]string{ + {"w0.healthcheck.com.:1.2.3.4", `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":2, "up_count": 3, "down_count": -3, "timeout":1000}`}, + {"w1.healthcheck.com.:2.3.4.5", `{"enable":true,"protocol":"https","uri":"/uri111","port":8081, "status":0, "up_count": 3, "down_count": -3, "timeout":1000}`}, + {"w3.healthcheck.com.:4.5.6.7", `{"enable":true,"protocol":"http","uri":"/uri3","port":80, "status":0, "up_count": 3, "down_count": -3, "timeout":1000}`}, } -var config = HealthcheckConfig { - Enable: true, - MaxRequests: 10, - MaxPendingRequests: 100, - UpdateInterval: 600, - CheckInterval: 600, - RedisStatusServer: uperdis.RedisConfig { - Ip: "redis", - Port: 6379, - DB: 0, - Password: "", - Prefix: "healthcheck_", - Suffix: "_healthcheck", - ConnectTimeout: 0, - ReadTimeout: 0, - }, - Log: logger.LogConfig { - Enable: true, - Path: "/tmp/healthcheck.log", - }, +var config = HealthcheckConfig{ + Enable: true, + MaxRequests: 10, + MaxPendingRequests: 100, + UpdateInterval: 600, + CheckInterval: 600, + RedisStatusServer: uperdis.RedisConfig{ + Ip: "redis", + Port: 6379, + DB: 0, + Password: "", + Prefix: "healthcheck_", + Suffix: "_healthcheck", + ConnectTimeout: 0, + ReadTimeout: 0, + }, + Log: logger.LogConfig{ + Enable: true, + Path: "/tmp/healthcheck.log", + }, } -var configRedisConf = uperdis.RedisConfig { - Ip: "redis", - Port: 6379, - DB: 0, - Password: "", - Prefix: "hcconfig_", - Suffix: "_hcconfig", - ConnectTimeout: 0, - ReadTimeout: 0, +var configRedisConf = uperdis.RedisConfig{ + Ip: "redis", + Port: 6379, + DB: 0, + Password: "", + Prefix: "hcconfig_", + Suffix: "_hcconfig", + ConnectTimeout: 0, + ReadTimeout: 0, } func TestGet(t *testing.T) { - log.Println("TestGet") - logger.Default = logger.NewLogger(&logger.LogConfig{}) - configRedis := uperdis.NewRedis(&configRedisConf) - h := NewHealthcheck(&config, configRedis) - - h.redisStatusServer.Del("*") - h.redisConfigServer.Del("*") - for _, entry := range healthcheckGetEntries { - h.redisStatusServer.Set("redins:healthcheck:" + entry[0], entry[1]) - } - - for i := range healthcheckGetEntries { - hostIp := strings.Split(healthcheckGetEntries[i][0], ":") - stat := h.getStatus(hostIp[0], net.ParseIP(hostIp[1])) - log.Println("[DEBUG]", stat, " ", stats[i]) - if stat != stats[i] { - t.Fail() - } - } - // h.Stop() - h.redisStatusServer.Del("*") + log.Println("TestGet") + logger.Default = logger.NewLogger(&logger.LogConfig{}) + configRedis := uperdis.NewRedis(&configRedisConf) + h := NewHealthcheck(&config, configRedis) + + h.redisStatusServer.Del("*") + h.redisConfigServer.Del("*") + for _, entry := range healthcheckGetEntries { + h.redisStatusServer.Set("redins:healthcheck:"+entry[0], entry[1]) + } + + for i := range healthcheckGetEntries { + hostIp := strings.Split(healthcheckGetEntries[i][0], ":") + stat := h.getStatus(hostIp[0], net.ParseIP(hostIp[1])) + log.Println("[DEBUG]", stat, " ", stats[i]) + if stat != stats[i] { + t.Fail() + } + } + // h.Stop() + h.redisStatusServer.Del("*") } func TestFilter(t *testing.T) { - log.Println("TestFilter") - logger.Default = logger.NewLogger(&logger.LogConfig{}) - configRedis := uperdis.NewRedis(&configRedisConf) - h := NewHealthcheck(&config, configRedis) - - h.redisStatusServer.Del("*") - h.redisConfigServer.Del("*") - for _, entry := range healthcheckGetEntries { - h.redisStatusServer.Set("redins:healthcheck:" + entry[0], entry[1]) - } - - w := []Record { - { - RRSets: RRSets { - A: IP_RRSet{ - Data: []IP_RR { - {Ip: net.ParseIP("1.2.3.4")}, - {Ip: net.ParseIP("2.3.4.5")}, - {Ip: net.ParseIP("3.4.5.6")}, - {Ip: net.ParseIP("4.5.6.7")}, - {Ip: net.ParseIP("5.6.7.8")}, - }, - FilterConfig: IpFilterConfig { - Count: "multi", - Order: "none", - GeoFilter: "none", - }, - HealthCheckConfig: IpHealthCheckConfig { - Enable: true, - DownCount: -3, - UpCount: 3, - Timeout: 1000, - }, - }, - }, - }, - { - RRSets: RRSets { - A: IP_RRSet{ - Data: []IP_RR { - {Ip: net.ParseIP("2.3.4.5")}, - {Ip: net.ParseIP("3.4.5.6")}, - {Ip: net.ParseIP("4.5.6.7")}, - {Ip: net.ParseIP("5.6.7.8")}, - }, - FilterConfig: IpFilterConfig { - Count: "multi", - Order: "none", - GeoFilter: "none", - }, - HealthCheckConfig: IpHealthCheckConfig { - Enable: true, - DownCount: -3, - UpCount: 3, - Timeout: 1000, - }, - }, - }, - }, - { - RRSets: RRSets { - A: IP_RRSet{ - Data: []IP_RR { - {Ip: net.ParseIP("3.4.5.6")}, - {Ip: net.ParseIP("4.5.6.7")}, - {Ip: net.ParseIP("5.6.7.8")}, - }, - FilterConfig: IpFilterConfig { - Count: "multi", - Order: "none", - GeoFilter: "none", - }, - HealthCheckConfig: IpHealthCheckConfig { - Enable: true, - DownCount: -3, - UpCount: 3, - Timeout: 1000, - }, - }, - }, - }, - { - RRSets: RRSets { - A: IP_RRSet{ - Data: []IP_RR { - {Ip: net.ParseIP("4.5.6.7")}, - {Ip: net.ParseIP("5.6.7.8")}, - }, - FilterConfig: IpFilterConfig { - Count: "multi", - Order: "none", - GeoFilter: "none", - }, - HealthCheckConfig: IpHealthCheckConfig { - Enable: true, - DownCount: -3, - UpCount: 3, - Timeout: 1000, - }, - }, - }, - }, - { - RRSets: RRSets { - A: IP_RRSet{ - Data: []IP_RR { - {Ip: net.ParseIP("5.6.7.8")}, - }, - FilterConfig: IpFilterConfig { - Count: "multi", - Order: "none", - GeoFilter: "none", - }, - HealthCheckConfig: IpHealthCheckConfig { - Enable: true, - DownCount: -3, - UpCount: 3, - Timeout: 1000, - }, - }, - }, - }, - } - for i := range w { - log.Println("[DEBUG]", w[i]) - ips := h.FilterHealthcheck("w" + strconv.Itoa(i) + ".healthcheck.com.", &w[i].A) - log.Println("[DEBUG]", w[i]) - if len(ips) != filterResult[i] { - t.Fail() - } - } - h.redisStatusServer.Del("*") - // h.Stop() + log.Println("TestFilter") + logger.Default = logger.NewLogger(&logger.LogConfig{}) + configRedis := uperdis.NewRedis(&configRedisConf) + h := NewHealthcheck(&config, configRedis) + + h.redisStatusServer.Del("*") + h.redisConfigServer.Del("*") + for _, entry := range healthcheckGetEntries { + h.redisStatusServer.Set("redins:healthcheck:"+entry[0], entry[1]) + } + + w := []Record{ + { + RRSets: RRSets{ + A: IP_RRSet{ + Data: []IP_RR{ + {Ip: net.ParseIP("1.2.3.4")}, + {Ip: net.ParseIP("2.3.4.5")}, + {Ip: net.ParseIP("3.4.5.6")}, + {Ip: net.ParseIP("4.5.6.7")}, + {Ip: net.ParseIP("5.6.7.8")}, + }, + FilterConfig: IpFilterConfig{ + Count: "multi", + Order: "none", + GeoFilter: "none", + }, + HealthCheckConfig: IpHealthCheckConfig{ + Enable: true, + DownCount: -3, + UpCount: 3, + Timeout: 1000, + }, + }, + }, + }, + { + RRSets: RRSets{ + A: IP_RRSet{ + Data: []IP_RR{ + {Ip: net.ParseIP("2.3.4.5")}, + {Ip: net.ParseIP("3.4.5.6")}, + {Ip: net.ParseIP("4.5.6.7")}, + {Ip: net.ParseIP("5.6.7.8")}, + }, + FilterConfig: IpFilterConfig{ + Count: "multi", + Order: "none", + GeoFilter: "none", + }, + HealthCheckConfig: IpHealthCheckConfig{ + Enable: true, + DownCount: -3, + UpCount: 3, + Timeout: 1000, + }, + }, + }, + }, + { + RRSets: RRSets{ + A: IP_RRSet{ + Data: []IP_RR{ + {Ip: net.ParseIP("3.4.5.6")}, + {Ip: net.ParseIP("4.5.6.7")}, + {Ip: net.ParseIP("5.6.7.8")}, + }, + FilterConfig: IpFilterConfig{ + Count: "multi", + Order: "none", + GeoFilter: "none", + }, + HealthCheckConfig: IpHealthCheckConfig{ + Enable: true, + DownCount: -3, + UpCount: 3, + Timeout: 1000, + }, + }, + }, + }, + { + RRSets: RRSets{ + A: IP_RRSet{ + Data: []IP_RR{ + {Ip: net.ParseIP("4.5.6.7")}, + {Ip: net.ParseIP("5.6.7.8")}, + }, + FilterConfig: IpFilterConfig{ + Count: "multi", + Order: "none", + GeoFilter: "none", + }, + HealthCheckConfig: IpHealthCheckConfig{ + Enable: true, + DownCount: -3, + UpCount: 3, + Timeout: 1000, + }, + }, + }, + }, + { + RRSets: RRSets{ + A: IP_RRSet{ + Data: []IP_RR{ + {Ip: net.ParseIP("5.6.7.8")}, + }, + FilterConfig: IpFilterConfig{ + Count: "multi", + Order: "none", + GeoFilter: "none", + }, + HealthCheckConfig: IpHealthCheckConfig{ + Enable: true, + DownCount: -3, + UpCount: 3, + Timeout: 1000, + }, + }, + }, + }, + } + for i := range w { + log.Println("[DEBUG]", w[i]) + ips := h.FilterHealthcheck("w"+strconv.Itoa(i)+".healthcheck.com.", &w[i].A) + log.Println("[DEBUG]", w[i]) + if len(ips) != filterResult[i] { + t.Fail() + } + } + h.redisStatusServer.Del("*") + // h.Stop() } func TestSet(t *testing.T) { - log.Println("TestSet") - logger.Default = logger.NewLogger(&logger.LogConfig{}) - configRedis := uperdis.NewRedis(&configRedisConf) - h := NewHealthcheck(&config, configRedis) - - h.redisConfigServer.Del("*") - h.redisStatusServer.Del("*") - for _, str := range healthCheckSetEntries { - a := fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", str[1], str[2]) - h.redisConfigServer.HSet("redins:zones:healthcheck.com.", str[0], a) - var key string - if str[0] == "@" { - key = fmt.Sprintf("arvancloud.com.:%s", str[1]) - } else { - key = fmt.Sprintf("%s.arvancloud.com.:%s", str[0], str[1]) - } - h.redisStatusServer.Set("redins:healthcheck:" + key, str[2]) - } - // h.transferItems() - go h.Start() - time.Sleep(time.Second * 10) - - log.Println("[DEBUG]", h.getStatus("arvancloud.com", net.ParseIP("185.143.233.2"))) - log.Println("[DEBUG]", h.getStatus("www.arvancloud.com", net.ParseIP("185.143.234.50"))) + log.Println("TestSet") + logger.Default = logger.NewLogger(&logger.LogConfig{}) + configRedis := uperdis.NewRedis(&configRedisConf) + h := NewHealthcheck(&config, configRedis) + + h.redisConfigServer.Del("*") + h.redisStatusServer.Del("*") + for _, str := range healthCheckSetEntries { + a := fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", str[1], str[2]) + h.redisConfigServer.HSet("redins:zones:healthcheck.com.", str[0], a) + var key string + if str[0] == "@" { + key = fmt.Sprintf("arvancloud.com.:%s", str[1]) + } else { + key = fmt.Sprintf("%s.arvancloud.com.:%s", str[0], str[1]) + } + h.redisStatusServer.Set("redins:healthcheck:"+key, str[2]) + } + // h.transferItems() + go h.Start() + time.Sleep(time.Second * 10) + + log.Println("[DEBUG]", h.getStatus("arvancloud.com", net.ParseIP("185.143.233.2"))) + log.Println("[DEBUG]", h.getStatus("www.arvancloud.com", net.ParseIP("185.143.234.50"))) } func TestTransfer(t *testing.T) { - log.Printf("TestTransfer") - logger.Default = logger.NewLogger(&logger.LogConfig{}) - configRedis := uperdis.NewRedis(&configRedisConf) - h := NewHealthcheck(&config, configRedis) - - h.redisConfigServer.Del("*") - h.redisStatusServer.Del("*") - h.redisConfigServer.SAdd("redins:zones", "healthcheck.com.") - for _, str := range healthcheckTransferItems { - if str[2] != "" { - a := fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", str[1], str[2]) - h.redisConfigServer.HSet("redins:zones:healthcheck.com.", str[0], a) - } - if str[3] != "" { - key := fmt.Sprintf("%s.healthcheck.com.:%s", str[0], str[1]) - h.redisStatusServer.Set("redins:healthcheck:" + key, str[3]) - } - } - - // h.transferItems() - go h.Start() - time.Sleep(time.Second * 10) - - itemsEqual := func(item1 *HealthCheckItem, item2 *HealthCheckItem) bool { - if item1.Ip != item2.Ip || item1.Uri != item2.Uri || item1.Port != item2.Port || - item1.Protocol != item2.Protocol || item1.Enable != item2.Enable || - item1.UpCount != item2.UpCount || item1.DownCount != item2.DownCount || item1.Timeout != item2.Timeout { - return false - } - return true - } - - for i, str := range healthCheckTransferResults { - h.redisStatusServer.Set("redins:healthcheck:" + str[0] + "res", str[1]) - resItem := h.loadItem(str[0] + "res") - resItem.Ip = strings.TrimRight(resItem.Ip, "res") - storedItem := h.loadItem(str[0]) - log.Println("** key : ", str[0]) - log.Println("** expected : ", resItem) - log.Println("** stored : ", storedItem) - if !itemsEqual(resItem, storedItem) { - log.Println(i, "failed") - t.Fail() - } - } + log.Printf("TestTransfer") + logger.Default = logger.NewLogger(&logger.LogConfig{}) + configRedis := uperdis.NewRedis(&configRedisConf) + h := NewHealthcheck(&config, configRedis) + + h.redisConfigServer.Del("*") + h.redisStatusServer.Del("*") + h.redisConfigServer.SAdd("redins:zones", "healthcheck.com.") + for _, str := range healthcheckTransferItems { + if str[2] != "" { + a := fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", str[1], str[2]) + h.redisConfigServer.HSet("redins:zones:healthcheck.com.", str[0], a) + } + if str[3] != "" { + key := fmt.Sprintf("%s.healthcheck.com.:%s", str[0], str[1]) + h.redisStatusServer.Set("redins:healthcheck:"+key, str[3]) + } + } + + // h.transferItems() + go h.Start() + time.Sleep(time.Second * 10) + + itemsEqual := func(item1 *HealthCheckItem, item2 *HealthCheckItem) bool { + if item1.Ip != item2.Ip || item1.Uri != item2.Uri || item1.Port != item2.Port || + item1.Protocol != item2.Protocol || item1.Enable != item2.Enable || + item1.UpCount != item2.UpCount || item1.DownCount != item2.DownCount || item1.Timeout != item2.Timeout { + return false + } + return true + } + + for i, str := range healthCheckTransferResults { + h.redisStatusServer.Set("redins:healthcheck:"+str[0]+"res", str[1]) + resItem := h.loadItem(str[0] + "res") + resItem.Ip = strings.TrimRight(resItem.Ip, "res") + storedItem := h.loadItem(str[0]) + log.Println("** key : ", str[0]) + log.Println("** expected : ", resItem) + log.Println("** stored : ", storedItem) + if !itemsEqual(resItem, storedItem) { + log.Println(i, "failed") + t.Fail() + } + } } func TestPing(t *testing.T) { - log.Println("TestPing") - if err := pingCheck("4.2.2.4", time.Second); err != nil { - t.Fail() - } + log.Println("TestPing") + if err := pingCheck("4.2.2.4", time.Second); err != nil { + t.Fail() + } } -var healthcheckConfig = HealthcheckConfig { - Enable: true, - Log: logger.LogConfig { - Enable: true, - Target: "file", - Level: "info", - Path: "/tmp/hctest.log", - TimeFormat: "2006-01-02 15:04:05", - }, - RedisStatusServer: uperdis.RedisConfig { - Ip: "redis", - Port: 6379, - DB: 0, - Password: "", - Prefix: "hcstattest_", - Suffix: "_hcstattest", - ConnectTimeout: 0, - ReadTimeout: 0, - }, - CheckInterval: 1, - UpdateInterval: 200, - MaxRequests: 20, - MaxPendingRequests: 100, +var healthcheckConfig = HealthcheckConfig{ + Enable: true, + Log: logger.LogConfig{ + Enable: true, + Target: "file", + Level: "info", + Path: "/tmp/hctest.log", + TimeFormat: "2006-01-02 15:04:05", + }, + RedisStatusServer: uperdis.RedisConfig{ + Ip: "redis", + Port: 6379, + DB: 0, + Password: "", + Prefix: "hcstattest_", + Suffix: "_hcstattest", + ConnectTimeout: 0, + ReadTimeout: 0, + }, + CheckInterval: 1, + UpdateInterval: 200, + MaxRequests: 20, + MaxPendingRequests: 100, } var hcConfig = `{"soa":{"ttl":300, "minttl":100, "mbox":"hostmaster.google.com.","ns":"ns1.google.com.","refresh":44,"retry":55,"expire":66}}` -var hcEntries = [][]string { - {"www", - `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"http","uri":"","port":80, "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"172.217.17.238"}]}}`, - }, - /* - {"y", - `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"ping", "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"4.2.2.4"}]}}`, - }, - {"ddd", - `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"http","uri":"/uri2","port":80, "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"3.3.3.3"}]}}`, - }, - */ - {"z", - `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"ping", "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"192.168.200.2"}]}}`, - }, +var hcEntries = [][]string{ + {"www", + `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"http","uri":"","port":80, "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"172.217.17.238"}]}}`, + }, + /* + {"y", + `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"ping", "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"4.2.2.4"}]}}`, + }, + {"ddd", + `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"http","uri":"/uri2","port":80, "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"3.3.3.3"}]}}`, + }, + */ + {"z", + `{"a":{"ttl":300, "health_check":{"enable":true,"protocol":"ping", "up_count": 3, "down_count": -3, "timeout":1000}, "records":[{"ip":"192.168.200.2"}]}}`, + }, } func TestHealthCheck(t *testing.T) { - log.Println("TestHealthCheck") - logger.Default = logger.NewLogger(&logger.LogConfig{Enable:true, Target:"stdout", Format:"text"}) - - configRedis := uperdis.NewRedis(&configRedisConf) - hc := NewHealthcheck(&healthcheckConfig, configRedis) - hc.redisStatusServer.Del("*") - hc.redisConfigServer.Del("*") - hc.redisConfigServer.SAdd("redins:zones", "google.com.") - for _, entry := range hcEntries { - configRedis.HSet("redins:zones:google.com.", entry[0], entry[1]) - } - configRedis.Set("redins:zones:google.com.:config", hcConfig) - - go hc.Start() - time.Sleep(10 * time.Second) - h1 := hc.getStatus("www.google.com.", net.ParseIP("172.217.17.238")) - /* - h2 := hc.getStatus("y.google.com.", net.ParseIP("4.2.2.4")) - h3 := hc.getStatus("ddd.google.com.", net.ParseIP("3.3.3.3")) - */ - h4 := hc.getStatus("z.google.com.", net.ParseIP("192.168.200.2")) - log.Println(h1, " ", /*h2, " ", h3,*/ " ", h4) - if h1 != 3 { - t.Fail() - } - /* - if h2 != 3 { - t.Fail() - } - if h3 != -3 { - t.Fail() - } - */ - if h4 != -3 { - t.Fail() - } + log.Println("TestHealthCheck") + logger.Default = logger.NewLogger(&logger.LogConfig{Enable: true, Target: "stdout", Format: "text"}) + + configRedis := uperdis.NewRedis(&configRedisConf) + hc := NewHealthcheck(&healthcheckConfig, configRedis) + hc.redisStatusServer.Del("*") + hc.redisConfigServer.Del("*") + hc.redisConfigServer.SAdd("redins:zones", "google.com.") + for _, entry := range hcEntries { + configRedis.HSet("redins:zones:google.com.", entry[0], entry[1]) + } + configRedis.Set("redins:zones:google.com.:config", hcConfig) + + go hc.Start() + time.Sleep(10 * time.Second) + h1 := hc.getStatus("www.google.com.", net.ParseIP("172.217.17.238")) + /* + h2 := hc.getStatus("y.google.com.", net.ParseIP("4.2.2.4")) + h3 := hc.getStatus("ddd.google.com.", net.ParseIP("3.3.3.3")) + */ + h4 := hc.getStatus("z.google.com.", net.ParseIP("192.168.200.2")) + log.Println(h1, " " /*h2, " ", h3,*/, " ", h4) + if h1 != 3 { + t.Fail() + } + /* + if h2 != 3 { + t.Fail() + } + if h3 != -3 { + t.Fail() + } + */ + if h4 != -3 { + t.Fail() + } } func TestExpire(t *testing.T) { - log.Printf("TestTransfer") - logger.Default = logger.NewLogger(&logger.LogConfig{}) - configRedis := uperdis.NewRedis(&configRedisConf) - config.UpdateInterval = 1 - h := NewHealthcheck(&config, configRedis) - - h.redisConfigServer.Del("*") - h.redisStatusServer.Del("*") - - expireItem := []string { - "w0", "1.2.3.4", - `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, - `{"enable":false,"protocol":"http","uri":"/uri0","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, - } - - a := fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", expireItem[1], expireItem[2]) - log.Println(a) - h.redisConfigServer.SAdd("redins:zones", "healthcheck.com.") - h.redisConfigServer.HSet("redins:zones:healthcheck.com.", expireItem[0], a) - key := fmt.Sprintf("%s.healthcheck.com.:%s", expireItem[0], expireItem[1]) - h.redisStatusServer.Set("redins:healthcheck:" + key, expireItem[2]) - - go h.Start() - time.Sleep(time.Second * 2) - status := h.getStatus("w0.healthcheck.com.", net.ParseIP("1.2.3.4")) - if status != 3 { - fmt.Println("1") - t.Fail() - } - - a = fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", expireItem[1], expireItem[3]) - log.Println(a) - h.redisConfigServer.HSet("redins:zones:healthcheck.com.", expireItem[0], a) - - time.Sleep(time.Second * 3) - status = h.getStatus("w0.healthcheck.com.", net.ParseIP("1.2.3.4")) - status = h.getStatus("w0.healthcheck.com.", net.ParseIP("1.2.3.4")) - if status != 0 { - fmt.Println("2") - t.Fail() - } -} \ No newline at end of file + log.Printf("TestTransfer") + logger.Default = logger.NewLogger(&logger.LogConfig{}) + configRedis := uperdis.NewRedis(&configRedisConf) + config.UpdateInterval = 1 + h := NewHealthcheck(&config, configRedis) + + h.redisConfigServer.Del("*") + h.redisStatusServer.Del("*") + + expireItem := []string{ + "w0", "1.2.3.4", + `{"enable":true,"protocol":"http","uri":"/uri0","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, + `{"enable":false,"protocol":"http","uri":"/uri0","port":80, "status":3, "up_count": 3, "down_count": -3, "timeout":1000}`, + } + + a := fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", expireItem[1], expireItem[2]) + log.Println(a) + h.redisConfigServer.SAdd("redins:zones", "healthcheck.com.") + h.redisConfigServer.HSet("redins:zones:healthcheck.com.", expireItem[0], a) + key := fmt.Sprintf("%s.healthcheck.com.:%s", expireItem[0], expireItem[1]) + h.redisStatusServer.Set("redins:healthcheck:"+key, expireItem[2]) + + go h.Start() + time.Sleep(time.Second * 2) + status := h.getStatus("w0.healthcheck.com.", net.ParseIP("1.2.3.4")) + if status != 3 { + fmt.Println("1") + t.Fail() + } + + a = fmt.Sprintf("{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"%s\"}],\"health_check\":%s}}", expireItem[1], expireItem[3]) + log.Println(a) + h.redisConfigServer.HSet("redins:zones:healthcheck.com.", expireItem[0], a) + + time.Sleep(time.Second * 3) + status = h.getStatus("w0.healthcheck.com.", net.ParseIP("1.2.3.4")) + status = h.getStatus("w0.healthcheck.com.", net.ParseIP("1.2.3.4")) + if status != 0 { + fmt.Println("2") + t.Fail() + } +} diff --git a/handler/limiter.go b/handler/limiter.go index 926975c..26ce398 100644 --- a/handler/limiter.go +++ b/handler/limiter.go @@ -1,95 +1,95 @@ package handler import ( - "time" - "sync" + "sync" + "time" - "github.com/patrickmn/go-cache" + "github.com/patrickmn/go-cache" ) type RateLimiter struct { - Limiters *cache.Cache - MaxTime time.Duration - TimeStep time.Duration - Config *RateLimiterConfig - WhiteList map[string]interface{} - BlackList map[string]interface{} + Limiters *cache.Cache + MaxTime time.Duration + TimeStep time.Duration + Config *RateLimiterConfig + WhiteList map[string]interface{} + BlackList map[string]interface{} } type RateLimiterConfig struct { - Enable bool `json:"enable"` - Burst int `json:"burst"` - Rate int `json:"rate"` - WhiteList []string `json:"whitelist"` - BlackList []string `json:"blacklist"` + Enable bool `json:"enable"` + Burst int `json:"burst"` + Rate int `json:"rate"` + WhiteList []string `json:"whitelist"` + BlackList []string `json:"blacklist"` } func NewRateLimiter(config *RateLimiterConfig) *RateLimiter { - rl := &RateLimiter{ - Config: config, - } - rl.Limiters = cache.New(time.Minute, time.Minute * 10) - rl.WhiteList = make(map[string]interface{}) - for _, x := range config.WhiteList { - rl.WhiteList[x] = nil - } - rl.BlackList = make(map[string]interface{}) - for _, x := range config.BlackList { - rl.BlackList[x] = nil - } - rl.TimeStep = time.Duration(60000 / config.Rate) * time.Millisecond - rl.MaxTime = rl.TimeStep * time.Duration(config.Burst) - return rl + rl := &RateLimiter{ + Config: config, + } + rl.Limiters = cache.New(time.Minute, time.Minute*10) + rl.WhiteList = make(map[string]interface{}) + for _, x := range config.WhiteList { + rl.WhiteList[x] = nil + } + rl.BlackList = make(map[string]interface{}) + for _, x := range config.BlackList { + rl.BlackList[x] = nil + } + rl.TimeStep = time.Duration(60000/config.Rate) * time.Millisecond + rl.MaxTime = rl.TimeStep * time.Duration(config.Burst) + return rl } type Limiter struct { - Size time.Duration - LastUpdate time.Time - Mutex *sync.Mutex + Size time.Duration + LastUpdate time.Time + Mutex *sync.Mutex } func (rl *RateLimiter) CanHandle(key string) bool { - if !rl.Config.Enable { - return true - } + if !rl.Config.Enable { + return true + } - if _, exist := rl.BlackList[key]; exist { - return false - } - if _, exist := rl.WhiteList[key]; exist { - return true - } - var ( - res bool - l *Limiter - ) - value, found := rl.Limiters.Get(key) - if found { - l = value.(*Limiter) - l.Mutex.Lock() + if _, exist := rl.BlackList[key]; exist { + return false + } + if _, exist := rl.WhiteList[key]; exist { + return true + } + var ( + res bool + l *Limiter + ) + value, found := rl.Limiters.Get(key) + if found { + l = value.(*Limiter) + l.Mutex.Lock() - l.Size -= time.Since(l.LastUpdate) - l.LastUpdate = time.Now() + l.Size -= time.Since(l.LastUpdate) + l.LastUpdate = time.Now() - if l.Size < 0 { - l.Size = 0 - } - if l.Size > rl.MaxTime { - res = false - } else { - l.Size += rl.TimeStep - res = true - } - rl.Limiters.Set(key, l, time.Minute) - l.Mutex.Unlock() - } else { - l = &Limiter { - Size: rl.TimeStep, - LastUpdate: time.Now(), - Mutex: &sync.Mutex{}, - } - res = true - rl.Limiters.Set(key, l, time.Minute) - } - return res + if l.Size < 0 { + l.Size = 0 + } + if l.Size > rl.MaxTime { + res = false + } else { + l.Size += rl.TimeStep + res = true + } + rl.Limiters.Set(key, l, time.Minute) + l.Mutex.Unlock() + } else { + l = &Limiter{ + Size: rl.TimeStep, + LastUpdate: time.Now(), + Mutex: &sync.Mutex{}, + } + res = true + rl.Limiters.Set(key, l, time.Minute) + } + return res } diff --git a/handler/limiter_test.go b/handler/limiter_test.go index 38bd2e0..582b934 100644 --- a/handler/limiter_test.go +++ b/handler/limiter_test.go @@ -1,76 +1,76 @@ package handler import ( - "testing" - "fmt" - "time" + "fmt" + "testing" + "time" ) func TestLimiter(t *testing.T) { - cfg := RateLimiterConfig { - Enable: true, - Rate: 60000, - Burst: 10, - WhiteList: []string{"w1", "w2"}, - BlackList: []string{"b1", "b2"}, - } - rl := NewRateLimiter(&cfg) + cfg := RateLimiterConfig{ + Enable: true, + Rate: 60000, + Burst: 10, + WhiteList: []string{"w1", "w2"}, + BlackList: []string{"b1", "b2"}, + } + rl := NewRateLimiter(&cfg) - fail := 0 - success := 0 - for i := 0; i< 10; i++ { - if rl.CanHandle("1") == false { - fail++ - } else { - success++ - } - } - fmt.Println("fail : ", fail, " success : ", success) - if fail != 0 { - t.Fail() - } - fail = 0 - success = 0 - for i := 0; i< 20; i++ { - if rl.CanHandle("2") == false { - fail++ - } else { - success++ - } - } - fmt.Println("fail : ", fail, " success : ", success) - if fail != 9 || success != 11 { - t.Fail() - } + fail := 0 + success := 0 + for i := 0; i < 10; i++ { + if rl.CanHandle("1") == false { + fail++ + } else { + success++ + } + } + fmt.Println("fail : ", fail, " success : ", success) + if fail != 0 { + t.Fail() + } + fail = 0 + success = 0 + for i := 0; i < 20; i++ { + if rl.CanHandle("2") == false { + fail++ + } else { + success++ + } + } + fmt.Println("fail : ", fail, " success : ", success) + if fail != 9 || success != 11 { + t.Fail() + } - if rl.CanHandle("b1") == true { - t.Fail() - } - if rl.CanHandle("b2") == true { - t.Fail() - } + if rl.CanHandle("b1") == true { + t.Fail() + } + if rl.CanHandle("b2") == true { + t.Fail() + } - for i := 0; i < 100; i++ { - if rl.CanHandle("w1") == false { - t.Fail() - } - if rl.CanHandle("w2") == false { - t.Fail() - } - } + for i := 0; i < 100; i++ { + if rl.CanHandle("w1") == false { + t.Fail() + } + if rl.CanHandle("w2") == false { + t.Fail() + } + } - fail = 0 - success = 0 - for i := 0; i < 10; i++ { - if rl.CanHandle("3") != true { - t.Fail() - } - } - for i := 0; i < 100; i++ { - time.Sleep(time.Millisecond) - if rl.CanHandle("3") != true { - t.Fail() - } - } - fmt.Println("fail : ", fail, " success : ", success) -} \ No newline at end of file + fail = 0 + success = 0 + for i := 0; i < 10; i++ { + if rl.CanHandle("3") != true { + t.Fail() + } + } + for i := 0; i < 100; i++ { + time.Sleep(time.Millisecond) + if rl.CanHandle("3") != true { + t.Fail() + } + } + fmt.Println("fail : ", fail, " success : ", success) +} diff --git a/handler/server.go b/handler/server.go index fe5b2ab..74419f5 100644 --- a/handler/server.go +++ b/handler/server.go @@ -1,26 +1,25 @@ package handler import ( - "strconv" + "strconv" - "github.com/miekg/dns" + "github.com/miekg/dns" ) - type ServerConfig struct { - Ip string `json:"ip,omitempty"` - Port int `json:"port,omitempty"` - Protocol string `json:"protocol,omitempty"` + Ip string `json:"ip,omitempty"` + Port int `json:"port,omitempty"` + Protocol string `json:"protocol,omitempty"` } func NewServer(config []ServerConfig) []dns.Server { - var servers []dns.Server - for _, cfg := range config { - server := dns.Server { - Addr: cfg.Ip + ":" + strconv.Itoa(cfg.Port), - Net: cfg.Protocol, - } - servers = append(servers, server) - } - return servers -} \ No newline at end of file + var servers []dns.Server + for _, cfg := range config { + server := dns.Server{ + Addr: cfg.Ip + ":" + strconv.Itoa(cfg.Port), + Net: cfg.Protocol, + } + servers = append(servers, server) + } + return servers +} diff --git a/handler/subnet_test.go b/handler/subnet_test.go index 8cd2779..a2a5230 100644 --- a/handler/subnet_test.go +++ b/handler/subnet_test.go @@ -1,49 +1,48 @@ package handler import ( - "testing" - "github.com/miekg/dns" - "github.com/coredns/coredns/request" - "net" - "log" - "arvancloud/redins/test" + "arvancloud/redins/test" + "github.com/coredns/coredns/request" + "github.com/miekg/dns" + "log" + "net" + "testing" ) func TestSubnet(t *testing.T) { - tc := test.Case { - Qname: "example.com.", Qtype: dns.TypeA, + tc := test.Case{ + Qname: "example.com.", Qtype: dns.TypeA, + } + sa := "192.168.1.2" + opt := &dns.OPT{ + Hdr: dns.RR_Header{Name: ".", Rrtype: dns.TypeOPT, Class: dns.ClassANY, Rdlength: 0, Ttl: 300}, + Option: []dns.EDNS0{ + &dns.EDNS0_SUBNET{ + Address: net.ParseIP(sa), + Code: dns.EDNS0SUBNET, + Family: 1, + SourceNetmask: 32, + SourceScope: 0, + }, + }, + } + r := tc.Msg() + r.Extra = append(r.Extra, opt) + if r.IsEdns0() == nil { + log.Printf("no edns\n") + t.Fail() + } + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} - } - sa := "192.168.1.2" - opt := &dns.OPT { - Hdr: dns.RR_Header{Name:".", Rrtype:dns.TypeOPT,Class:dns.ClassANY, Rdlength:0, Ttl: 300,}, - Option: []dns.EDNS0 { - &dns.EDNS0_SUBNET{ - Address:net.ParseIP(sa), - Code:dns.EDNS0SUBNET, - Family: 1, - SourceNetmask:32, - SourceScope:0, - }, - }, - } - r := tc.Msg() - r.Extra = append(r.Extra, opt) - if r.IsEdns0() == nil { - log.Printf("no edns\n") - t.Fail() - } - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - - subnet := GetSourceSubnet(&state) - if subnet != sa + "/32/0" { - log.Printf("subnet = %s should be %s\n", subnet, sa) - t.Fail() - } - address := GetSourceIp(&state) - if address.String() != sa { - log.Printf("address = %s should be %s\n", address.String(), sa) - t.Fail() - } -} \ No newline at end of file + subnet := GetSourceSubnet(&state) + if subnet != sa+"/32/0" { + log.Printf("subnet = %s should be %s\n", subnet, sa) + t.Fail() + } + address := GetSourceIp(&state) + if address.String() != sa { + log.Printf("address = %s should be %s\n", address.String(), sa) + t.Fail() + } +} diff --git a/handler/upstream.go b/handler/upstream.go index 79fa129..664b860 100644 --- a/handler/upstream.go +++ b/handler/upstream.go @@ -1,86 +1,86 @@ package handler import ( - "time" - "strconv" + "strconv" + "time" - "github.com/miekg/dns" - "github.com/patrickmn/go-cache" - "github.com/hawell/logger" + "github.com/hawell/logger" + "github.com/miekg/dns" + "github.com/patrickmn/go-cache" ) type UpstreamConnection struct { - client *dns.Client - connectionStr string + client *dns.Client + connectionStr string } type Upstream struct { - connections []*UpstreamConnection - cache *cache.Cache + connections []*UpstreamConnection + cache *cache.Cache } type UpstreamConfig struct { - Ip string `json:"ip,omitempty"` - Port int `json:"port,omitempty"` - Protocol string `json:"protocol,omitempty"` - Timeout int `json:"timeout,omitempty"` + Ip string `json:"ip,omitempty"` + Port int `json:"port,omitempty"` + Protocol string `json:"protocol,omitempty"` + Timeout int `json:"timeout,omitempty"` } func NewUpstream(config []UpstreamConfig) *Upstream { - u := &Upstream{} + u := &Upstream{} - u.cache = cache.New(time.Second * time.Duration(defaultCacheTtl), time.Second * time.Duration(defaultCacheTtl) * 10) - for _, upstreamConfig := range config { - client := &dns.Client { - Net: upstreamConfig.Protocol, - Timeout: time.Duration(upstreamConfig.Timeout) * time.Millisecond, - } - connectionStr := upstreamConfig.Ip + ":" + strconv.Itoa(upstreamConfig.Port) - connection := &UpstreamConnection { - client: client, - connectionStr: connectionStr, - } - u.connections = append(u.connections, connection) - } + u.cache = cache.New(time.Second*time.Duration(defaultCacheTtl), time.Second*time.Duration(defaultCacheTtl)*10) + for _, upstreamConfig := range config { + client := &dns.Client{ + Net: upstreamConfig.Protocol, + Timeout: time.Duration(upstreamConfig.Timeout) * time.Millisecond, + } + connectionStr := upstreamConfig.Ip + ":" + strconv.Itoa(upstreamConfig.Port) + connection := &UpstreamConnection{ + client: client, + connectionStr: connectionStr, + } + u.connections = append(u.connections, connection) + } - return u + return u } func (u *Upstream) Query(location string, qtype uint16) ([]dns.RR, int) { - key := location + ":" + strconv.Itoa(int(qtype)) - res, exp, found := u.cache.GetWithExpiration(key) - if found { - records := res.([]dns.RR) - for _, record := range records { - record.Header().Ttl = uint32(time.Until(exp).Seconds()) - } - return records, dns.RcodeSuccess - } - m := new(dns.Msg) - m.SetQuestion(location, qtype) - for _, c := range u.connections { - r, _, err := c.client.Exchange(m, c.connectionStr) - if err != nil { - logger.Default.Errorf("failed to retrieve record %s from upstream %s : %s", location, c.connectionStr, err) - continue - } - if len(r.Answer) == 0 { - return []dns.RR{}, dns.RcodeNameError - } - minTtl := r.Answer[0].Header().Ttl - for _, record := range r.Answer { - if record.Header().Ttl < minTtl { - minTtl = record.Header().Ttl - } - } - u.cache.Set(key, r.Answer, time.Duration(minTtl) * time.Second) - u.connections[0], c = c, u.connections[0] + key := location + ":" + strconv.Itoa(int(qtype)) + res, exp, found := u.cache.GetWithExpiration(key) + if found { + records := res.([]dns.RR) + for _, record := range records { + record.Header().Ttl = uint32(time.Until(exp).Seconds()) + } + return records, dns.RcodeSuccess + } + m := new(dns.Msg) + m.SetQuestion(location, qtype) + for _, c := range u.connections { + r, _, err := c.client.Exchange(m, c.connectionStr) + if err != nil { + logger.Default.Errorf("failed to retrieve record %s from upstream %s : %s", location, c.connectionStr, err) + continue + } + if len(r.Answer) == 0 { + return []dns.RR{}, dns.RcodeNameError + } + minTtl := r.Answer[0].Header().Ttl + for _, record := range r.Answer { + if record.Header().Ttl < minTtl { + minTtl = record.Header().Ttl + } + } + u.cache.Set(key, r.Answer, time.Duration(minTtl)*time.Second) + u.connections[0], c = c, u.connections[0] - return r.Answer, dns.RcodeSuccess - } - return []dns.RR{}, dns.RcodeServerFailure + return r.Answer, dns.RcodeSuccess + } + return []dns.RR{}, dns.RcodeServerFailure } const ( - defaultCacheTtl = 600 -) \ No newline at end of file + defaultCacheTtl = 600 +) diff --git a/handler/upstream_test.go b/handler/upstream_test.go index c2de835..cf2ebe3 100644 --- a/handler/upstream_test.go +++ b/handler/upstream_test.go @@ -1,84 +1,84 @@ package handler import ( - "testing" - "log" + "log" + "testing" - "github.com/miekg/dns" - "github.com/coredns/coredns/request" - "github.com/hawell/uperdis" - "github.com/hawell/logger" - "arvancloud/redins/test" + "arvancloud/redins/test" + "github.com/coredns/coredns/request" + "github.com/hawell/logger" + "github.com/hawell/uperdis" + "github.com/miekg/dns" ) -var upstreamTestConfig = HandlerConfig { - MaxTtl: 300, - CacheTimeout: 60, - ZoneReload: 600, - UpstreamFallback: true, - Redis: uperdis.RedisConfig { - Ip: "redis", - Port: 6379, - DB: 0, - Password: "", - Prefix: "test_", - Suffix: "_test", - ConnectTimeout: 0, - ReadTimeout: 0, - }, - Log: logger.LogConfig { - Enable: false, - }, - Upstream: []UpstreamConfig { - { - Ip: "1.1.1.1", - Port: 53, - Protocol: "udp", - Timeout: 1000, - }, - }, - GeoIp: GeoIpConfig { - Enable: true, - CountryDB: "../geoCity.mmdb", - }, +var upstreamTestConfig = HandlerConfig{ + MaxTtl: 300, + CacheTimeout: 60, + ZoneReload: 600, + UpstreamFallback: true, + Redis: uperdis.RedisConfig{ + Ip: "redis", + Port: 6379, + DB: 0, + Password: "", + Prefix: "test_", + Suffix: "_test", + ConnectTimeout: 0, + ReadTimeout: 0, + }, + Log: logger.LogConfig{ + Enable: false, + }, + Upstream: []UpstreamConfig{ + { + Ip: "1.1.1.1", + Port: 53, + Protocol: "udp", + Timeout: 1000, + }, + }, + GeoIp: GeoIpConfig{ + Enable: true, + CountryDB: "../geoCity.mmdb", + }, } func TestUpstream(t *testing.T) { - logger.Default = logger.NewLogger(&logger.LogConfig{}) - u := NewUpstream(upstreamTestConfig.Upstream) - rs, res := u.Query("google.com.", dns.TypeAAAA) - if len(rs) == 0 || res != 0 { - log.Printf("[ERROR] AAAA failed") - t.Fail() - } - rs, res = u.Query("google.com.", dns.TypeA) - if len(rs) == 0 || res != 0 { - log.Printf("[ERROR] A failed") - t.Fail() - } - rs, res = u.Query("google.com.", dns.TypeTXT) - if len(rs) == 0 || res != 0 { - log.Printf("[ERROR] TXT failed") - t.Fail() - } + logger.Default = logger.NewLogger(&logger.LogConfig{}) + u := NewUpstream(upstreamTestConfig.Upstream) + rs, res := u.Query("google.com.", dns.TypeAAAA) + if len(rs) == 0 || res != 0 { + log.Printf("[ERROR] AAAA failed") + t.Fail() + } + rs, res = u.Query("google.com.", dns.TypeA) + if len(rs) == 0 || res != 0 { + log.Printf("[ERROR] A failed") + t.Fail() + } + rs, res = u.Query("google.com.", dns.TypeTXT) + if len(rs) == 0 || res != 0 { + log.Printf("[ERROR] TXT failed") + t.Fail() + } } func TestFallback(t *testing.T) { - tc := test.Case{ - Qname: "google.com.", Qtype: dns.TypeAAAA, - } - logger.Default = logger.NewLogger(&logger.LogConfig{}) + tc := test.Case{ + Qname: "google.com.", Qtype: dns.TypeAAAA, + } + logger.Default = logger.NewLogger(&logger.LogConfig{}) - h := NewHandler(&upstreamTestConfig) + h := NewHandler(&upstreamTestConfig) - r := tc.Msg() - w := test.NewRecorder(&test.ResponseWriter{}) - state := request.Request{W: w, Req: r} - h.HandleRequest(&state) + r := tc.Msg() + w := test.NewRecorder(&test.ResponseWriter{}) + state := request.Request{W: w, Req: r} + h.HandleRequest(&state) - resp := w.Msg + resp := w.Msg - if resp.Rcode != dns.RcodeSuccess { - t.Fail() - } + if resp.Rcode != dns.RcodeSuccess { + t.Fail() + } } diff --git a/perf/bulk.go b/perf/bulk.go index 6cf7535..6f1b05f 100644 --- a/perf/bulk.go +++ b/perf/bulk.go @@ -1,57 +1,57 @@ package main import ( - "os" - "fmt" - "bufio" - "github.com/miekg/dns" - "time" + "bufio" + "fmt" + "github.com/miekg/dns" + "os" + "time" ) func main() { - client := &dns.Client { - Net: "udp", - Timeout: time.Millisecond * 100, - } + client := &dns.Client{ + Net: "udp", + Timeout: time.Millisecond * 100, + } - fq, err := os.Open("query.txt") - if err != nil { - fmt.Errorf("cannot open query.txt") - return - } - defer fq.Close() - rq := bufio.NewReader(fq) - var duration time.Duration - for { - line, err := rq.ReadString('\n') - if err != nil { - break - } - var queryAddr, queryResult string - // fmt.Println("line = ", line) - fmt.Sscan(line, &queryAddr, &queryResult) - // fmt.Println("addr = ", queryAddr, "result = ", queryResult) - m := new(dns.Msg) - m.SetQuestion(queryAddr, dns.TypeA) - r, rtt, err := client.Exchange(m, "localhost:1053") - if err != nil { - fmt.Println("error: ", err) - break - } - if r.Rcode != dns.RcodeSuccess { - fmt.Println("bad response : ", r.Rcode) - break - } - if len(r.Answer) == 0 { - fmt.Println("empty response") - break - } - a := r.Answer[0].(*dns.A) - if a.A.String() != queryResult { - fmt.Printf("error: incorrect answer : expected %s got %s", queryResult, a.A.String()) - break - } - duration += rtt - } - fmt.Println(duration) -} \ No newline at end of file + fq, err := os.Open("query.txt") + if err != nil { + fmt.Errorf("cannot open query.txt") + return + } + defer fq.Close() + rq := bufio.NewReader(fq) + var duration time.Duration + for { + line, err := rq.ReadString('\n') + if err != nil { + break + } + var queryAddr, queryResult string + // fmt.Println("line = ", line) + fmt.Sscan(line, &queryAddr, &queryResult) + // fmt.Println("addr = ", queryAddr, "result = ", queryResult) + m := new(dns.Msg) + m.SetQuestion(queryAddr, dns.TypeA) + r, rtt, err := client.Exchange(m, "localhost:1053") + if err != nil { + fmt.Println("error: ", err) + break + } + if r.Rcode != dns.RcodeSuccess { + fmt.Println("bad response : ", r.Rcode) + break + } + if len(r.Answer) == 0 { + fmt.Println("empty response") + break + } + a := r.Answer[0].(*dns.A) + if a.A.String() != queryResult { + fmt.Printf("error: incorrect answer : expected %s got %s", queryResult, a.A.String()) + break + } + duration += rtt + } + fmt.Println(duration) +} diff --git a/perf/gen.go b/perf/gen.go index e263764..c94985a 100644 --- a/perf/gen.go +++ b/perf/gen.go @@ -1,11 +1,11 @@ package main import ( + "bufio" "flag" "fmt" "math/rand" "os" - "bufio" "time" "github.com/gomodule/redigo/redis" @@ -67,7 +67,7 @@ func main() { defer fq.Close() wq := bufio.NewWriter(fq) - for i := 0; i<*zonesPtr; i++ { + for i := 0; i < *zonesPtr; i++ { zoneName := RandomString(15) + suffix fz, err := os.Create(zoneName) if err != nil { @@ -87,13 +87,13 @@ func main() { "900 ; ncache\n" + ")\n" + "@ NS ns1." + zoneName + "\n" + - "ns1 A 1.2.3.4\n\n"); + "ns1 A 1.2.3.4\n\n") - for j := 0; j<*entriesPtr; j++ { + for j := 0; j < *entriesPtr; j++ { location := RandomString(15) ip := fmt.Sprintf("%d.%d.%d.%d", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)) - con.Do("HSET", "redins:zones:" + zoneName, location, `{"a":{"ttl":300, "records":[{"ip":"` + ip + `"}]}}`) + con.Do("HSET", "redins:zones:"+zoneName, location, `{"a":{"ttl":300, "records":[{"ip":"`+ip+`"}]}}`) wq.WriteString(location + "." + zoneName + " " + ip + "\n") @@ -102,4 +102,4 @@ func main() { wz.Flush() } wq.Flush() -} \ No newline at end of file +} diff --git a/redins.go b/redins.go index d37dfd5..af42702 100644 --- a/redins.go +++ b/redins.go @@ -1,214 +1,214 @@ package main import ( - "log" - "os" - "time" - "io/ioutil" - "encoding/json" - "os/signal" - "syscall" - - "github.com/miekg/dns" - "github.com/coredns/coredns/request" - "github.com/hawell/logger" - "github.com/hawell/uperdis" - "arvancloud/redins/handler" + "encoding/json" + "io/ioutil" + "log" + "os" + "os/signal" + "syscall" + "time" + + "arvancloud/redins/handler" + "github.com/coredns/coredns/request" + "github.com/hawell/logger" + "github.com/hawell/uperdis" + "github.com/miekg/dns" ) var ( - s []dns.Server - h *handler.DnsRequestHandler - l *handler.RateLimiter + s []dns.Server + h *handler.DnsRequestHandler + l *handler.RateLimiter ) func handleRequest(w dns.ResponseWriter, r *dns.Msg) { - // log.Printf("[DEBUG] handle request") - state := request.Request{W: w, Req: r} - - if l.CanHandle(state.IP()) { - h.HandleRequest(&state) - } else { - msg := new(dns.Msg) - msg.SetRcode(r, dns.RcodeRefused) - state.W.WriteMsg(msg) - } + // log.Printf("[DEBUG] handle request") + state := request.Request{W: w, Req: r} + + if l.CanHandle(state.IP()) { + h.HandleRequest(&state) + } else { + msg := new(dns.Msg) + msg.SetRcode(r, dns.RcodeRefused) + state.W.WriteMsg(msg) + } } type RedinsConfig struct { - Server []handler.ServerConfig `json:"server,omitempty"` - ErrorLog logger.LogConfig `json:"error_log,omitempty"` - Handler handler.HandlerConfig `json:"handler,omitempty"` - RateLimit handler.RateLimiterConfig `json:"ratelimit,omitempty"` + Server []handler.ServerConfig `json:"server,omitempty"` + ErrorLog logger.LogConfig `json:"error_log,omitempty"` + Handler handler.HandlerConfig `json:"handler,omitempty"` + RateLimit handler.RateLimiterConfig `json:"ratelimit,omitempty"` } func LoadConfig(path string) *RedinsConfig { - config := &RedinsConfig { - Server: []handler.ServerConfig { - { - Ip: "127.0.0.1", - Port: 1053, - Protocol: "udp", - }, - }, - Handler: handler.HandlerConfig { - Upstream: []handler.UpstreamConfig { - { - Ip: "1.1.1.1", - Port: 53, - Protocol: "udp", - Timeout: 400, - }, - }, - GeoIp: handler.GeoIpConfig { - Enable: false, - CountryDB: "geoCity.mmdb", - ASNDB: "geoIsp.mmdb", - }, - HealthCheck: handler.HealthcheckConfig { - Enable: false, - MaxRequests: 10, - MaxPendingRequests: 100, - UpdateInterval: 600, - CheckInterval: 600, - RedisStatusServer: uperdis.RedisConfig { - Ip: "127.0.0.1", - Port: 6379, - DB: 0, - Password: "", - Prefix: "redins_", - Suffix: "_redins", - ConnectTimeout: 0, - ReadTimeout: 0, - ActiveConnections: 10, - }, - Log: logger.LogConfig { - Enable: true, - Target: "file", - Level: "info", - Path: "/tmp/healthcheck.log", - Format: "json", - TimeFormat: time.RFC3339, - Sentry: logger.SentryConfig { - Enable: false, - }, - Syslog: logger.SyslogConfig { - Enable: false, - }, - }, - }, - MaxTtl: 3600, - CacheTimeout: 60, - ZoneReload: 600, - LogSourceLocation: false, - UpstreamFallback: false, - Redis: uperdis.RedisConfig { - Ip: "127.0.0.1", - Port: 6379, - DB: 0, - Password: "", - Prefix: "redins_", - Suffix: "_redins", - ConnectTimeout: 0, - ReadTimeout: 0, - ActiveConnections: 10, - }, - Log: logger.LogConfig { - Enable: true, - Target: "file", - Level: "info", - Path: "/tmp/redins.log", - Format: "json", - TimeFormat: time.RFC3339, - Sentry: logger.SentryConfig { - Enable: false, - }, - Syslog: logger.SyslogConfig { - Enable: false, - }, - }, - }, - ErrorLog: logger.LogConfig { - Enable: true, - Target: "stdout", - Level: "info", - Format: "text", - TimeFormat: time.RFC3339, - Sentry: logger.SentryConfig { - Enable: false, - }, - Syslog: logger.SyslogConfig { - Enable: false, - }, - }, - RateLimit: handler.RateLimiterConfig { - Enable: false, - Rate: 60, - Burst: 10, - BlackList: []string{}, - WhiteList: []string{}, - }, - } - raw, err := ioutil.ReadFile(path) - if err != nil { - log.Printf("[ERROR] cannot load file %s : %s", path, err) - log.Printf("[INFO] loading default config") - return config - } - err = json.Unmarshal(raw, config) - if err != nil { - log.Printf("[ERROR] cannot load json file") - log.Printf("[INFO] loading default config") - return config - } - return config + config := &RedinsConfig{ + Server: []handler.ServerConfig{ + { + Ip: "127.0.0.1", + Port: 1053, + Protocol: "udp", + }, + }, + Handler: handler.HandlerConfig{ + Upstream: []handler.UpstreamConfig{ + { + Ip: "1.1.1.1", + Port: 53, + Protocol: "udp", + Timeout: 400, + }, + }, + GeoIp: handler.GeoIpConfig{ + Enable: false, + CountryDB: "geoCity.mmdb", + ASNDB: "geoIsp.mmdb", + }, + HealthCheck: handler.HealthcheckConfig{ + Enable: false, + MaxRequests: 10, + MaxPendingRequests: 100, + UpdateInterval: 600, + CheckInterval: 600, + RedisStatusServer: uperdis.RedisConfig{ + Ip: "127.0.0.1", + Port: 6379, + DB: 0, + Password: "", + Prefix: "redins_", + Suffix: "_redins", + ConnectTimeout: 0, + ReadTimeout: 0, + ActiveConnections: 10, + }, + Log: logger.LogConfig{ + Enable: true, + Target: "file", + Level: "info", + Path: "/tmp/healthcheck.log", + Format: "json", + TimeFormat: time.RFC3339, + Sentry: logger.SentryConfig{ + Enable: false, + }, + Syslog: logger.SyslogConfig{ + Enable: false, + }, + }, + }, + MaxTtl: 3600, + CacheTimeout: 60, + ZoneReload: 600, + LogSourceLocation: false, + UpstreamFallback: false, + Redis: uperdis.RedisConfig{ + Ip: "127.0.0.1", + Port: 6379, + DB: 0, + Password: "", + Prefix: "redins_", + Suffix: "_redins", + ConnectTimeout: 0, + ReadTimeout: 0, + ActiveConnections: 10, + }, + Log: logger.LogConfig{ + Enable: true, + Target: "file", + Level: "info", + Path: "/tmp/redins.log", + Format: "json", + TimeFormat: time.RFC3339, + Sentry: logger.SentryConfig{ + Enable: false, + }, + Syslog: logger.SyslogConfig{ + Enable: false, + }, + }, + }, + ErrorLog: logger.LogConfig{ + Enable: true, + Target: "stdout", + Level: "info", + Format: "text", + TimeFormat: time.RFC3339, + Sentry: logger.SentryConfig{ + Enable: false, + }, + Syslog: logger.SyslogConfig{ + Enable: false, + }, + }, + RateLimit: handler.RateLimiterConfig{ + Enable: false, + Rate: 60, + Burst: 10, + BlackList: []string{}, + WhiteList: []string{}, + }, + } + raw, err := ioutil.ReadFile(path) + if err != nil { + log.Printf("[ERROR] cannot load file %s : %s", path, err) + log.Printf("[INFO] loading default config") + return config + } + err = json.Unmarshal(raw, config) + if err != nil { + log.Printf("[ERROR] cannot load json file") + log.Printf("[INFO] loading default config") + return config + } + return config } func Start() { - configFile := "config.json" - if len(os.Args) > 1 { - configFile = os.Args[1] - } - cfg := LoadConfig(configFile) + configFile := "config.json" + if len(os.Args) > 1 { + configFile = os.Args[1] + } + cfg := LoadConfig(configFile) - logger.Default = logger.NewLogger(&cfg.ErrorLog) + logger.Default = logger.NewLogger(&cfg.ErrorLog) - s = handler.NewServer(cfg.Server) + s = handler.NewServer(cfg.Server) - h = handler.NewHandler(&cfg.Handler) + h = handler.NewHandler(&cfg.Handler) - l = handler.NewRateLimiter(&cfg.RateLimit) + l = handler.NewRateLimiter(&cfg.RateLimit) - dns.HandleFunc(".", handleRequest) + dns.HandleFunc(".", handleRequest) - for i := range s { - go s[i].ListenAndServe() - time.Sleep(1 * time.Second) - } + for i := range s { + go s[i].ListenAndServe() + time.Sleep(1 * time.Second) + } } func Stop() { - for i := range s { - s[i].Shutdown() - } - h.ShutDown() + for i := range s { + s[i].Shutdown() + } + h.ShutDown() } func main() { - Start() - - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGHUP) - - for sig := range c { - switch sig { - case syscall.SIGINT: - Stop() - return - case syscall.SIGHUP: - Stop() - Start() - } - } + Start() + + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGHUP) + + for sig := range c { + switch sig { + case syscall.SIGINT: + Stop() + return + case syscall.SIGHUP: + Stop() + Start() + } + } } -- GitLab