type: log
paths:
{{ range $i, $path := .paths }}
 - {{$path}}
{{ end }}
exclude_files: [".gz$"]
tags: {{.tags | tojson}}
publisher_pipeline.disable_host: {{ inList .tags "forwarded" }}

fields_under_root: true
fields:
  network.protocol: dns

processors:
  - rename:
      fields:
        - {from: message, to: event.original}
  - decode_json_fields:
      fields: [event.original]
      target: zeek.dns
  - registered_domain:
      ignore_missing: true
      ignore_failure: true
      field: zeek.dns.query
      target_field: dns.question.registered_domain
      target_subdomain_field: dns.question.subdomain
      target_etld_field: dns.question.top_level_domain
  - script:
      lang: javascript
      id: zeek_dns_flags
      source: >
        var net = require("net");

        function addDnsHeaderFlags(evt) {
            var flag = evt.Get("zeek.dns.AA");
            if (flag === true) {
                evt.AppendTo("dns.header_flags", "AA");
            }
            flag = evt.Get("zeek.dns.TC");
            if (flag === true) {
                evt.AppendTo("dns.header_flags", "TC");
            }
            flag = evt.Get("zeek.dns.RD");
            if (flag === true) {
                evt.AppendTo("dns.header_flags", "RD");
            }
            flag = evt.Get("zeek.dns.RA");
            if (flag === true) {
                evt.AppendTo("dns.header_flags", "RA");
            }
        }

        function addDnsQuestionClass(evt) {
            var qclass = evt.Get("zeek.dns.qclass");
            if (!qclass) {
                return;
            }
            switch (qclass) {
                case 1:
                    qclass = "IN";
                    break;
                case 3:
                    qclass = "CH";
                    break;
                case 4:
                    qclass = "HS";
                    break;
                case 254:
                    qclass = "NONE";
                    break;
                case 255:
                    qclass = "ANY";
                    break;
            }
            evt.Put("dns.question.class", qclass);
        }

        function addDnsAnswers(evt) {
            var answers = evt.Get("zeek.dns.answers");
            var ttls = evt.Get("zeek.dns.TTLs");
            if (!answers || !ttls || answers.length != ttls.length) {
                return;
            }

            var resolvedIps = [];
            var answersObjs = [];
            for (var i = 0; i < answers.length; i++) {
                var answer = answers[i];
                answersObjs.push({
                    data: answer,
                    ttl: ttls[i],
                })
                if (net.isIP(answer)) {
                    resolvedIps.push(answer);
                }
            }
            evt.Put("dns.answers", answersObjs);
            if (resolvedIps.length > 0) {
                evt.Put("dns.resolved_ip", resolvedIps);
            }
        }

        function setDnsType(evt) {
            var response_code = evt.Get("zeek.dns.rcode_name");
            if (response_code) {
              evt.Put("dns.type", "answer");
            } else {
              evt.Put("dns.type", "query");
            }
        }

        function addEventDuration(evt) {
            var rttSec = evt.Get("zeek.dns.rtt");
            if (!rttSec) {
                return;
            }
            evt.Put("event.duration", rttSec * 1000000000);
        }

        function addTopLevelDomain(evt) {
            var rd = evt.Get("dns.question.registered_domain");
            if (!rd) {
                return;
            }
            var firstPeriod = rd.indexOf(".");
            if (firstPeriod == -1) {
                return;
            }
            evt.Put("dns.question.top_level_domain", rd.substr(firstPeriod + 1));
        }

        function addEventOutcome(evt) {
            var rcode = evt.Get("zeek.dns.rcode");
            if (rcode == null) {
                return;
            }
            if (rcode == 0) {
                evt.Put("event.outcome", "success");
            } else {
                evt.Put("event.outcome", "failure");
            }
        }

        function addRelatedIP(evt) {
            var related = [];
            var src = evt.Get("zeek.dns.id.orig_h");
            if (src != null) {
                related.push(src);
            }
            var dst = evt.Get("zeek.dns.id.resp_h");
            if (dst != null) {
                related.push(dst);
            }
            if (related.length > 0) {
                evt.Put("related.ip", related);
            }
        }

        function process(evt) {
            addDnsHeaderFlags(evt);
            addDnsQuestionClass(evt);
            addDnsAnswers(evt);
            setDnsType(evt);
            addEventDuration(evt);
            addTopLevelDomain(evt);
            addEventOutcome(evt);
            addRelatedIP(evt);
        }
  - convert:
      ignore_missing: true
      fail_on_error: false
      mode: rename
      fields:
        - {from: zeek.dns.id.orig_h, to: source.address}
        - {from: zeek.dns.id.orig_p, to: source.port, type: long}
        - {from: zeek.dns.id.resp_h, to: destination.address}
        - {from: zeek.dns.id.resp_p, to: destination.port, type: long}
        - {from: zeek.dns.uid, to: zeek.session_id}
        - {from: zeek.dns.proto, to: network.transport}
  - convert:
      ignore_missing: true
      fail_on_error: false
      mode: copy
      fields:
        - {from: source.address, to: source.ip, type: ip}
        - {from: destination.address, to: destination.ip, type: ip}
        - {from: zeek.session_id, to: event.id}
        - {from: zeek.dns.trans_id, to: dns.id, type: string}
        - {from: zeek.dns.query, to: dns.question.name}
        - {from: zeek.dns.qtype_name, to: dns.question.type}
        - {from: zeek.dns.rcode_name, to: dns.response_code}
  - convert:
      ignore_missing: true
      fail_on_error: false
      fields:
        - {from: zeek.dns.trans_id, type: string}
  - add_fields:
      target: event
      fields:
        kind: event
        category:
          - network
        type:
          - connection
          - info
          - protocol
  - community_id:
{{ if .internal_networks }}
  - add_network_direction:
      source: source.ip
      destination: destination.ip
      target: network.direction
      internal_networks: {{ .internal_networks | tojson }}
{{ end }}
  - drop_fields:
      ignore_missing: true
      fields:
        - zeek.dns.Z
        - zeek.dns.auth
        - zeek.dns.addl
  - add_fields:
      target: ''
      fields:
        ecs.version: 1.12.0
