Multiple records support

This commit is contained in:
Mael G. 2020-12-27 19:05:16 -04:00
parent a1cd3ea5a1
commit e05cab9d5e
6 changed files with 65 additions and 56 deletions

View file

@ -25,8 +25,9 @@ func parseQuery(m *dns.Msg) {
log.Infof("DNS : Query for %s (type : %v)\n", q.Name, q.Qtype) //Log log.Infof("DNS : Query for %s (type : %v)\n", q.Name, q.Qtype) //Log
record := utils.GetRecord(utils.Record{Fqdn: q.Name, Qtype: q.Qtype}) //Get the record in the SQL or Redis database records := utils.GetRecord(utils.Record{Fqdn: q.Name, Qtype: q.Qtype}) //Get the record in the SQL or Redis database
for _, record := range records {
if record.Content != "" { //If the record is not empty if record.Content != "" { //If the record is not empty
log.Infof("DNS : Record found for '%s' => '%s'\n", q.Name, record.Content) //Log the content as INFO log.Infof("DNS : Record found for '%s' => '%s'\n", q.Name, record.Content) //Log the content as INFO
rr, err := dns.NewRR(fmt.Sprintf("%s %v %s %s", q.Name, record.TTL, dns.TypeToString[q.Qtype], record.Content)) //Create the response rr, err := dns.NewRR(fmt.Sprintf("%s %v %s %s", q.Name, record.TTL, dns.TypeToString[q.Qtype], record.Content)) //Create the response
@ -36,6 +37,6 @@ func parseQuery(m *dns.Msg) {
} else { //If the record is empty log it as DEBUG } else { //If the record is empty log it as DEBUG
logrus.Debugf("DNS : No record for '%s' (type '%v')\n", record.Fqdn, record.Qtype) logrus.Debugf("DNS : No record for '%s' (type '%v')\n", record.Fqdn, record.Qtype)
} }
}
} }
} }

1
go.mod
View file

