예제 #1
0
// Notify is responsable for selecting the domains that should be notified in the system.
// It will send alert e-mails for each owner of a domain
func Notify() {
	defer func() {
		// Something went really wrong while notifying the owners. Log the error stacktrace
		// and move out
		if r := recover(); r != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			log.Printf("Panic detected while notifying the owners. Details: %v\n%s", r, buf)
		}
	}()

	log.Info("Start notification job")
	defer func() {
		log.Info("End notification job")
	}()

	log.Debugf("Initializing database with the parameters: URIS - %v | Name - %s | Auth - %t | Username - %s",
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
	)

	database, databaseSession, err := mongodb.Open(
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
		config.ShelterConfig.Database.Auth.Password,
	)

	if err != nil {
		log.Println("Error while initializing database. Details:", err)
		return
	}
	defer databaseSession.Close()

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

	domainChannel, err := domainDAO.FindAllAsyncToBeNotified(
		config.ShelterConfig.Notification.NameserverErrorAlertDays,
		config.ShelterConfig.Notification.NameserverTimeoutAlertDays,
		config.ShelterConfig.Notification.DSErrorAlertDays,
		config.ShelterConfig.Notification.DSTimeoutAlertDays,

		// TODO: Should we move this configuration parameter to a place were both modules can
		// access it. This sounds better for configuration deployment
		config.ShelterConfig.Scan.VerificationIntervals.MaxExpirationAlertDays,
	)

	if err != nil {
		log.Println("Error retrieving domains to notify. Details:", err)
		return
	}

	// Dispatch the asynchronous part of the method
	for {
		// Get domain from the database (one-by-one)
		domainResult := <-domainChannel

		// Detect errors while retrieving a specific domain. We are not going to stop all the
		// process when only one domain got an error
		if domainResult.Error != nil {
			log.Println("Error retrieving domain to notify. Details:", domainResult.Error)
			continue
		}

		// Problem detected while retrieving a domain or we don't have domains anymore
		if domainResult.Error != nil || domainResult.Domain == nil {
			break
		}

		if err := notifyDomain(domainResult.Domain); err != nil {
			log.Println("Error notifying a domain. Details:", err)
		}
	}
}
예제 #2
0
// The main function of the system is responsable for deploying all system components that
// are enabled. For now we have the REST server and the scan system
func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	logPath := filepath.Join(
		config.ShelterConfig.BasePath,
		config.ShelterConfig.LogFilename,
	)

	if err := log.SetOutput(logPath); err != nil {
		log.Println(err)
		return
	}
	defer log.Close()

	if config.ShelterConfig.RESTServer.Enabled {
		var err error
		restListeners, err = rest.Listen()
		if err != nil {
			log.Println("Error while aquiring interfaces for REST server. Details:", err)
			os.Exit(ErrListeningRESTInterfaces)
		}

		if err := rest.Start(restListeners); err != nil {
			log.Println("Error starting the REST server. Details:", err)
			os.Exit(ErrStartingRESTServer)
		}

		log.Info("REST server started")
	}

	if config.ShelterConfig.WebClient.Enabled {
		var err error
		clientListeners, err = client.Listen()
		if err != nil {
			log.Println("Error while aquiring interfaces for Client server. Details:", err)
			os.Exit(ErrListeningClientInterfaces)
		}

		if err := client.Start(clientListeners); err != nil {
			log.Println("Error starting the Client server. Details:", err)
			os.Exit(ErrStartingWebClient)
		}

		log.Info("Web client started")
	}

	if config.ShelterConfig.Scan.Enabled {
		// Attention: Cannot use timezone abbreviations
		// http://stackoverflow.com/questions/25368415/golang-timezone-parsing
		scanTime, err := time.Parse("15:04:05 -0700", config.ShelterConfig.Scan.Time)
		if err != nil {
			log.Println("Scan time not in a valid format. Details:", err)
			os.Exit(ErrScanTimeFormat)
		}

		scanTime = scanTime.UTC()
		now := time.Now().UTC()

		nextExecution := time.Date(
			now.Year(),
			now.Month(),
			now.Day(),
			scanTime.Hour(),
			scanTime.Minute(),
			scanTime.Second(),
			scanTime.Nanosecond(),
			scanTime.Location(),
		)

		scheduler.Register(scheduler.Job{
			Type:          scheduler.JobTypeScan,
			NextExecution: nextExecution,
			Interval:      time.Duration(config.ShelterConfig.Scan.IntervalHours) * time.Hour,
			Task:          scan.ScanDomains,
		})

		// Must be called after registering in scheduler, because we retrieve the next execution time
		// from it
		if err := model.InitializeCurrentScan(); err != nil {
			log.Println("Current scan information got an error while initializing. Details:", err)
			os.Exit(ErrCurrentScanInitialize)
		}
	}

	if config.ShelterConfig.Notification.Enabled {
		if err := notification.LoadTemplates(); err != nil {
			log.Println("Error loading notification templates. Details:", err)
			os.Exit(ErrNotificationTemplates)
		}

		// Attention: Cannot use timezone abbreviations
		// http://stackoverflow.com/questions/25368415/golang-timezone-parsing
		notificationTime, err := time.Parse("15:04:05 -0700", config.ShelterConfig.Scan.Time)
		if err != nil {
			log.Println("Scan time not in a valid format. Details:", err)
			os.Exit(ErrScanTimeFormat)
		}

		notificationTime = notificationTime.UTC()
		now := time.Now().UTC()

		nextExecution := time.Date(
			now.Year(),
			now.Month(),
			now.Day(),
			notificationTime.Hour(),
			notificationTime.Minute(),
			notificationTime.Second(),
			notificationTime.Nanosecond(),
			notificationTime.Location(),
		)

		scheduler.Register(scheduler.Job{
			Type:          scheduler.JobTypeNotification,
			NextExecution: nextExecution,
			Interval:      time.Duration(config.ShelterConfig.Notification.IntervalHours) * time.Hour,
			Task:          notification.Notify,
		})
	}

	scheduler.Start()

	select {}
}
예제 #3
0
파일: scan.go 프로젝트: rafaeljusto/shelter
// Function responsible for running the domain scan system, checking the configuration of each
// domain in the database according to an algorithm. This method is synchronous and will return only
// after the scan proccess is done
func ScanDomains() {
	defer func() {
		// Something went really wrong while scanning the domains. Log the error stacktrace
		// and move out
		if r := recover(); r != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			log.Printf("Panic detected while scanning domains. Details: %v\n%s", r, buf)
		}
	}()

	log.Info("Start scan job")
	defer func() {
		log.Info("End scan job")
	}()

	log.Debugf("Initializing database with the parameters: URIS - %v | Name - %s | Auth - %t | Username - %s",
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
	)

	database, databaseSession, err := mongodb.Open(
		config.ShelterConfig.Database.URIs,
		config.ShelterConfig.Database.Name,
		config.ShelterConfig.Database.Auth.Enabled,
		config.ShelterConfig.Database.Auth.Username,
		config.ShelterConfig.Database.Auth.Password,
	)

	if err != nil {
		log.Println("Error while initializing database. Details:", err)
		return
	}
	defer databaseSession.Close()

	injector := NewInjector(
		database,
		config.ShelterConfig.Scan.DomainsBufferSize,
		config.ShelterConfig.Scan.VerificationIntervals.MaxOKDays,
		config.ShelterConfig.Scan.VerificationIntervals.MaxErrorDays,
		config.ShelterConfig.Scan.VerificationIntervals.MaxExpirationAlertDays,
	)

	querierDispatcher := NewQuerierDispatcher(
		config.ShelterConfig.Scan.NumberOfQueriers,
		config.ShelterConfig.Scan.DomainsBufferSize,
		config.ShelterConfig.Scan.UDPMaxSize,
		time.Duration(config.ShelterConfig.Scan.Timeouts.DialSeconds)*time.Second,
		time.Duration(config.ShelterConfig.Scan.Timeouts.ReadSeconds)*time.Second,
		time.Duration(config.ShelterConfig.Scan.Timeouts.WriteSeconds)*time.Second,
		config.ShelterConfig.Scan.ConnectionRetries,
	)

	collector := NewCollector(
		database,
		config.ShelterConfig.Scan.SaveAtOnce,
	)

	// Create a new scan information
	model.StartNewScan()

	var scanGroup sync.WaitGroup
	errorsChannel := make(chan error, config.ShelterConfig.Scan.ErrorsBufferSize)
	domainsToQueryChannel := injector.Start(&scanGroup, errorsChannel)
	domainsToSaveChannel := querierDispatcher.Start(&scanGroup, domainsToQueryChannel)
	collector.Start(&scanGroup, domainsToSaveChannel, errorsChannel)

	// Keep track of errors for the scan information structure
	errorDetected := false

	go func() {
		for {
			select {
			case err := <-errorsChannel:
				// Detect the poison pill to finish the error listener go routine. This poison
				// pill should be sent after all parts of the scan are done and we are sure that
				// we don't have any error to log anymore
				if err == nil {
					return

				} else {
					errorDetected = true
					log.Println("Error detected while executing the scan. Details:", err)
				}
			}
		}
	}()

	// Wait for all parts of the scan to finish their job
	scanGroup.Wait()

	// Finish the error listener sending a poison pill
	errorsChannel <- nil

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

	// Save the scan information for future reports
	if err := model.FinishAndSaveScan(errorDetected, scanDAO.Save); err != nil {
		log.Println("Error while saving scan information. Details:", err)
	}
}