From 98537616c86778097d7845844bd13e59a67a772e Mon Sep 17 00:00:00 2001 From: Mael GRAMAIN Date: Mon, 14 Dec 2020 18:20:24 -0400 Subject: [PATCH] Commenting --- core/handleDnsRequest.go | 5 +++-- core/parseQuery.go | 11 ++++++----- main.go | 19 ++++++++++--------- utils/queries.go | 10 ++++++++-- utils/redis.go | 12 +++++++++++- utils/reverse.go | 3 ++- utils/sql.go | 32 ++++++++++++++++++-------------- utils/structs.go | 8 +++++++- utils/utils.go | 21 ++++++++++++--------- 9 files changed, 77 insertions(+), 44 deletions(-) diff --git a/core/handleDnsRequest.go b/core/handleDnsRequest.go index a0a926b..a9851a9 100644 --- a/core/handleDnsRequest.go +++ b/core/handleDnsRequest.go @@ -2,14 +2,15 @@ package core import "github.com/miekg/dns" -//Handle the DNS request +//Handle the DNS request using miekg/dns +//Requires dns.ReponseWriter and dns.Msg args func HandleDnsRequest(w dns.ResponseWriter, r *dns.Msg) { //dns.Msg object //Will be passed to the parseQuery() function m := new(dns.Msg) m.SetReply(r) - m.Compress = true //Less CPU usage (?) + m.Compress = false if r.Opcode == dns.OpcodeQuery { //Only respond to dns queries parseQuery(m) diff --git a/core/parseQuery.go b/core/parseQuery.go index 467a323..533d69d 100644 --- a/core/parseQuery.go +++ b/core/parseQuery.go @@ -19,20 +19,21 @@ import ( */ //Function called by handleDnsRequest to parse the query from records +//Requires dns.ReponseWriter args func parseQuery(m *dns.Msg) { for _, q := range m.Question { 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}) + record := utils.GetRecord(utils.Record{Fqdn: q.Name, Qtype: q.Qtype}) //Get the record in the SQL or Redis database - if record.Content != "" { //If the record is found, return it - log.Infof("DNS : Record found for '%s' => '%s'\n", q.Name, record.Content) + 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 rr, err := dns.NewRR(fmt.Sprintf("%s %v %s %s", q.Name, record.TTL, dns.TypeToString[q.Qtype], record.Content)) //Create the response if err == nil { //If no err - m.Answer = append(m.Answer, rr) + m.Answer = append(m.Answer, rr) //Append the record to the response } - } else { + } else { //If the record is empty log it as DEBUG logrus.Debugf("DNS : No record for '%s' (type '%v')\n", record.Fqdn, record.Qtype) } diff --git a/main.go b/main.go index 09245fa..2c122d9 100644 --- a/main.go +++ b/main.go @@ -18,30 +18,31 @@ var DB *sql.DB //Main loop func main() { - //Get config patch + //Get the config patch from --config flag configPatch := flag.String("config", "extra/config.ini.example", "the patch to the config file") flag.Parse() - //Load Configuration + //Load the INI configuration file conf = new(utils.Conf) err := ini.MapTo(conf, *configPatch) utils.CheckErr(err) + //Set up the Logrus logger utils.InitLogger(conf) - // attach request handler func + //Attach DNS request handler func for all domains dns.HandleFunc(".", core.HandleDnsRequest) - //Init redis database + //Initialize the redis database utils.RedisDatabase(conf) - //Init sql database + //Initialize the sql database utils.SqlDatabase(conf) - // start server - server := &dns.Server{Addr: conf.App.Ip + strconv.Itoa(conf.App.Port), Net: "udp"} //define the server - logrus.WithFields(logrus.Fields{"ip": conf.App.Ip, "port": conf.App.Port}).Infof("SERVER : Started") - err = server.ListenAndServe() //start it + //Start the DNS server + server := &dns.Server{Addr: conf.App.Ip + strconv.Itoa(conf.App.Port), Net: "udp"} //define the server + logrus.WithFields(logrus.Fields{"ip": conf.App.Ip, "port": conf.App.Port}).Infof("SERVER : Started") //log + err = server.ListenAndServe() //start it utils.CheckErr(err) defer server.Shutdown() //shut down on application closing diff --git a/utils/queries.go b/utils/queries.go index 5b0a106..7654294 100644 --- a/utils/queries.go +++ b/utils/queries.go @@ -8,28 +8,34 @@ import ( "github.com/sirupsen/logrus" ) +//Check the SQL and REDIS database for a Record. +//A Record struct is used as input and output func GetRecord(entry Record) Record { //Check for strict record in Redis cache redisKey := entry.Fqdn + "--" + fmt.Sprint(entry.Qtype) - result, redisErr := redisCheckForRecord(redisKey, entry) - var sqlErr int + var sqlErr int //The err returned for sqlCheckForRecord or sqlCheckForReverse6Wildcard //If reverse DNS reverseCheck := IsReverse(entry.Fqdn) if reverseCheck > 0 { + //If reverse record not found in redis if redisErr == redis.Nil { + //Check for it in the SQL database logrus.Debug("QUERIES : Check for strict reverse in MySQL") result, sqlErr = sqlCheckForRecord(redisKey, entry.Fqdn, entry) if sqlErr == 1 { + //Check for wildcard reverse in the SQL logrus.Debug("QUERIES : Check for wildcard reverse in MySQL") result, _ = sqlCheckForReverse6Wildcard(redisKey, entry.Fqdn, entry) } } //For dynamic reverse dns + //Check for it by looking for a "%s" in the record content + //If true, replace it with the formated IP if strings.Contains(result.Content, "%s") { record := ExtractAddressFromReverse(entry.Fqdn) var recordFormated string diff --git a/utils/redis.go b/utils/redis.go index 9197346..0793b6d 100644 --- a/utils/redis.go +++ b/utils/redis.go @@ -10,17 +10,22 @@ import ( "github.com/sirupsen/logrus" ) +//Redis context var ctx = context.Background() +//Redis client as global var var redisDb *redis.Client +//Initialize the Redis Database +//Requires a conf struct +//Return a *redis.Client func RedisDatabase(conf *Conf) *redis.Client { logrus.WithFields(logrus.Fields{"ip": conf.Redis.Ip, "port": conf.Redis.Port}).Infof("REDIS : Connection to DB") rdb := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%v", conf.Redis.Ip, conf.Redis.Port), Password: conf.Redis.Password, DB: conf.Redis.Db, - }) + }) //Connect to the DB //Test Redis connection err := rdb.Set(ctx, "alive", 1, 0).Err() @@ -38,6 +43,9 @@ func RedisDatabase(conf *Conf) *redis.Client { return rdb } +//Check for a record in the Redis database +//Requires the redis key (as string) and the record to check (struct) +//Return a Record (struct) and error (if any) func redisCheckForRecord(redisKey string, entry Record) (Record, error) { val, err := redisDb.Get(ctx, redisKey).Result() @@ -52,6 +60,8 @@ func redisCheckForRecord(redisKey string, entry Record) (Record, error) { } } +//Add a record in the Redis database +//Return an error (if any) func redisSet(c *redis.Client, key string, ttl time.Duration, value interface{}) error { p, err := json.Marshal(value) if err != nil { diff --git a/utils/reverse.go b/utils/reverse.go index 6a6bbb6..5179cba 100644 --- a/utils/reverse.go +++ b/utils/reverse.go @@ -7,7 +7,8 @@ import ( "github.com/sirupsen/logrus" ) -// Based on github.com/coredns/coredns/plugin/pkg/dnsutil/reverse.go +// DISCLAIMER : Based on https://github.com/coredns/coredns/blob/master/plugin/pkg/dnsutil/reverse.go +// DISCLAIMER : Will be rewrited from scratch in future release // IsReverse returns 0 is name is not in a reverse zone. Anything > 0 indicates // name is in a reverse zone. The returned integer will be 1 for in-addr.arpa. (IPv4) diff --git a/utils/sql.go b/utils/sql.go index d4e6eb6..e442b61 100644 --- a/utils/sql.go +++ b/utils/sql.go @@ -10,24 +10,27 @@ import ( "github.com/sirupsen/logrus" ) +//SQL database as global var var DB *sql.DB +//Initialize the (My)SQL Database +//Requires a conf struct func SqlDatabase(conf *Conf) { logrus.WithFields(logrus.Fields{"database": conf.Database.Db}).Infof("SQL : Connection to DB") - //db conn + //Connect to the Database db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", conf.Database.Username, conf.Database.Password, conf.Database.Ip, conf.Database.Port, conf.Database.Db)) CheckErr(err) DB = db - SqlTest() //Test SQL conn - // if there is an error opening the connection, handle it - CheckErr(err) + SqlTest() //Test SQL connexion } +//Test the SQL connexion by selecting all records from the database func SqlTest() { _, err := DB.Query("SELECT name, content FROM records") - CheckErr(err) + CheckErr(err) //Panic if any error } +//Check for a record in the SQL database func sqlCheckForRecord(redisKey string, dKey string, entry Record) (Record, int) { dbg := DB.QueryRow( "SELECT id, content, ttl FROM records WHERE `name` = ? AND `type` = ?;", dKey, entry.Qtype).Scan( @@ -36,35 +39,36 @@ func sqlCheckForRecord(redisKey string, dKey string, entry Record) (Record, int) &entry.TTL, ) - //logrus.WithFields(logrus.Fields{"name": dKey, "type": entry.Qtype}).Debugf("SQL : ") - - if dbg != nil { + if dbg != nil { //If any err logrus.Debugf("SQL : %v", dbg) } - logrus.Debugf("SQL : %s => %s", entry.Fqdn, entry.Content) + logrus.Debugf("SQL : %s => %s", entry.Fqdn, entry.Content) //log the result - if entry.Content != "" { + if entry.Content != "" { //If Record content not empty //Cache the request in Redis if any result logrus.Debugf("REDIS : Set entry for %s", redisKey) - _ = redisSet(redisDb, redisKey, 30*time.Second, entry) + _ = redisSet(redisDb, redisKey, 30*time.Second, entry) //Set it in the Redis database for 30sec return entry, 0 } else { - //Else return nil + //Else return 1 for err return entry, 1 } } +//Check for a wildcard record in the SQL database func sqlCheckForReverse6Wildcard(redisKey string, dKey string, entry Record) (Record, error) { returnedEntry := entry - results, err := DB.Query("SELECT id, content, name FROM records WHERE name LIKE '*%.ip6.arpa.';") - DbgErr(err) + results, err := DB.Query("SELECT id, content, name FROM records WHERE name LIKE '*%.ip6.arpa.';") //Get ALL reverse IPs + DbgErr(err) //Check for empty row or non important error + //For each result check if it match the reverse IP for results.Next() { err = results.Scan(&returnedEntry.Id, &returnedEntry.Content, &returnedEntry.Fqdn) CheckErr(err) + //Check if the record is matching the reversed IP if checkReverse6(entry, returnedEntry) { logrus.Debug("REVERSE : Correct wildcard reverse.") //Cache the request in Redis if any result diff --git a/utils/structs.go b/utils/structs.go index 2a98e8a..d41eb94 100644 --- a/utils/structs.go +++ b/utils/structs.go @@ -1,6 +1,6 @@ package utils -//Structs for configuration +//Struct for App (dns server) configuration in the config.ini file type App struct { Port int Ip string @@ -8,6 +8,7 @@ type App struct { Logfile bool } +//Struct for SQL Database configuration in the config.ini file type Database struct { Ip string Port string @@ -16,6 +17,7 @@ type Database struct { Db string } +//Struct for Redis Database configuration in the config.ini file type Redis struct { Ip string Port int @@ -24,6 +26,7 @@ type Redis struct { Ttl int } +//Struct for the whole config.ini file when it will be parsed by go-ini type Conf struct { App_mode string App @@ -31,6 +34,7 @@ type Conf struct { Redis } +//Struct for a Domain (not used currently). type Domain struct { ID int `json:"id"` FriendlyName string @@ -39,6 +43,8 @@ type Domain struct { LastEdit string } +//Struct for a domain record +//Defined by it's ID, DomainID (parent domain), Fqdn (or name), Content (value of the record), Type (as Qtype/int), TTL (used only for the DNS response and not the Redis TTL) type Record struct { Id int DomainId int diff --git a/utils/utils.go b/utils/utils.go index 48f05e2..d591766 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -10,29 +10,30 @@ import ( "github.com/snowzach/rotatefilehook" ) +//If fatal error, log it and panic func CheckErr(err error) { if err != nil { log.Fatalf("%s\n ", err.Error()) - panic(err) } } -//Only used for non fatal errors. +//If basic error, log it as classic error but don't panic and keep kalm func DbgErr(err error) { if err != nil { log.Errorf("%s\n ", err.Error()) - panic(err) } } +//Init the logrus logger with rotateFileHook. +//Conf struct passed to get informations about the logger (debug or not) func InitLogger(conf *Conf) { - var logLevel = logrus.InfoLevel + var logLevel = logrus.InfoLevel //By default the level is Info. - if conf.App_mode != "production" { + if conf.App_mode != "production" { //If the configuration contains anything different than "production"; the level is set to Debug logLevel = logrus.DebugLevel } - rotateFileHook, err := rotatefilehook.NewRotateFileHook(rotatefilehook.RotateFileConfig{ + rotateFileHook, err := rotatefilehook.NewRotateFileHook(rotatefilehook.RotateFileConfig{ //Rotate file hook, By default 50Mb max and 28 days retention Filename: conf.App.Logdir + "/console.log", MaxSize: 50, // megabytes MaxBackups: 3, @@ -49,15 +50,15 @@ func InitLogger(conf *Conf) { logrus.Fatalf("Failed to initialize file rotate hook: %v", err) } - logrus.SetLevel(logLevel) - logrus.SetOutput(colorable.NewColorableStdout()) + logrus.SetLevel(logLevel) //Set the log level + logrus.SetOutput(colorable.NewColorableStdout()) //Force colors in the Stdout logrus.SetFormatter(&logrus.TextFormatter{ ForceColors: false, FullTimestamp: true, TimestampFormat: time.RFC822, }) - if conf.App.Logfile { + if conf.App.Logfile { //If file logging is enabled logrus.AddHook(rotateFileHook) } @@ -65,6 +66,8 @@ func InitLogger(conf *Conf) { log.WithFields(log.Fields{"logLevel": logLevel}).Debug("Log level") } +//Check if a reverse wildcard correspond to a record using strings.Contains +//Return bool func checkReverse6(entry Record, result Record) bool { check := strings.Replace(entry.Fqdn, result.Fqdn[1:], "", 1) logrus.WithFields(logrus.Fields{"entry": entry.Fqdn, "result": result.Fqdn[1:]}).Debug("REVERSE checkReverse6 :")