Add new syntax for DNS header generation via 'dns()' proto function.

The fields are supported:

        id              - 16 bit identifier
        qr              - message is a query(0) or response(1)
        op|oper         - specified kind of query
        aanswer         - authoritative answer flag
        trunc           - message was truncated flag
        rdesired        - recursion desired flag
        ravail          - recursion available flag
        zero            - reserved for future use
        rcode           - response code
        qdcount         - number of entries in question section
        ancount         - number of entries in answer section
        nscount         - number of entries in authority section
        arcount         - number of entries in additional section

Also there are functions to generate DNS sections:

'qry()' function to generate separate query entry:

        name            - variable domain name
        type            - type of the query
        class           - class of the query

'ans()', 'auth()', 'add'  functions to generate separate answer,
authoritative, adidditional entry with the same fields layout:

        name            - variable domain name
        type            - resource record type
        class           - class of the data
        ttl             - time interval that the record may be cached
        len             - length of data
        data            - variable length of bytes

All the DNS section entries will be automaticlly sorted by DNS proto API
in the way which is required by DNS header:

        query entries
        answer entries
        authoritative entries
        additional entries

'name' field in qry/ans/auth/add functions is automatically converted to
FQDN format if it was specified as "string".

There are also added functions to simplify the way of filling
some often used RR types for using them inside ans/auth/add functions:

        addr(ipv4_addr | ipv6_addr) - fills the following RR fields:
                len  - 4 or 16 depends on IPv4 or IPv6 address was specified
                data - is filled with IPv4 or IPv6 address
                type - 1 for IPv4 address, 28 - for IPv6

        ns(string)
                type - 2

        cname(string)
                type - 5

        ptr(string)
                type - 12

EXAMPLES:

{
    dns(qr=1,
        auth(name="ns1", ns("ns1.org")),
        ans(name="www.google.com", cname("google.com")),
        auth(name="aa", ns("bb")),
        qry(name="www.google.com"))
}

{
    dns(qr=1, ans(name="www.google.com", addr(1.2.3.4)))
}

{
    dns(qr=1, ans(name="www.google.com", addr(1::)))
}

Signed-off-by: Vadim Kochan <vadi...@gmail.com>
---
 trafgen_lexer.l  |  24 +++++++
 trafgen_parser.y | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 217 insertions(+)

diff --git a/trafgen_lexer.l b/trafgen_lexer.l
index cf67c74..da072e1 100644
--- a/trafgen_lexer.l
+++ b/trafgen_lexer.l
@@ -239,6 +239,29 @@ ip6_addr   
({v680}|{v67}|{v66}|{v65}|{v64}|{v63}|{v62}|{v61}|{v60})
 "win"|"window" { return K_WINDOW; }
 "urgptr"       { return K_URG_PTR; }
 
+       /* DNS */
+"qr"           { return K_QR; }
+"aa"|"aanswer" { return K_AANSWER; }
+"trunc"                { return K_TRUNC; }
+"ravail"       { return K_RAVAIL; }
+"rdesired"     { return K_RDESIRED; }
+"zero"         { return K_ZERO; }
+"rc"|"rcode"   { return K_RCODE; }
+"qdcount"      { return K_QDCOUNT; }
+"ancount"      { return K_ANCOUNT; }
+"nscount"      { return K_NSCOUNT; }
+"arcount"      { return K_ARCOUNT; }
+"name"         { return K_NAME; }
+"class"                { return K_CLASS; }
+"data"         { return K_DATA; }
+"qry"|"query"  { return K_QUERY; }
+"ans"|"answer" { return K_ANSWER; }
+"auth"         { return K_AUTH; }
+"add"          { return K_ADD; }
+"ns"           { return K_NS; }
+"cname"                { return K_CNAME; }
+"ptr"          { return K_PTR; }
+
 "eth"          { return K_ETH; }
 "pause"         { return K_PAUSE; }
 "pfc"          { return K_PFC; }
@@ -251,6 +274,7 @@ ip6_addr    
({v680}|{v67}|{v66}|{v65}|{v64}|{v63}|{v62}|{v61}|{v60})
 "icmp6"|"icmpv6"       { return K_ICMP6; }
 "udp"          { return K_UDP; }
 "tcp"          { return K_TCP; }
