Example #1
0
func 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") {
			continue
		}

		zoneName := zoneNameFromFile(fileName)

		seenZones[zoneName] = true

		if zone, ok := zones[zoneName]; !ok || file.ModTime().After(zone.LastRead) {
			if ok {
				logPrintf("Reloading %s\n", fileName)
			} else {
				logPrintf("Reading new file %s\n", fileName)
			}

			//log.Println("FILE:", i, file, zoneName)
			config, err := readZoneFile(zoneName, path.Join(dirName, fileName))
			if config == nil || err != nil {
				parseErr = fmt.Errorf("Error reading zone '%s': %s", zoneName, err)
				log.Println(parseErr.Error())
				continue
			}
			config.LastRead = file.ModTime()

			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)
		zone.Close()
		dns.HandleRemove(zoneName)
		delete(zones, zoneName)
	}

	return parseErr
}
Example #2
0
func 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

			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
}