summaryrefslogtreecommitdiffstats
path: root/contrib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-04 19:22:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-04 20:43:22 +0000
commit22c74419e2c258319bc723351876604b3304604b (patch)
tree8c799a78d53f67388fdf42900657eda617c1306a /contrib
parentInitial commit. (diff)
downloaddnscap-22c74419e2c258319bc723351876604b3304604b.tar.xz
dnscap-22c74419e2c258319bc723351876604b3304604b.zip
Adding upstream version 2.0.0+debian.upstream/2.0.0+debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib')
-rwxr-xr-xcontrib/cdsdump.py699
-rwxr-xr-xcontrib/cdsidxchk.py797
2 files changed, 1496 insertions, 0 deletions
diff --git a/contrib/cdsdump.py b/contrib/cdsdump.py
new file mode 100755
index 0000000..3eee22f
--- /dev/null
+++ b/contrib/cdsdump.py
@@ -0,0 +1,699 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2016-2021, OARC, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import sys
+import logging
+import optparse
+import struct
+import socket
+from cbor2 import CBORDecoder;
+
+logging.basicConfig(format='%(levelname).5s: %(module)s:%(lineno)d: '
+ '%(message)s')
+log = logging.getLogger(__name__)
+
+class SimpleValue(object):
+ def __init__(self, value):
+ self.value = value
+
+ def get(self):
+ return self.value
+
+ def __repr__(self):
+ return "{}".format(self.value)
+
+def decode_simple_value(self, fp, shareable_index=None):
+ return SimpleValue(struct.unpack('>B', fp.read(1))[0])
+
+try:
+ from cbor2.types import CBORSimpleValue
+except Exception:
+ CBORSimpleValue = SimpleValue
+
+class LastValues(object):
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.ts = None
+ self.src_addr4 = None
+ self.src_port4 = None
+ self.dest_addr4 = None
+ self.dest_port4 = None
+ self.src_addr6 = None
+ self.src_port6 = None
+ self.dest_addr6 = None
+ self.dest_port6 = None
+ self.rlabel = []
+ self.mlabel = []
+ self.rr_type = None
+ self.rr_class = None
+ self.rr_ttl = None
+
+last = LastValues()
+
+MAX_RLABELS = 255
+MIN_RLABEL_SIZE = 3
+
+def get_rlabel(idx):
+ rlabel_idx = -idx - 1
+ try:
+ label = last.rlabel.pop(rlabel_idx)
+ last.rlabel.insert(0, label)
+ return label
+ except:
+ raise Exception("rlabel index {} out of range".format(rlabel_idx))
+
+def add_rlabel(label):
+ size = 0
+ if isinstance(label, list):
+ for l in label:
+ if isinstance(l, str):
+ size += len(l)
+ if size < MIN_RLABEL_SIZE:
+ return
+ last.rlabel.insert(0, label)
+ if len(last.rlabel) > MAX_RLABELS:
+ last.rlabel.pop()
+
+def build_mlabel_label(label):
+ if isinstance(label, int) and label < 0:
+ label = get_rlabel(label)
+ else:
+ add_rlabel(label)
+
+ if isinstance(label, str):
+ last.mlabel.append(label)
+ elif isinstance(label, list):
+ if len(label) and isinstance(label[0], int):
+ last.mlabel.append(label)
+ return
+ label = list(label)
+ while len(label):
+ last.mlabel.append(list(label))
+ label.pop(0)
+
+def build_mlabel(rrs):
+ for rr in rrs:
+ if len(rr) and isinstance(rr[0], bool):
+ continue
+ if len(rr):
+ build_mlabel_label(rr[0])
+ if len(rr) > 1 and isinstance(rr[len(rr)-1], list):
+ for l in rr[len(rr)-1]:
+ build_mlabel_label(l)
+
+def parse_label(label, lvl):
+ if isinstance(label, int) and label < 0:
+ label = get_rlabel(label)
+ else:
+ add_rlabel(label)
+
+ if isinstance(label, bytes):
+ print((" " * lvl)+"label: {}".format(bytes))
+ elif isinstance(label, list):
+ if len(label) and isinstance(label[0], int) and label[0] < 0:
+ dn = list(get_rlabel(label[0]))
+ else:
+ dn = list(label)
+ print((" " * lvl)+"clabel: {}".format(dn))
+ dnstr = []
+ seen_mlabel = {}
+ while len(dn):
+ while isinstance(dn[0], int):
+ if dn[0] in seen_mlabel:
+ dn = [ "{ name compression loop }" ]
+ break
+ seen_mlabel[dn[0]] = 1
+ dn = list(last.mlabel[dn[0]])
+ dnstr.append(dn.pop(0))
+ print((" " * lvl)+"label: "+ " . ".join(dnstr))
+
+ else:
+ raise Exception("invalid label type {}".format(type(label)))
+
+
+def parse_rrs(rrs, lvl):
+ for rr in rrs:
+ print((" " * lvl)+"rr:")
+ lvl+=2
+
+ if len(rr) and isinstance(rr[0], bool):
+ print((" " * lvl)+"incomplete/broken DNS RR, no support for these yet")
+ continue
+
+ parse_label(rr.pop(0), lvl)
+
+ bits = 0
+ if isinstance(rr[0], CBORSimpleValue):
+ bits = rr.pop(0).value
+ print((" " * lvl)+"type (0): "+("yes" if bits & 1 else "no"))
+ print((" " * lvl)+"class (1): "+("yes" if bits & 1<<1 else "no"))
+ print((" " * lvl)+"ttl (2): "+("yes" if bits & 1<<2 else "no"))
+ print((" " * lvl)+"rdlength(3): "+("yes" if bits & 1<<3 else "no"))
+
+ rr_type = None
+ rr_class = None
+ rr_ttl = None
+ rdlength = None
+ if not bits:
+ if len(rr) > 4:
+ bits = 0xff
+ elif len(rr) > 1:
+ raise Exception("invalid rr, expected none (0) or all (4) optional values but got {}".format(len(rr)-1))
+ if bits & 1:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.type, expected int but got: {}".format(type(rr[0])))
+ rr_type = rr.pop(0)
+ if bits & 1<<1:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.class, expected int but got: {}".format(type(rr[0])))
+ rr_class = rr.pop(0)
+ if bits & 1<<2:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.ttl, expected int but got: {}".format(type(rr[0])))
+ rr_ttl = rr.pop(0)
+ if bits & 1<<3:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.rdlength, expected int but got: {}".format(type(rr[0])))
+ rdlength = rr.pop(0)
+
+ if not rr_type:
+ rr_type = last.rr_type
+ if not rr_class:
+ rr_class = last.rr_class
+ if not rr_ttl:
+ rr_ttl = last.rr_ttl
+
+ print((" " * lvl)+"type: {}".format(rr_type))
+ print((" " * lvl)+"class: {}".format(rr_class))
+ print((" " * lvl)+"ttl: {}".format(rr_ttl))
+ if rdlength:
+ print((" " * lvl)+"rdlength: {}".format(rdlength))
+
+ if rr_type != 41:
+ last.rr_type = rr_type
+ last.rr_class = rr_class
+ last.rr_ttl = rr_ttl
+
+ if isinstance(rr[0], bytes):
+ print((" " * lvl)+"rdata: "+"".join("{:02x}".format(byte) for byte in rr.pop(0)))
+ elif isinstance(rr[0], list):
+ rdata = []
+ for i in rr.pop(0):
+ if isinstance(i, int) and i < 0:
+ i = get_rlabel(i)
+ elif not isinstance(i, bytes):
+ add_rlabel(i)
+
+ if isinstance(i, bytes):
+ rdata.append("".join("{:02x}".format(byte) for byte in i))
+ elif isinstance(i, list):
+ dn = list(i)
+ dnstr = []
+ seen_mlabel = {}
+ while len(dn):
+ while isinstance(dn[0], int):
+ if dn[0] in seen_mlabel:
+ dn = [ "{ name compression loop }" ]
+ break
+ seen_mlabel[dn[0]] = 1
+ dn = list(last.mlabel[dn[0]])
+ dnstr.append(dn.pop(0))
+ rdata.append("[ clabel: {} label: ".format(i) + " . ".join(dnstr) + " ]")
+ else:
+ raise Exception("invalid rr.rdata[], expected bytes|list but got: {}".format(type(i)))
+
+ print((" " * lvl)+"rdata: "+" ".join(rdata))
+ else:
+ raise Exception("invalid rr.rdata, expected bytes|list but got: {}".format(type(rr[0])))
+
+ lvl-=2
+
+def parse_qrs(qrs, lvl):
+ for qr in qrs:
+ print((" " * lvl)+"qr:")
+ lvl+=2
+ parse_label(qr.pop(0), lvl)
+
+ rr_type = None
+ rr_class = None
+ if len(qr):
+ if not isinstance(qr[0], int):
+ raise Exception("invalid qr.type|class, expected int but got {}".format(type(qr[0])))
+ if qr[0] > -1:
+ rr_type = qr.pop(0)
+ if len(qr):
+ if not isinstance(qr[0], int):
+ raise Exception("invalid qr.class, expected int but got {}".format(type(qr[0])))
+ elif not qr[0] < 0:
+ raise Exception("invalid qr.class, expected negative int but got positive")
+ rr_class = -qr.pop(0) - 1
+ else:
+ rr_class = -qr.pop(0) - 1
+
+ if not rr_type:
+ rr_type = last.rr_type
+ if not rr_class:
+ rr_class = last.rr_class
+
+ print((" " * lvl)+"type: {}".format(rr_type))
+ print((" " * lvl)+"class: {}".format(rr_class))
+
+ if rr_type != 41:
+ last.rr_type = rr_type
+ last.rr_class = rr_class
+
+ lvl-=2
+
+def parse_dns_message(dns, lvl):
+ print((" " * lvl)+"dns:")
+ lvl+=2
+
+ if isinstance(dns[0], bool):
+ print((" " * lvl)+"incomplete/broken DNS packet, no support for these yet")
+ return
+
+ print((" " * lvl)+"header:")
+ lvl+=2
+ id = dns.pop(0)
+ print((" " * lvl)+"id: {}".format(id))
+ raw = dns.pop(0)
+ print((" " * lvl)+"raw: 0x{:04x}".format(raw))
+ lvl+=2
+ print((" " * lvl)+" QR: "+("yes" if raw & 1<<15 else "no"))
+ print((" " * lvl)+"Opcode: {}".format(((raw >> 11) & 0xf)))
+ print((" " * lvl)+" AA: "+("yes" if raw & 1<<10 else "no"))
+ print((" " * lvl)+" TC: "+("yes" if raw & 1<<9 else "no"))
+ print((" " * lvl)+" RD: "+("yes" if raw & 1<<8 else "no"))
+ print((" " * lvl)+" RA: "+("yes" if raw & 1<<7 else "no"))
+ print((" " * lvl)+" Z: "+("yes" if raw & 1<<6 else "no"))
+ print((" " * lvl)+" AD: "+("yes" if raw & 1<<5 else "no"))
+ print((" " * lvl)+" CD: "+("yes" if raw & 1<<4 else "no"))
+ print((" " * lvl)+" RCODE: {}".format(raw & 0xf))
+ lvl-=2
+
+ bits = 0
+ if isinstance(dns[0], int) and dns[0] < 0:
+ bits = -dns.pop(0) - 1
+ print((" " * lvl)+"qdcount(0): "+("yes" if bits & 1 else "no"))
+ print((" " * lvl)+"ancount(1): "+("yes" if bits & 1<<1 else "no"))
+ print((" " * lvl)+"nscount(2): "+("yes" if bits & 1<<2 else "no"))
+ print((" " * lvl)+"arcount(3): "+("yes" if bits & 1<<3 else "no"))
+
+ if not bits:
+ if isinstance(dns[0], int):
+ bits = 0xff
+
+ if bits & 1:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.qdcount, expected int but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"qdcount: {}".format(dns.pop(0)))
+ if bits & 1<<1:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.ancount, expected int but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"ancount: {}".format(dns.pop(0)))
+ if bits & 1<<2:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.nscount, expected int but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"nscount: {}".format(dns.pop(0)))
+ if bits & 1<<3:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.arcount, expected int but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"arcount: {}".format(dns.pop(0)))
+
+ bits = 0
+ if isinstance(dns[0], CBORSimpleValue):
+ bits = dns.pop(0).value
+ print((" " * lvl)+"questions (0): "+("yes" if bits & 1 else "no"))
+ print((" " * lvl)+"answers (1): "+("yes" if bits & 1<<1 else "no"))
+ print((" " * lvl)+"authorities(2): "+("yes" if bits & 1<<2 else "no"))
+ print((" " * lvl)+"additionals(3): "+("yes" if bits & 1<<3 else "no"))
+
+ last.mlabel = []
+ rlabel = list(last.rlabel)
+ for n in range(4):
+ if len(dns) > n and isinstance(dns[n], list):
+ build_mlabel(dns[n])
+ last.rlabel = rlabel
+
+ if not bits:
+ if len(dns) > 3:
+ bits = 0xff
+ elif len(dns) > 0:
+ raise Exception("invalid dns.message rr's, expected none (0) or all (4) but got {}".format(len(dns)))
+
+ if bits & 1:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.questions, expected list but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"questions:")
+ parse_qrs(dns.pop(0), lvl+2)
+ if bits & 1<<1:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.answers, expected list but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"answers:")
+ parse_rrs(dns.pop(0), lvl+2)
+ if bits & 1<<2:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.authorities, expected list but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"authorities:")
+ parse_rrs(dns.pop(0), lvl+2)
+ if bits & 1<<3:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.additionals, expected list but got: {}".format(type(dns[0])))
+ print((" " * lvl)+"additionals:")
+ parse_rrs(dns.pop(0), lvl+2)
+
+ if len(dns):
+ if isinstance(dns[0], bytes):
+ print((" " * lvl)+"malformed: "+"".join("{:02x}".format(byte) for byte in dns.pop(0)))
+ if len(dns):
+ raise Exception("invalid dns.message, garbage at end: {}".format(dns))
+
+def parse_ip_header(ip_header, lvl):
+ print((" " * lvl)+"ip_header:")
+ lvl+=2
+
+ print((" " * lvl)+"bits:")
+ lvl+=2
+ bits = ip_header.pop(0)
+ reverse = False
+ if isinstance(bits, int):
+ if bits < 0:
+ print((" " * lvl)+"reverse: yes")
+ bits = -bits - 1
+ reverse = True
+ print((" " * lvl)+"family (0): "+("INET6" if bits & 1 else "INET"))
+ print((" " * lvl)+"have_src (1): "+("yes" if bits & 1<<1 else "no"))
+ print((" " * lvl)+"have_dest(2): "+("yes" if bits & 1<<2 else "no"))
+ print((" " * lvl)+"have_port(3): "+("yes" if bits & 1<<3 else "no"))
+ else:
+ raise Exception("invalid ip_header.bits, expected int but got: {}".format(type(bits)))
+ lvl-=2
+
+ src_addr = None
+ dest_addr = None
+ src_port = None
+ dest_port = None
+
+ if bits & 1<<1:
+ src_addr = ip_header.pop(0)
+ if not isinstance(src_addr, bytes):
+ raise Exception("invalid ip_header.src_addr, expected bytes but got: {}".format(type(src_addr)))
+ else:
+ if reverse:
+ src_addr = last.dest_addr6 if bits & 1 else last.dest_addr4
+ if not src_addr:
+ raise Exception("invalid ip_header.bits, expected to have last dest addr but don't")
+ else:
+ src_addr = last.src_addr6 if bits & 1 else last.src_addr4
+ if not src_addr:
+ raise Exception("invalid ip_header.bits, expected to have last src addr but don't")
+
+ if bits & 1<<2:
+ dest_addr = ip_header.pop(0)
+ if not isinstance(dest_addr, bytes):
+ raise Exception("invalid ip_header.dest_addr, expected bytes but got: {}".format(type(dest_addr)))
+ else:
+ if reverse:
+ dest_addr = last.src_addr6 if bits & 1 else last.src_addr4
+ if not dest_addr:
+ raise Exception("invalid ip_header.bits, expected to have last src addr but don't")
+ else:
+ dest_addr = last.dest_addr6 if bits & 1 else last.dest_addr4
+ if not dest_addr:
+ raise Exception("invalid ip_header.bits, expected to have last dest addr but don't")
+
+ if bits & 1<<3:
+ ports = ip_header.pop(0)
+ if not isinstance(ports, int):
+ raise Exception("invalid ip_header.src_dest_port, expected int but got: {}".format(type(ports)))
+ if ports > 0xffff:
+ src_port = ports & 0xffff
+ dest_port = ports >> 16
+ elif ports < 0:
+ if reverse:
+ src_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+ else:
+ src_port = last.src_port6 if bits & 1 else last.src_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ dest_port = -ports - 1
+ else:
+ src_port = ports
+ if reverse:
+ dest_port = last.src_port6 if bits & 1 else last.src_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ else:
+ dest_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+ else:
+ if reverse:
+ src_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+ else:
+ src_port = last.src_port6 if bits & 1 else last.src_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ if reverse:
+ dest_port = last.src_port6 if bits & 1 else last.src_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ else:
+ dest_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+
+ print((" " * lvl)+" src addr: " + socket.inet_ntop(socket.AF_INET6 if bits & 1 else socket.AF_INET, src_addr))
+ print((" " * lvl)+"dest addr: " + socket.inet_ntop(socket.AF_INET6 if bits & 1 else socket.AF_INET, dest_addr))
+ print((" " * lvl)+" src port: {}".format(src_port))
+ print((" " * lvl)+"dest port: {}".format(dest_port))
+
+ if bits & 1:
+ last.src_addr6 = src_addr
+ last.dest_addr6 = dest_addr
+ last.src_port6 = src_port
+ last.dest_port6 = dest_port
+ else:
+ last.src_addr4 = src_addr
+ last.dest_addr4 = dest_addr
+ last.src_port4 = src_port
+ last.dest_port4 = dest_port
+
+
+def parse_message_bits(bits, lvl):
+ print((" " * lvl)+"message_bits:")
+ lvl+=2
+ dns = "no"
+ if isinstance(bits, int):
+ if bits & 1:
+ dns = "yes"
+ print((" " * lvl)+"dns (0): "+dns)
+
+ if bits & 1<<1:
+ proto = "tcp"
+ elif dns == "yes":
+ proto = "udp"
+ else:
+ proto = "icmp"
+ print((" " * lvl)+"proto (1): "+proto)
+
+ if bits & 1<<2:
+ frag = "yes"
+ else:
+ frag = "no"
+ print((" " * lvl)+"frag (2): "+frag)
+
+ if bits & 1<<3:
+ malformed = "yes"
+ else:
+ malformed = "no"
+ print((" " * lvl)+"malformed(3): "+malformed)
+
+ else:
+ raise Exception("invalid message_bits, expected int but got: {}".format(type(bits)))
+
+ return 1 if dns == "yes" else 0
+
+def parse_timestamp(ts, lvl):
+ print((" " * lvl)+"timestamp:")
+ lvl+=2
+
+ if isinstance(ts, list):
+ if ts[0] < 0:
+ if not last.ts:
+ raise Exception("invalid timestamp.seconds, got diff from last value but have no last value")
+ if not len(last.ts) == len(ts):
+ raise Exception("invalid timestamp.seconds, differentialy precision missmatch")
+
+ ts[0] = last.ts[0] + ( -ts[0] - 1 )
+ print((" " * lvl)+"seconds: {}".format(ts[0]))
+
+ if len(ts) > 1:
+ ts[1] = last.ts[1] + ts[1]
+ print((" " * lvl)+"useconds: {}".format(ts[1]))
+ if len(ts) > 2:
+ ts[2] = last.ts[2] + ts[2]
+ print((" " * lvl)+"nseconds: {}".format(ts[2]))
+ else:
+ print((" " * lvl)+"seconds: {}".format(ts[0]))
+ if len(ts) > 1:
+ print((" " * lvl)+"useconds: {}".format(ts[1]))
+ if len(ts) > 2:
+ print((" " * lvl)+"nseconds: {}".format(ts[2]))
+ last.ts = ts
+
+ elif isinstance(ts, int):
+ print((" " * lvl)+"seconds: {}".format(ts))
+ else:
+ raise Exception("invalid timestamp, expected list|int but got: {}".format(type(ts)))
+
+def parse(cds):
+ print("paket:")
+ try:
+ parse_timestamp(cds.pop(0), 2)
+ is_dns = parse_message_bits(cds.pop(0), 2)
+ parse_ip_header(cds, 2)
+ if not is_dns:
+ raise Exception("not dns? huh?")
+ parse_dns_message(cds, 2)
+ except IndexError as idx:
+ if not str(idx) == "pop from empty list":
+ raise
+ print(" ...")
+ except:
+ raise
+
+def main():
+ usage = '%prog [-v] [-h] <cds file...>'
+ parser = optparse.OptionParser(usage, version='%prog 0.01')
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
+ help='turn verbose mode on')
+
+ (options, args) = parser.parse_args()
+
+ if options.verbose == True:
+ log.setLevel(logging.DEBUG)
+ log.debug('argv: %s', sys.argv)
+ log.debug('options: %s', options)
+ log.debug('args: %s', args)
+ else:
+ log.setLevel(logging.WARNING)
+
+ if not args:
+ parser.print_usage()
+ exit(1)
+
+ decoder = CBORDecoder()
+ # if https://github.com/agronholm/cbor2/pull/5 is not merged/released yet
+ if 0 not in decoder.special_decoders:
+ decoder.special_decoders[0] = lambda self, fp, shareable_index=None: SimpleValue(0)
+ decoder.special_decoders[1] = lambda self, fp, shareable_index=None: SimpleValue(1)
+ decoder.special_decoders[2] = lambda self, fp, shareable_index=None: SimpleValue(2)
+ decoder.special_decoders[3] = lambda self, fp, shareable_index=None: SimpleValue(3)
+ decoder.special_decoders[4] = lambda self, fp, shareable_index=None: SimpleValue(4)
+ decoder.special_decoders[5] = lambda self, fp, shareable_index=None: SimpleValue(5)
+ decoder.special_decoders[6] = lambda self, fp, shareable_index=None: SimpleValue(6)
+ decoder.special_decoders[7] = lambda self, fp, shareable_index=None: SimpleValue(7)
+ decoder.special_decoders[8] = lambda self, fp, shareable_index=None: SimpleValue(8)
+ decoder.special_decoders[9] = lambda self, fp, shareable_index=None: SimpleValue(9)
+ decoder.special_decoders[10] = lambda self, fp, shareable_index=None: SimpleValue(10)
+ decoder.special_decoders[11] = lambda self, fp, shareable_index=None: SimpleValue(11)
+ decoder.special_decoders[12] = lambda self, fp, shareable_index=None: SimpleValue(12)
+ decoder.special_decoders[13] = lambda self, fp, shareable_index=None: SimpleValue(13)
+ decoder.special_decoders[14] = lambda self, fp, shareable_index=None: SimpleValue(14)
+ decoder.special_decoders[15] = lambda self, fp, shareable_index=None: SimpleValue(15)
+ decoder.special_decoders[16] = lambda self, fp, shareable_index=None: SimpleValue(16)
+ decoder.special_decoders[17] = lambda self, fp, shareable_index=None: SimpleValue(17)
+ decoder.special_decoders[18] = lambda self, fp, shareable_index=None: SimpleValue(18)
+ decoder.special_decoders[19] = lambda self, fp, shareable_index=None: SimpleValue(19)
+ decoder.special_decoders[24] = decode_simple_value
+
+ version = None
+
+ for f in args:
+ log.debug('file: %s', f)
+ with open(f, 'rb') as fp:
+ obj = None
+ try:
+ obj = decoder.decode(fp)
+ except Exception as e:
+ if e.__str__().find("index out of range") == -1:
+ raise
+ if not isinstance(obj, list):
+ raise Exception("Invalid element, expected an array but found: {}".format(type(obj)))
+
+ version = obj.pop(0)
+ if version != "CDSv1":
+ raise Exception("Invalid version, expected CDSv1 but got: {}".format(version))
+
+ while len(obj):
+ opt = obj.pop(0)
+ if not isinstance(opt, int):
+ raise Exception("Invalid option, expected int but got: {}".format(type(opt)))
+ if opt == 0:
+ MAX_RLABELS = obj.pop(0)
+ if not isinstance(MAX_RLABELS, int) or MAX_RLABELS < 1:
+ raise Exception("Invalid option for maximum rlabels, got: {}".format(MAX_RLABELS))
+ log.debug("Using maximum rlabels {}".format(MAX_RLABELS))
+ elif opt == 1:
+ MIN_RLABEL_SIZE = obj.pop(0)
+ if not isinstance(MIN_RLABEL_SIZE, int) or MIN_RLABEL_SIZE < 1:
+ raise Exception("Invalid option for minimum rlabel size, got: {}".format(MIN_RLABEL_SIZE))
+ log.debug("Using minimum rlabel size {}".format(MIN_RLABEL_SIZE))
+ else:
+ raise Exception("Unknown option: {}".format(opt))
+
+ while True:
+ obj = None
+ try:
+ obj = decoder.decode(fp)
+ except Exception as e:
+ if e.__str__().find("index out of range") == -1:
+ raise
+ if obj is None:
+ break
+ if not isinstance(obj, list):
+ raise Exception("Invalid element, expected an array but found: {}".format(type(obj)))
+ parse(obj)
+
+ last.reset()
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/cdsidxchk.py b/contrib/cdsidxchk.py
new file mode 100755
index 0000000..bcc1a67
--- /dev/null
+++ b/contrib/cdsidxchk.py
@@ -0,0 +1,797 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2016-2021, OARC, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import sys
+import logging
+import optparse
+import struct
+from cbor2 import CBORDecoder;
+
+logging.basicConfig(format='%(levelname).5s: %(module)s:%(lineno)d: '
+ '%(message)s')
+log = logging.getLogger(__name__)
+
+class SimpleValue(object):
+ def __init__(self, value):
+ self.value = value
+
+ def get(self):
+ return self.value
+
+ def __repr__(self):
+ return "{}".format(self.value)
+
+def decode_simple_value(self, fp, shareable_index=None):
+ return SimpleValue(struct.unpack('>B', fp.read(1))[0])
+
+try:
+ from cbor2.types import CBORSimpleValue
+except Exception:
+ CBORSimpleValue = SimpleValue
+
+class LastValues(object):
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self.ts = None
+ self.src_addr4 = None
+ self.src_port4 = None
+ self.dest_addr4 = None
+ self.dest_port4 = None
+ self.src_addr6 = None
+ self.src_port6 = None
+ self.dest_addr6 = None
+ self.dest_port6 = None
+ self.rlabel = []
+ self.mlabel = []
+ self.rr_type = None
+ self.rr_class = None
+ self.rr_ttl = None
+ self.labels = {}
+ self.label_len = {}
+ self.label_parts = {}
+ self.label_part_len = {}
+ self.rdata = {}
+ self.rdata_len = {}
+
+
+last = LastValues()
+
+MAX_RLABELS = 255
+MIN_RLABEL_SIZE = 3
+
+def add_label(label):
+ size = 0
+ if isinstance(label, list):
+ for l in label:
+ if isinstance(l, str) and len(l) > 2:
+ if not l in last.label_parts:
+ last.label_parts[l] = 1
+ last.label_part_len[l] = len(l)
+ else:
+ last.label_parts[l] += 1
+ if not isinstance(l, int):
+ size += len(l)
+ else:
+ size = len(label)
+ if size < 3:
+ return
+
+ idx = "{}".format(label)
+# print(idx)
+
+ if not idx in last.labels:
+ last.labels[idx] = 1
+ last.label_len[idx] = size
+ else:
+ last.labels[idx] += 1
+
+def add_rdata(rdata):
+ size = 0
+ if isinstance(rdata, list):
+ for l in rdata:
+ if not isinstance(l, int):
+ size += len(l)
+ else:
+ size = len(rdata)
+ if size < 3:
+ return
+
+ idx = "{}".format(rdata)
+# print(idx)
+ if not idx in last.rdata:
+ last.rdata[idx] = 1
+ last.rdata_len[idx] = size
+ else:
+ last.rdata[idx] += 1
+
+
+
+def get_rlabel(idx):
+ rlabel_idx = -idx - 1
+ try:
+ label = last.rlabel.pop(rlabel_idx)
+ last.rlabel.insert(0, label)
+ return label
+ except:
+ raise Exception("rlabel index {} out of range".format(rlabel_idx))
+
+def add_rlabel(label):
+ size = 0
+ if isinstance(label, list):
+ for l in label:
+ if isinstance(l, str):
+ size += len(l)
+ if size < MIN_RLABEL_SIZE:
+ return
+ last.rlabel.insert(0, label)
+ if len(last.rlabel) > MAX_RLABELS:
+ last.rlabel.pop()
+
+def build_mlabel_label(label):
+ if isinstance(label, int) and label < 0:
+ label = get_rlabel(label)
+ else:
+ add_rlabel(label)
+
+ if isinstance(label, str):
+ last.mlabel.append(label)
+ elif isinstance(label, list):
+ if len(label) and isinstance(label[0], int):
+ last.mlabel.append(label)
+ return
+ label = list(label)
+ while len(label):
+ last.mlabel.append(list(label))
+ label.pop(0)
+
+def build_mlabel(rrs):
+ for rr in rrs:
+ if len(rr) and isinstance(rr[0], bool):
+ continue
+ if len(rr):
+ build_mlabel_label(rr[0])
+ if len(rr) > 1 and isinstance(rr[len(rr)-1], list):
+ for l in rr[len(rr)-1]:
+ build_mlabel_label(l)
+
+def parse_label(label, lvl):
+ if isinstance(label, int) and label < 0:
+ label = get_rlabel(label)
+ else:
+ add_rlabel(label)
+ add_label(label)
+
+ if isinstance(label, bytes):
+ #print((" " * lvl)+"label: {}".format(bytes))
+ pass
+ elif isinstance(label, list):
+ if len(label) and isinstance(label[0], int) and label[0] < 0:
+ dn = list(get_rlabel(label[0]))
+ else:
+ dn = list(label)
+ #print((" " * lvl)+"clabel: {}".format(dn))
+ dnstr = []
+ seen_mlabel = {}
+ while len(dn):
+ while isinstance(dn[0], int):
+ if dn[0] in seen_mlabel:
+ dn = [ "{ name compression loop }" ]
+ break
+ seen_mlabel[dn[0]] = 1
+ dn = list(last.mlabel[dn[0]])
+ dnstr.append(dn.pop(0))
+ #print((" " * lvl)+"label: "+ " . ".join(dnstr))
+
+ else:
+ raise Exception("invalid label type {}".format(type(label)))
+
+
+def parse_rrs(rrs, lvl):
+ for rr in rrs:
+ #print((" " * lvl)+"rr:")
+ lvl+=2
+
+ if len(rr) and isinstance(rr[0], bool):
+ #print((" " * lvl)+"incomplete/broken DNS RR, no support for these yet")
+ continue
+
+ parse_label(rr.pop(0), lvl)
+
+ bits = 0
+ if isinstance(rr[0], CBORSimpleValue):
+ bits = rr.pop(0).value
+ #print((" " * lvl)+"type (0): "+("yes" if bits & 1 else "no"))
+ #print((" " * lvl)+"class (1): "+("yes" if bits & 1<<1 else "no"))
+ #print((" " * lvl)+"ttl (2): "+("yes" if bits & 1<<2 else "no"))
+ #print((" " * lvl)+"rdlength(3): "+("yes" if bits & 1<<3 else "no"))
+
+ rr_type = None
+ rr_class = None
+ rr_ttl = None
+ rdlength = None
+ if not bits:
+ if len(rr) > 4:
+ bits = 0xff
+ elif len(rr) > 1:
+ raise Exception("invalid rr, expected none (0) or all (4) optional values but got {}".format(len(rr)-1))
+ if bits & 1:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.type, expected int but got: {}".format(type(rr[0])))
+ rr_type = rr.pop(0)
+ if bits & 1<<1:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.class, expected int but got: {}".format(type(rr[0])))
+ rr_class = rr.pop(0)
+ if bits & 1<<2:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.ttl, expected int but got: {}".format(type(rr[0])))
+ rr_ttl = rr.pop(0)
+ if bits & 1<<3:
+ if not isinstance(rr[0], int):
+ raise Exception("invalid rr.rdlength, expected int but got: {}".format(type(rr[0])))
+ rdlength = rr.pop(0)
+
+ if not rr_type:
+ rr_type = last.rr_type
+ if not rr_class:
+ rr_class = last.rr_class
+ if not rr_ttl:
+ rr_ttl = last.rr_ttl
+
+ #print((" " * lvl)+"type: {}".format(rr_type))
+ #print((" " * lvl)+"class: {}".format(rr_class))
+ #print((" " * lvl)+"ttl: {}".format(rr_ttl))
+ if rdlength:
+ #print((" " * lvl)+"rdlength: {}".format(rdlength))
+ pass
+
+ if rr_type != 41:
+ last.rr_type = rr_type
+ last.rr_class = rr_class
+ last.rr_ttl = rr_ttl
+
+ if isinstance(rr[0], bytes):
+ add_rdata(rr[0])
+ rr.pop(0)
+ #print((" " * lvl)+"rdata: "+"".join("{:02x}".format(byte) for byte in rr.pop(0)))
+ elif isinstance(rr[0], list):
+ add_rdata(rr[0])
+ rdata = []
+ for i in rr.pop(0):
+ if isinstance(i, int) and i < 0:
+ i = get_rlabel(i)
+ elif not isinstance(i, bytes):
+ add_rlabel(i)
+ add_label(i)
+
+ if isinstance(i, bytes):
+ rdata.append("".join("{:02x}".format(byte) for byte in i))
+ elif isinstance(i, list):
+ dn = list(i)
+ dnstr = []
+ seen_mlabel = {}
+ while len(dn):
+ while isinstance(dn[0], int):
+ if dn[0] in seen_mlabel:
+ dn = [ "{ name compression loop }" ]
+ break
+ seen_mlabel[dn[0]] = 1
+ dn = list(last.mlabel[dn[0]])
+ dnstr.append(dn.pop(0))
+ rdata.append("[ clabel: {} label: ".format(i) + " . ".join(dnstr) + " ]")
+ else:
+ raise Exception("invalid rr.rdata[], expected bytes|list but got: {}".format(type(i)))
+
+ #print((" " * lvl)+"rdata: "+" ".join(rdata))
+ else:
+ raise Exception("invalid rr.rdata, expected bytes|list but got: {}".format(type(rr[0])))
+
+ lvl-=2
+
+def parse_qrs(qrs, lvl):
+ for qr in qrs:
+ #print((" " * lvl)+"qr:")
+ lvl+=2
+ parse_label(qr.pop(0), lvl)
+
+ rr_type = None
+ rr_class = None
+ if len(qr):
+ if not isinstance(qr[0], int):
+ raise Exception("invalid qr.type|class, expected int but got {}".format(type(qr[0])))
+ if qr[0] > -1:
+ rr_type = qr.pop(0)
+ if len(qr):
+ if not isinstance(qr[0], int):
+ raise Exception("invalid qr.class, expected int but got {}".format(type(qr[0])))
+ elif not qr[0] < 0:
+ raise Exception("invalid qr.class, expected negative int but got positive")
+ rr_class = -qr.pop(0) - 1
+ else:
+ rr_class = -qr.pop(0) - 1
+
+ if not rr_type:
+ rr_type = last.rr_type
+ if not rr_class:
+ rr_class = last.rr_class
+
+ #print((" " * lvl)+"type: {}".format(rr_type))
+ #print((" " * lvl)+"class: {}".format(rr_class))
+
+ if rr_type != 41:
+ last.rr_type = rr_type
+ last.rr_class = rr_class
+
+ lvl-=2
+
+def parse_dns_message(dns, lvl):
+ #print((" " * lvl)+"dns:")
+ lvl+=2
+
+ if isinstance(dns[0], bool):
+ #print((" " * lvl)+"incomplete/broken DNS packet, no support for these yet")
+ return
+
+ #print((" " * lvl)+"header:")
+ lvl+=2
+ id = dns.pop(0) # lgtm [py/unused-local-variable]
+ #print((" " * lvl)+"id: {}".format(id))
+ raw = dns.pop(0) # lgtm [py/unused-local-variable]
+ #print((" " * lvl)+"raw: 0x{:04x}".format(raw))
+ lvl+=2
+ #print((" " * lvl)+" QR: "+("yes" if raw & 1<<15 else "no"))
+ #print((" " * lvl)+"Opcode: {}".format(((raw >> 11) & 0xf)))
+ #print((" " * lvl)+" AA: "+("yes" if raw & 1<<10 else "no"))
+ #print((" " * lvl)+" TC: "+("yes" if raw & 1<<9 else "no"))
+ #print((" " * lvl)+" RD: "+("yes" if raw & 1<<8 else "no"))
+ #print((" " * lvl)+" RA: "+("yes" if raw & 1<<7 else "no"))
+ #print((" " * lvl)+" Z: "+("yes" if raw & 1<<6 else "no"))
+ #print((" " * lvl)+" AD: "+("yes" if raw & 1<<5 else "no"))
+ #print((" " * lvl)+" CD: "+("yes" if raw & 1<<4 else "no"))
+ #print((" " * lvl)+" RCODE: {}".format(raw & 0xf))
+ lvl-=2
+
+ bits = 0
+ if isinstance(dns[0], int) and dns[0] < 0:
+ bits = -dns.pop(0) - 1
+ #print((" " * lvl)+"qdcount(0): "+("yes" if bits & 1 else "no"))
+ #print((" " * lvl)+"ancount(1): "+("yes" if bits & 1<<1 else "no"))
+ #print((" " * lvl)+"nscount(2): "+("yes" if bits & 1<<2 else "no"))
+ #print((" " * lvl)+"arcount(3): "+("yes" if bits & 1<<3 else "no"))
+
+ if not bits:
+ if isinstance(dns[0], int):
+ bits = 0xff
+
+ if bits & 1:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.qdcount, expected int but got: {}".format(type(dns[0])))
+ dns.pop(0)
+ #print((" " * lvl)+"qdcount: {}".format(dns.pop(0)))
+ if bits & 1<<1:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.ancount, expected int but got: {}".format(type(dns[0])))
+ dns.pop(0)
+ #print((" " * lvl)+"ancount: {}".format(dns.pop(0)))
+ if bits & 1<<2:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.nscount, expected int but got: {}".format(type(dns[0])))
+ dns.pop(0)
+ #print((" " * lvl)+"nscount: {}".format(dns.pop(0)))
+ if bits & 1<<3:
+ if not isinstance(dns[0], int):
+ raise Exception("invalid dns.header.arcount, expected int but got: {}".format(type(dns[0])))
+ dns.pop(0)
+ #print((" " * lvl)+"arcount: {}".format(dns.pop(0)))
+
+ bits = 0
+ if isinstance(dns[0], CBORSimpleValue):
+ bits = dns.pop(0).value
+ #print((" " * lvl)+"questions (0): "+("yes" if bits & 1 else "no"))
+ #print((" " * lvl)+"answers (1): "+("yes" if bits & 1<<1 else "no"))
+ #print((" " * lvl)+"authorities(2): "+("yes" if bits & 1<<2 else "no"))
+ #print((" " * lvl)+"additionals(3): "+("yes" if bits & 1<<3 else "no"))
+
+ last.mlabel = []
+ rlabel = list(last.rlabel)
+ for n in range(4):
+ if len(dns) > n and isinstance(dns[n], list):
+ build_mlabel(dns[n])
+ last.rlabel = rlabel
+
+ if not bits:
+ if len(dns) > 3:
+ bits = 0xff
+ elif len(dns) > 0:
+ raise Exception("invalid dns.message rr's, expected none (0) or all (4) but got {}".format(len(dns)))
+
+ if bits & 1:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.questions, expected list but got: {}".format(type(dns[0])))
+ #print((" " * lvl)+"questions:")
+ parse_qrs(dns.pop(0), lvl+2)
+ if bits & 1<<1:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.answers, expected list but got: {}".format(type(dns[0])))
+ #print((" " * lvl)+"answers:")
+ parse_rrs(dns.pop(0), lvl+2)
+ if bits & 1<<2:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.authorities, expected list but got: {}".format(type(dns[0])))
+ #print((" " * lvl)+"authorities:")
+ parse_rrs(dns.pop(0), lvl+2)
+ if bits & 1<<3:
+ if not isinstance(dns[0], list):
+ raise Exception("invalid dns.message.additionals, expected list but got: {}".format(type(dns[0])))
+ #print((" " * lvl)+"additionals:")
+ parse_rrs(dns.pop(0), lvl+2)
+
+ if len(dns):
+ if isinstance(dns[0], bytes):
+ dns.pop(0)
+ #print((" " * lvl)+"malformed: "+"".join("{:02x}".format(byte) for byte in dns.pop(0)))
+ if len(dns):
+ raise Exception("invalid dns.message, garbage at end: {}".format(dns))
+
+def parse_ip_header(ip_header, lvl):
+ #print((" " * lvl)+"ip_header:")
+ lvl+=2
+
+ #print((" " * lvl)+"bits:")
+ lvl+=2
+ bits = ip_header.pop(0)
+ reverse = False
+ if isinstance(bits, int):
+ if bits < 0:
+ #print((" " * lvl)+"reverse: yes")
+ bits = -bits - 1
+ reverse = True
+ #print((" " * lvl)+"family (0): "+("INET6" if bits & 1 else "INET"))
+ #print((" " * lvl)+"have_src (1): "+("yes" if bits & 1<<1 else "no"))
+ #print((" " * lvl)+"have_dest(2): "+("yes" if bits & 1<<2 else "no"))
+ #print((" " * lvl)+"have_port(3): "+("yes" if bits & 1<<3 else "no"))
+ else:
+ raise Exception("invalid ip_header.bits, expected int but got: {}".format(type(bits)))
+ lvl-=2
+
+ src_addr = None
+ dest_addr = None
+ src_port = None
+ dest_port = None
+
+ if bits & 1<<1:
+ src_addr = ip_header.pop(0)
+ if not isinstance(src_addr, bytes):
+ raise Exception("invalid ip_header.src_addr, expected bytes but got: {}".format(type(src_addr)))
+ else:
+ if reverse:
+ src_addr = last.dest_addr6 if bits & 1 else last.dest_addr4
+ if not src_addr:
+ raise Exception("invalid ip_header.bits, expected to have last dest addr but don't")
+ else:
+ src_addr = last.src_addr6 if bits & 1 else last.src_addr4
+ if not src_addr:
+ raise Exception("invalid ip_header.bits, expected to have last src addr but don't")
+
+ if bits & 1<<2:
+ dest_addr = ip_header.pop(0)
+ if not isinstance(dest_addr, bytes):
+ raise Exception("invalid ip_header.dest_addr, expected bytes but got: {}".format(type(dest_addr)))
+ else:
+ if reverse:
+ dest_addr = last.src_addr6 if bits & 1 else last.src_addr4
+ if not dest_addr:
+ raise Exception("invalid ip_header.bits, expected to have last src addr but don't")
+ else:
+ dest_addr = last.dest_addr6 if bits & 1 else last.dest_addr4
+ if not dest_addr:
+ raise Exception("invalid ip_header.bits, expected to have last dest addr but don't")
+
+ if bits & 1<<3:
+ ports = ip_header.pop(0)
+ if not isinstance(ports, int):
+ raise Exception("invalid ip_header.src_dest_port, expected int but got: {}".format(type(ports)))
+ if ports > 0xffff:
+ src_port = ports & 0xffff
+ dest_port = ports >> 16
+ elif ports < 0:
+ if reverse:
+ src_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+ else:
+ src_port = last.src_port6 if bits & 1 else last.src_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ dest_port = -ports - 1
+ else:
+ src_port = ports
+ if reverse:
+ dest_port = last.src_port6 if bits & 1 else last.src_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ else:
+ dest_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+ else:
+ if reverse:
+ src_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+ else:
+ src_port = last.src_port6 if bits & 1 else last.src_port4
+ if src_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ if reverse:
+ dest_port = last.src_port6 if bits & 1 else last.src_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last src port but don't")
+ else:
+ dest_port = last.dest_port6 if bits & 1 else last.dest_port4
+ if dest_port is None:
+ raise Exception("invalid ip_header.bits, expected to have last dest port but don't")
+
+ #print((" " * lvl)+" src addr: " + socket.inet_ntop(socket.AF_INET6 if bits & 1 else socket.AF_INET, src_addr))
+ #print((" " * lvl)+"dest addr: " + socket.inet_ntop(socket.AF_INET6 if bits & 1 else socket.AF_INET, dest_addr))
+ #print((" " * lvl)+" src port: {}".format(src_port))
+ #print((" " * lvl)+"dest port: {}".format(dest_port))
+
+ if bits & 1:
+ last.src_addr6 = src_addr
+ last.dest_addr6 = dest_addr
+ last.src_port6 = src_port
+ last.dest_port6 = dest_port
+ else:
+ last.src_addr4 = src_addr
+ last.dest_addr4 = dest_addr
+ last.src_port4 = src_port
+ last.dest_port4 = dest_port
+
+
+def parse_message_bits(bits, lvl):
+ #print((" " * lvl)+"message_bits:")
+ lvl+=2
+ dns = "no"
+ if isinstance(bits, int):
+ # if bits & 1:
+ # dns = "yes"
+ # #print((" " * lvl)+"dns (0): "+dns)
+ #
+ # if bits & 1<<1:
+ # proto = "tcp"
+ # elif dns == "yes":
+ # proto = "udp"
+ # else:
+ # proto = "icmp"
+ # #print((" " * lvl)+"proto (1): "+proto)
+ #
+ # if bits & 1<<2:
+ # frag = "yes"
+ # else:
+ # frag = "no"
+ # #print((" " * lvl)+"frag (2): "+frag)
+ #
+ # if bits & 1<<3:
+ # malformed = "yes"
+ # else:
+ # malformed = "no"
+ # #print((" " * lvl)+"malformed(3): "+malformed)
+ pass
+
+ else:
+ raise Exception("invalid message_bits, expected int but got: {}".format(type(bits)))
+
+ return 1 if dns == "yes" else 0
+
+def parse_timestamp(ts, lvl):
+ #print((" " * lvl)+"timestamp:")
+ lvl+=2
+
+ if isinstance(ts, list):
+ if ts[0] < 0:
+ if not last.ts:
+ raise Exception("invalid timestamp.seconds, got diff from last value but have no last value")
+ if not len(last.ts) == len(ts):
+ raise Exception("invalid timestamp.seconds, differentialy precision missmatch")
+
+ ts[0] = last.ts[0] + ( -ts[0] - 1 )
+ #print((" " * lvl)+"seconds: {}".format(ts[0]))
+
+ if len(ts) > 1:
+ ts[1] = last.ts[1] + ts[1]
+ #print((" " * lvl)+"useconds: {}".format(ts[1]))
+ if len(ts) > 2:
+ ts[2] = last.ts[2] + ts[2]
+ #print((" " * lvl)+"nseconds: {}".format(ts[2]))
+ else:
+ #print((" " * lvl)+"seconds: {}".format(ts[0]))
+ if len(ts) > 1:
+ #print((" " * lvl)+"useconds: {}".format(ts[1]))
+ pass
+ if len(ts) > 2:
+ #print((" " * lvl)+"nseconds: {}".format(ts[2]))
+ pass
+ last.ts = ts
+
+ elif isinstance(ts, int):
+ #print((" " * lvl)+"seconds: {}".format(ts))
+ pass
+ else:
+ raise Exception("invalid timestamp, expected list|int but got: {}".format(type(ts)))
+
+def parse(cds):
+ #print("paket:")
+ try:
+ parse_timestamp(cds.pop(0), 2)
+ is_dns = parse_message_bits(cds.pop(0), 2)
+ parse_ip_header(cds, 2)
+ if not is_dns:
+ raise Exception("not dns? huh?")
+ parse_dns_message(cds, 2)
+ except IndexError as idx:
+ if not str(idx) == "pop from empty list":
+ raise
+ #print(" ...")
+ except:
+ raise
+
+def main():
+ usage = '%prog [-v] [-h] <cds file...>'
+ parser = optparse.OptionParser(usage, version='%prog 0.01')
+ parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
+ help='turn verbose mode on')
+
+ (options, args) = parser.parse_args()
+
+ if options.verbose == True:
+ log.setLevel(logging.DEBUG)
+ log.debug('argv: %s', sys.argv)
+ log.debug('options: %s', options)
+ log.debug('args: %s', args)
+ else:
+ log.setLevel(logging.WARNING)
+
+ if not args:
+ parser.print_usage()
+ exit(1)
+
+ decoder = CBORDecoder()
+ # if https://github.com/agronholm/cbor2/pull/5 is not merged/released yet
+ if 0 not in decoder.special_decoders:
+ decoder.special_decoders[0] = lambda self, fp, shareable_index=None: SimpleValue(0)
+ decoder.special_decoders[1] = lambda self, fp, shareable_index=None: SimpleValue(1)
+ decoder.special_decoders[2] = lambda self, fp, shareable_index=None: SimpleValue(2)
+ decoder.special_decoders[3] = lambda self, fp, shareable_index=None: SimpleValue(3)
+ decoder.special_decoders[4] = lambda self, fp, shareable_index=None: SimpleValue(4)
+ decoder.special_decoders[5] = lambda self, fp, shareable_index=None: SimpleValue(5)
+ decoder.special_decoders[6] = lambda self, fp, shareable_index=None: SimpleValue(6)
+ decoder.special_decoders[7] = lambda self, fp, shareable_index=None: SimpleValue(7)
+ decoder.special_decoders[8] = lambda self, fp, shareable_index=None: SimpleValue(8)
+ decoder.special_decoders[9] = lambda self, fp, shareable_index=None: SimpleValue(9)
+ decoder.special_decoders[10] = lambda self, fp, shareable_index=None: SimpleValue(10)
+ decoder.special_decoders[11] = lambda self, fp, shareable_index=None: SimpleValue(11)
+ decoder.special_decoders[12] = lambda self, fp, shareable_index=None: SimpleValue(12)
+ decoder.special_decoders[13] = lambda self, fp, shareable_index=None: SimpleValue(13)
+ decoder.special_decoders[14] = lambda self, fp, shareable_index=None: SimpleValue(14)
+ decoder.special_decoders[15] = lambda self, fp, shareable_index=None: SimpleValue(15)
+ decoder.special_decoders[16] = lambda self, fp, shareable_index=None: SimpleValue(16)
+ decoder.special_decoders[17] = lambda self, fp, shareable_index=None: SimpleValue(17)
+ decoder.special_decoders[18] = lambda self, fp, shareable_index=None: SimpleValue(18)
+ decoder.special_decoders[19] = lambda self, fp, shareable_index=None: SimpleValue(19)
+ decoder.special_decoders[24] = decode_simple_value
+
+ version = None
+
+ for f in args:
+ log.debug('file: %s', f)
+ with open(f, 'rb') as fp:
+ obj = None
+ try:
+ obj = decoder.decode(fp)
+ except Exception as e:
+ if e.__str__().find("index out of range") == -1:
+ raise
+ if not isinstance(obj, list):
+ raise Exception("Invalid element, expected an array but found: {}".format(type(obj)))
+
+ version = obj.pop(0)
+ if version != "CDSv1":
+ raise Exception("Invalid version, expected CDSv1 but got: {}".format(version))
+
+ while len(obj):
+ opt = obj.pop(0)
+ if not isinstance(opt, int):
+ raise Exception("Invalid option, expected int but got: {}".format(type(opt)))
+ if opt == 0:
+ MAX_RLABELS = obj.pop(0)
+ if not isinstance(MAX_RLABELS, int) or MAX_RLABELS < 1:
+ raise Exception("Invalid option for maximum rlabels, got: {}".format(MAX_RLABELS))
+ log.debug("Using maximum rlabels {}".format(MAX_RLABELS))
+ elif opt == 1:
+ MIN_RLABEL_SIZE = obj.pop(0)
+ if not isinstance(MIN_RLABEL_SIZE, int) or MIN_RLABEL_SIZE < 1:
+ raise Exception("Invalid option for minimum rlabel size, got: {}".format(MIN_RLABEL_SIZE))
+ log.debug("Using minimum rlabel size {}".format(MIN_RLABEL_SIZE))
+ else:
+ raise Exception("Unknown option: {}".format(opt))
+
+ while True:
+ obj = None
+ try:
+ obj = decoder.decode(fp)
+ except Exception as e:
+ if e.__str__().find("index out of range") == -1:
+ raise
+ if obj is None:
+ break
+ if not isinstance(obj, list):
+ raise Exception("Invalid element, expected an array but found: {}".format(type(obj)))
+ parse(obj)
+
+ log.debug("unique labels: {} parts: {} rdata: {}".format(len(last.labels), len(last.label_parts), len(last.rdata)))
+
+ n = 0
+ e = 0
+ for l in last.labels:
+# print("{}: {}".format(l, last.labels[l]))
+ if last.labels[l] > 1:
+ n += last.label_len[l] * ( last.labels[l] - 1 )
+ e += 2 * ( last.labels[l] - 1 )
+ log.debug("reduce labels: {} - {}".format(n, e))
+
+ n = 0
+ e = 0
+ for l in last.label_parts:
+# print("{}: {}".format(l, last.label_parts[l]))
+ if last.label_parts[l] > 1:
+ n += last.label_part_len[l] * ( last.label_parts[l] - 1 )
+ e += 2 * ( last.label_parts[l] - 1 )
+ log.debug("reduce label parts: {} - {}".format(n, e))
+
+ n = 0
+ e = 0
+ for l in last.rdata:
+ if last.rdata[l] > 1:
+ n += last.rdata_len[l] * ( last.rdata[l] - 1 )
+ e += 2 * ( last.rdata[l] - 1 )
+# print("{}: {}".format(l, last.rdata[l]))
+ log.debug("reduce rdata: {} - {}".format(n, e))
+
+ last.reset()
+
+if __name__ == '__main__':
+ main()