From c9ceeec44484e67cc70545ccd20a8005fe3d9dcc Mon Sep 17 00:00:00 2001 From: Motalleb Fallahnehzad Date: Sat, 7 Oct 2023 16:27:28 +0330 Subject: [PATCH] [Feat] --- config.new.yaml | 2 +- cord_locator.go | 24 +++++-- go.mod | 6 ++ go.sum | 5 ++ lib/internal_dns/internal_messages.go | 28 +++++++++ lib/new_config/entry_point/entry_point.go | 46 +++++++------- lib/new_config/entry_point/entry_type.go | 20 +++--- lib/new_config/new_config.go | 17 ++++- lib/new_config/provider/provider.go | 76 +++++++++++++++++++++++ lib/new_config/provider/provider_type.go | 42 +++++++++++++ lib/utils/iterable/mapper.go | 11 ++++ lib/validator/validator.go | 4 +- 12 files changed, 239 insertions(+), 42 deletions(-) create mode 100644 lib/internal_dns/internal_messages.go create mode 100644 lib/new_config/provider/provider.go create mode 100644 lib/new_config/provider/provider_type.go create mode 100644 lib/utils/iterable/mapper.go diff --git a/config.new.yaml b/config.new.yaml index 78d6685..6e72cab 100644 --- a/config.new.yaml +++ b/config.new.yaml @@ -10,7 +10,7 @@ default_providers: # (def(null)) - opendns providers: - cf: # naming providers is mandatory + - name: cf # naming providers is mandatory # DNS_SERVER_DEFINITION type: raw # (tls || https || lua) (if lua addresses should point to lua file) addresses: diff --git a/cord_locator.go b/cord_locator.go index 506a6dd..76e55e0 100644 --- a/cord_locator.go +++ b/cord_locator.go @@ -27,21 +27,34 @@ var ( ) var data = ` entry_points: - - name: - port: 53 # (def(53)) - type: raw # (def(raw) || tls || https) + - name: + port: # (def(53)) + type: raw # (def(raw) || tls || https) + +providers: + - name: # naming providers is mandatory + # DNS_SERVER_DEFINITION + type: raw # (tls || https || lua) (if lua addresses should point to lua file) + addresses: + - 1.1.1.1:53 + - 1.0.0.1:53 ` func main() { testConfig := newconfig.Config{} - yaml.Unmarshal([]byte(data), &testConfig) + err := yaml.Unmarshal([]byte(data), &testConfig) + if err != nil { + println(err.Error()) + } confs := make([]string, 0) - for _, item := range testConfig.EntryPoints { + for _, item := range testConfig.Providers { + println(item.Validate().Error()) confs = append(confs, item.String()) } log.Info().Msg(strings.Join(confs, ",")) return + log.Info().Msg("Starting DNS Server") address := DNSConfig.Global.Address udpServer := &dns.Server{Addr: address, Net: "udp"} @@ -95,6 +108,7 @@ func init() { } } + return configPath, hasConfigFile := os.LookupEnv("CONFIG_FILE") if !hasConfigFile { log.Warn().Msg("`CONFIG_FILE` was missing from environment table default value is `./config.yaml`") diff --git a/go.mod b/go.mod index 2ec3507..e6e6cf7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,11 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require ( + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect +) + require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -17,6 +22,7 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/sourcegraph/conc v0.3.0 github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index e6132d3..f263646 100644 --- a/go.sum +++ b/go.sum @@ -252,6 +252,8 @@ github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/sagikazarmark/crypt v0.10.0 h1:96E1qrToLBU6fGzo+PRRz7KGOc9FkYFiPnR3/zf8Smg= github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= @@ -267,6 +269,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -303,6 +306,8 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/lib/internal_dns/internal_messages.go b/lib/internal_dns/internal_messages.go new file mode 100644 index 0000000..cf657f9 --- /dev/null +++ b/lib/internal_dns/internal_messages.go @@ -0,0 +1,28 @@ +package data + +import ( + "github.com/FMotalleb/cord-locator/lib/utils/iterable" + "github.com/miekg/dns" +) + +type Question struct { + Type string + Class string + Name string +} + +func GetQuestions(msg *dns.Msg) []Question { + if msg == nil || len(msg.Question) == 0 { + return make([]Question, 0) + } + mapper := func(entry dns.Question) Question { + Qtype := dns.TypeToString[entry.Qtype] + Qclass := dns.ClassToString[entry.Qclass] + return Question{ + Type: Qtype, + Class: Qclass, + Name: entry.Name, + } + } + return iterable.Map[dns.Question, Question](msg.Question, mapper) +} diff --git a/lib/new_config/entry_point/entry_point.go b/lib/new_config/entry_point/entry_point.go index 190ec8a..785fab4 100644 --- a/lib/new_config/entry_point/entry_point.go +++ b/lib/new_config/entry_point/entry_point.go @@ -7,43 +7,49 @@ import ( "github.com/FMotalleb/cord-locator/lib/validator" ) -type EntryPoint struct { +type EntryPoint interface { + GetName() string + GetPort() int + GetType() EntryType + String() string + validator.Validatable +} +type EntryPointData struct { Name *string `yaml:"name,omitempty"` Port *int `yaml:"port,omitempty"` Type *string `yaml:"type,omitempty"` + EntryPoint } -func (receiver EntryPoint) GetName() string { +func (receiver EntryPointData) GetName() string { if receiver.Name == nil { return "" } return *receiver.Name } -func (receiver EntryPoint) GetPort() int { +func (receiver EntryPointData) GetPort() int { if receiver.Port == nil { return 53 } return *receiver.Port } -func (receiver EntryPoint) getTypeRaw() string { +func (receiver EntryPointData) getTypeRaw() string { if receiver.Type == nil { - return "" + return "Raw" } return *receiver.Type } -func (receiver EntryPoint) GetType() EntryType { - actual := parseType(receiver.Type) - switch actual { - case Undefined: +func (receiver EntryPointData) GetType() EntryType { + if receiver.Type == nil { return Raw - default: - return actual } + current := parseType(receiver.Type) + return current } -func (receiver EntryPoint) String() string { +func (receiver EntryPointData) String() string { buffer := strings.Builder{} buffer.WriteString("EntryPoint(") if len(receiver.GetName()) > 0 { @@ -52,20 +58,14 @@ func (receiver EntryPoint) String() string { buffer.WriteString("Name: '', ") } buffer.WriteString(fmt.Sprintf("Port: %d, ", receiver.GetPort())) - buffer.WriteString(fmt.Sprintf("Type: %v", parseType(receiver.Type).String())) + buffer.WriteString(fmt.Sprintf("Type: %v", receiver.GetType().String())) buffer.WriteString(")") return buffer.String() } -func (receiver EntryPoint) Validate() error { - // if len(receiver.GetName()) == 0 { - // return validator.NewValidationError( - // "to receive entry_points.name", - // "no name or empty name received", - // "missing name for an entry point in config file") - // } +func (receiver EntryPointData) Validate() error { if receiver.GetPort() < 1 || receiver.GetPort() > 65535 { return validator.NewValidationError( - "to receive a valid entry_points.port value (1-65535)", + "a valid entry_points.*.port value (1-65535)", fmt.Sprintf("%d", receiver.GetPort()), fmt.Sprintf("port value in entrypoint: %s", receiver.GetName())) } @@ -73,8 +73,8 @@ func (receiver EntryPoint) Validate() error { switch parseType(receiver.Type) { case Undefined: return validator.NewValidationError( - "to receive one of (raw | tls | https) in entry_points.type", - fmt.Sprintf("given type does not match expectee %s", receiver.getTypeRaw()), + "one of (raw | tls | https) in entry_points.*.type", + fmt.Sprintf("given type (%s) does not match expected values", receiver.getTypeRaw()), fmt.Sprintf("type value in entrypoint: %s", receiver.GetName())) default: break diff --git a/lib/new_config/entry_point/entry_type.go b/lib/new_config/entry_point/entry_type.go index 2705321..db0b91b 100644 --- a/lib/new_config/entry_point/entry_type.go +++ b/lib/new_config/entry_point/entry_type.go @@ -4,8 +4,8 @@ type EntryType int const ( Raw EntryType = iota - TLS - HTTPS + // TLS + // HTTPS Undefined = -1 ) @@ -16,10 +16,10 @@ func parseType(typeName *string) EntryType { switch *typeName { case "raw": return Raw - case "tls": - return TLS - case "https": - return HTTPS + // case "tls": + // return TLS + // case "https": + // return HTTPS default: return Undefined } @@ -28,10 +28,10 @@ func (receiver EntryType) String() string { switch receiver { case 0: return "Raw" - case 1: - return "TLS" - case 2: - return "HTTPS" + // case 1: + // return "TLS" + // case 2: + // return "HTTPS" default: return "Undefined" } diff --git a/lib/new_config/new_config.go b/lib/new_config/new_config.go index c5d3741..87b91b0 100644 --- a/lib/new_config/new_config.go +++ b/lib/new_config/new_config.go @@ -1,7 +1,20 @@ package newconfig -import entrypoint "github.com/FMotalleb/cord-locator/lib/new_config/entry_point" +import ( + entrypoint "github.com/FMotalleb/cord-locator/lib/new_config/entry_point" + "github.com/FMotalleb/cord-locator/lib/new_config/provider" + "github.com/FMotalleb/cord-locator/lib/utils/iterable" +) type Config struct { - EntryPoints []entrypoint.EntryPoint `yaml:"entry_points,flow"` + EntryPoints []entrypoint.EntryPointData `yaml:"entry_points,flow"` + Providers []provider.ProviderData `yaml:"providers,flow"` +} + +func (conf Config) GetEntryPoints() []entrypoint.EntryPoint { + mapper := func(entry entrypoint.EntryPointData) entrypoint.EntryPoint { + return entrypoint.EntryPoint(entry) + } + return iterable.Map(conf.EntryPoints, mapper) + } diff --git a/lib/new_config/provider/provider.go b/lib/new_config/provider/provider.go new file mode 100644 index 0000000..f60da38 --- /dev/null +++ b/lib/new_config/provider/provider.go @@ -0,0 +1,76 @@ +package provider + +import ( + "fmt" + "strings" + + "github.com/FMotalleb/cord-locator/lib/validator" + "github.com/miekg/dns" +) + +type Provider interface { + GetName() string + GetType() ProviderType + Resolve([]dns.Question) (answer []dns.RR, err error) + String() string + validator.Validatable +} + +type ProviderData struct { + Name *string `yaml:"name,omitempty"` + Type *string `yaml:"type,omitempty"` + Addresses *[]string `yaml:"addresses,omitempty"` + Provider +} + +func (receiver ProviderData) GetName() string { + if receiver.Name == nil { + panic("provider name is missing") + } + return *receiver.Name +} + +func (receiver ProviderData) GetType() ProviderType { + if receiver.Type == nil { + return Raw + } + current := parseType(receiver.Type) + return current +} + +func (receiver ProviderData) Resolve([]dns.Question) (answer []dns.RR, err error) { + // TODO: Implement Resolve method + panic("UnImplemented") +} + +func (receiver ProviderData) String() string { + buffer := strings.Builder{} + buffer.WriteString("Provider(") + buffer.WriteString(fmt.Sprintf("Name: %s, ", receiver.GetName())) + if receiver.Addresses != nil && len(*receiver.Addresses) > 0 { + addr := *receiver.Addresses + buffer.WriteString(fmt.Sprintf("Addresses: %s, ", strings.Join(addr, ", "))) + } + + buffer.WriteString(fmt.Sprintf("Type: %v", receiver.GetType().String())) + buffer.WriteString(")") + return buffer.String() +} +func (receiver ProviderData) Validate() error { + if receiver.Name == nil { + return validator.NewValidationError( + "a name for every provider -> providers.*.name", + "no name", + "missing name on a provider in yaml config") + } + switch receiver.GetType() { + case Undefined: + return validator.NewValidationError( + "one of (raw,lua,https,tls...) in providers.*.type", + fmt.Sprintf("`%s`", *receiver.Type), + fmt.Sprintf("the type in provider: `%s`", receiver.GetName())) + default: + break + } + return nil +} diff --git a/lib/new_config/provider/provider_type.go b/lib/new_config/provider/provider_type.go new file mode 100644 index 0000000..fd3bcb6 --- /dev/null +++ b/lib/new_config/provider/provider_type.go @@ -0,0 +1,42 @@ +package provider + +type ProviderType int + +const ( + Raw ProviderType = iota + // LUA + // TLS + // HTTPS + // MYSQL + // SQLLITE + // CSV + Undefined = -1 +) + +func parseType(typeName *string) ProviderType { + if typeName == nil { + return Undefined + } + switch *typeName { + case "raw": + return Raw + // case "tls": + // return TLS + // case "https": + // return HTTPS + default: + return Undefined + } +} +func (receiver ProviderType) String() string { + switch receiver { + case 0: + return "Raw" + // case 1: + // return "TLS" + // case 2: + // return "HTTPS" + default: + return "Undefined" + } +} diff --git a/lib/utils/iterable/mapper.go b/lib/utils/iterable/mapper.go new file mode 100644 index 0000000..0ec4e59 --- /dev/null +++ b/lib/utils/iterable/mapper.go @@ -0,0 +1,11 @@ +package iterable + +type Mapper[P any, R any] func(from P) R + +func Map[P, R any](from []P, mapper Mapper[P, R]) []R { + result := make([]R, len(from)) + for i := range from { + result[i] = mapper(from[i]) + } + return result +} diff --git a/lib/validator/validator.go b/lib/validator/validator.go index 61e74db..9c8e1a3 100644 --- a/lib/validator/validator.go +++ b/lib/validator/validator.go @@ -1,5 +1,7 @@ package validator -type Validator interface { +// Validatable Object that is able to validate itself +type Validatable interface { + // Validate that this object is correctly configured and usable Validate() error }