+"dns"          { return K_DNS; }
 
 [ ]*"-"[ ]*    { return '-'; }
 [ ]*"+"[ ]*    { return '+'; }
diff --git a/trafgen_parser.y b/trafgen_parser.y
index 4abf778..e128527 100644
--- a/trafgen_parser.y
+++ b/trafgen_parser.y
@@ -31,6 +31,7 @@
 #include "trafgen_l2.h"
 #include "trafgen_l3.h"
 #include "trafgen_l4.h"
+#include "trafgen_l7.h"
 #include "built_in.h"
 #include "die.h"
 #include "str.h"
@@ -82,6 +83,8 @@ enum field_expr_type_t {
        FIELD_EXPR_INC          = 1 << 4,
        FIELD_EXPR_RND          = 1 << 5,
        FIELD_EXPR_OFFSET       = 1 << 6,
+       FIELD_EXPR_STRING       = 1 << 7,
+       FIELD_EXPR_FQDN         = 1 << 8,
 };
 
 struct proto_field_expr {
@@ -93,6 +96,7 @@ struct proto_field_expr {
                struct in6_addr ip6_addr;
                long long int number;
                uint8_t mac[256];
+               char *str;
                struct proto_field_func func;
        } val;
 };
@@ -436,6 +440,14 @@ static void proto_field_expr_eval(void)
                        panic("Invalid value length %zu, can be 1,2 or 4\n", 
field->len);
        } else if (field_expr.type & FIELD_EXPR_MAC) {
                proto_field_set_bytes(field, field_expr.val.mac, 6);
+       } else if (field_expr.type & FIELD_EXPR_FQDN) {
+               char *fqdn = str2fqdn(field_expr.val.str);
+               proto_field_set_bytes(field, (uint8_t *) fqdn, strlen(fqdn) + 
1);
+               xfree(field_expr.val.str);
+               xfree(fqdn);
+       } else if (field_expr.type & FIELD_EXPR_STRING) {
+               proto_field_set_string(field, field_expr.val.str);
+               xfree(field_expr.val.str);
        } else if (field_expr.type & FIELD_EXPR_IP4_ADDR) {
                proto_field_set_u32(field, field_expr.val.ip4_addr.s_addr);
        } else if (field_expr.type & FIELD_EXPR_IP6_ADDR) {
@@ -474,6 +486,19 @@ static void field_index_validate(struct proto_field 
*field, uint16_t index, size
        }
 }
 
