Пример #1
0
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)
		}
	}
}
Пример #2
0
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)
		}
	}
}
Пример #3
0
// 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)
		}
	}
}
Пример #4
0
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")
	}
}
Пример #5
0
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)
	}
}
Пример #6
0
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)
			}
		})
	}
}
Пример #7
0
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()
}
Пример #8
0
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)
	}
}
Пример #9
0
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)
	}
}
Пример #10
0
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)
	}
}
Пример #11
0
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()
}
Пример #12
0
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)
	}
}
Пример #13
0
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)
	}
}
Пример #14
0
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.")
	}
}
Пример #15
0
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)
	}
}
Пример #16
0
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)
}
Пример #17
0
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)
		}
	}
}
Пример #18
0
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")
		}
	}
}
Пример #19
0
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)
			}
		}
	}
}
Пример #20
0
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)
			}
		}
	}
}
Пример #21
0
// 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)
			}
		}

	}

}
Пример #22
0
// DropZone discards a zone from the config.
func (c *Config) DropZone(origin string) error {
	dns.HandleRemove(origin)
	delete(c.Zones, origin)
	return nil
}
Пример #23
0
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
}
Пример #24
0
// 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.")
		}
	}
}
Пример #25
0
// 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.")
		}
	}
}