Commenting
This commit is contained in:
parent
3459498605
commit
98537616c8
9 changed files with 77 additions and 44 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
15
main.go
15
main.go
|
@ -18,29 +18,30 @@ 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
|
||||
//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")
|
||||
logrus.WithFields(logrus.Fields{"ip": conf.App.Ip, "port": conf.App.Port}).Infof("SERVER : Started") //log
|
||||
err = server.ListenAndServe() //start it
|
||||
utils.CheckErr(err)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
32
utils/sql.go
32
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 :")
|
||||
|
|
Loading…
Reference in a new issue