Пример #1
0
func createScans(database *mgo.Database) {
	scanDAO := dao.ScanDAO{
		Database: database,
	}

	for i := 0; i < 100; i++ {
		scan := model.Scan{
			Status:                   model.ScanStatusExecuted,
			StartedAt:                time.Now().Add(time.Duration(-i*2) * time.Minute),
			FinishedAt:               time.Now().Add(time.Duration(-i) * time.Minute),
			DomainsScanned:           5000000,
			DomainsWithDNSSECScanned: 250000,
			NameserverStatistics: map[string]uint64{
				"OK":      4800000,
				"TIMEOUT": 200000,
			},
			DSStatistics: map[string]uint64{
				"OK":     220000,
				"EXPSIG": 30000,
			},
		}

		if err := scanDAO.Save(&scan); err != nil {
			utils.Fatalln("Error creating scan", err)
		}
	}
}
Пример #2
0
func (i *Scan) Before(w http.ResponseWriter, r *http.Request) {
	date, err := time.Parse(time.RFC3339Nano, strings.ToUpper(i.scanHandler.GetStartedAt()))

	if err != nil {
		if err := i.scanHandler.MessageResponse("invalid-uri", r.URL.RequestURI()); err == nil {
			w.WriteHeader(http.StatusBadRequest)
		} else {
			log.Println("Error while writing response. Details:", err)
			w.WriteHeader(http.StatusInternalServerError)
		}
		return
	}

	scanDAO := dao.ScanDAO{
		Database: i.scanHandler.GetDatabase(),
	}

	scan, err := scanDAO.FindByStartedAt(date)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	i.scanHandler.SetScan(scan)
}
Пример #3
0
func createScan(database *mgo.Database) time.Time {
	scan := model.Scan{
		Status:                   model.ScanStatusExecuted,
		StartedAt:                time.Now().Add(-60 * time.Minute),
		FinishedAt:               time.Now().Add(-10 * time.Minute),
		DomainsScanned:           5000000,
		DomainsWithDNSSECScanned: 250000,
		NameserverStatistics: map[string]uint64{
			"OK":      4800000,
			"TIMEOUT": 200000,
		},
		DSStatistics: map[string]uint64{
			"OK":     220000,
			"EXPSIG": 30000,
		},
	}

	scanDAO := dao.ScanDAO{
		Database: database,
	}

	if err := scanDAO.Save(&scan); err != nil {
		utils.Fatalln("Error creating scan", err)
	}

	return scan.StartedAt
}
Пример #4
0
func deleteScan(database *mgo.Database, startedAt time.Time) {
	scanDAO := dao.ScanDAO{
		Database: database,
	}

	if err := scanDAO.RemoveByStartedAt(startedAt); err != nil {
		utils.Fatalln("Error removing scan", err)
	}
}
Пример #5
0
func deleteScans(database *mgo.Database) {
	scanDAO := dao.ScanDAO{
		Database: database,
	}

	if err := scanDAO.RemoveAll(); err != nil {
		utils.Fatalln("Error removing scans", err)
	}
}
Пример #6
0
// Function created to remove all entries from the database to ensure that the tests
// enviroments are always equal
func ClearDatabase(database *mgo.Database) {
	domainDAO := dao.DomainDAO{
		Database: database,
	}
	domainDAO.RemoveAll()

	scanDAO := dao.ScanDAO{
		Database: database,
	}
	scanDAO.RemoveAll()
}
Пример #7
0
func main() {
	flag.Parse()

	var config ScanDAOTestConfigFile
	err := utils.ReadConfigFile(configFilePath, &config)

	if err == utils.ErrConfigFileUndefined {
		fmt.Println(err.Error())
		fmt.Println("Usage:")
		flag.PrintDefaults()
		return

	} else if err != nil {
		utils.Fatalln("Error reading configuration file", err)
	}

	database, databaseSession, err := mongodb.Open(
		[]string{config.Database.URI},
		config.Database.Name,
		false, "", "",
	)

	if err != nil {
		utils.Fatalln("Error connecting the database", err)
	}
	defer databaseSession.Close()

	scanDAO := dao.ScanDAO{
		Database: database,
	}

	// If there was some problem in the last test, there could be some data in the
	// database, so let's clear it to don't affect this test. We avoid checking the error,
	// because if the collection does not exist yet, it will be created in the first
	// insert
	scanDAO.RemoveAll()

	scanLifeCycle(scanDAO)
	scanConcurrency(scanDAO)
	scanStatistics(scanDAO)
	scansPagination(scanDAO)
	scansExpand(scanDAO)

	utils.Println("SUCCESS!")
}
Пример #8
0
func scansExpand(scanDAO dao.ScanDAO) {
	newScans := newScans()
	for _, scan := range newScans {
		if err := scanDAO.Save(&scan); err != nil {
			utils.Fatalln("Error creating scans", err)
		}
	}

	pagination := dao.ScanDAOPagination{}
	scans, err := scanDAO.FindAll(&pagination, false)

	if err != nil {
		utils.Fatalln("Error retrieving scans", err)
	}

	for _, scan := range scans {
		if scan.DomainsScanned != 0 ||
			scan.DomainsWithDNSSECScanned != 0 ||
			!scan.FinishedAt.Equal(time.Time{}) ||
			len(scan.NameserverStatistics) > 0 ||
			len(scan.DSStatistics) > 0 {
			utils.Fatalln("Not compressing scan in results", nil)
		}
	}

	scans, err = scanDAO.FindAll(&pagination, true)

	if err != nil {
		utils.Fatalln("Error retrieving scans", err)
	}

	for _, scan := range scans {
		if scan.DomainsScanned == 0 ||
			scan.DomainsWithDNSSECScanned == 0 ||
			scan.FinishedAt.Equal(time.Time{}) ||
			scan.StartedAt.Equal(time.Time{}) ||
			len(scan.NameserverStatistics) == 0 ||
			len(scan.DSStatistics) == 0 {
			fmt.Println(scan.DomainsScanned == 0,
				scan.DomainsWithDNSSECScanned == 0,
				scan.FinishedAt.Equal(time.Time{}),
				scan.StartedAt.Equal(time.Time{}),
				len(scan.NameserverStatistics) == 0,
				len(scan.DSStatistics) == 0)
			utils.Fatalln("Compressing scan in results when it shouldn't", nil)
		}
	}

	if err := scanDAO.RemoveAll(); err != nil {
		utils.Fatalln("Error removing scans", err)
	}
}
Пример #9
0
func calculateScanDurations(numberOfDomains int, scanDAO dao.ScanDAO) (
	totalDuration time.Duration, domainsPerSecond int64,
) {

	beginTimer := time.Now()
	scan.ScanDomains()
	totalDuration = time.Since(beginTimer)

	totalDurationSeconds := int64(totalDuration / time.Second)
	if totalDurationSeconds > 0 {
		domainsPerSecond = int64(numberOfDomains) / totalDurationSeconds

	} else {
		domainsPerSecond = int64(numberOfDomains)
	}

	// As we are running a lot of scans at the same time, and the scan information unique
	// key is the start time of the scan, we must clear the database to avoid log messages
	// of scan insert errors
	scanDAO.RemoveAll()

	return
}
Пример #10
0
func scanStatistics(scanDAO dao.ScanDAO) {
	scan := newScan()
	scan.NameserverStatistics = map[string]uint64{
		"OK":      40,
		"TIMEOUT": 10,
	}
	scan.DSStatistics = map[string]uint64{
		"OK":     8,
		"DNSERR": 2,
	}

	// Create scan
	if err := scanDAO.Save(&scan); err != nil {
		utils.Fatalln("Couldn't save scan in database", err)
	}

	var err error
	scan, err = scanDAO.FindByStartedAt(scan.StartedAt)
	if err != nil {
		utils.Fatalln("Couldn't find created scan in database", err)
	}

	if len(scan.NameserverStatistics) != 2 ||
		scan.NameserverStatistics["OK"] != 40 ||
		scan.NameserverStatistics["TIMEOUT"] != 10 {
		utils.Fatalln("Not retrieving nameserver statistics correctly", nil)
	}

	if len(scan.DSStatistics) != 2 ||
		scan.DSStatistics["OK"] != 8 ||
		scan.DSStatistics["DNSERR"] != 2 {
		utils.Fatalln("Not retrieving DS statistics correctly", nil)
	}

	if err := scanDAO.RemoveAll(); err != nil {
		utils.Fatalln("Error removing scans from database", err)
	}
}
Пример #11
0
// Test all phases of the scan life cycle
func scanLifeCycle(scanDAO dao.ScanDAO) {
	scan := newScan()

	// Create scan
	if err := scanDAO.Save(&scan); err != nil {
		utils.Fatalln("Couldn't save scan in database", err)
	}

	// Search and compare created scan
	if scanRetrieved, err := scanDAO.FindByStartedAt(scan.StartedAt); err != nil {
		utils.Fatalln("Couldn't find created scan in database", err)

	} else if !utils.CompareScan(scan, scanRetrieved) {
		utils.Fatalln("Scan created in being persisted wrongly", nil)
	}

	// Update scan
	scan.DomainsScanned = 100
	if err := scanDAO.Save(&scan); err != nil {
		utils.Fatalln("Couldn't save scan in database", err)
	}

	// Search and compare updated scan
	if scanRetrieved, err := scanDAO.FindByStartedAt(scan.StartedAt); err != nil {
		utils.Fatalln("Couldn't find updated scan in database", err)

	} else if !utils.CompareScan(scan, scanRetrieved) {
		utils.Fatalln("Scan updated in being persisted wrongly", nil)
	}

	// Remove scan
	if err := scanDAO.RemoveByStartedAt(scan.StartedAt); err != nil {
		utils.Fatalln("Error while trying to remove a scan", err)
	}

	// Check removal
	if _, err := scanDAO.FindByStartedAt(scan.StartedAt); err == nil {
		utils.Fatalln("Scan was not removed from database", nil)
	}
}
Пример #12
0
func scansPagination(scanDAO dao.ScanDAO) {
	numberOfItems := 1000

	for i := 0; i < numberOfItems; i++ {
		scan := model.Scan{
			StartedAt: time.Now().Add(time.Duration(-i) * time.Minute),
		}

		if err := scanDAO.Save(&scan); err != nil {
			utils.Fatalln("Error saving scan in database", err)
		}
	}

	pagination := dao.ScanDAOPagination{
		PageSize: 10,
		Page:     5,
		OrderBy: []dao.ScanDAOSort{
			{
				Field:     dao.ScanDAOOrderByFieldStartedAt,
				Direction: dao.DAOOrderByDirectionAscending,
			},
		},
	}

	scans, err := scanDAO.FindAll(&pagination, true)
	if err != nil {
		utils.Fatalln("Error retrieving scans", err)
	}

	if pagination.NumberOfItems != numberOfItems {
		utils.Errorln("Number of items not calculated correctly", nil)
	}

	if pagination.NumberOfPages != numberOfItems/pagination.PageSize {
		utils.Errorln("Number of pages not calculated correctly", nil)
	}

	if len(scans) != pagination.PageSize {
		utils.Errorln("Number of scans not following page size", nil)
	}

	pagination = dao.ScanDAOPagination{
		PageSize: 10000,
		Page:     1,
		OrderBy: []dao.ScanDAOSort{
			{
				Field:     dao.ScanDAOOrderByFieldStartedAt,
				Direction: dao.DAOOrderByDirectionAscending,
			},
		},
	}

	scans, err = scanDAO.FindAll(&pagination, true)
	if err != nil {
		utils.Fatalln("Error retrieving scans", err)
	}

	if pagination.NumberOfPages != 1 {
		utils.Fatalln("Calculating wrong number of pages when there's only one page", nil)
	}

	if err := scanDAO.RemoveAll(); err != nil {
		utils.Fatalln("Error removing scans from database", err)
	}
}
Пример #13
0
// Check if the revision field avoid data concurrency. Is better to fail than to store the
// wrong state
func scanConcurrency(scanDAO dao.ScanDAO) {
	scan := newScan()

	// Create scan
	if err := scanDAO.Save(&scan); err != nil {
		utils.Fatalln("Couldn't save scan in database", err)
	}

	scan1, err := scanDAO.FindByStartedAt(scan.StartedAt)
	if err != nil {
		utils.Fatalln("Couldn't find created scan in database", err)
	}

	scan2, err := scanDAO.FindByStartedAt(scan.StartedAt)
	if err != nil {
		utils.Fatalln("Couldn't find created scan in database", err)
	}

	if err := scanDAO.Save(&scan1); err != nil {
		utils.Fatalln("Couldn't save scan in database", err)
	}

	if err := scanDAO.Save(&scan2); err == nil {
		utils.Fatalln("Not controlling scan concurrency", nil)
	}

	// Remove scan
	if err := scanDAO.RemoveByStartedAt(scan.StartedAt); err != nil {
		utils.Fatalln("Error while trying to remove a scan", err)
	}
}
Пример #14
0
// The HEAD method is identical to GET except that the server MUST NOT return a message-
// body in the response. But now the responsability for don't adding the body is from the
// mux while writing the response
func (h *ScansHandler) retrieveScans(w http.ResponseWriter, r *http.Request) {
	var pagination dao.ScanDAOPagination

	expand := false
	returnCurrent := false

	for key, values := range r.URL.Query() {
		key = strings.TrimSpace(key)
		key = strings.ToLower(key)

		// A key can have multiple values in a query string, we are going to always consider
		// the last one (overwrite strategy)
		for _, value := range values {
			value = strings.TrimSpace(value)
			value = strings.ToLower(value)

			switch key {
			case "orderby":
				// OrderBy parameter will store the fields that the user want to be the keys of the sort
				// algorithm in the result set and the direction that each sort field will have. The format
				// that will be used is:
				//
				// <field1>:<direction1>@<field2>:<direction2>@...@<fieldN>:<directionN>

				orderByParts := strings.Split(value, "@")

				for _, orderByPart := range orderByParts {
					orderByPart = strings.TrimSpace(orderByPart)
					orderByAndDirection := strings.Split(orderByPart, ":")

					var field, direction string

					if len(orderByAndDirection) == 1 {
						field, direction = orderByAndDirection[0], "asc"

					} else if len(orderByAndDirection) == 2 {
						field, direction = orderByAndDirection[0], orderByAndDirection[1]

					} else {
						if err := h.MessageResponse("invalid-query-order-by", ""); err == nil {
							w.WriteHeader(http.StatusBadRequest)

						} else {
							log.Println("Error while writing response. Details:", err)
							w.WriteHeader(http.StatusInternalServerError)
						}

						return
					}

					orderByField, err := dao.ScanDAOOrderByFieldFromString(field)
					if err != nil {
						if err := h.MessageResponse("invalid-query-order-by", ""); err == nil {
							w.WriteHeader(http.StatusBadRequest)

						} else {
							log.Println("Error while writing response. Details:", err)
							w.WriteHeader(http.StatusInternalServerError)
						}

						return
					}

					orderByDirection, err := dao.DAOOrderByDirectionFromString(direction)
					if err != nil {
						if err := h.MessageResponse("invalid-query-order-by", ""); err == nil {
							w.WriteHeader(http.StatusBadRequest)

						} else {
							log.Println("Error while writing response. Details:", err)
							w.WriteHeader(http.StatusInternalServerError)
						}

						return
					}

					pagination.OrderBy = append(pagination.OrderBy, dao.ScanDAOSort{
						Field:     orderByField,
						Direction: orderByDirection,
					})
				}

			case "pagesize":
				var err error
				pagination.PageSize, err = strconv.Atoi(value)
				if err != nil {
					if err := h.MessageResponse("invalid-query-page-size", ""); err == nil {
						w.WriteHeader(http.StatusBadRequest)

					} else {
						log.Println("Error while writing response. Details:", err)
						w.WriteHeader(http.StatusInternalServerError)
					}

					return
				}

			case "page":
				var err error
				pagination.Page, err = strconv.Atoi(value)
				if err != nil {
					if err := h.MessageResponse("invalid-query-page", ""); err == nil {
						w.WriteHeader(http.StatusBadRequest)

					} else {
						log.Println("Error while writing response. Details:", err)
						w.WriteHeader(http.StatusInternalServerError)
					}

					return
				}

			case "expand":
				expand = true

			case "current":
				returnCurrent = true
			}
		}
	}

	scanDAO := dao.ScanDAO{
		Database: h.GetDatabase(),
	}

	// As we need to inform the user about the number of items, we always try to retrieve the scan
	// objects even if is requested only the current object
	scans, err := scanDAO.FindAll(&pagination, expand)
	if err != nil {
		log.Println("Error while searching scans objects. Details:", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	var scansResponse protocol.ScansResponse
	var current model.CurrentScan

	if returnCurrent {
		// The current page will be page zero to avoid misunderstandment
		pagination.Page = 0
		current = model.GetCurrentScan()
		scansResponse = protocol.CurrentScanToScansResponse(current, pagination)

	} else {
		scansResponse = protocol.ScansToScansResponse(scans, pagination)
	}

	h.Response = &scansResponse

	// Last-Modified is going to be the most recent date of the list
	if returnCurrent {
		h.lastModifiedAt = current.LastModifiedAt

	} else {
		for _, scan := range scans {
			if scan.LastModifiedAt.After(h.lastModifiedAt) {
				h.lastModifiedAt = scan.LastModifiedAt
			}
		}
	}

	w.Header().Add("ETag", h.GetETag())
	w.Header().Add("Last-Modified", h.lastModifiedAt.Format(time.RFC1123))
	w.WriteHeader(http.StatusOK)
}