@ -3,7 +3,6 @@ module github.com/outout14/sacrebleu-dns
go 1.15 go 1.15
require ( require (
github.com/go-redis/redis v6.15.9+incompatible
github.com/go-redis/redis/v8 v8.4.2 github.com/go-redis/redis/v8 v8.4.2
github.com/mattn/go-colorable v0.1.8 github.com/mattn/go-colorable v0.1.8
github.com/miekg/dns v1.1.35 github.com/miekg/dns v1.1.35

2
go.sum
View file

@ -15,8 +15,6 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.4.2 h1:gKRo1KZ+O3kXRfxeRblV5Tr470d2YJZJVIAv2/S8960= github.com/go-redis/redis/v8 v8.4.2 h1:gKRo1KZ+O3kXRfxeRblV5Tr470d2YJZJVIAv2/S8960=
github.com/go-redis/redis/v8 v8.4.2/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= github.com/go-redis/redis/v8 v8.4.2/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=

View file

@ -10,7 +10,7 @@ import (
//GetRecord : Check the SQL and REDIS database for a Record. //GetRecord : Check the SQL and REDIS database for a Record.
//A Record struct is used as input and output //A Record struct is used as input and output
func GetRecord(entry Record) Record { func GetRecord(entry Record) []Record {
//Check for strict record in Redis cache //Check for strict record in Redis cache
redisKey := entry.Fqdn + "--" + fmt.Sprint(entry.Qtype) redisKey := entry.Fqdn + "--" + fmt.Sprint(entry.Qtype)
result, redisErr := redisCheckForRecord(redisKey, entry) result, redisErr := redisCheckForRecord(redisKey, entry)
@ -29,14 +29,15 @@ func GetRecord(entry Record) Record {
if sqlErr { if sqlErr {
//Check for wildcard reverse in the SQL //Check for wildcard reverse in the SQL
logrus.Debug("QUERIES : Check for wildcard reverse in MySQL") logrus.Debug("QUERIES : Check for wildcard reverse in MySQL")
result, _ = sqlCheckForReverse6Wildcard(redisKey, entry.Fqdn, entry) result = sqlCheckForReverse6Wildcard(redisKey, entry.Fqdn, entry)
} }
} }
//For dynamic reverse dns //For dynamic reverse dns
//Check for it by looking for a "%s" in the record content //Check for it by looking for a "%s" in the record content
//If true, replace it with the formated IP //If true, replace it with the formated IP
if strings.Contains(result.Content, "%s") { for _, r := range result {
if strings.Contains(r.Content, "%s") {
record := ExtractAddressFromReverse(entry.Fqdn) record := ExtractAddressFromReverse(entry.Fqdn)
var recordFormated string var recordFormated string
if reverseCheck == 1 { if reverseCheck == 1 {
@ -44,8 +45,10 @@ func GetRecord(entry Record) Record {
} else { } else {
recordFormated = strings.ReplaceAll(record, ":", "-") recordFormated = strings.ReplaceAll(record, ":", "-")
} }
result.Content = fmt.Sprintf(result.Content, recordFormated) r.Content = fmt.Sprintf(r.Content, recordFormated)
} }
}
} else if redisErr == redis.Nil { //If strict record NOT in Redis cache & not Reverse } else if redisErr == redis.Nil { //If strict record NOT in Redis cache & not Reverse
//Check for wildcard in Redis cache //Check for wildcard in Redis cache
logrus.Debug("QUERIES : Check for wildcard in redis cache") logrus.Debug("QUERIES : Check for wildcard in redis cache")

View file

@ -46,16 +46,17 @@ func RedisDatabase(conf *Conf) *redis.Client {
//Check for a record in the Redis database //Check for a record in the Redis database
//Requires the redis key (as string) and the record to check (struct) //Requires the redis key (as string) and the record to check (struct)
//Return a Record (struct) and error (if any) //Return a Record (struct) and error (if any)
func redisCheckForRecord(redisKey string, entry Record) (Record, error) { func redisCheckForRecord(redisKey string, entry Record) ([]Record, error) {
val, err := redisDb.Get(ctx, redisKey).Result() val, err := redisDb.Get(ctx, redisKey).Result()
var result []Record
//If Record in Redis cache //If Record in Redis cache
if err == nil { if err == nil {
err := json.Unmarshal([]byte(val), &entry) err := json.Unmarshal([]byte(val), &result)
logrus.Debugf("REDIS : %s => %s", redisKey, entry.Content) return result, err
return entry, err
} }
return entry, redis.Nil return result, redis.Nil
} }
//Add a record in the Redis database //Add a record in the Redis database

View file

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/go-redis/redis"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"gorm.io/driver/mysql" "gorm.io/driver/mysql"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
@ -15,13 +14,10 @@ import (
//DB SQL database as global var //DB SQL database as global var
var db *gorm.DB var db *gorm.DB
//SQLDatabase Initialize the (My)SQL Database //SQLDatabase Initialize the SQL Database
//Requires a conf struct //Requires a conf struct
func SQLDatabase(conf *Conf) { func SQLDatabase(conf *Conf) *gorm.DB {
logrus.WithFields(logrus.Fields{"database": conf.Database.Db, "driver": conf.Database.Type}).Infof("SQL : Connection to DB") logrus.WithFields(logrus.Fields{"database": conf.Database.Db, "driver": conf.Database.Type}).Infof("SQL : Connection to DB")
//Connect to the Database
var err error
var gormLogLevel logger.LogLevel var gormLogLevel logger.LogLevel
//Set GORM log level based on conf AppMode //Set GORM log level based on conf AppMode
@ -31,21 +27,25 @@ func SQLDatabase(conf *Conf) {
gormLogLevel = logger.Silent gormLogLevel = logger.Silent
} }
//Connect to the Database
if conf.Database.Type == "postgresql" { if conf.Database.Type == "postgresql" {
dsn := fmt.Sprintf("user=%s password=%s host=%s port=%s database=%s sslmode=disable", conf.Database.Username, conf.Database.Password, conf.Database.Host, conf.Database.Port, conf.Database.Db) dsn := fmt.Sprintf("user=%s password=%s host=%s port=%s database=%s sslmode=disable", conf.Database.Username, conf.Database.Password, conf.Database.Host, conf.Database.Port, conf.Database.Db)
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{ DB, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(gormLogLevel), Logger: logger.Default.LogMode(gormLogLevel),
}) })
CheckErr(err)
} else { db = DB
return DB
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", conf.Database.Username, conf.Database.Password, conf.Database.Host, conf.Database.Port, conf.Database.Db) dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", conf.Database.Username, conf.Database.Password, conf.Database.Host, conf.Database.Port, conf.Database.Db)
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{ DB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(gormLogLevel), Logger: logger.Default.LogMode(gormLogLevel),
}) })
}
CheckErr(err) CheckErr(err)
db = DB
return DB
} }
//SQLMigrate : Launch the database migration (creation of tables) //SQLMigrate : Launch the database migration (creation of tables)
@ -55,33 +55,40 @@ func SQLMigrate() {
} }
//Check for a record in the SQL database //Check for a record in the SQL database
func sqlCheckForRecord(redisKey string, dKey string, entry Record) (Record, bool) { func sqlCheckForRecord(redisKey string, dKey string, entry Record) ([]Record, bool) {
db.Where("fqdn = ? AND type = ?", dKey, entry.Qtype).First(&entry) var records []Record
logrus.Debugf("SQL : %s => %s", entry.Fqdn, entry.Content) //log the result rows, err := db.Where("fqdn = ? AND type = ?", dKey, entry.Qtype).Model(&Record{}).Rows()
if err != nil {
return records, true
}
defer rows.Close()
for rows.Next() {
var entry Record
db.ScanRows(rows, &entry)
if entry.Content != "" { //If Record content not empty if entry.Content != "" { //If Record content not empty
//Cache the request in Redis if any result records = append(records, entry)
logrus.Debugf("REDIS : Set entry for %s", redisKey)
_ = redisSet(redisDb, redisKey, 30*time.Second, entry) //Set it in the Redis database for 30sec
return entry, false
} }
//Else return 1 for err }
return entry, true //Cache the request in Redis if any result
_ = redisSet(redisDb, redisKey, 30*time.Second, records) //Set it in the Redis database for 30sec
return records, false
} }
//Check for a wildcard record in the SQL database //Check for a wildcard record in the SQL database
func sqlCheckForReverse6Wildcard(redisKey string, dKey string, entry Record) (Record, error) { func sqlCheckForReverse6Wildcard(redisKey string, dKey string, entry Record) []Record {
returnedEntry := entry returnedEntry := entry
rows, err := db.Table("records").Select("id", "content", "fqdn").Where("fqdn LIKE ?", "*%.ip6.arpa.").Rows() rows, err := db.Table("records").Select("id", "content", "fqdn").Where("fqdn LIKE ?", "*%.ip6.arpa.").Rows()
DbgErr(err) //Check for empty row or non important error DbgErr(err) //Check for empty row or non important error
var records []Record
//For each result check if it match the reverse IP //For each result check if it match the reverse IP
for rows.Next() { for rows.Next() {
err = rows.Scan(&returnedEntry.ID, &returnedEntry.Content, &returnedEntry.Fqdn) rows.Scan(&returnedEntry.ID, &returnedEntry.Content, &returnedEntry.Fqdn)
CheckErr(err) CheckErr(err)
//Check if the record is matching the reversed IP //Check if the record is matching the reversed IP
@ -89,11 +96,11 @@ func sqlCheckForReverse6Wildcard(redisKey string, dKey string, entry Record) (Re
logrus.Debug("REVERSE : Correct wildcard reverse.") logrus.Debug("REVERSE : Correct wildcard reverse.")
//Cache the request in Redis if any result //Cache the request in Redis if any result
_ = redisSet(redisDb, redisKey, 10*time.Second, returnedEntry) _ = redisSet(redisDb, redisKey, 10*time.Second, returnedEntry)
return returnedEntry, err records = append(records, entry)
} }
logrus.Debug("REVERSE : WRONG wildcard reverse .") logrus.Debug("REVERSE : WRONG wildcard reverse .")
} }
return entry, redis.Nil return records
} }