diff --git a/config.yml b/config.yml index 7083cf83..fb6b239a 100644 --- a/config.yml +++ b/config.yml @@ -207,8 +207,6 @@ multiplexer: # listen-ip: 0.0.0.0 # # listening port # listen-port: 8080 -# # enable basic authentication -# basic-auth-enable: true # # default login # basic-auth-login: admin # # default password @@ -232,6 +230,8 @@ multiplexer: # basic-auth-login: admin # # default password # basic-auth-pwd: changeme +# # enable basic authentication +# basic-auth-enable: true # # tls support # tls-support: false # # tls mutual @@ -541,11 +541,15 @@ multiplexer: ################################################ # # Use this transformer to add base64 dns payload in JSON ouput +# # additionnals directive for text format +# # - extracted-dns-payload: dns payload encoded in base64 # extract: # # enable payload base64 encoding # add-payload: true # # Use this transformer to detect trafic duplication +# # additionnals directive for text format +# # - repeated: number of duplication # reducer: # # enable detector # repetitive-traffic-detector: true @@ -553,6 +557,8 @@ multiplexer: # watch-interval: 5 # # Use this transformer to compute latency and detect timeout on queries +# # additionnals directive for text format +# # - computed-latency: computed latency between queries and replies # latency: # # Measure latency between replies and queries # measure-latency: false diff --git a/dnsutils/message.go b/dnsutils/message.go index 6fd66908..16d28859 100644 --- a/dnsutils/message.go +++ b/dnsutils/message.go @@ -308,7 +308,7 @@ func (dm *DnsMessage) handlePdnsDirectives(directives []string, s *bytes.Buffer) } func (dm *DnsMessage) handleSuspiciousDirectives(directives []string, s *bytes.Buffer) { - if dm.PowerDns == nil { + if dm.Suspicious == nil { s.WriteString("-") } else { switch directive := directives[0]; { @@ -506,6 +506,22 @@ func (dm *DnsMessage) String(format []string, fieldDelimiter string, fieldBounda return string(dm.Bytes(format, fieldDelimiter, fieldBoundary)) } +func (dm *DnsMessage) ToJson() string { + buffer := new(bytes.Buffer) + json.NewEncoder(buffer).Encode(dm) + return buffer.String() +} + +func (dm *DnsMessage) ToFlattenJson() (string, error) { + buffer := new(bytes.Buffer) + flat, err := dm.Flatten() + if err != nil { + return "", err + } + json.NewEncoder(buffer).Encode(flat) + return buffer.String(), nil +} + func (dm *DnsMessage) ToDnstap() ([]byte, error) { if len(dm.DnsTap.Payload) > 0 { return dm.DnsTap.Payload, nil diff --git a/dnsutils/message_test.go b/dnsutils/message_test.go index d939f9e0..df2eaccf 100644 --- a/dnsutils/message_test.go +++ b/dnsutils/message_test.go @@ -159,3 +159,252 @@ func TestDnsMessage_TextFormat_DefaultDirectives(t *testing.T) { }) } } + +func TestDnsMessage_TextFormat_Directives_PublicSuffix(t *testing.T) { + config := GetFakeConfig() + + testcases := []struct { + name string + format string + dm DnsMessage + expected string + }{ + { + name: "undefined", + format: "publixsuffix-tld", + dm: DnsMessage{}, + expected: "-", + }, + { + name: "default", + format: "publixsuffix-tld publixsuffix-etld+1", + dm: DnsMessage{PublicSuffix: &PublicSuffix{QnamePublicSuffix: "com", QnameEffectiveTLDPlusOne: "google.com"}}, + expected: "com google.com", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + line := tc.dm.String( + strings.Fields(tc.format), + config.Global.TextFormatDelimiter, + config.Global.TextFormatBoundary, + ) + if line != tc.expected { + t.Errorf("Want: %s, got: %s", tc.expected, line) + } + }) + } +} + +func TestDnsMessage_TextFormat_Directives_Geo(t *testing.T) { + config := GetFakeConfig() + + testcases := []struct { + name string + format string + dm DnsMessage + expected string + }{ + { + name: "undefined", + format: "geoip-continent", + dm: DnsMessage{}, + expected: "-", + }, + { + name: "default", + format: "geoip-continent geoip-country geoip-city geoip-as-number geoip-as-owner", + dm: DnsMessage{Geo: &DnsGeo{City: "Paris", Continent: "Europe", + CountryIsoCode: "FR", AutonomousSystemNumber: "AS1", AutonomousSystemOrg: "Google"}}, + expected: "Europe FR Paris AS1 Google", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + line := tc.dm.String( + strings.Fields(tc.format), + config.Global.TextFormatDelimiter, + config.Global.TextFormatBoundary, + ) + if line != tc.expected { + t.Errorf("Want: %s, got: %s", tc.expected, line) + } + }) + } +} + +func TestDnsMessage_TextFormat_Directives_Pdns(t *testing.T) { + config := GetFakeConfig() + + testcases := []struct { + name string + format string + dm DnsMessage + expected string + }{ + { + name: "undefined", + format: "powerdns-tags", + dm: DnsMessage{}, + expected: "-", + }, + { + name: "empty_attributes", + format: "powerdns-tags powerdns-applied-policy powerdns-original-request-subnet powerdns-metadata", + dm: DnsMessage{PowerDns: &PowerDns{}}, + expected: "- - - -", + }, + { + name: "applied_policy", + format: "powerdns-applied-policy", + dm: DnsMessage{PowerDns: &PowerDns{AppliedPolicy: "test"}}, + expected: "test", + }, + { + name: "original_request_subnet", + format: "powerdns-original-request-subnet", + dm: DnsMessage{PowerDns: &PowerDns{OriginalRequestSubnet: "test"}}, + expected: "test", + }, + { + name: "metadata_badsyntax", + format: "powerdns-metadata", + dm: DnsMessage{PowerDns: &PowerDns{Metadata: map[string]string{"test_key1": "test_value1"}}}, + expected: "-", + }, + { + name: "metadata", + format: "powerdns-metadata:test_key1", + dm: DnsMessage{PowerDns: &PowerDns{Metadata: map[string]string{"test_key1": "test_value1"}}}, + expected: "test_value1", + }, + { + name: "metadata_invalid", + format: "powerdns-metadata:test_key2", + dm: DnsMessage{PowerDns: &PowerDns{Metadata: map[string]string{"test_key1": "test_value1"}}}, + expected: "-", + }, + { + name: "tags_all", + format: "powerdns-tags", + dm: DnsMessage{PowerDns: &PowerDns{Tags: []string{"tag1", "tag2"}}}, + expected: "tag1,tag2", + }, + { + name: "tags_index", + format: "powerdns-tags:1", + dm: DnsMessage{PowerDns: &PowerDns{Tags: []string{"tag1", "tag2"}}}, + expected: "tag2", + }, + { + name: "tags_invalid_index", + format: "powerdns-tags:3", + dm: DnsMessage{PowerDns: &PowerDns{Tags: []string{"tag1", "tag2"}}}, + expected: "-", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + line := tc.dm.String( + strings.Fields(tc.format), + config.Global.TextFormatDelimiter, + config.Global.TextFormatBoundary, + ) + if line != tc.expected { + t.Errorf("Want: %s, got: %s", tc.expected, line) + } + }) + } +} + +func TestDnsMessage_TextFormat_Directives_Suspicious(t *testing.T) { + config := GetFakeConfig() + + testcases := []struct { + name string + format string + dm DnsMessage + expected string + }{ + { + name: "undefined", + format: "suspicious-score", + dm: DnsMessage{}, + expected: "-", + }, + { + name: "default", + format: "suspicious-score", + dm: DnsMessage{Suspicious: &Suspicious{Score: 4.0}}, + expected: "4", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + line := tc.dm.String( + strings.Fields(tc.format), + config.Global.TextFormatDelimiter, + config.Global.TextFormatBoundary, + ) + if line != tc.expected { + t.Errorf("Want: %s, got: %s", tc.expected, line) + } + }) + } +} + +func TestDnsMessage_TextFormat_Directives_Extracted(t *testing.T) { + config := GetFakeConfig() + + testcases := []struct { + name string + format string + dm DnsMessage + expected string + }{ + { + name: "undefined", + format: "extracted-dns-payload", + dm: DnsMessage{}, + expected: "-", + }, + { + name: "default", + format: "extracted-dns-payload", + dm: DnsMessage{Extracted: &Extracted{}, DNS: Dns{Payload: []byte{ + 0x9e, 0x84, 0x01, 0x20, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + // query 1 + 0x01, 0x61, 0x00, + // type A, class IN + 0x00, 0x01, 0x00, 0x01, + // query 2 + 0x01, 0x62, 0x00, + // type A, class IN + 0x00, 0x01, 0x00, 0x01, + // query 3 + 0x01, 0x63, 0x00, + // type AAAA, class IN + 0x00, 0x1c, 0x00, 0x01, + }}}, + expected: "noQBIAADAAAAAAAAAWEAAAEAAQFiAAABAAEBYwAAHAAB", + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + line := tc.dm.String( + strings.Fields(tc.format), + config.Global.TextFormatDelimiter, + config.Global.TextFormatBoundary, + ) + if line != tc.expected { + t.Errorf("Want: %s, got: %s", tc.expected, line) + } + }) + } +} diff --git a/go.mod b/go.mod index 7b01b231..e9e68be7 100644 --- a/go.mod +++ b/go.mod @@ -133,9 +133,9 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/common v0.42.0 github.com/prometheus/procfs v0.9.0 // indirect - github.com/prometheus/prometheus v0.42.0 // indirect + github.com/prometheus/prometheus v0.42.0 golang.org/x/mod v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/tools v0.6.0 // indirect