func TestParseMXNegativeTests(t *testing.T) { /* helper functions */ dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeMX] = []string{ "mail.matching.com. 0 IN MX 5 mail.matching.com.", "mail.matching.com. 0 IN MX 10 mail2.matching.com.", "mail.matching.com. 0 IN MX 15 mail3.matching.com.", } hosts[dns.TypeAAAA] = []string{ "mail.matching.com. 0 IN AAAA 2001:4860:1:2001::80", } hosts[dns.TypeA] = []string{ "mail.matching.com. 0 IN A 172.18.0.2", "mail2.matching.com. 0 IN A 172.20.20.20", "mail3.matching.com. 0 IN A 172.100.0.1", } mxMatchingCom := generateZone(hosts) dns.HandleFunc("matching.com.", mxMatchingCom) defer dns.HandleRemove("matching.com.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr /* ***************** */ ip := net.IP{127, 0, 0, 1} domain := "matching.com" p := NewParser(domain, domain, ip, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tMX, qPlus, "matching.com"}, Pass, false}, TokenTestCase{&Token{tMX, qPlus, ""}, Pass, false}, //TokenTestCase{&Token{tMX, qPlus, "google.com"}, Pass, false}, TokenTestCase{&Token{tMX, qPlus, "idontexist"}, Pass, false}, TokenTestCase{&Token{tMX, qMinus, "matching.com"}, Fail, false}, } var match bool var result SPFResult for _, testcase := range testcases { match, result = p.parseMX(testcase.Input) if testcase.Match != match { t.Error("Match mismatch, expected ", testcase.Match, " got ", match) } if testcase.Result != result { t.Error("Result mismatch, expected ", testcase.Result, " got ", result) } } }
func TestHandleExplanation(t *testing.T) { const domain = "matching.com" // static.exp.matching.com. IN TXT "Invalid SPF record" // ip.exp.matching.com. IN TXT "%{i} is not one of %{d}'s designated mail servers." // redirect.exp.matching.com. IN TXT "See http://%{d}/why.html?s=%{s}&i=%{i}" dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "static.exp.matching.com. 0 IN TXT \"Invalid SPF record\"", } dns.HandleFunc("static.exp.matching.com.", generateZone(hosts)) defer dns.HandleRemove("static.exp.matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "ip.exp.matching.com. 0 in TXT \"%{i} is not one of %{d}'s designated mail servers.\"", } dns.HandleFunc("ip.exp.matching.com.", generateZone(hosts)) defer dns.HandleRemove("ip.exp.matching.com.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr ExpTestCases := []ExpTestCase{ ExpTestCase{"v=spf1 -all exp=static.exp.matching.com", "Invalid SPF record"}, ExpTestCase{"v=spf1 -all exp=ip.exp.matching.com", "127.0.0.1 is not one of matching.com's designated mail servers."}, // TODO(zaccone): Cover this testcase //ExpTestCase{"v=spf1 -all exp=redirect.exp.matching.com", //ExpT"See http://matching.com/why.html?s=&i="}, } for _, testcase := range ExpTestCases { p := NewParser(domain, domain, ip, testcase.Query) _, exp, err := p.Parse() if err != nil { t.Error("Unexpected error while parsing: ", err) } else if exp != testcase.Explanation { t.Errorf("Explanation mismatch, expected %s, got %s\n", testcase.Explanation, exp) } } }
// TestParseExists executes tests for exists term. func TestParseExists(t *testing.T) { dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeA] = []string{ "postitive.matching.net. 0 IN A 172.20.20.20", "postitive.matching.net. 0 IN A 172.18.0.1", "postitive.matching.net. 0 IN A 172.18.0.2", } dns.HandleFunc("positive.matching.net.", generateZone(hosts)) defer dns.HandleRemove("positive.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "postitive.matching.com. 0 IN A 172.20.20.20", "postitive.matching.com. 0 IN A 172.18.0.1", "postitive.matching.com. 0 IN A 172.18.0.2", } dns.HandleFunc("positive.matching.com.", generateZone(hosts)) defer dns.HandleRemove("positive.matching.com.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr domain := "matching.com" p := NewParser(domain, domain, ip, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tExists, qPlus, "positive.matching.net"}, Pass, true}, TokenTestCase{&Token{tExists, qMinus, "positive.matching.net"}, Fail, true}, TokenTestCase{&Token{tExists, qMinus, "idontexist.matching.net"}, Fail, false}, TokenTestCase{&Token{tExists, qMinus, "idontexist.%{d}"}, Fail, false}, TokenTestCase{&Token{tExists, qTilde, "positive.%{d}"}, Softfail, true}, TokenTestCase{&Token{tExists, qTilde, "positive.%{d}"}, Softfail, true}, TokenTestCase{&Token{tExists, qTilde, ""}, Permerror, true}, TokenTestCase{&Token{tExists, qTilde, "invalidsyntax%{}"}, Permerror, true}, } for _, testcase := range testcases { match, result := p.parseExists(testcase.Input) if testcase.Match != match { t.Error("Match mismatch, expected ", testcase.Match, " got ", match) } if testcase.Result != result { t.Error("Result mismatch, expected ", testcase.Result, " got ", result) } } }
func TestTransferIn(t *testing.T) { soa := soa{250} dns.HandleFunc(testZone, soa.Handler) defer dns.HandleRemove(testZone) s, addrstr, err := test.TCPServer("127.0.0.1:0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() z := new(Zone) z.Expired = new(bool) z.origin = testZone z.TransferFrom = []string{addrstr} err = z.TransferIn() if err != nil { t.Fatalf("unable to run TransferIn: %v", err) } if z.Apex.SOA.String() != fmt.Sprintf("%s 3600 IN SOA bla. bla. 250 0 0 0 0", testZone) { t.Fatalf("unknown SOA transferred") } }
func TestShouldTransfer(t *testing.T) { soa := soa{250} dns.HandleFunc(testZone, soa.Handler) defer dns.HandleRemove(testZone) s, addrstr, err := test.TCPServer("127.0.0.1:0") if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() z := new(Zone) z.origin = testZone z.TransferFrom = []string{addrstr} // Serial smaller z.Apex.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial-1)) should, err := z.shouldTransfer() if err != nil { t.Fatalf("unable to run shouldTransfer: %v", err) } if !should { t.Fatalf("shouldTransfer should return true for serial: %q", soa.serial-1) } // Serial equal z.Apex.SOA = test.SOA(fmt.Sprintf("%s IN SOA bla. bla. %d 0 0 0 0 ", testZone, soa.serial)) should, err = z.shouldTransfer() if err != nil { t.Fatalf("unable to run shouldTransfer: %v", err) } if should { t.Fatalf("shouldTransfer should return false for serial: %d", soa.serial) } }
func updateDns(ethereum *eth.Ethereum) { stateObject := ethereum.StateManager().CurrentState().GetStateObject(dnsreg) if stateObject != nil { ethutil.Config.Log.Debugln("Updating DNS") stateObject.State().EachStorage(func(name string, value *ethutil.Value) { val := value.Bytes()[1:] name = sanitizeString(name) + ".eth." dns.HandleRemove(name) zoneString := fmt.Sprintf("%s 2044 IN A %s", name, val) zone := NewRR(zoneString) if zone != nil { ethutil.Config.Log.Debugln("[DNS] Updated zone:", zone) dns.HandleFunc(name, func(w dns.ResponseWriter, r *dns.Msg) { switch r.Question[0].Qtype { case dns.TypeA: m := new(dns.Msg) m.SetReply(r) m.Answer = []dns.RR{zone} m.RecursionAvailable = true w.WriteMsg(m) default: ethutil.Config.Log.Debugln("[DNS] Type not supported yet") } }) } else { ethutil.Config.Log.Debugln("Invalid zone", zoneString) } }) } }
func main() { flag.Parse() *memSize *= 1024 * 1024 if *cacheSize < 2 { log.Fatal("Cache size too small") } parseLocalRRSFile(*localRRSFile) cache, _ = lru.NewARC(*cacheSize) upstreamServers, _ = parseUpstreamServers(*upstreamServersStr) sipHashKey = SipHashKey{k1: randUint64(), k2: randUint64()} resolverRing = make(chan QueuedRequest, *maxClients) globalTimeout = time.Duration((*maxRTT) * 3.0 * 1E9) udpClient = dns.Client{Net: "udp", DialTimeout: globalTimeout, ReadTimeout: globalTimeout, WriteTimeout: globalTimeout, SingleInflight: true} tcpClient = dns.Client{Net: "tcp", DialTimeout: globalTimeout, ReadTimeout: globalTimeout, WriteTimeout: globalTimeout, SingleInflight: true} probeUpstreamServers(true) upstreamServers.lock.Lock() log.Printf("Live upstream servers: %v\n", upstreamServers.live) upstreamServers.lock.Unlock() for i := uint(0); i < *maxClients; i++ { go func() { resolverThread() }() } dns.HandleFunc(".", route) defer dns.HandleRemove(".") udpServer := &dns.Server{Addr: *address, Net: "udp"} defer udpServer.Shutdown() udpAddr, err := net.ResolveUDPAddr(udpServer.Net, udpServer.Addr) if err != nil { log.Fatal(err) } udpPacketConn, err := net.ListenUDP(udpServer.Net, udpAddr) if err != nil { log.Fatal(err) } udpServer.PacketConn = udpPacketConn udpPacketConn.SetReadBuffer(MaxUDPBufferSize) udpPacketConn.SetWriteBuffer(MaxUDPBufferSize) tcpServer := &dns.Server{Addr: *address, Net: "tcp"} defer tcpServer.Shutdown() tcpAddr, err := net.ResolveTCPAddr(tcpServer.Net, tcpServer.Addr) if err != nil { log.Fatal(err) } tcpListener, err := net.ListenTCP(tcpServer.Net, tcpAddr) if err != nil { log.Fatal(err) } tcpServer.Listener = tcpListener go func() { log.Fatal(udpServer.ActivateAndServe()) }() go func() { log.Fatal(tcpServer.ActivateAndServe()) }() fmt.Println("Ready") vacuumThread() }
func TestRFC2136ValidUpdatePacket(t *testing.T) { dns.HandleFunc(rfc2136TestZone, serverHandlerPassBackRequest) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() rr := new(dns.TXT) rr.Hdr = dns.RR_Header{ Name: rfc2136TestFqdn, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: uint32(rfc2136TestTTL), } rr.Txt = []string{rfc2136TestValue} rrs := make([]dns.RR, 1) rrs[0] = rr m := new(dns.Msg) m.SetUpdate(dns.Fqdn(rfc2136TestZone)) m.Insert(rrs) expectstr := m.String() expect, err := m.Pack() if err != nil { t.Fatalf("Error packing expect msg: %v", err) } provider, err := NewDNSProviderRFC2136(addrstr, rfc2136TestZone, "", "") if err != nil { t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) } rcvMsg := <-reqChan rcvMsg.Id = m.Id actual, err := rcvMsg.Pack() if err != nil { t.Fatalf("Error packing actual msg: %v", err) } if !bytes.Equal(actual, expect) { tmp := new(dns.Msg) if err := tmp.Unpack(actual); err != nil { t.Fatalf("Error unpacking actual msg: %v", err) } t.Errorf("Expected msg:\n%s", expectstr) t.Errorf("Actual msg:\n%v", tmp) } }
func TestRFC2136ValidUpdatePacket(t *testing.T) { acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerPassBackRequest) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() txtRR, _ := dns.NewRR(fmt.Sprintf("%s %d IN TXT %s", rfc2136TestFqdn, rfc2136TestTTL, rfc2136TestValue)) rrs := []dns.RR{txtRR} m := new(dns.Msg) m.SetUpdate(rfc2136TestZone) m.RemoveRRset(rrs) m.Insert(rrs) expectstr := m.String() expect, err := m.Pack() if err != nil { t.Fatalf("Error packing expect msg: %v", err) } provider, err := NewDNSProvider(addrstr, "", "", "") if err != nil { t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", "1234d=="); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) } rcvMsg := <-reqChan rcvMsg.Id = m.Id actual, err := rcvMsg.Pack() if err != nil { t.Fatalf("Error packing actual msg: %v", err) } if !bytes.Equal(actual, expect) { tmp := new(dns.Msg) if err := tmp.Unpack(actual); err != nil { t.Fatalf("Error unpacking actual msg: %v", err) } t.Errorf("Expected msg:\n%s", expectstr) t.Errorf("Actual msg:\n%v", tmp) } }
func TestRFC2136TsigClient(t *testing.T) { dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", true) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() provider, err := NewDNSProviderRFC2136(addrstr, rfc2136TestZone, rfc2136TestTsigKey, rfc2136TestTsigSecret) if err != nil { t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) } }
func main() { flag.Parse() dns.HandleFunc(".", route) defer dns.HandleRemove(".") udpServer := &dns.Server{Addr: *address, Net: "udp"} defer udpServer.Shutdown() udpAddr, err := net.ResolveUDPAddr(udpServer.Net, udpServer.Addr) if err != nil { log.Fatal(err) } udpPacketConn, err := net.ListenUDP(udpServer.Net, udpAddr) if err != nil { log.Fatal(err) } udpServer.PacketConn = udpPacketConn fmt.Println("Ready") udpServer.ActivateAndServe() }
func TestRFC2136ServerSuccess(t *testing.T) { dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() provider, err := NewDNSProviderRFC2136(addrstr, rfc2136TestZone, "", "") if err != nil { t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) } if err := provider.CreateTXTRecord(rfc2136TestFqdn, rfc2136TestValue, rfc2136TestTTL); err != nil { t.Errorf("Expected CreateTXTRecord() to return no error but the error was -> %v", err) } }
func TestRFC2136ServerSuccess(t *testing.T) { acme.ClearFqdnCache() dns.HandleFunc(rfc2136TestZone, serverHandlerReturnSuccess) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() provider, err := NewDNSProvider(addrstr, "", "", "") if err != nil { t.Fatalf("Expected NewDNSProvider() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err != nil { t.Errorf("Expected Present() to return no error but the error was -> %v", err) } }
func TestRFC2136ServerError(t *testing.T) { dns.HandleFunc(rfc2136TestZone, serverHandlerReturnErr) defer dns.HandleRemove(rfc2136TestZone) server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() provider, err := NewDNSProviderRFC2136(addrstr, rfc2136TestZone, "", "") if err != nil { t.Fatalf("Expected NewDNSProviderRFC2136() to return no error but the error was -> %v", err) } if err := provider.Present(rfc2136TestDomain, "", rfc2136TestKeyAuth); err == nil { t.Errorf("Expected Present() to return an error but it did not.") } else if !strings.Contains(err.Error(), "NOTZONE") { t.Errorf("Expected Present() to return an error with the 'NOTZONE' rcode string but it did not.") } }
func TestRFC2136CanaryLocalTestServer(t *testing.T) { dns.HandleFunc("example.com.", serverHandlerHello) defer dns.HandleRemove("example.com.") server, addrstr, err := runLocalDNSTestServer("127.0.0.1:0", false) if err != nil { t.Fatalf("Failed to start test server: %v", err) } defer server.Shutdown() c := new(dns.Client) m := new(dns.Msg) m.SetQuestion("example.com.", dns.TypeTXT) r, _, err := c.Exchange(m, addrstr) if err != nil || len(r.Extra) == 0 { t.Fatalf("Failed to communicate with test server:", err) } txt := r.Extra[0].(*dns.TXT).Txt[0] if txt != "Hello world" { t.Error("Expected test server to return 'Hello world' but got: ", txt) } }
func TestTruncated(t *T) { dns.HandleFunc(testDomain, returnTruncated) defer dns.HandleRemove(testDomain) s, addr := startServer(t) defer s.Shutdown() r := new(dns.Msg) r.SetQuestion(testDomain, dns.TypeA) a := tryProxy(r, addr) assert.Nil(t, a) allowTruncated = true defer func() { allowTruncated = false }() r = new(dns.Msg) r.SetQuestion(testDomain, dns.TypeA) a = tryProxy(r, addr) assert.NotNil(t, a) assert.True(t, a.Truncated) }
func TestParseA(t *testing.T) { ip := net.IP{172, 18, 0, 2} domain := "matching.com" hosts := make(map[uint16][]string) hosts[dns.TypeA] = []string{ "positive.matching.com. 0 IN A 172.20.21.1", "positive.matching.com. 0 IN A 172.18.0.2", "positive.matching.com. 0 IN A 172.20.20.1", } hosts[dns.TypeAAAA] = []string{ "positive.matching.com. 0 IN AAAA 2001:4860:0:2001::68", } positiveMatchingCom := generateZone(hosts) dns.HandleFunc("positive.matching.com.", positiveMatchingCom) defer dns.HandleRemove("positive.matching.com.") dns.HandleFunc("matching.com.", positiveMatchingCom) defer dns.HandleRemove("matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "negative.matching.com. 0 IN A 172.20.21.1", } negativeMatchingCom := generateZone(hosts) dns.HandleFunc("negative.matching.com.", negativeMatchingCom) defer dns.HandleRemove("negative.matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "range.matching.com. 0 IN A 172.18.0.2", } rangeMatchingCom := generateZone(hosts) dns.HandleFunc("range.matching.com.", rangeMatchingCom) defer dns.HandleRemove("range.matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "lb.matching.com. 0 IN A 172.18.0.2", } lbMatchingCom := generateZone(hosts) dns.HandleFunc("lb.matching.com.", lbMatchingCom) defer dns.HandleRemove("lb.matching.com.") dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr p := NewParser(domain, domain, ip, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tA, qPlus, "positive.matching.com"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/32"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "negative.matching.com"}, Pass, false}, TokenTestCase{&Token{tA, qPlus, "range.matching.com/16"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "range.matching.com/128"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "idontexist"}, Pass, false}, TokenTestCase{&Token{tA, qPlus, "#%$%^"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "lb.matching.com"}, Pass, true}, TokenTestCase{&Token{tA, qMinus, ""}, Fail, true}, TokenTestCase{&Token{tA, qTilde, ""}, Softfail, true}, // expect (Permerror, true) results as a result of syntax errors TokenTestCase{&Token{tA, qPlus, "range.matching.com/wrongmask"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "range.matching.com/129"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "range.matching.com/-1"}, Permerror, true}, // expect (Permerror, true) due to wrong netmasks. // It's a syntax error to specify a netmask over 32 bits for IPv4 addresses TokenTestCase{&Token{tA, qPlus, "negative.matching.com/128"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/128"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/128"}, Permerror, true}, // test dual-cidr syntax TokenTestCase{&Token{tA, qPlus, "positive.matching.com//128"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/32/"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/0/0"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/33/100"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/24/129"}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/128/32"}, Permerror, true}, } var match bool var result SPFResult for _, testcase := range testcases { match, result = p.parseA(testcase.Input) if testcase.Match != match { t.Errorf("Match mismatch, expected %s, got %s\n", testcase.Match, match) } if testcase.Result != result { t.Errorf("Result mismatch, expected %s, got %s\n", testcase.Result, result) } } }
func TestParseAIpv6(t *testing.T) { hosts := make(map[uint16][]string) hosts[dns.TypeA] = []string{ "positive.matching.com. 0 IN A 172.20.21.1", "positive.matching.com. 0 IN A 172.18.0.2", "positive.matching.com. 0 IN A 172.20.20.1", } hosts[dns.TypeAAAA] = []string{ "positive.matching.com. 0 IN AAAA 2001:4860:0:2001::68", } positiveMatchingCom := generateZone(hosts) dns.HandleFunc("positive.matching.com.", positiveMatchingCom) defer dns.HandleRemove("positive.matching.com.") dns.HandleFunc("matching.com.", positiveMatchingCom) defer dns.HandleRemove("matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "negative.matching.com. 0 IN A 172.20.21.1", } negativeMatchingCom := generateZone(hosts) dns.HandleFunc("negative.matching.com.", negativeMatchingCom) defer dns.HandleRemove("negative.matching.com.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr domain := "matching.com" p := NewParser(domain, domain, ipv6, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tA, qPlus, "positive.matching.com"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com//128"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com//64"}, Pass, true}, TokenTestCase{&Token{tA, qPlus, "negative.matching.com"}, Pass, false}, TokenTestCase{&Token{tA, qPlus, "negative.matching.com//64"}, Pass, false}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com// "}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/ "}, Permerror, true}, TokenTestCase{&Token{tA, qPlus, "positive.matching.com/ / "}, Permerror, true}, } var match bool var result SPFResult for _, testcase := range testcases { match, result = p.parseA(testcase.Input) if testcase.Match != match { t.Error("Match mismatch") } if testcase.Result != result { t.Error("Result mismatch") } } }
func TestParseMX(t *testing.T) { ips := []net.IP{ net.IP{172, 18, 0, 2}, net.IP{172, 20, 20, 20}, net.IP{172, 100, 0, 1}, net.ParseIP("2001:4860:1:2001::80"), } /* helper functions */ dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeMX] = []string{ "mail.matching.com. 0 IN MX 5 mail.matching.com.", "mail.matching.com. 0 IN MX 10 mail2.matching.com.", "mail.matching.com. 0 IN MX 15 mail3.matching.com.", } hosts[dns.TypeAAAA] = []string{ "mail.matching.com. 0 IN AAAA 2001:4860:1:2001::80", } hosts[dns.TypeA] = []string{ "mail.matching.com. 0 IN A 172.18.0.2", "mail2.matching.com. 0 IN A 172.20.20.20", "mail3.matching.com. 0 IN A 172.100.0.1", } mxMatchingCom := generateZone(hosts) dns.HandleFunc("matching.com.", mxMatchingCom) defer dns.HandleRemove("matching.com.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr /* ***************** */ domain := "matching.com" p := NewParser(domain, domain, net.IP{0, 0, 0, 0}, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tMX, qPlus, "matching.com"}, Pass, true}, TokenTestCase{&Token{tMX, qPlus, "matching.com/24"}, Pass, true}, TokenTestCase{&Token{tMX, qPlus, "matching.com/24/64"}, Pass, true}, TokenTestCase{&Token{tMX, qPlus, ""}, Pass, true}, TokenTestCase{&Token{tMX, qMinus, ""}, Fail, true}, TokenTestCase{&Token{tMX, qPlus, "idontexist"}, Pass, false}, // Mind that the domain is matching.NET and we expect Parser // to not match results. TokenTestCase{&Token{tMX, qPlus, "matching.net"}, Pass, false}, TokenTestCase{&Token{tMX, qPlus, "matching.net/24"}, Pass, false}, TokenTestCase{&Token{tMX, qPlus, "matching.net/24/64"}, Pass, false}, } var match bool var result SPFResult for _, testcase := range testcases { for _, ip := range ips { p.IP = ip match, result = p.parseMX(testcase.Input) if testcase.Match != match { t.Error("Match mismatch, expected ", testcase.Match, " got ", match) } if testcase.Result != result { t.Error("Result mismatch, expected ", testcase.Result, " got ", result) } } } }
func TestParseInclude(t *testing.T) { /* helper functions */ dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "_spf.matching.net. 0 IN TXT \"v=spf1 a:positive.matching.net -a:negative.matching.net ~mx -all\"", } hosts[dns.TypeMX] = []string{ "mail.matching.net. 0 IN MX 5 mail.matching.net.", "mail.matching.net. 0 IN MX 10 mail2.matching.net.", } hosts[dns.TypeA] = []string{ "postivie.matching.net. 0 IN A 172.100.100.1", "positive.matching.net. 0 IN A 173.18.0.2", "positive.matching.net. 0 IN A 173.20.20.1", "positive.matching.net. 0 IN A 173.20.21.1", "negative.matching.net. 0 IN A 172.18.100.100", "negative.matching.net. 0 IN A 172.18.100.101", "negative.matching.net. 0 IN A 172.18.100.102", "negative.matching.net. 0 IN A 172.18.100.103", "mail.matching.net. 0 IN A 173.18.0.2", "mail2.matching.net. 0 IN A 173.20.20.20", } includeMatchingCom := generateZone(hosts) dns.HandleFunc("matching.net.", includeMatchingCom) defer dns.HandleRemove("matching.net.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr /*******************************/ ips := []net.IP{ net.IP{172, 100, 100, 1}, net.IP{173, 20, 20, 1}, net.IP{173, 20, 21, 1}, } domain := "matching.net" p := NewParser(domain, domain, net.IP{0, 0, 0, 0}, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tInclude, qPlus, "_spf.matching.net"}, Pass, true}, TokenTestCase{&Token{tInclude, qMinus, "_spf.matching.net"}, Fail, true}, TokenTestCase{&Token{tInclude, qTilde, "_spf.matching.net"}, Softfail, true}, TokenTestCase{&Token{tInclude, qQuestionMark, "_spf.matching.net"}, Neutral, true}, } var match bool var result SPFResult for _, testcase := range testcases { for _, ip := range ips { p.IP = ip match, result = p.parseInclude(testcase.Input) if testcase.Match != match { t.Error("Match mismatch, expected ", testcase.Match, " got ", match) } if testcase.Result != result { t.Error("Result mismatch, expected ", testcase.Result, " got ", result) } } } }
// TestParseIncludeNegative shows correct behavior of include qualifier. func TestParseIncludeNegative(t *testing.T) { /* helper functions */ dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "_spf.matching.net. 0 IN TXT \"v=spf1 a:positive.matching.net -a:negative.matching.net ~mx -all\"", } hosts[dns.TypeMX] = []string{ "mail.matching.net. 0 IN MX 5 mail.matching.net.", "mail.matching.net. 0 IN MX 10 mail2.matching.net.", } hosts[dns.TypeA] = []string{ "postivie.matching.net. 0 IN A 172.100.100.1", "positive.matching.net. 0 IN A 173.18.0.2", "positive.matching.net. 0 IN A 173.20.20.1", "positive.matching.net. 0 IN A 173.20.21.1", "negative.matching.net. 0 IN A 172.18.100.100", "negative.matching.net. 0 IN A 172.18.100.101", "negative.matching.net. 0 IN A 172.18.100.102", "negative.matching.net. 0 IN A 172.18.100.103", "mail.matching.net. 0 IN A 173.18.0.2", "mail2.matching.net. 0 IN A 173.20.20.20", } includeMatchingCom := generateZone(hosts) dns.HandleFunc("matching.net.", includeMatchingCom) defer dns.HandleRemove("matching.net.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr /*******************************/ ips := []net.IP{ // completely random IP addres out of the net segment net.IP{80, 81, 82, 83}, // ip addresses from failing negative.matching.net A records net.IP{173, 18, 100, 100}, net.IP{173, 18, 100, 101}, net.IP{173, 18, 100, 102}, net.IP{173, 18, 100, 103}, } domain := "matching.net" p := NewParser(domain, domain, ip, stub) testcases := []TokenTestCase{ TokenTestCase{&Token{tInclude, qMinus, "_spf.matching.net"}, None, false}, TokenTestCase{&Token{tInclude, qPlus, "_spf.matching.net"}, None, false}, TokenTestCase{&Token{tInclude, qPlus, "_errspf.matching.net"}, None, false}, TokenTestCase{&Token{tInclude, qPlus, "nospf.matching.net"}, None, false}, TokenTestCase{&Token{tInclude, qPlus, "idontexist.matching.net"}, None, false}, // empty input qualifier results in Permerror withour recursive calls TokenTestCase{&Token{tInclude, qMinus, ""}, Permerror, true}, } var match bool var result SPFResult for _, testcase := range testcases { for _, ip := range ips { p.IP = ip match, result = p.parseInclude(testcase.Input) if testcase.Match != match { t.Error("Match mismatch, expected ", testcase.Match, " got ", match) } if testcase.Result != result { t.Error("Result mismatch, expected ", testcase.Result, " got ", result) } } } }
// DropZone discards a zone from the config. func (c *Config) DropZone(origin string) error { dns.HandleRemove(origin) delete(c.Zones, origin) return nil }
func (srv *Server) zonesReadDir(dirName string, zones Zones) error { dir, err := ioutil.ReadDir(dirName) if err != nil { log.Println("Could not read", dirName, ":", err) return err } seenZones := map[string]bool{} var parseErr error for _, file := range dir { fileName := file.Name() if !strings.HasSuffix(strings.ToLower(fileName), ".json") || strings.HasPrefix(path.Base(fileName), ".") || file.IsDir() { continue } zoneName := zoneNameFromFile(fileName) seenZones[zoneName] = true if _, ok := lastRead[zoneName]; !ok || file.ModTime().After(lastRead[zoneName].time) { modTime := file.ModTime() if ok { logPrintf("Reloading %s\n", fileName) lastRead[zoneName].time = modTime } else { logPrintf("Reading new file %s\n", fileName) lastRead[zoneName] = &ZoneReadRecord{time: modTime} } filename := path.Join(dirName, fileName) // Check the sha256 of the file has not changed. It's worth an explanation of // why there isn't a TOCTOU race here. Conceivably after checking whether the // SHA has changed, the contents then change again before we actually load // the JSON. This can occur in two situations: // // 1. The SHA has not changed when we read the file for the SHA, but then // changes before we process the JSON // // 2. The SHA has changed when we read the file for the SHA, but then changes // again before we process the JSON // // In circumstance (1) we won't reread the file the first time, but the subsequent // change should alter the mtime again, causing us to reread it. This reflects // the fact there were actually two changes. // // In circumstance (2) we have already reread the file once, and then when the // contents are changed the mtime changes again // // Provided files are replaced atomically, this should be OK. If files are not // replaced atomically we have other problems (e.g. partial reads). sha256 := sha256File(filename) if lastRead[zoneName].hash == sha256 { logPrintf("Skipping new file %s as hash is unchanged\n", filename) continue } config, err := readZoneFile(zoneName, filename) if config == nil || err != nil { parseErr = fmt.Errorf("Error reading zone '%s': %s", zoneName, err) log.Println(parseErr.Error()) continue } (lastRead[zoneName]).hash = sha256 srv.addHandler(zones, zoneName, config) } } for zoneName, zone := range zones { if zoneName == "pgeodns" { continue } if ok, _ := seenZones[zoneName]; ok { continue } log.Println("Removing zone", zone.Origin) delete(lastRead, zoneName) zone.Close() dns.HandleRemove(zoneName) delete(zones, zoneName) } return parseErr }
// TestParseRedirect tests whole parsing behavior with a special testing of // redirect modifier func TestHandleRedirect(t *testing.T) { dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeMX] = []string{ "matching.net. 0 IN MX 5 matching.net.", } hosts[dns.TypeA] = []string{ "matching.net. 0 IN A 173.18.0.2", "matching.net. 0 IN A 173.20.20.20", } dns.HandleFunc("matching.net.", generateZone(hosts)) defer dns.HandleRemove("matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "_spf.matching.net. 0 IN TXT \"v=spf1 a:positive.matching.net -a:negative.matching.net ~mx -all\"", } dns.HandleFunc("_spf.matching.net.", generateZone(hosts)) defer dns.HandleRemove("_spf.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "nospf.matching.net. 0 IN TXT \"no spf here\"", } dns.HandleFunc("nospf.matching.net.", generateZone(hosts)) defer dns.HandleRemove("nospf.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "positive.matching.net. 0 IN A 172.100.100.1", "positive.matching.net. 0 IN A 173.18.0.2", "positive.matching.net. 0 IN A 173.20.20.1", "positive.matching.net. 0 IN A 173.20.21.1", } dns.HandleFunc("positive.matching.net.", generateZone(hosts)) defer dns.HandleRemove("positive.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "negative.matching.net. 0 IN A 172.100.100.1", "negative.matching.net. 0 IN A 173.18.0.2", "negative.matching.net. 0 IN A 173.20.20.1", "negative.matching.net. 0 IN A 173.20.21.1", } dns.HandleFunc("negative.matching.net.", generateZone(hosts)) defer dns.HandleRemove("negative.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "redirect.matching.net. 0 IN TXT \"v=spf1 redirect=matching.com\"", } dns.HandleFunc("redirect.matching.net.", generateZone(hosts)) defer dns.HandleRemove("redirect.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "redirect.matching.com. 0 IN TXT \"v=spf1 redirect=redirect.matching.net\"", } dns.HandleFunc("redirect.matching.com.", generateZone(hosts)) defer dns.HandleRemove("redirect.matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "matching.com. 0 IN TXT \"v=spf1 mx:matching.com -all\"", } hosts[dns.TypeMX] = []string{ "matching.com 0 IN MX 5 mail.matching.com", } hosts[dns.TypeA] = []string{ "mail.matching.com. 0 IN A 172.18.0.2", } dns.HandleFunc("matching.com.", generateZone(hosts)) defer dns.HandleRemove("matching.com.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr const domain = "matching.com" ParseTestCases := []ParseTestCase{ ParseTestCase{"v=spf1 -all redirect=_spf.matching.net", net.IP{172, 100, 100, 1}, Fail}, ParseTestCase{"v=spf1 redirect=_spf.matching.net -all", net.IP{172, 100, 100, 1}, Fail}, ParseTestCase{"v=spf1 redirect=_spf.matching.net", net.IP{172, 100, 100, 1}, Pass}, ParseTestCase{"v=spf1 redirect=malformed", net.IP{172, 100, 100, 1}, Permerror}, ParseTestCase{"v=spf1 redirect=_spf.matching.net", net.IP{127, 0, 0, 1}, Fail}, ParseTestCase{"v=spf1 redirect=nospf.matching.net", net.IP{127, 0, 0, 1}, Permerror}, ParseTestCase{"v=spf1 +ip4:127.0.0.1 redirect=nospf.matching.net", net.IP{127, 0, 0, 1}, Pass}, ParseTestCase{"v=spf1 -ip4:127.0.0.1 redirect=nospf.matching.net", net.IP{127, 0, 0, 1}, Fail}, ParseTestCase{"v=spf1 +include:_spf.matching.net redirect=_spf.matching.net", net.IP{127, 0, 0, 1}, Fail}, ParseTestCase{"v=spf1 ~include:_spf.matching.net redirect=_spf.matching.net", net.IP{172, 100, 100, 1}, Softfail}, // Ensure recursive redirects work ParseTestCase{"v=spf1 redirect=redirect.matching.com", net.IP{172, 18, 0, 2}, Pass}, ParseTestCase{"v=spf1 redirect=redirect.matching.com", net.IP{127, 0, 0, 1}, Fail}, } for _, testcase := range ParseTestCases { p := NewParser(domain, domain, testcase.IP, testcase.Query) result, _, err := p.Parse() if err != nil { t.Error("Unexpected error while parsing: ", err) } else if result != testcase.Result { t.Error("Expected ", testcase.Result, " got ", result, " instead.") } } }
// TestParse tests whole Parser.Parse() method func TestParse(t *testing.T) { dns.HandleFunc(".", rootZone) defer dns.HandleRemove(".") hosts := make(map[uint16][]string) hosts[dns.TypeMX] = []string{ "matching.com. 0 in MX 5 matching.com.", } hosts[dns.TypeA] = []string{ "matching.com. 0 IN A 172.20.20.20", "matching.com. 0 IN A 172.18.0.1", "matching.com. 0 IN A 172.18.0.2", } dns.HandleFunc("matching.com.", generateZone(hosts)) defer dns.HandleRemove("matching.com.") hosts = make(map[uint16][]string) hosts[dns.TypeMX] = []string{ "matching.net. 0 IN MX 5 matching.net.", } hosts[dns.TypeA] = []string{ "matching.net. 0 IN A 173.18.0.2", "matching.net. 0 IN A 173.20.20.20", } dns.HandleFunc("matching.net.", generateZone(hosts)) defer dns.HandleRemove("matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeTXT] = []string{ "_spf.matching.net. 0 IN TXT \"v=spf1 a:positive.matching.net -a:negative.matching.net ~mx -all\"", } dns.HandleFunc("_spf.matching.net.", generateZone(hosts)) defer dns.HandleRemove("_spf.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "postivie.matching.net. 0 IN A 172.100.100.1", "positive.matching.net. 0 IN A 173.18.0.2", "positive.matching.net. 0 IN A 173.20.20.1", "positive.matching.net. 0 IN A 173.20.21.1", } dns.HandleFunc("positive.matching.net.", generateZone(hosts)) defer dns.HandleRemove("positive.matching.net.") hosts = make(map[uint16][]string) hosts[dns.TypeA] = []string{ "negative.matching.net. 0 IN A 172.100.100.1", "negative.matching.net. 0 IN A 173.18.0.2", "negative.matching.net. 0 IN A 173.20.20.1", "negative.matching.net. 0 IN A 173.20.21.1", } dns.HandleFunc("negative.matching.net.", generateZone(hosts)) defer dns.HandleRemove("negative.matching.net.") s, addr, err := runLocalUDPServer(dnsServer) if err != nil { t.Fatalf("unable to run test server: %v", err) } defer s.Shutdown() Nameserver = addr domain := "matching.com" ParseTestCases := []ParseTestCase{ ParseTestCase{"v=spf1 -all", net.IP{127, 0, 0, 1}, Fail}, ParseTestCase{"v=spf1 mx -all", net.IP{172, 20, 20, 20}, Pass}, ParseTestCase{"v=spf1 ?mx -all", net.IP{172, 20, 20, 20}, Neutral}, ParseTestCase{"v=spf1 ~mx -all", net.IP{172, 20, 20, 20}, Softfail}, ParseTestCase{"v=spf1 a -mx -all", net.IP{172, 18, 0, 2}, Pass}, ParseTestCase{"v=spf1 -mx a -all", net.IP{172, 18, 0, 2}, Fail}, ParseTestCase{"v=spf1 +mx:matching.net -a -all", net.IP{173, 18, 0, 2}, Pass}, ParseTestCase{"v=spf1 +mx:matching.net -a -all", net.IP{172, 17, 0, 2}, Fail}, ParseTestCase{"v=spf1 a:matching.net -all", net.IP{173, 18, 0, 2}, Pass}, ParseTestCase{"v=spf1 +ip4:128.14.15.16 -all", net.IP{128, 14, 15, 16}, Pass}, ParseTestCase{"v=spf1 ~ip6:2001:56::2 -all", net.ParseIP("2001:56::2"), Softfail}, //Test will return SPFResult Fail as 172.20.20.1 does not result //positively for domain _spf.matching.net ParseTestCase{"v=spf1 ip4:127.0.0.1 +include:_spf.matching.net -all", net.IP{172, 20, 20, 1}, Fail}, // Test will return SPFResult Pass as 172.100.100.1 is withing // positive.matching.net A records, that are marked as +a: ParseTestCase{"v=spf1 ip4:127.0.0.1 +include:_spf.matching.net -all", net.IP{172, 100, 100, 1}, Pass}, // Test for syntax errors (include must have nonempty domain parameter) ParseTestCase{"v=spf1 ip4:127.0.0.1 +include -all", net.IP{172, 100, 100, 1}, Permerror}, ParseTestCase{"v=spf1 ip4:127.0.0.1 ?include -all", net.IP{172, 100, 100, 1}, Permerror}, // Include didn't match domain:yyz and underneath returned Temperror, // however parent Parse() exection path marked the result as not // matching and proceeded to next term ParseTestCase{"v=spf1 +include:yyz -all", net.IP{172, 100, 100, 1}, Fail}, ParseTestCase{"v=spf1 ?exists:lb.%{d} -all", ip, Neutral}, // domain is set to matching.com, macro >>d1r<< will reverse domain to // >>com.matching<< and trim to first part counting from right, // effectively returning >>matching<<, which we later concatenate with // the >>.com<< suffix. This test should give same matching result as // the test above, as effectively the host to be queried is identical. ParseTestCase{"v=spf1 ?exists:lb.%{d1r}.com -all", ip, Neutral}, } for _, testcase := range ParseTestCases { p := NewParser(domain, domain, testcase.IP, testcase.Query) result, _, err := p.Parse() if err != nil { t.Error("Unexpected error while parsing: ", err) } else if result != testcase.Result { t.Error("Expected ", testcase.Result, " got ", result, " instead.") } } }