+static void proto_push_sub_hdr(uint32_t id)
+{
+       hdr = proto_hdr_push_sub_header(hdr, id);
+}
+
+static void proto_pop_sub_hdr(void)
+{
+       if (hdr->ops->header_finish)
+               hdr->ops->header_finish(hdr);
+
+       hdr = hdr->parent;
+}
+
 %}
 
 %union {
@@ -500,6 +525,10 @@ static void field_index_validate(struct proto_field 
*field, uint16_t index, size
 
 %token K_ADDR K_MTU
 
+%token K_QR K_AANSWER K_TRUNC K_RAVAIL K_RDESIRED K_ZERO K_RCODE K_QDCOUNT 
K_ANCOUNT K_NSCOUNT K_ARCOUNT
+%token K_QUERY K_ANSWER K_AUTH K_ADD
+%token K_NAME K_CLASS K_DATA K_NS K_CNAME K_PTR
+
 %token K_ETH
 %token K_PAUSE
 %token K_PFC
@@ -508,6 +537,7 @@ static void field_index_validate(struct proto_field *field, 
uint16_t index, size
 %token K_IP4 K_IP6
 %token K_ICMP4 K_ICMP6
 %token K_UDP K_TCP
+%token K_DNS
 
 %token ',' '{' '}' '(' ')' '[' ']' ':' '-' '+' '*' '/' '%' '&' '|' '<' '>' '^'
 
@@ -740,6 +770,7 @@ proto
        | icmpv6_proto { }
        | udp_proto { }
        | tcp_proto { }
+       | dns_proto { }
        ;
 
 field_expr
@@ -760,6 +791,9 @@ field_value_expr
                   field_expr.val.number = $1; }
        | mac { field_expr.type |= FIELD_EXPR_MAC;
                memcpy(field_expr.val.mac, $1, sizeof(field_expr.val.mac)); }
+       | string { field_expr.type |= FIELD_EXPR_STRING;
+                  field_expr.val.str = xstrdup($1 + 1);
+                  field_expr.val.str[strlen($1 + 1) - 1] = '\0'; }
        | ip4_addr { field_expr.type |= FIELD_EXPR_IP4_ADDR;
                     field_expr.val.ip4_addr = $1; }
        | ip6_addr { field_expr.type |= FIELD_EXPR_IP6_ADDR;
@@ -1231,6 +1265,157 @@ tcp
        : K_TCP { proto_add(PROTO_TCP); }
        ;
 
+dns_proto
+       : dns '(' dns_param_list ')' { }
+       ;
+
+dns_param_list
+       : { }
+       | dns_expr { }
+       | dns_expr delimiter dns_param_list { }
+       ;
+
+dns_field
+       : K_ID { proto_field_set(DNS_ID); }
+       | K_QR { proto_field_set(DNS_QR); }
+       | K_OPER { proto_field_set(DNS_OPCODE); }
+       | K_AANSWER { proto_field_set(DNS_AA); }
+       | K_TRUNC { proto_field_set(DNS_TC); }
+       | K_RDESIRED { proto_field_set(DNS_RD); }
+       | K_RAVAIL { proto_field_set(DNS_RA); }
+       | K_ZERO { proto_field_set(DNS_ZERO); }
+       | K_RCODE { proto_field_set(DNS_RCODE); }
+       | K_QDCOUNT { proto_field_set(DNS_QD_COUNT); }
+       | K_ANCOUNT { proto_field_set(DNS_AN_COUNT); }
+       | K_NSCOUNT { proto_field_set(DNS_NS_COUNT); }
+       | K_ARCOUNT { proto_field_set(DNS_AR_COUNT); }
+       ;
+
+dns_query
+       : K_QUERY { proto_push_sub_hdr(DNS_QUERY_HDR); }
+       ;
+
+dns_query_name
+       : K_NAME { proto_field_set(DNS_QUERY_NAME); }
+       ;
+
+dns_query_field
+       : K_TYPE { proto_field_set(DNS_QUERY_TYPE); }
+       | K_CLASS { proto_field_set(DNS_QUERY_CLASS); }
+       ;
+
+dns_query_expr
+       : dns_query_field field_expr skip_white '=' skip_white field_value_expr
+               { proto_field_expr_eval(); }
+       | dns_query_field skip_white '=' skip_white field_value_expr
+               { proto_field_expr_eval(); }
+       | dns_query_name field_expr skip_white '=' skip_white field_value_expr
+               { if (field_expr.type & FIELD_EXPR_STRING)
+                       field_expr.type = FIELD_EXPR_FQDN;
+                 proto_field_expr_eval(); }
+       | dns_query_name skip_white '=' skip_white field_value_expr
+               { if (field_expr.type & FIELD_EXPR_STRING)
+                       field_expr.type = FIELD_EXPR_FQDN;
+                 proto_field_expr_eval(); }
+       ;
+
+dns_query_param_list
+       : { }
+       | dns_query_expr { }
+       | dns_query_expr delimiter dns_query_param_list { }
+       ;
+
+dns_query_hdr
+       : dns_query '(' dns_query_param_list ')' { }
+       ;
+
+dns_rrecord
+       : K_ANSWER { proto_push_sub_hdr(DNS_ANSWER_HDR); }
+       | K_AUTH { proto_push_sub_hdr(DNS_AUTH_HDR); }
+       | K_ADD { proto_push_sub_hdr(DNS_ADD_HDR); }
+       ;
+
+dns_rrecord_name
+       : K_NAME { proto_field_set(DNS_RRECORD_NAME); }
+       ;
+
+dns_rrecord_data_addr
+       : ip4_addr
+               { proto_hdr_field_set_u32(hdr, DNS_RRECORD_DATA, $1.s_addr);
+                 proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 1); }
+       | ip6_addr
+               { proto_hdr_field_set_bytes(hdr, DNS_RRECORD_DATA, (uint8_t 
*)&$1.s6_addr, 16);
+                 proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 28); }
+       ;
+
+dns_rrecord_data_fqdn
+       : string
+               { char *str = xstrdup($1 + 1);
+                 char *fqdn;
+                 str[strlen($1 + 1) - 1] = '\0';
+                 fqdn = str2fqdn(str);
+                 proto_hdr_field_set_bytes(hdr, DNS_RRECORD_DATA, (uint8_t *) 
fqdn, strlen(fqdn) + 1);
+                 xfree(fqdn); }
+       ;
+
+dns_rrecord_data_expr
+       : K_ADDR '(' skip_white dns_rrecord_data_addr skip_white ')'
+               { }
+       | K_NS '(' skip_white dns_rrecord_data_fqdn skip_white ')'
+               { proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 2); }
+       | K_CNAME '(' skip_white dns_rrecord_data_fqdn skip_white ')'
+               { proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 5); }
+       | K_PTR '(' skip_white dns_rrecord_data_fqdn skip_white ')'
+               { proto_hdr_field_set_be16(hdr, DNS_RRECORD_TYPE, 12); }
+       ;
+
+dns_rrecord_field
+       : K_TYPE { proto_field_set(DNS_RRECORD_TYPE); }
+       | K_CLASS { proto_field_set(DNS_RRECORD_CLASS); }
+       | K_TTL { proto_field_set(DNS_RRECORD_TTL); }
+       | K_LEN { proto_field_set(DNS_RRECORD_LEN); }
+       | K_DATA { proto_field_set(DNS_RRECORD_DATA); }
+       ;
+
+dns_rrecord_expr
+       : dns_rrecord_field field_expr skip_white '=' skip_white 
field_value_expr
+               { proto_field_expr_eval(); }
+       | dns_rrecord_field skip_white '=' skip_white field_value_expr
+               { proto_field_expr_eval(); }
+       | dns_rrecord_name field_expr skip_white '=' skip_white field_value_expr
+               { if (field_expr.type & FIELD_EXPR_STRING)
+                       field_expr.type = FIELD_EXPR_FQDN;
+                 proto_field_expr_eval(); }
+       | dns_rrecord_name skip_white '=' skip_white field_value_expr
+               { if (field_expr.type & FIELD_EXPR_STRING)
+                       field_expr.type = FIELD_EXPR_FQDN;
+                 proto_field_expr_eval(); }
+       | dns_rrecord_data_expr
+               { }
+       ;
+
+dns_rrecord_param_list
+       : { }
+       | dns_rrecord_expr { }
+       | dns_rrecord_expr delimiter dns_rrecord_param_list { }
+       ;
+
+dns_rrecord_hdr
+       : dns_rrecord '(' dns_rrecord_param_list ')' { }
+       ;
+
+dns_expr
+       : dns_field field_expr skip_white '=' skip_white field_value_expr
+               { proto_field_expr_eval(); }
+       | dns_field skip_white '=' skip_white field_value_expr
+               { proto_field_expr_eval(); }
+       | dns_query_hdr { proto_pop_sub_hdr(); }
+       | dns_rrecord_hdr { proto_pop_sub_hdr(); }
+       ;
+
+dns
+       : K_DNS { proto_add(PROTO_DNS); }
+       ;
 %%
 
 static void finalize_packet(void)
@@ -1283,9 +1468,17 @@ void cleanup_packets(void)
 
                for (j = 0; j < pkt->headers_count; j++) {
                        struct proto_hdr *hdr = pkt->headers[j];
+                       int k;
+
+                       for (k = 0; k < hdr->headers_count; k++)
+                               xfree(hdr->sub_headers[k]);
+
+                       if (hdr->sub_headers)
+                               xfree(hdr->sub_headers);
 
                        if (hdr->fields)
                                xfree(hdr->fields);
+
                        xfree(hdr);
                }
        }
-- 
2.11.0

-- 
You received this message because you are subscribed to the Google Groups 
"netsniff-ng" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to netsniff-ng+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to