summaryrefslogtreecommitdiffstats
path: root/contrib/impcap/dns_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/impcap/dns_parser.c')
-rw-r--r--contrib/impcap/dns_parser.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/contrib/impcap/dns_parser.c b/contrib/impcap/dns_parser.c
new file mode 100644
index 0000000..f9f4e68
--- /dev/null
+++ b/contrib/impcap/dns_parser.c
@@ -0,0 +1,372 @@
+/* dns_parser.c
+ *
+ * This file contains functions to parse DNS headers.
+ *
+ * File begun on 2018-11-13
+ *
+ * Created by:
+ * - Kevin Guillemot (kevin.guillemot@advens.fr)
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+#include "parsers.h"
+
+
+/* List of RCodes defined in RFC6895 : https://tools.ietf.org/html/rfc6895 */
+static const char *dns_rcodes[] = {
+ "NoError", // 0
+ "FormErr", // 1
+ "ServFail", // 2
+ "NXDomain", // 3
+ "NotImp", // 4
+ "Refused", // 5
+ "YXDomain", // 6
+ "YXRRSet", // 7
+ "NXRRSet", // 8
+ "NotAuth", // 9
+ "NotZone", // 10
+ "", // 11 - Reserved
+ "", // 12 - Reserved
+ "", // 13 - Reserved
+ "", // 14 - Reserved
+ "", // 15 - Reserved
+ "BADVERS|BADSIG", // 16
+ "BADKEY", // 17
+ "BADTIME", // 18
+ "BADMODE", // 19
+ "BADNAME", // 20
+ "BADALG", // 21
+ "BADTRUNC", // 22
+ /* Reserved for private use */
+ NULL
+};
+
+/* List of record types (maybe not complete) */
+static const char *dns_types[] = {
+ 0,
+ "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
+ "RP", // 17
+ "AFSDB", // 18
+ "X25", // 19
+ "ISDN", // 20
+ "RT", // 21
+ "NSAP", // 22
+ "NSAP-PTR", // 23
+ "SIG", // 24
+ "KEY", // 25
+ "PX", // 26
+ "GPOS", // 27
+ "AAAA", // 28
+ "LOC", // 29
+ "NXT", // 30
+ "EID", // 31
+ "NIMLOC", // 32
+ "SRV", // 33
+ "ATMA", // 34
+ "NAPTR", // 35
+ "KX", // 36
+ "CERT", // 37
+ "A6", // 38
+ "DNAME", // 39
+ "SINK", // 40
+ "OPT", // 41
+ "APL", // 42
+ "DS", // 43
+ "SSHFP", // 44
+ "IPSECKEY", // 45
+ "RRSIG", // 46
+ "NSEC", // 47
+ "DNSKEY", // 48
+ "DHCID", // 49
+ "NSEC3", // 50
+ "NSEC3PARAM", // 51
+ "TLSA", // 51
+ "SMIMEA", // 52
+ "Unassigned", // 53
+ "HIP", // 53
+ "NINFO", // 54
+ "RKEY", // 55
+ "TALINK", // 56
+ "CDS", // 57
+ "CDNSKEY", // 58
+ "OPENPGPKEY", // 59
+ "CSYNC", // 60
+ "ZONEMD", // 61
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ "SPF", // 99
+ "UINFO", // 100
+ "UID", // 101
+ "GID", // 102
+ "UNSPEC", // 103
+ "NID", // 104
+ "L32", // 105
+ "L64", // 106
+ "LP", // 107
+ "EUI48", // 108
+ "EUI64", // 109
+ /* Reserved for private use */
+ NULL
+};
+/* Part 2, since 249. To prevent useless large buffer in memory */
+static const char *dns_types2[] = {
+ "TKEY",
+ "TSIG",
+ "IXFR",
+ "AXFR",
+ "MAILB",
+ "MAILA",
+ "*",
+ "URI",
+ "CAA",
+ "AVC",
+ "DOA",
+ "AMTRELAY",
+ NULL
+};
+/* Part 3, since 32768. To prevent useless large buffer in memory */
+static const char *dns_types3[] = {
+ "TA",
+ "DLV",
+ NULL
+};
+
+
+/* This function takes an integer as parameter
+ * and returns the corresponding string type of DNS query
+ */
+static const char *get_type(uint16_t x) {
+ const char **types = NULL;
+ uint16_t len_types3 = (sizeof(dns_types3) / sizeof(char *)) - 1;
+ uint16_t len_types2 = (sizeof(dns_types2) / sizeof(char *)) - 1;
+ uint16_t len_types = (sizeof(dns_types) / sizeof(char *)) - 1;
+ if (x >= 32768 && x < 32768 + len_types3) {
+ types = dns_types3;
+ x -= 32768;
+ }
+ else if (x >= 249 && x < 249 + len_types2) {
+ types = dns_types2;
+ x -= 249;
+ }
+ else if (x > 0 && x < len_types)
+ types = dns_types;
+ else
+ return "UNKNOWN";
+ if (types[x] != NULL)
+ return types[x];
+ return "UNKNOWN";
+}
+
+
+/* This function takes an integer as parameter
+ * and returns the corresponding string class of DNS query
+ */
+static const char *get_class(uint16_t x) {
+ switch (x) {
+ case 1:
+ return "IN";
+ case 3:
+ return "CH";
+ case 4:
+ return "HS";
+ case 254:
+ return "QCLASS NONE";
+ case 255:
+ return "QCLASS *";
+ }
+ return "UNKNOWN";
+}
+
+
+/*
+ * This function parses the bytes in the received packet to extract DNS metadata.
+ *
+ * its parameters are:
+ * - a pointer on the list of bytes representing the packet
+ * - the size of the list passed as first parameter
+ * - a pointer on a json_object, containing all the metadata recovered so far
+ * this is also where DNS metadata will be added
+ *
+ * This function returns a structure containing the data unprocessed by this parser
+ * or the ones after (as a list of bytes), and the length of this data.
+*/
+data_ret_t *dns_parse(const uchar *packet, int pktSize, struct json_object *jparent) {
+ const uchar *packet_ptr = packet;
+ const uchar *end_packet = packet + pktSize;
+ DBGPRINTF("dns_parse\n");
+ DBGPRINTF("packet size %d\n", pktSize);
+
+ /* Union to prevent cast from uchar to smb_header_t */
+ union {
+ unsigned short int *two_bytes;
+ const uchar *pckt;
+ } union_short_int;
+
+ /* Get transaction id */
+ union_short_int.pckt = packet_ptr;
+ unsigned short int transaction_id = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("transaction_id = %02x \n", transaction_id);
+ union_short_int.pckt += 2;
+
+ /* Get flags */
+ unsigned short int flags = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("flags = %02x \n", flags);
+
+ /* Get response flag */
+ unsigned short int response_flag = (flags >> 15) & 0b1; // Get the left bit
+ //DBGPRINTF("response_flag = %02x \n", response_flag);
+
+ /* Get Opcode */
+ unsigned short int opcode = (flags >> 11) & 0b1111;
+ //DBGPRINTF("opcode = %02x \n", opcode);
+
+ /* Verify Z: reserved bit */
+ unsigned short int reserved = (flags >> 6) & 0b1;
+ //DBGPRINTF("reserved = %02x \n", reserved);
+ /* Reserved bit MUST be 0 */
+ if (reserved != 0) {
+ DBGPRINTF("DNS packet reserved bit (Z) is not 0, aborting message. \n");
+ RETURN_DATA_AFTER(0)
+ }
+
+ /* Get reply code : 4 last bits */
+ unsigned short int reply_code = flags & 0b1111;
+ //DBGPRINTF("reply_code = %02x \n", reply_code);
+
+ union_short_int.pckt += 2;
+
+ /* Get QDCOUNT */
+ unsigned short int query_count = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("query_count = %02x \n", query_count);
+ union_short_int.pckt += 2;
+
+ /* Get ANCOUNT */
+ unsigned short int answer_count = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("answer_count = %02x \n", answer_count);
+ union_short_int.pckt += 2;
+
+ /* Get NSCOUNT */
+ unsigned short int authority_count = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("authority_count = %02x \n", authority_count);
+ union_short_int.pckt += 2;
+
+ /* Get ARCOUNT */
+ unsigned short int additionnal_count = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("additionnal_count = %02x \n", additionnal_count);
+ union_short_int.pckt += 2;
+ packet_ptr = union_short_int.pckt;
+
+ fjson_object *queries = NULL;
+ if ((queries = json_object_new_array()) == NULL) {
+ DBGPRINTF("impcap::dns_parser: Cannot create new json array. Stopping.\n");
+ RETURN_DATA_AFTER(0)
+ }
+
+ // For each query of query_count
+ int query_cpt = 0;
+ while (query_cpt < query_count && packet_ptr < end_packet) {
+ size_t query_size = strnlen((const char *)packet_ptr, (size_t)(end_packet - packet_ptr));
+ // Check if query is valid (max 255 bytes, plus a '\0')
+ if (query_size >= 256) {
+ DBGPRINTF("impcap::dns_parser: Length of domain queried is > 255. Stopping.\n");
+ break;
+ }
+ // Check if remaining data is enough to hold query + '\0' + 4 bytes (QTYPE and QCLASS fields)
+ if (query_size + 5 > (size_t)(end_packet - packet_ptr)) {
+ DBGPRINTF("impcap::dns_parser: packet size too small to parse query. Stopping.\n");
+ break;
+ }
+ fjson_object *query = NULL;
+ if ((query = json_object_new_object()) == NULL) {
+ DBGPRINTF("impcap::dns_parser: Cannot create new json object. Stopping.\n");
+ break;
+ }
+ char domain_query[256] = {0};
+ uchar nb_char = *packet_ptr;
+ packet_ptr++;
+ size_t cpt = 0;
+ while (cpt + 1 < query_size) {
+ if (nb_char == 0) {
+ nb_char = *packet_ptr;
+ domain_query[cpt] = '.';
+ } else {
+ domain_query[cpt] = (char)*packet_ptr;
+ nb_char--;
+ }
+ cpt++;
+ packet_ptr++;
+ }
+ domain_query[cpt] = '\0';
+ if (cpt)
+ packet_ptr++; // pass the last \0, only if query was not empty
+ // DBGPRINTF("Requested domain : '%s' \n", domain_query);
+
+ /* Register the name in dict */
+ json_object_object_add(query, "qname", json_object_new_string(domain_query));
+ /* Get QTYPE */
+ union_short_int.pckt = packet_ptr;
+ unsigned short int qtype = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("qtype = %02x \n", qtype);
+ json_object_object_add(query, "qtype", json_object_new_int((int)qtype));
+ json_object_object_add(query, "type", json_object_new_string(get_type(qtype)));
+ union_short_int.pckt += 2;
+ /* Retrieve QCLASS */
+ unsigned short int qclass = ntohs(*(union_short_int.two_bytes));
+ //DBGPRINTF("qclass = %02x \n", qclass);
+ json_object_object_add(query, "qclass", json_object_new_int((int)qclass));
+ json_object_object_add(query, "class", json_object_new_string(get_class(qclass)));
+ packet_ptr = union_short_int.pckt + 2;
+ /* Register the query in json array */
+ json_object_array_add(queries, query);
+ query_cpt++;
+ }
+
+ json_object_object_add(jparent, "DNS_transaction_id", json_object_new_int((int)transaction_id));
+
+ json_bool is_reponse = FALSE;
+ if (response_flag)
+ is_reponse = TRUE;
+ json_object_object_add(jparent, "DNS_response_flag", json_object_new_boolean(is_reponse));
+
+ json_object_object_add(jparent, "DNS_opcode", json_object_new_int(opcode));
+ json_object_object_add(jparent, "DNS_rcode", json_object_new_int((int)reply_code));
+ json_object_object_add(jparent, "DNS_error", json_object_new_string(dns_rcodes[reply_code]));
+ json_object_object_add(jparent, "DNS_QDCOUNT", json_object_new_int((int)query_count));
+ json_object_object_add(jparent, "DNS_ANCOUNT", json_object_new_int((int)answer_count));
+ json_object_object_add(jparent, "DNS_NSCOUNT", json_object_new_int((int)authority_count));
+ json_object_object_add(jparent, "DNS_ARCOUNT", json_object_new_int((int)additionnal_count));
+ json_object_object_add(jparent, "DNS_Names", queries);
+
+ /* Packet has been successfully parsed, there still can be some responses left, but do not process them */
+ RETURN_DATA_AFTER(0);
+}