Ejemplo n.º 1
0
func TestScanToScanResponse(t *testing.T) {
	scan := model.Scan{
		Status:                   model.ScanStatusExecuted,
		StartedAt:                time.Now().Add(-1 * time.Hour),
		FinishedAt:               time.Now().Add(-30 * time.Minute),
		DomainsScanned:           10,
		DomainsWithDNSSECScanned: 4,
		NameserverStatistics: map[string]uint64{
			model.NameserverStatusToString(model.NameserverStatusOK):      16,
			model.NameserverStatusToString(model.NameserverStatusTimeout): 4,
		},
		DSStatistics: map[string]uint64{
			model.DSStatusToString(model.DSStatusOK):               3,
			model.DSStatusToString(model.DSStatusExpiredSignature): 1,
		},
	}

	scanResponse := ScanToScanResponse(scan)

	if scanResponse.Status != "EXECUTED" {
		t.Error("Status is not being translated correctly for a scan")
	}

	if scanResponse.DomainsToBeScanned != 0 {
		t.Error("Domains to be scanned field was not converted correctly")
	}

	if scanResponse.DomainsScanned != 10 {
		t.Error("Domains scanned field was not converted correctly")
	}

	if scanResponse.DomainsWithDNSSECScanned != 4 {
		t.Error("Domains with DNSSEC scanned field was not converted correctly")
	}

	if !scanResponse.StartedAt.Equal(scan.StartedAt) {
		t.Error("Started time was not converted correctly")
	}

	if !scanResponse.FinishedAt.Equal(scan.FinishedAt) {
		t.Error("Finished time was not converted correctly")
	}

	if scanResponse.NameserverStatistics["OK"] != 16 ||
		scanResponse.NameserverStatistics["TIMEOUT"] != 4 {
		t.Error("Nameserver statistics weren't converted correctly")
	}

	if scanResponse.DSStatistics["OK"] != 3 ||
		scanResponse.DSStatistics["EXPSIG"] != 1 {
		t.Error("DS statistics weren't converted correctly")
	}

	if len(scanResponse.Links) != 1 ||
		scanResponse.Links[0].HRef != fmt.Sprintf("/scan/%s", scan.StartedAt.Format(time.RFC3339Nano)) {
		t.Error("Links weren't added correctly")
	}
}
Ejemplo n.º 2
0
// Convert a nameserver of the system into a format with limited information to return it
// to the user
func toNameserverResponse(nameserver model.Nameserver) NameserverResponse {
	ipv4 := ""
	if len(nameserver.IPv4) > 0 {
		ipv4 = nameserver.IPv4.String()
	}

	ipv6 := ""
	if len(nameserver.IPv6) > 0 {
		ipv6 = nameserver.IPv6.String()
	}

	return NameserverResponse{
		Host:        nameserver.Host,
		IPv4:        ipv4,
		IPv6:        ipv6,
		LastStatus:  model.NameserverStatusToString(nameserver.LastStatus),
		LastCheckAt: nameserver.LastCheckAt,
		LastOKAt:    nameserver.LastOKAt,
	}
}
Ejemplo n.º 3
0
func TestToNameserverResponse(t *testing.T) {
	now := time.Now()

	nameserver := model.Nameserver{
		Host:        "ns1.example.com.br.",
		IPv4:        net.ParseIP("127.0.0.1"),
		IPv6:        net.ParseIP("::1"),
		LastStatus:  model.NameserverStatusOK,
		LastCheckAt: now,
		LastOKAt:    now,
	}

	nameserverResponse := toNameserverResponse(nameserver)

	if nameserverResponse.Host != "ns1.example.com.br." {
		t.Error("Fail to convert host")
	}

	if nameserverResponse.IPv4 != "127.0.0.1" {
		t.Error("Fail to convert IPv4")
	}

	if nameserverResponse.IPv6 != "::1" {
		t.Error("Fail to convert IPv6")
	}

	if nameserverResponse.LastStatus !=
		model.NameserverStatusToString(model.NameserverStatusOK) {

		t.Error("Fail to convert last status")
	}

	if nameserverResponse.LastCheckAt.Unix() != now.Unix() ||
		nameserverResponse.LastOKAt.Unix() != now.Unix() {

		t.Error("Fail to convert dates")
	}
}
func scanDomain() {
	dns.HandleFunc("example.com.br.", func(w dns.ResponseWriter, dnsRequestMessage *dns.Msg) {
		defer w.Close()

		dnsResponseMessage := &dns.Msg{
			MsgHdr: dns.MsgHdr{
				Authoritative: true,
			},
			Question: dnsRequestMessage.Question,
			Answer: []dns.RR{
				&dns.SOA{
					Hdr: dns.RR_Header{
						Name:   "example.com.br.",
						Rrtype: dns.TypeSOA,
						Class:  dns.ClassINET,
						Ttl:    86400,
					},
					Ns:      "ns1.example.com.br.",
					Mbox:    "rafael.justo.net.br.",
					Serial:  2013112600,
					Refresh: 86400,
					Retry:   86400,
					Expire:  86400,
					Minttl:  900,
				},
			},
		}

		dnsResponseMessage.SetReply(dnsRequestMessage)
		w.WriteMsg(dnsResponseMessage)
	})

	var client http.Client

	url := ""
	if len(config.ShelterConfig.WebClient.Listeners) > 0 {
		url = fmt.Sprintf("http://%s:%d", config.ShelterConfig.WebClient.Listeners[0].IP,
			config.ShelterConfig.WebClient.Listeners[0].Port)
	}

	if len(url) == 0 {
		utils.Fatalln("There's no interface to connect to", nil)
	}

	content := `{
      "Nameservers": [
        { "Host": "ns1.example.com.br.", "ipv4": "127.0.0.1" },
        { "Host": "ns2.example.com.br.", "ipv4": "127.0.0.1" }
      ]
    }`

	r, err := http.NewRequest("PUT",
		fmt.Sprintf("%s%s", url, "/domain/example.com.br./verification"),
		strings.NewReader(content))

	if err != nil {
		utils.Fatalln("Error creating the HTTP request", err)
	}

	utils.BuildHTTPHeader(r, []byte(content))

	response, err := client.Do(r)
	if err != nil {
		utils.Fatalln("Error sending request", err)
	}

	responseContent, err := ioutil.ReadAll(response.Body)
	if err != nil {
		utils.Fatalln("Error reading response content", err)
	}

	if response.StatusCode != http.StatusOK {
		utils.Fatalln("Error scanning domain", errors.New(string(responseContent)))
	}

	var domainResponse protocol.DomainResponse
	if err := json.Unmarshal(responseContent, &domainResponse); err != nil {
		utils.Fatalln("Error decoding domain response", err)
	}

	if len(domainResponse.Nameservers) != 2 {
		utils.Fatalln("Wrong number of nameservers", nil)
	}

	if domainResponse.Nameservers[0].LastStatus != model.NameserverStatusToString(model.NameserverStatusOK) {
		utils.Fatalln("Scan did not work for ns1", nil)
	}

	if domainResponse.Nameservers[1].LastStatus != model.NameserverStatusToString(model.NameserverStatusOK) {
		utils.Fatalln("Scan did not work for ns2", nil)
	}
}
Ejemplo n.º 5
0
func domainWithErrors(config ScanCollectorTestConfigFile, database *mgo.Database) {
	domainsToSave := make(chan *model.Domain, config.Scan.DomainsBufferSize)
	domainsToSave <- &model.Domain{
		FQDN: "br.",
		Nameservers: []model.Nameserver{
			{
				Host:       "ns1.br",
				IPv4:       net.ParseIP("127.0.0.1"),
				LastStatus: model.NameserverStatusTimeout,
			},
		},
		DSSet: []model.DS{
			{
				Keytag:     1234,
				Algorithm:  model.DSAlgorithmRSASHA1NSEC3,
				DigestType: model.DSDigestTypeSHA1,
				Digest:     "EAA0978F38879DB70A53F9FF1ACF21D046A98B5C",
				LastStatus: model.DSStatusExpiredSignature,
			},
		},
	}
	domainsToSave <- nil

	model.StartNewScan()
	runScan(config, database, domainsToSave)

	domainDAO := dao.DomainDAO{
		Database: database,
	}

	domain, err := domainDAO.FindByFQDN("br.")
	if err != nil {
		utils.Fatalln("Error loading domain with problems", err)
	}

	if len(domain.Nameservers) == 0 {
		utils.Fatalln("Error saving nameservers", nil)
	}

	if domain.Nameservers[0].LastStatus != model.NameserverStatusTimeout {
		utils.Fatalln("Error setting status in the nameserver", nil)
	}

	if len(domain.DSSet) == 0 {
		utils.Fatalln("Error saving the DS set", nil)
	}

	if domain.DSSet[0].LastStatus != model.DSStatusExpiredSignature {
		utils.Fatalln("Error setting status in the DS", nil)
	}

	if err := domainDAO.RemoveByFQDN("br."); err != nil {
		utils.Fatalln("Error removing test domain", err)
	}

	currentScan := model.GetCurrentScan()
	if currentScan.DomainsScanned != 1 || currentScan.DomainsWithDNSSECScanned != 1 {
		utils.Fatalln("Not counting domains for scan progress when there're errors", nil)
	}

	if currentScan.NameserverStatistics[model.NameserverStatusToString(model.NameserverStatusTimeout)] != 1 ||
		currentScan.DSStatistics[model.DSStatusToString(model.DSStatusExpiredSignature)] != 1 {
		utils.Fatalln("Not counting statistics properly when there're errors", nil)
	}
}
Ejemplo n.º 6
0
// Auxiliary function for template that compares two nameserver status (case insensitive)
func nameserverStatusEquals(nameserverStatus model.NameserverStatus,
	expectedNameserverTextStatus string) bool {

	return strings.ToLower(model.NameserverStatusToString(nameserverStatus)) ==
		strings.TrimSpace(strings.ToLower(expectedNameserverTextStatus))
}
Ejemplo n.º 7
0
// This method is the last part of the scan, when the new state of the domain object is
// persisted back to the database. It receives a go routine control group to sinalize to
// the main thread when the scan ends, a domain channel to receive each domain that need
// to be save and an error channel to send back all errors while persisting the data. It
// was created to be asynchronous and finish after receiving a poison pill from querier
// dispatcher
func (c *Collector) Start(scanGroup *sync.WaitGroup,
	domainsToSaveChannel chan *model.Domain, errorsChannel chan error) {

	// Add one more to the group of scan go routines
	scanGroup.Add(1)

	go func() {
		// Initialize Domain DAO using injected database connection
		domainDAO := dao.DomainDAO{
			Database: c.Database,
		}

		// Add a safety check to avoid an infinite loop
		if c.SaveAtOnce == 0 {
			c.SaveAtOnce = 1
		}

		finished := false
		nameserverStatistics := make(map[string]uint64)
		dsStatistics := make(map[string]uint64)

		for {
			// Using make for faster allocation
			domains := make([]*model.Domain, 0, c.SaveAtOnce)

			for i := 0; i < c.SaveAtOnce; i++ {
				domain := <-domainsToSaveChannel

				// Detect poison pill. We don't return from function here because we can still
				// have some domains to save in the domains array
				if domain == nil {
					finished = true
					break
				}

				// Count this domain for the scan information to estimate the scan progress
				model.FinishAnalyzingDomainForScan(len(domain.DSSet) > 0)

				// Keep track of nameservers statistics
				for _, nameserver := range domain.Nameservers {
					status := model.NameserverStatusToString(nameserver.LastStatus)
					nameserverStatistics[status] += 1
				}

				// Keep track of DS statistics
				for _, ds := range domain.DSSet {
					status := model.DSStatusToString(ds.LastStatus)
					dsStatistics[status] += 1
				}

				domains = append(domains, domain)
			}

			domainsResults := domainDAO.SaveMany(domains)
			for _, domainResult := range domainsResults {
				if domainResult.Error != nil {
					// Error channel should have a buffer or this will block the collector until
					// someone check this error. One question here is that we are returning the
					// error, but not telling wich domain got the error, we should improve the error
					// communication system between the go routines
					errorsChannel <- domainResult.Error
				}
			}

			// Now that everything is done, check if we received a poison pill
			if finished {
				model.StoreStatisticsOfTheScan(nameserverStatistics, dsStatistics)
				scanGroup.Done()
				return
			}
		}
	}()
}
Ejemplo n.º 8
0
// Generates a report with the result of a scan in the root zone file, it should be last
// last thing from the test, because it changes the DNS test port to the original one for
// real tests
func inputScanReport(config ScanQuerierTestConfigFile) {
	// Move back to default port, because we are going to query the world for real to check
	// querier performance
	scan.DNSPort = 53

	// As we are using the same domains repeatedly we should be careful about how many
	// requests we send to only one host to avoid abuses. This value should be beteween 5
	// and 10
	scan.MaxQPSPerHost = 5

	report := " #       | Total            | QPS  | Memory (MB)\n" +
		"---------------------------------------------------\n"

	domains, err := readInputFile(config.Report.InputFile)
	if err != nil {
		utils.Fatalln("Error while loading input data for report", err)
	}

	nameserverStatusCounter := 0
	nameserversStatus := make(map[model.NameserverStatus]int)

	dsStatusCounter := 0
	dsSetStatus := make(map[model.DSStatus]int)

	totalDuration, queriesPerSecond, nameserversStatus, dsSetStatus :=
		calculateScanQuerierDurations(config, domains)

	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)

	report += fmt.Sprintf("% -8d | %16s | %4d | %14.2f\n",
		len(domains),
		time.Duration(int64(totalDuration)).String(),
		queriesPerSecond,
		float64(memStats.Alloc)/float64(MB),
	)

	report += "\nNameserver Status\n" +
		"-----------------\n"
	for _, counter := range nameserversStatus {
		nameserverStatusCounter += counter
	}
	for status, counter := range nameserversStatus {
		report += fmt.Sprintf("%16s: % 3.2f%%\n",
			model.NameserverStatusToString(status),
			(float64(counter*100) / float64(nameserverStatusCounter)),
		)
	}

	report += "\nDS Status\n" +
		"---------\n"
	for _, counter := range dsSetStatus {
		dsStatusCounter += counter
	}
	for status, counter := range dsSetStatus {
		report += fmt.Sprintf("%16s: % 3.2f%%\n",
			model.DSStatusToString(status),
			(float64(counter*100) / float64(dsStatusCounter)),
		)
	}

	utils.WriteReport(config.Report.OutputFile, report)
}
Ejemplo n.º 9
0
func brDomainWithoutDNSSEC(domainDAO dao.DomainDAO) {
	domain := model.Domain{
		FQDN: "br.",
		Nameservers: []model.Nameserver{
			{
				Host: "a.dns.br.",
				IPv4: net.ParseIP("200.160.0.10"),
				IPv6: net.ParseIP("2001:12ff::10"),
			},
			{
				Host: "b.dns.br.",
				IPv4: net.ParseIP("200.189.41.10"),
			},
			{
				Host: "c.dns.br.",
				IPv4: net.ParseIP("200.192.233.10"),
			},
			{
				Host: "d.dns.br.",
				IPv4: net.ParseIP("200.219.154.10"),
				IPv6: net.ParseIP("2001:12f8:4::10"),
			},
			{
				Host: "f.dns.br.",
				IPv4: net.ParseIP("200.219.159.10"),
			},
		},

		// We are going to add the current DNSKEYs from .br but we are not going to check it.
		// This is because there's a strange case that when it found a problem on a DS (such
		// as bit SEP) it does not check other nameservers
		DSSet: []model.DS{
			{
				Keytag:     41674,
				Algorithm:  model.DSAlgorithmRSASHA1,
				DigestType: model.DSDigestTypeSHA256,
				Digest:     "6ec74914376b4f383ede3840088ae1d7bf13a19bfc51465cc2da57618889416a",
			},
			{
				Keytag:     57207,
				Algorithm:  model.DSAlgorithmRSASHA1,
				DigestType: model.DSDigestTypeSHA256,
				Digest:     "d46f059860d31a0965f925ac6ff97ed0975f33a14e2d01ec5ab5dd543624d307",
			},
		},
	}

	var err error

	if err = domainDAO.Save(&domain); err != nil {
		utils.Fatalln("Error saving the domain", err)
	}

	scan.ScanDomains()

	domain, err = domainDAO.FindByFQDN(domain.FQDN)
	if err != nil {
		utils.Fatalln("Didn't find scanned domain", err)
	}

	for _, nameserver := range domain.Nameservers {
		if nameserver.LastStatus != model.NameserverStatusOK {
			utils.Fatalln(fmt.Sprintf("Fail to validate a supposedly well configured nameserver '%s'. Found status: %s",
				nameserver.Host, model.NameserverStatusToString(nameserver.LastStatus)), nil)
		}
	}

	if err := domainDAO.RemoveByFQDN(domain.FQDN); err != nil {
		utils.Fatalln(fmt.Sprintf("Error removing domain %s", domain.FQDN), err)
	}
}
Ejemplo n.º 10
0
func domainWithNoErrorsOnTheFly() {
	domain, dnskey, rrsig, lastCheckAt, lastOKAt := generateAndSignDomain("br.")

	dns.HandleFunc("br.", func(w dns.ResponseWriter, dnsRequestMessage *dns.Msg) {
		defer w.Close()

		if dnsRequestMessage.Question[0].Qtype == dns.TypeSOA {
			dnsResponseMessage := &dns.Msg{
				MsgHdr: dns.MsgHdr{
					Authoritative: true,
				},
				Question: dnsRequestMessage.Question,
				Answer: []dns.RR{
					&dns.SOA{
						Hdr: dns.RR_Header{
							Name:   "br.",
							Rrtype: dns.TypeSOA,
							Class:  dns.ClassINET,
							Ttl:    86400,
						},
						Ns:      "ns1.br.",
						Mbox:    "rafael.justo.net.br.",
						Serial:  2013112600,
						Refresh: 86400,
						Retry:   86400,
						Expire:  86400,
						Minttl:  900,
					},
				},
			}

			dnsResponseMessage.SetReply(dnsRequestMessage)
			w.WriteMsg(dnsResponseMessage)

		} else if dnsRequestMessage.Question[0].Qtype == dns.TypeDNSKEY {
			dnsResponseMessage := &dns.Msg{
				MsgHdr: dns.MsgHdr{
					Authoritative: true,
				},
				Question: dnsRequestMessage.Question,
				Answer: []dns.RR{
					dnskey,
					rrsig,
				},
			}

			dnsResponseMessage.SetReply(dnsRequestMessage)
			w.WriteMsg(dnsResponseMessage)
		}
	})

	scan.ScanDomain(&domain)

	for _, nameserver := range domain.Nameservers {
		if nameserver.LastStatus != model.NameserverStatusOK {
			utils.Fatalln(fmt.Sprintf("Fail to validate a supposedly well configured nameserver '%s'. Found status: %s",
				nameserver.Host, model.NameserverStatusToString(nameserver.LastStatus)), nil)
		}

		if nameserver.LastCheckAt.Before(lastCheckAt) ||
			nameserver.LastCheckAt.Equal(lastCheckAt) {
			utils.Fatalln(fmt.Sprintf("Last check date was not updated in nameserver '%s'",
				nameserver.Host), nil)
		}

		if nameserver.LastOKAt.Before(lastOKAt) || nameserver.LastOKAt.Equal(lastOKAt) {
			utils.Fatalln(fmt.Sprintf("Last OK date was not updated in nameserver '%s'",
				nameserver.Host), nil)
		}
	}

	for _, ds := range domain.DSSet {
		if ds.LastStatus != model.DSStatusOK {
			utils.Fatalln(fmt.Sprintf("Fail to validate a supposedly well configured DS %d. "+
				"Found status: %s", ds.Keytag, model.DSStatusToString(ds.LastStatus)), nil)
		}

		if ds.LastCheckAt.Before(lastCheckAt) || ds.LastCheckAt.Equal(lastCheckAt) {
			utils.Fatalln(fmt.Sprintf("Last check date was not updated in DS %d",
				ds.Keytag), nil)
		}

		if ds.LastOKAt.Before(lastOKAt) || ds.LastOKAt.Equal(lastOKAt) {
			utils.Fatalln(fmt.Sprintf("Last OK date was not updated in DS %d",
				ds.Keytag), nil)
		}
	}
}
func scanDomain() {
	dns.HandleFunc("example.com.br.", func(w dns.ResponseWriter, dnsRequestMessage *dns.Msg) {
		defer w.Close()

		dnsResponseMessage := &dns.Msg{
			MsgHdr: dns.MsgHdr{
				Authoritative: true,
			},
			Question: dnsRequestMessage.Question,
			Answer: []dns.RR{
				&dns.SOA{
					Hdr: dns.RR_Header{
						Name:   "example.com.br.",
						Rrtype: dns.TypeSOA,
						Class:  dns.ClassINET,
						Ttl:    86400,
					},
					Ns:      "ns1.example.com.br.",
					Mbox:    "rafael.justo.net.br.",
					Serial:  2013112600,
					Refresh: 86400,
					Retry:   86400,
					Expire:  86400,
					Minttl:  900,
				},
			},
		}

		dnsResponseMessage.SetReply(dnsRequestMessage)
		w.WriteMsg(dnsResponseMessage)
	})

	mux := handy.NewHandy()

	h := new(handler.DomainVerificationHandler)
	mux.Handle("/domain/{fqdn}/verification", func() handy.Handler {
		return h
	})

	requestContent := `{
      "Nameservers": [
        { "Host": "ns1.example.com.br.", "ipv4": "127.0.0.1" },
        { "Host": "ns2.example.com.br.", "ipv4": "127.0.0.1" }
      ]
    }`

	r, err := http.NewRequest("PUT", "/domain/example.com.br./verification",
		strings.NewReader(requestContent))
	if err != nil {
		utils.Fatalln("Error creating the HTTP request", err)
	}
	utils.BuildHTTPHeader(r, []byte(requestContent))

	w := httptest.NewRecorder()
	mux.ServeHTTP(w, r)

	responseContent, err := ioutil.ReadAll(w.Body)
	if err != nil {
		utils.Fatalln("Error reading response body", err)
	}

	if w.Code != http.StatusOK {
		utils.Fatalln(fmt.Sprintf("Error scanning domain. "+
			"Expected %d and got %d", http.StatusOK, w.Code),
			errors.New(string(responseContent)))
	}

	if len(h.Response.Nameservers) != 2 {
		utils.Fatalln("Wrong number of nameservers", nil)
	}

	if h.Response.Nameservers[0].LastStatus !=
		model.NameserverStatusToString(model.NameserverStatusOK) {

		utils.Fatalln("Scan did not work for ns1", nil)
	}

	if h.Response.Nameservers[1].LastStatus !=
		model.NameserverStatusToString(model.NameserverStatusOK) {

		utils.Fatalln("Scan did not work for ns2", nil)
	}
}