Skip to content

Commit

Permalink
Import DNS from zeek/spicy-analyzers@2e5a185.
Browse files Browse the repository at this point in the history
  • Loading branch information
bbannier committed Dec 8, 2021
0 parents commit 490029d
Show file tree
Hide file tree
Showing 27 changed files with 613 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .cmake-format.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"parse": {
"additional_commands": {
"spicy_add_analyzer": {
"kwargs": {
"NAME": "*",
"PACKAGE_NAME": "*",
"SOURCES": "*",
"SCRIPTS": "*"
}
}
}
},
"format": {
"line_width": 100,
"tab_size": 4,
"separate_ctrl_name_with_space": true,
"max_subgroups_hwrap": 3
},
"markup": {
"enable_markup": false
},
"lint": {
"disabled_codes": ["C0103"]
}
}
39 changes: 39 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Check

on:
pull_request:
push:
branches: [main]

jobs:
Check:
runs-on: ubuntu-latest
container: zeekurity/spicy

steps:
- uses: actions/checkout@v2
- name: Prepare
env:
PATH: /usr/local/bin:/opt/cmake/bin:/opt/spicy/bin:/opt/zeek/bin:/opt/zeek/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
run: |
# Clean out leftover logs for to make it easier to print "our" logs.
zkg autoconfig
rm -f $(zkg config state_dir)/logs/*.log
zkg -vvvvv purge --force
# Manually install spicy-plugin until the image contains a zkg version with a fix for https://github.com/zeek/package-manager/issues/106 (>=zkg-2.12.0).
zkg -vvvvv install --force --skiptests spicy-plugin
- name: Install
env:
PATH: /usr/local/bin:/opt/cmake/bin:/opt/spicy/bin:/opt/zeek/bin:/opt/zeek/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
run: |
git clean -fd
eval $(zkg env)
echo Y | zkg -vvvvv install .
- name: Show logs
if: always()
run: |
tail -n 1000000 $(zkg config state_dir)/logs/*.log
14 changes: 14 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: pre-commit

on:
pull_request:
push:
branches: [main]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: pre-commit/[email protected]
1 change: 1 addition & 0 deletions .mdlrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rules "~MD033", "~MD013", "~MD046", "~MD010"
23 changes: 23 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files

- repo: https://github.com/markdownlint/markdownlint
rev: v0.11.0
hooks:
- id: markdownlint

- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13
hooks:
- id: cmake-format
- id: cmake-lint

exclude: '^tests/Baseline'
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.15 FATAL_ERROR)

project(Message LANGUAGES C)

list(PREPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
find_package(SpicyPlugin REQUIRED)

# Set mininum versions that this plugin needs. Make sure to use "x.y.z" format.
spicy_require_version("1.2.0")
spicy_plugin_require_version("0.99.0")
zeek_require_version("3.0.0")

if (NOT CMAKE_BUILD_TYPE)
# Default to release build.
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "")
endif ()

add_subdirectory(analyzer)
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DNS analyzer
============

This repository contains a
[Spicy](https://docs.zeek.org/projects/spicy/en/latest/)-based analyzer for the
DNS protocol. This analyzer replaces the builtin Zeek DNS analyzer.
5 changes: 5 additions & 0 deletions analyzer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
spicy_add_analyzer(
NAME Message
PACKAGE_NAME Message
SOURCES analyzer.spicy analyzer.evt zeek_analyzer.spicy
SCRIPTS __load__.zeek)
2 changes: 2 additions & 0 deletions analyzer/__load__.zeek
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@load-sigs ./dpd.sig
@load ./main.zeek
85 changes: 85 additions & 0 deletions analyzer/analyzer.evt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright (c) 2021 by the Zeek Project. See LICENSE for details.

protocol analyzer spicy::DNS over UDP:
parse with DNS::Message,
port 53/udp,
port 137/udp,
port 5353/udp,
port 5355/udp,
replaces DNS;

import Zeek_DNS;


@if ZEEK_VERSION >= 30200
# Zeek >= 3.2 adds an additional parameter with the query in its original spelling.

on DNS::Question if ( msg.header.flags.qr == 0 )
-> event dns_request($conn, Zeek_DNS::message(msg.header), Zeek_DNS::name(self.qname).lower(), self.qtype, self.qclass, Zeek_DNS::name(self.qname));

on DNS::Question if ( msg.header.flags.qr == 1 && ! msg.header.rejected )
-> event dns_query_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::name(self.qname).lower(), self.qtype, self.qclass, Zeek_DNS::name(self.qname));

on DNS::Question if ( msg.header.flags.qr == 1 && msg.header.rejected )
-> event dns_rejected($conn, Zeek_DNS::message(msg.header), Zeek_DNS::name(self.qname).lower(), self.qtype, self.qclass, Zeek_DNS::name(self.qname));

on DNS::Message if ( |self.question| == 0 && ! self.header.rejected )
-> event dns_query_reply($conn, Zeek_DNS::message(self.header), b"<no query>", cast<uint64>(0), cast<uint64>(0), b"<no query>");

on DNS::Message if ( |self.question| == 0 && self.header.rejected )
-> event dns_rejected($conn, Zeek_DNS::message(self.header), b"<no query>", cast<uint64>(0), cast<uint64>(0), b"<no query>");
@else
on DNS::Question if ( msg.header.flags.qr == 0 )
-> event dns_request($conn, Zeek_DNS::message(msg.header), Zeek_DNS::name(self.qname).lower(), self.qtype, self.qclass);

on DNS::Question if ( msg.header.flags.qr == 1 && ! msg.header.rejected )
-> event dns_query_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::name(self.qname).lower(), self.qtype, self.qclass);

on DNS::Question if ( msg.header.flags.qr == 1 && msg.header.rejected )
-> event dns_rejected($conn, Zeek_DNS::message(msg.header), Zeek_DNS::name(self.qname).lower(), self.qtype, self.qclass);

on DNS::Message if ( |self.question| == 0 && ! self.header.rejected )
-> event dns_query_reply($conn, Zeek_DNS::message(self.header), b"<no query>", cast<uint64>(0), cast<uint64>(0));

on DNS::Message if ( |self.question| == 0 && self.header.rejected )
-> event dns_rejected($conn, Zeek_DNS::message(self.header), b"<no query>", cast<uint64>(0), cast<uint64>(0));
@endif

# TODO: Length of raw payload?
on DNS::Message::header -> event dns_message($conn, Zeek_DNS::is_query(self.header), Zeek_DNS::message(self.header), cast<uint64>(0));

on DNS::Message
-> event dns_end($conn, Zeek_DNS::message(self.header));

on DNS::ResourceRecord if ( self.ty == DNS::RDType::A )
-> event dns_A_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), self.a);

on DNS::ResourceRecord if ( self.ty == DNS::RDType::A6 )
-> event dns_A6_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), self.a);

on DNS::ResourceRecord if ( self.ty == DNS::RDType::AAAA )
-> event dns_AAAA_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), self.a);

on DNS::ResourceRecord if ( self.ty == DNS::RDType::NS )
-> event dns_NS_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), Zeek_DNS::name(self.rname));

on DNS::ResourceRecord if ( self.ty == DNS::RDType::CNAME )
-> event dns_CNAME_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), Zeek_DNS::name(self.rname));

on DNS::ResourceRecord if ( self.ty == DNS::RDType::PTR )
-> event dns_PTR_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), Zeek_DNS::name(self.rname));

on DNS::ResourceRecord if ( self.ty == DNS::RDType::MX )
-> event dns_MX_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), Zeek_DNS::name(self.mx.name), self.mx.preference);

on DNS::ResourceRecord if ( self.ty == DNS::RDType::SOA )
-> event dns_SOA_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), Zeek_DNS::soa(self.soa));

on DNS::ResourceRecord if ( self.ty == DNS::RDType::TXT )
-> event dns_TXT_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), [i.data for i in self.txt]);

on DNS::ResourceRecord if ( self.ty == DNS::RDType::WKS )
-> event dns_WKS_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype));

on DNS::ResourceRecord if ( self.ty == DNS::RDType::SRV )
-> event dns_SRV_reply($conn, Zeek_DNS::message(msg.header), Zeek_DNS::answer(self, rrtype), Zeek_DNS::name(self.srv.target), self.srv.priority_, self.srv.weight, self.srv.port_);
155 changes: 155 additions & 0 deletions analyzer/analyzer.spicy
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Copyright (c) 2021 by the Zeek Project. See LICENSE for details.

module DNS;

import spicy;

type RDType = enum {
A = 1, NS = 2, MD = 3, MF = 4, CNAME = 5, SOA = 6, MB = 7, MG = 8, MR = 9,
NULL = 10, WKS = 11, PTR = 12, HINFO = 13, MINFO = 14, MX = 15, TXT = 16,
AAAA = 28, NBS = 32, SRV= 33, A6 = 38, EDNS = 41 };

type RRType = enum {
ANSWER = 1, AUTH = 2, ADDL = 3
};

public type Message = unit {
%random-access;

header: Header;
question: (Question(self))[self.header.qdcount];
answer: (ResourceRecord(self, RRType::ANSWER))[self.header.ancount];
authority: (ResourceRecord(self, RRType::AUTH))[self.header.nscount];
additional: (ResourceRecord(self, RRType::ADDL))[self.header.arcount];
};

type Header = unit {
id : uint16;
flags : bitfield(16) {
qr: 0;
opcode: 1..4;
aa: 5;
tc: 6;
rd: 7;
ra: 8;
z: 9..11;
rcode: 12..15;
} &bit-order = spicy::BitOrder::MSB0;

qdcount: uint16;
ancount: uint16;
nscount: uint16;
arcount: uint16;

var rejected : bool;

on %done {
# Mimic Zeek in determining when a request has been rejected.
if ( self.qdcount == 0 )
self.rejected = (self.flags.rcode != 0); # 0 == NoError;

else
self.rejected = (self.flags.qr == 1 &&
self.ancount == 0 &&
self.nscount == 0 &&
self.arcount == 0);
}
};

type Question = unit(msg: Message) {
qname: Name(msg);
qtype: uint16;
qclass: uint16;
};

type ResourceRecord = unit(msg: Message, rrtype: RRType) {
name: Name(msg);
ty: uint16 &convert=RDType($$);
class: uint16;
ttl: uint32 &convert=cast<interval>($$);
rdlen: uint16;

switch ( self.ty ) {
RDType::NS, RDType::CNAME, RDType::PTR
-> rname: Name(msg);
RDType::A -> a: addr &ipv4;
RDType::AAAA -> a: addr &ipv6;
RDType::MX -> mx: RDataMX(msg);
RDType::SOA -> soa: RDataSOA(msg);
RDType::SRV -> srv: RDataSRV(msg);
RDType::TXT -> txt: (CharacterString(msg))[self.rdlen];

* -> rdata: bytes &size=self.rdlen;
};
};

type RDataMX = unit(msg: Message) {
preference: uint16;
name: Name(msg);
};

type RDataSOA = unit(msg: Message) {
mname: Name(msg);
rname: Name(msg);
serial: uint32;
refresh: uint32 &convert=cast<interval>($$);
retry: uint32 &convert=cast<interval>($$);
expire: uint32 &convert=cast<interval>($$);
minimum: uint32 &convert=cast<interval>($$);
};

type RDataSRV = unit(msg: Message) {
priority_: uint16;
weight: uint16;
port_: uint16;
target: Name(msg);
};

type CharacterString = unit(msg: Message) {
len: uint8;
data: bytes &size=(self.len);
};

type Name = unit(msg: Message) {
: (Label(msg, self))[] &until=($$.len.offset == 0 || $$.len.compressed != 0);

var label: bytes = b"";
};

type Pointer = unit(msg: Message, label: Label) {
len: bitfield(16) {
offset: 0..13;
};

name: Name(msg) &parse-at=(msg.input() + self.len.offset);
};

type Label = unit(msg: Message, inout name: Name) {
%random-access;

len: bitfield(8) {
offset: 0..5;
compressed: 6..7;
};

switch ( self.len.compressed ) {
0 -> label: bytes &size=self.len.offset {
if ( |self.label| ) {
name.label += b".";
name.label += self.label;
}
}

3 -> ptr: Pointer(msg, self) &parse-at=self.input() {
name.label += self.ptr.name.label;
self.adjust = 2; # Consume the additional byte in %done.
}
};

on %done {
if ( self.adjust > 0 )
self.set_input(self.input() + self.adjust);
}

var adjust: uint64 = 0;
};
Loading

0 comments on commit 490029d

Please sign in to comment.