summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorliuwentan <[email protected]>2024-02-28 08:07:55 +0000
committerliuwentan <[email protected]>2024-02-28 08:07:55 +0000
commit877b94f48de3bd0fff87634587d1ea03998768cc (patch)
tree75b8a4e371571c7a7e030afe3ad6dc708a106771
parent0fdca1d9ff83765a355a12e18c806303bdc8488f (diff)
[DNS_DECODER]first tcp/udp dns unit_test
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/dns_decoder/CMakeLists.txt13
-rw-r--r--src/dns_decoder/dns_decoder.c1317
-rw-r--r--src/dns_decoder/dns_decoder.h271
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/dns_decoder/CMakeLists.txt44
-rw-r--r--test/dns_decoder/dns_decoder_gtest.cpp280
-rw-r--r--test/dns_decoder/dns_pcap/dns_tcp_simple.pcapbin0 -> 2361 bytes
-rw-r--r--test/dns_decoder/dns_pcap/dns_udp_simple.pcapbin0 -> 1624 bytes
-rw-r--r--test/dns_decoder/test_env/conflist.inf9
-rw-r--r--test/dns_decoder/test_env/sapp4.el8.x86_64.rpmbin0 -> 1052968 bytes
-rw-r--r--test/dns_decoder/test_env/spec.toml12
-rw-r--r--test/dns_decoder/test_env/start_loader.inf17
-rw-r--r--test/dns_decoder/test_env/tsg_l7_protocol.conf57
-rw-r--r--test/dns_decoder/test_result_json/dns_tcp_simple.json58
-rw-r--r--test/dns_decoder/test_result_json/dns_udp_simple.json25
-rw-r--r--test/http_decoder/test_env/spec.toml2
17 files changed, 2106 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 90bef99..9a8b8ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -79,6 +79,7 @@ add_subdirectory(deps/toml)
add_subdirectory(src/adapter)
add_subdirectory(src/stellar_on_sapp)
add_subdirectory(src/http_decoder)
+add_subdirectory(src/dns_decoder)
add_subdirectory(examples/sapp_plugin)
add_subdirectory(examples/stellar_plugin)
diff --git a/src/dns_decoder/CMakeLists.txt b/src/dns_decoder/CMakeLists.txt
new file mode 100644
index 0000000..da7c4c3
--- /dev/null
+++ b/src/dns_decoder/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_definitions(-fPIC)
+
+include_directories(/opt/MESA/include/)
+include_directories(${PROJECT_SOURCE_DIR}/deps/)
+
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/mempool DEPS_SRC)
+aux_source_directory(${PROJECT_SOURCE_DIR}/deps/toml DEPS_SRC)
+
+set(DNS_SRC ${DEPS_SRC} dns_decoder.c)
+
+add_library(dns_decoder SHARED ${DNS_SRC})
+#set_target_properties(dns_decoder_shared PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/version.map")
+set_target_properties(dns_decoder PROPERTIES PREFIX "") \ No newline at end of file
diff --git a/src/dns_decoder/dns_decoder.c b/src/dns_decoder/dns_decoder.c
new file mode 100644
index 0000000..3e76d03
--- /dev/null
+++ b/src/dns_decoder/dns_decoder.c
@@ -0,0 +1,1317 @@
+/*
+**********************************************************************************************
+* File: dns_decoder.c
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2024-02-07
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "stellar/utils.h"
+#include "stellar/session.h"
+#include "stellar/stellar.h"
+#include "stellar/session_mq.h"
+#include "stellar/session_exdata.h"
+#include "dns_decoder.h"
+
+#define DNS_HEADER_SIZE 12
+
+#define NS_INT16SZ 2
+#define NS_INT32SZ 4
+
+#define NS_GET16(s, cp) do { \
+ register uint8_t *t_cp = (uint8_t *)(cp); \
+ (s) = ((uint16_t)t_cp[0] << 8) \
+ | ((uint16_t)t_cp[1]); \
+ (cp) += NS_INT16SZ; \
+} while (0)
+
+#define NS_GET32(l, cp) do { \
+ register uint8_t *t_cp = (uint8_t *)(cp); \
+ (l) = ((uint32_t)t_cp[0] << 24) \
+ | ((uint32_t)t_cp[1] << 16) \
+ | ((uint32_t)t_cp[2] << 8) \
+ | ((uint32_t)t_cp[3]); \
+ (cp) += NS_INT32SZ; \
+} while (0)
+
+const char *dns_decoder_topic = "DNS_DECODER_MESSAGE";
+
+struct dns_message {
+ enum dns_message_type type;
+ union {
+ struct dns_query query;
+ struct dns_response response;
+ };
+};
+
+struct dns_decoder_context {
+ int plugin_id;
+ int topic_id;
+ int ex_data_idx;
+ struct stellar *st;
+};
+
+struct dns_decoder_exdata {
+ char *cache_ptr;
+ int one_byte_len;
+ size_t cache_len;
+ size_t cache_capacity;
+};
+
+struct dns_header {
+ uint16_t id;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ uint8_t rd:1;
+ uint8_t tc:1;
+ uint8_t aa:1;
+ uint8_t opcode:4;
+ uint8_t qr:1;
+ uint8_t rcode:4;
+ uint8_t z:3;
+ uint8_t ra:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ uint8_t qr:1;
+ uint8_t opcode:4;
+ uint8_t aa:1;
+ uint8_t tc:1;
+ uint8_t rd:1;
+ uint8_t ra:1;
+ uint8_t z:3;
+ uint8_t rcode:4;
+#endif
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t aucount; //authority count
+ uint16_t adcount; //additional count
+};
+
+struct dns_message *
+dns_message_new(enum dns_message_type type)
+{
+ struct dns_message *msg = CALLOC(struct dns_message, 1);
+
+ msg->type = type;
+
+ return msg;
+}
+
+static void dns_message_free(void *dns_msg, void *cb_arg)
+{
+ if (NULL == dns_msg) {
+ return;
+ }
+
+ struct dns_message *msg = (struct dns_message *)dns_msg;
+ if (msg->type == DNS_MESSAGE_RESPONSE) {
+ if (msg->response.answer_rr != NULL) {
+ FREE(msg->response.answer_rr);
+ }
+
+ if (msg->response.authority_rr != NULL) {
+ FREE(msg->response.authority_rr);
+ }
+
+ if (msg->response.additional_rr != NULL) {
+ FREE(msg->response.additional_rr);
+ }
+ }
+
+ FREE(msg);
+}
+
+enum dns_message_type dns_message_type(struct dns_message *msg) {
+ if (NULL == msg) {
+ return DNS_MESSAGE_MAX;
+ }
+
+ return msg->type;
+}
+
+int dns_message_get_query(struct dns_message *msg, struct dns_query *query)
+{
+ if (NULL == msg || msg->type != DNS_MESSAGE_QUERY || NULL == query) {
+ return -1;
+ }
+
+ *query = msg->query;
+ return 0;
+}
+
+int dns_message_get_response(struct dns_message *msg,
+ struct dns_response *response)
+{
+ if (NULL == msg || msg->type != DNS_MESSAGE_RESPONSE ||
+ NULL == response) {
+ return -1;
+ }
+ *response = msg->response;
+ return 0;
+}
+
+int dns_serialize_query(uint8_t **buff, size_t *buff_len, struct dns_query *query)
+{
+ return 0;
+}
+
+int dns_serialize_response(uint8_t **buff, size_t *buff_len, struct dns_response *response)
+{
+ return 0;
+}
+
+static void
+dns_decoder_ex_data_free(struct session *s, int idx, void *ex_data, void *arg)
+{
+
+}
+
+static void _dns_decoder_context_free(struct dns_decoder_context *ctx)
+{
+ if (NULL == ctx) {
+ return;
+ }
+
+ if (ctx->topic_id >= 0) {
+ session_mq_destroy_topic(ctx->st, ctx->topic_id);
+ ctx->topic_id = -1;
+ }
+
+ FREE(ctx);
+}
+
+static void populate_dns_header(struct dns_header *dns_hdr, const uint8_t *payload)
+{
+ struct dns_header *tmp = (struct dns_header *)payload;
+
+ dns_hdr->qr = tmp->qr;
+ dns_hdr->opcode= tmp->opcode;
+ dns_hdr->aa = tmp->aa;
+ dns_hdr->tc = tmp->tc;
+ dns_hdr->rd = tmp->rd;
+ dns_hdr->ra = tmp->ra;
+ dns_hdr->z = tmp->z;
+ dns_hdr->rcode = tmp->rcode;
+
+ dns_hdr->id = ntohs(tmp->id);
+ dns_hdr->qdcount = ntohs(tmp->qdcount);
+ dns_hdr->ancount = ntohs(tmp->ancount);
+ dns_hdr->aucount = ntohs(tmp->aucount);
+ dns_hdr->adcount = ntohs(tmp->adcount);
+}
+
+static int validate_dns_header(struct dns_header *dns_hdr)
+{
+
+ return 0;
+}
+
+static int
+get_decompressed_name(uint8_t *dns_payload, size_t dns_payload_len,
+ uint8_t **cur_pos, uint8_t *buf, size_t buf_len)
+{
+ size_t index = 0;
+ size_t len = 0;
+ size_t tot_len = 0;
+ uint8_t np = 0;
+ uint8_t *cursor = *cur_pos;
+ uint8_t *dns_payload_end = dns_payload + dns_payload_len - 1;
+ *cur_pos = NULL;
+
+ while (cursor <= dns_payload_end) {
+ if (0 == cursor[0]) {
+ break;
+ }
+
+ if (0x0c0 == (cursor[0] & 0x0c0)) {
+ if(cursor + 1 > dns_payload_end) {
+ return -1;
+ }
+
+ /* udp payload offset */
+ len = ((cursor[0] & 0x03f) << 8) + cursor[1];
+
+ if (NULL == *cur_pos) {
+ tot_len += 2;
+ *cur_pos = cursor + 2;
+ }
+
+ /* udp payload offset = dns_header (12bytes) + dns_payload */
+ cursor = dns_payload + len - 12;
+ if (cursor > dns_payload_end) {
+ return -1;
+ }
+
+ /* too many pointers. */
+ if (np++ > 16) {
+ return -1;
+ }
+
+ continue;
+ }
+
+ len = cursor[0];
+ cursor++;
+ tot_len++;
+
+ if (cursor + len - 1 > dns_payload_end) {
+ return -1;
+ }
+
+ if (index + len >= (buf_len - 1)) {
+ return -1;
+ }
+
+ memcpy(buf + index, cursor, len);
+ index += len;
+ buf[index++] = '.';
+ cursor += len;
+ tot_len += len;
+ }
+
+ if (NULL == *cur_pos) {
+ *cur_pos = cursor + 1;
+ tot_len++;
+ }
+
+ /* omit last '.' */
+ if (index > 0) {
+ buf[index - 1] = '\0';
+ } else {
+ buf[0] = '\0';
+ }
+
+ return tot_len;
+}
+
+static int get_rr_domain(uint8_t *dns_payload, size_t dns_payload_len,
+ uint8_t **cur_pos, uint8_t *buf, size_t buf_len)
+{
+ return get_decompressed_name(dns_payload, dns_payload_len,
+ cur_pos, buf, buf_len);
+}
+
+static int
+parse_dns_query_question(struct dns_query_question *question,
+ uint8_t *dns_payload, size_t dns_payload_len,
+ uint8_t **cur_pos)
+{
+ *cur_pos = dns_payload;
+ uint8_t *dns_payload_end = dns_payload + dns_payload_len - 1;
+
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ question->qname, DNS_NAME_MAX) <= 0) {
+ return -1;
+ }
+
+ if (*cur_pos + 1 > dns_payload_end) {
+ return -1;
+ }
+ NS_GET16(question->qtype, *cur_pos);
+
+ if (*cur_pos + 1 > dns_payload_end) {
+ return -1;
+ }
+ NS_GET16(question->qclass, *cur_pos);
+
+ return 0;
+}
+
+static void dns_flag_copy(struct dns_flag *flag, struct dns_header *dns_hdr)
+{
+ flag->qr = dns_hdr->qr;
+ flag->opcode = dns_hdr->opcode;
+ flag->aa = dns_hdr->aa;
+ flag->tc = dns_hdr->tc;
+ flag->rd = dns_hdr->rd;
+ flag->ra = dns_hdr->ra;
+ flag->z = dns_hdr->z;
+ flag->rcode = dns_hdr->rcode;
+}
+
+static int
+parse_dns_query(struct dns_query *query, struct dns_header *dns_hdr,
+ uint8_t *dns_payload, size_t dns_payload_len)
+{
+ query->transaction_id = dns_hdr->id;
+
+ //dns query flag
+ dns_flag_copy(&query->flag, dns_hdr);
+
+ query->n_question = dns_hdr->qdcount;
+
+ uint8_t *cur_pos = NULL;
+ int ret = parse_dns_query_question(&query->query_question, dns_payload,
+ dns_payload_len, &cur_pos);
+ if (ret < 0) {
+ fprintf(stderr, "parse dns query question failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+dns_response_init(struct dns_response *response, struct dns_header *dns_hdr)
+{
+ struct dns_query *query = &response->query;
+
+ query->transaction_id = dns_hdr->id;
+ dns_flag_copy(&query->flag, dns_hdr);
+
+ query->n_question = dns_hdr->qdcount;
+
+ response->n_answer_rr = dns_hdr->ancount;
+ response->n_authority_rr = dns_hdr->aucount;
+ response->n_additional_rr = dns_hdr->adcount;
+
+ if (response->n_answer_rr > 0) {
+ response->answer_rr = CALLOC(struct dns_rr, response->n_answer_rr);
+ }
+
+ if (response->n_authority_rr > 0) {
+ response->authority_rr = CALLOC(struct dns_rr, response->n_authority_rr);
+ }
+
+ if (response->n_additional_rr > 0) {
+ response->additional_rr = CALLOC(struct dns_rr, response->n_additional_rr);
+ }
+}
+
+static int
+get_rr_common_field(uint8_t *dns_payload, size_t dns_payload_len,
+ uint8_t **cur_pos, struct dns_rr *dns_rr)
+{
+ uint8_t *dns_payload_end = dns_payload + dns_payload_len - 1;
+
+ if (NULL == *cur_pos) {
+ return -1;
+ }
+
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->name, DNS_NAME_MAX) <= 0) {
+ return -1;
+ }
+
+ if (NULL == *cur_pos || (*cur_pos + 1) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET16(dns_rr->type, *cur_pos);
+
+ if (dns_rr->type == DNS_RR_TYPE_OPT) {
+ return 0;
+ }
+
+ if (NULL == *cur_pos || (*cur_pos + 1) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET16(dns_rr->rr_class, *cur_pos);
+
+ if (NULL == *cur_pos || (*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(dns_rr->ttl, *cur_pos);
+
+ if (NULL == *cur_pos || (*cur_pos + 1) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET16(dns_rr->rdlength, *cur_pos);
+
+ if (NULL == *cur_pos ||
+ (*cur_pos + dns_rr->rdlength - 1) > dns_payload_end) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_rr_type_info(uint8_t **cur_pos, struct rdata_hinfo *hinfo)
+{
+ hinfo->cpu_len = (*cur_pos)[0];
+ *cur_pos += 1;
+
+ uint8_t length = MIN(hinfo->cpu_len, sizeof(hinfo->cpu) - 1);
+ if (length > 0) {
+ memcpy(hinfo->cpu, *cur_pos, length);
+ hinfo->cpu[length] = '\0';
+ } else {
+ hinfo->cpu[0] = '\0';
+ }
+
+ *cur_pos += hinfo->cpu_len;
+ hinfo->cpu_len = (length > 0 ? length : 0);
+
+ hinfo->os_len = (*cur_pos)[0];
+ *cur_pos += 1;
+
+ length = MIN(hinfo->os_len, sizeof(hinfo->os) - 1);
+ if (length > 0) {
+ memcpy(hinfo->os, *cur_pos, length);
+ hinfo->os[length] = '\0';
+ } else {
+ hinfo->os[0] = '\0';
+ }
+
+ *cur_pos += hinfo->os_len;
+ hinfo->os_len = (length > 0 ? length : 0);
+
+ return 0;
+}
+
+static int
+get_rr_type_soa(uint8_t *dns_payload, size_t dns_payload_len,
+ uint8_t **cur_pos, struct rdata_soa *soa)
+{
+ uint8_t *dns_payload_end = dns_payload + dns_payload_len - 1;
+
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ soa->mname, sizeof(soa->mname)) <= 0) {
+ return -1;
+ }
+
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ soa->rname, sizeof(soa->rname)) <= 0) {
+ return -1;
+ }
+
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(soa->serial, *cur_pos);
+
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(soa->refresh, *cur_pos);
+
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(soa->retry, *cur_pos);
+
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(soa->expire, *cur_pos);
+
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(soa->minimum, *cur_pos);
+
+ return 0;
+}
+
+static int
+get_rr_type_wks(uint8_t **cur_pos, struct rdata_wks *wks,
+ const uint8_t *dns_payload_end)
+{
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET32(wks->addr, *cur_pos);
+
+ if ((*cur_pos + 1) > dns_payload_end) {
+ return -1;
+ }
+
+ wks->protocol = **cur_pos;
+ *cur_pos += 1;
+ wks->bitmap = *cur_pos;
+
+ return 0;
+}
+
+static int
+get_rr_type_rrsig(uint8_t **cur_pos, struct rdata_rrsig *rrsig,
+ const uint8_t *dns_payload_end)
+{
+ if ((*cur_pos + 1) > dns_payload_end) {
+ return -1;
+ }
+ NS_GET16(rrsig->type_covered, *cur_pos);
+
+ rrsig->algo = **cur_pos;
+ *cur_pos += 1;
+ rrsig->labels = **cur_pos;
+ *cur_pos += 1;
+
+ if ((*cur_pos + 13) > dns_payload_end) {
+ return -1;
+ }
+
+ NS_GET32(rrsig->original_ttl, *cur_pos);
+ NS_GET32(rrsig->sig_expiration, *cur_pos);
+ NS_GET32(rrsig->sig_inception, *cur_pos);
+ NS_GET16(rrsig->key_tag, *cur_pos);
+
+ return 0;
+}
+
+static int
+dissect_type_bitmap(uint8_t *cur_pos, size_t rr_len, uint8_t *maps_buff,
+ uint16_t *maps_len)
+{
+ size_t cur_offset = 0;
+ size_t map_offset = 0;
+
+ if (NULL == cur_pos || 0 == rr_len) {
+ *maps_len = 0;
+ return 0;
+ }
+
+ while ((rr_len - cur_offset) > 0) {
+ if (map_offset + 2 > *maps_len) {
+ break;
+ }
+
+ maps_buff[map_offset++] = cur_pos[cur_offset++];
+ maps_buff[map_offset++] = cur_pos[cur_offset];
+ size_t blocksize = cur_pos[cur_offset++];
+
+ size_t length = MIN(*maps_len - map_offset, blocksize);
+ if (length == 0 || blocksize > (rr_len - cur_offset)) {
+ break;
+ }
+
+ memcpy(maps_buff + map_offset, cur_pos + cur_offset, length);
+ cur_offset += blocksize;
+ map_offset += length;
+ }
+
+ *maps_len = map_offset;
+
+ return cur_offset;
+}
+
+static int
+get_rr_type_nsec3(uint8_t **cur_pos, struct rdata_nsec3 *nsec3,
+ const uint8_t *dns_payload_end)
+{
+ if ((*cur_pos + 4) > dns_payload_end) {
+ return -1;
+ }
+
+ nsec3->hash_algo = **cur_pos;
+ *cur_pos += 1;
+ nsec3->flags= **cur_pos;
+ *cur_pos += 1;
+
+ NS_GET16(nsec3->iteration, *cur_pos);
+
+ nsec3->salt_len = **cur_pos;
+ *cur_pos += 1;
+
+ if (nsec3->salt_len > 0) {
+ if ((*cur_pos + nsec3->salt_len) > dns_payload_end) {
+ return -1;
+ }
+
+ nsec3->salt_value = *cur_pos;
+ *cur_pos += nsec3->salt_len; /* jump salt_value */
+ }
+
+ nsec3->hash_len = **cur_pos;
+ *cur_pos += 1;
+
+ if (nsec3->hash_len > 0) {
+ if ((*cur_pos + nsec3->hash_len) > dns_payload_end) {
+ return -1;
+ }
+
+ nsec3->next_hash_owner = *cur_pos;
+ *cur_pos += nsec3->hash_len; /* jump next_hash_owner */
+ }
+
+ return 0;
+}
+
+static int
+get_one_rr(uint8_t *dns_payload, size_t dns_payload_len, uint8_t **cur_pos,
+ struct dns_rr *dns_rr)
+{
+ int len = 0;
+ uint8_t *original_ptr = NULL;
+ uint8_t *dns_payload_end =dns_payload + dns_payload_len - 1;
+
+ switch(dns_rr->type) {
+ case DNS_RR_TYPE_CNAME:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.cname, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.cname[0] = '\0';
+ return -1;
+ }
+
+ break;
+ case DNS_RR_TYPE_HINFO:
+ if (get_rr_type_info(cur_pos, &(dns_rr->rdata.hinfo)) != 0) {
+ memset(&(dns_rr->rdata.hinfo), 0, sizeof(struct rdata_hinfo));
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_MB:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.mb, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.mb[0] = '\0';
+ return -1;
+ }
+
+ break;
+ case DNS_RR_TYPE_MD:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.md, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.md[0] = '\0';
+ return -1;
+ }
+
+ break;
+ case DNS_RR_TYPE_MF:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.mf, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.mf[0] = '\0';
+ return -1;
+ }
+
+ break;
+ case DNS_RR_TYPE_MG:
+ if(get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.mg, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.mg[0] = '\0';
+ return -1;
+ }
+
+ break;
+ case DNS_RR_TYPE_MINFO:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.minfo.rmailbx, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.minfo.rmailbx[0] = '\0';
+ return -1;
+ }
+
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.minfo.emailbx, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.minfo.emailbx[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_MR:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.mr, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.mr[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_MX:
+ if((*cur_pos + 1) > dns_payload_end) {
+ return -1;
+ }
+
+ NS_GET16(dns_rr->rdata.mx.preference, *cur_pos);
+
+ if ((dns_rr->rdlength - 2) < (*cur_pos)[0]) {
+ if (dns_rr->rdlength < 2) {
+ *cur_pos += dns_rr->rdlength;
+ break;
+ }
+
+ len = MIN(DNS_NAME_MAX - 2, dns_rr->rdlength - 2);/*size=1byte*/
+ memcpy(dns_rr->rdata.mx.exchange, *cur_pos, len); /* error labels */
+ dns_rr->rdata.mx.exchange[len] = '\0';
+ *cur_pos += dns_rr->rdlength - 2;
+ } else {
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.mx.exchange, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.mx.exchange[0] = '\0';
+ return -1;
+ }
+ }
+ break;
+ case DNS_RR_TYPE_NS:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.ns, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.ns[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_PTR:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.ptr, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.ptr[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_SOA:
+ original_ptr = *cur_pos;
+ if (get_rr_type_soa(dns_payload, dns_payload_len, cur_pos,
+ &(dns_rr->rdata.soa)) != 0) {
+ memset(&(dns_rr->rdata.soa), 0, sizeof(struct rdata_soa));
+ return -1;
+ }
+
+ if(original_ptr + dns_rr->rdlength != *cur_pos) {
+ *cur_pos = original_ptr + dns_rr->rdlength;
+ }
+ break;
+ case DNS_RR_TYPE_A:
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+
+ memcpy(dns_rr->rdata.a, *cur_pos, NS_INT32SZ);
+ (*cur_pos) += NS_INT32SZ;
+ break;
+ case DNS_RR_TYPE_AAAA:
+ if((*cur_pos + 15) > dns_payload_end) {
+ return -1;
+ }
+
+ memcpy(dns_rr->rdata.aaaa, *cur_pos, 16);
+ (*cur_pos) += 16;
+ break;
+ case DNS_RR_TYPE_DNAME:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.dname, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.dname[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_ISDN:
+ memcpy(dns_rr->rdata.isdn, *cur_pos, sizeof(uint8_t));
+ (*cur_pos) += 1;
+ break;
+ case DNS_RR_TYPE_TXT:
+ len = MIN(DNS_NAME_MAX - 2, dns_rr->rdlength - 1); /*size=1byte*/
+ memcpy(dns_rr->rdata.txt.txt, *cur_pos + 1, len);
+ dns_rr->rdata.txt.size = len;
+ *cur_pos += dns_rr->rdlength;
+ break;
+ case DNS_RR_TYPE_RP:
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.rp.mailbox, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.rp.mailbox[0] = '\0';
+ return -1;
+ }
+
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.rp.txt_rr, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.rp.txt_rr[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_NULL:
+ len = MIN(DNS_NAME_MAX-2, dns_rr->rdlength - 1); /*size=1byte*/
+ memcpy(dns_rr->rdata.null.null, *cur_pos + 1, len);
+ dns_rr->rdata.null.size = len;
+ *cur_pos += dns_rr->rdlength;
+ break;
+ case DNS_RR_TYPE_WKS:
+ if (get_rr_type_wks(cur_pos, &(dns_rr->rdata.wks), dns_payload_end) != 0) {
+ memset(&(dns_rr->rdata.wks), 0, sizeof(struct rdata_wks));
+ return -1;
+ }
+
+ dns_rr->rdata.wks.size = dns_rr->rdlength - 5;
+ if (0 == dns_rr->rdata.wks.size) {
+ dns_rr->rdata.wks.size = 0;
+ dns_rr->rdata.wks.bitmap = NULL;
+
+ }
+ *cur_pos += dns_rr->rdlength - 5;
+ case DNS_RR_TYPE_SRV:
+ NS_GET16(dns_rr->rdata.srv.priority, *cur_pos);
+ NS_GET16(dns_rr->rdata.srv.weight, *cur_pos);
+ NS_GET16(dns_rr->rdata.srv.port, *cur_pos);
+ if (get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.srv.target, DNS_NAME_MAX) <= 0) {
+ dns_rr->rdata.srv.target[0] = '\0';
+ return -1;
+ }
+ break;
+ case DNS_RR_TYPE_OPT:
+ break;
+ case DNS_RR_TYPE_DS:
+ case DNS_RR_TYPE_DLV:
+ if ((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+
+ NS_GET16(dns_rr->rdata.ds.key_tag, *cur_pos);
+ dns_rr->rdata.ds.algo = **cur_pos;
+ *cur_pos += 1;
+ dns_rr->rdata.ds.digest_type = **cur_pos;
+ *cur_pos += 1;
+ dns_rr->rdata.ds.digest = *cur_pos;
+ dns_rr->rdata.ds.digest_len = dns_rr->rdlength - 4;
+ if (dns_rr->rdata.ds.digest_len == 0) {
+ dns_rr->rdata.ds.digest = NULL;
+ dns_rr->rdata.ds.digest_len = 0;
+ }
+ *cur_pos += dns_rr->rdlength - 4;
+ break;
+ case DNS_RR_TYPE_RRSIG:
+ if((*cur_pos + 17) > dns_payload_end) {
+ return -1;
+ }
+
+ memset(&(dns_rr->rdata.rrsig), 0, sizeof(struct rdata_rrsig));
+ get_rr_type_rrsig(cur_pos, &(dns_rr->rdata.rrsig), dns_payload_end);
+ len = get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.rrsig.signer_name, DNS_NAME_MAX);
+ if (len <= 0) {
+ dns_rr->rdata.rrsig.signer_name[0] = '\0';
+ return -1;
+ }
+
+ dns_rr->rdata.rrsig.signature = *cur_pos;
+ dns_rr->rdata.rrsig.signature_len = dns_rr->rdlength - 18 - len;
+ if (dns_rr->rdata.rrsig.signature_len == 0) {
+ dns_rr->rdata.rrsig.signature = NULL;
+ dns_rr->rdata.rrsig.signature_len = 0;
+ }
+ *cur_pos += dns_rr->rdlength - 18 - len;
+ break;
+ case DNS_RR_TYPE_NSEC:
+ original_ptr = *cur_pos;
+ len = get_rr_domain(dns_payload, dns_payload_len, cur_pos,
+ dns_rr->rdata.nsec.next_domain, DNS_NAME_MAX);
+ if (len <= 0) {
+ dns_rr->rdata.nsec.next_domain[0] = '\0';
+ return -1;
+ }
+
+ if ((int)(dns_rr->rdlength - len) < (int)(sizeof(dns_rr->rdata.nsec.type_bit_maps))) {
+ dns_rr->rdata.nsec.maps_len = DNS_NAME_MAX;
+ dissect_type_bitmap(*cur_pos, dns_rr->rdlength - len,
+ (dns_rr->rdata.nsec.type_bit_maps),
+ &(dns_rr->rdata.nsec.maps_len));
+ }
+ *cur_pos = original_ptr + dns_rr->rdlength;
+ break;
+ case DNS_RR_TYPE_DNSKEY:
+ if((*cur_pos + 3) > dns_payload_end) {
+ return -1;
+ }
+
+ NS_GET16(dns_rr->rdata.dnskey.flags, *cur_pos);
+ dns_rr->rdata.dnskey.protocol = **cur_pos;
+ *cur_pos += 1;
+ dns_rr->rdata.dnskey.algo = **cur_pos;
+ *cur_pos += 1;
+ dns_rr->rdata.dnskey.public_key = *cur_pos;
+ dns_rr->rdata.dnskey.public_key_len = dns_rr->rdlength - 4; /* sizeof(flags)+sizeof(protocol)+sizeof(algo) */
+ if (dns_rr->rdata.dnskey.public_key_len == 0) {
+ dns_rr->rdata.dnskey.public_key = NULL;
+ dns_rr->rdata.dnskey.public_key_len = 0;
+ }
+ *cur_pos += dns_rr->rdlength - 4; /* todo add log */
+ break;
+ case DNS_RR_TYPE_NSEC3:
+ original_ptr = *cur_pos;
+ if (get_rr_type_nsec3(cur_pos, &(dns_rr->rdata.nsec3), dns_payload_end) < 0) {
+ return -1;
+ }
+
+ len = *cur_pos - original_ptr;
+ if ((int)(dns_rr->rdlength - len) < (int)(sizeof(dns_rr->rdata.nsec3.type_bit_maps))) {
+ dns_rr->rdata.nsec3.maps_len = DNS_NAME_MAX;
+ dissect_type_bitmap(*cur_pos, dns_rr->rdlength - len,
+ (dns_rr->rdata.nsec3.type_bit_maps),
+ &(dns_rr->rdata.nsec3.maps_len));
+ }
+ *cur_pos = original_ptr + dns_rr->rdlength;
+ break;
+ case DNS_RR_TYPE_NSEC3PARAM:
+ dns_rr->rdata.nsec3param.hash_algo = **cur_pos;
+ *cur_pos += 1;
+
+ dns_rr->rdata.nsec3param.flags = **cur_pos;
+ *cur_pos += 1;
+
+ NS_GET16(dns_rr->rdata.nsec3param.iteration, *cur_pos);
+
+ dns_rr->rdata.nsec3param.salt_len = dns_rr->rdlength - 4 - 1;
+ *cur_pos += 1;
+
+ if (dns_rr->rdata.nsec3param.salt_len == 0) {
+ dns_rr->rdata.nsec3param.salt_value = NULL;
+ dns_rr->rdata.nsec3param.salt_len = 0;
+ } else {
+ dns_rr->rdata.nsec3param.salt_value = *cur_pos;
+ }
+ *cur_pos += dns_rr->rdlength - 5;
+ break;
+ case DNS_RR_TYPE_UNKNOWN:
+ len = MIN(dns_rr->rdlength, sizeof(dns_rr->rdata.unknown_data) - 1);
+ memcpy(dns_rr->rdata.unknown_data, *cur_pos, len);
+ dns_rr->rdata.unknown_data[len] = '\0';
+ (*cur_pos) += dns_rr->rdlength;
+ break;
+ default:
+ *cur_pos += dns_rr->rdlength;
+ fprintf(stderr, "No support dns rr type, type: %d", dns_rr->type);
+ break;
+ }
+
+ return 0;
+}
+
+static int
+parse_common_dns_rr(uint8_t *dns_payload, size_t dns_payload_len,
+ uint8_t **cur_pos, struct dns_rr *rr, size_t n_rr)
+{
+ uint8_t *dns_payload_end = dns_payload + dns_payload_len - 1;
+
+ for (size_t i = 0; i < n_rr; i++) {
+ if (*cur_pos < dns_payload || *cur_pos >= dns_payload_end) {
+ return -1;
+ }
+
+ if (get_rr_common_field(dns_payload, dns_payload_len,
+ cur_pos, &rr[i]) < 0) {
+ return -1;
+ }
+
+ if (get_one_rr(dns_payload, dns_payload_len, cur_pos, &rr[i]) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_dns_rr(struct dns_response *response, uint8_t *dns_payload,
+ size_t dns_payload_len, uint8_t *cur_pos)
+{
+ size_t i = 0;
+ uint8_t *dns_payload_end = dns_payload + dns_payload_len - 1;
+
+ /* parse answer */
+ int ret = parse_common_dns_rr(dns_payload, dns_payload_len, &cur_pos,
+ response->answer_rr, response->n_answer_rr);
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = parse_common_dns_rr(dns_payload, dns_payload_len, &cur_pos,
+ response->authority_rr, response->n_authority_rr);
+ if (ret < 0) {
+ return -1;
+ }
+
+ ret = parse_common_dns_rr(dns_payload, dns_payload_len, &cur_pos,
+ response->additional_rr, response->n_additional_rr);
+ if (ret < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+parse_dns_response(struct dns_response *response, struct dns_header *dns_hdr,
+ uint8_t *dns_payload, size_t dns_payload_len)
+{
+ dns_response_init(response, dns_hdr);
+
+ uint8_t *cur_pos = NULL;
+ int ret = parse_dns_query_question(&response->query.query_question,
+ dns_payload, dns_payload_len, &cur_pos);
+ if (ret < 0) {
+ fprintf(stderr, "parse dns response question failed.\n");
+ return -1;
+ }
+
+ ret = parse_dns_rr(response, dns_payload, dns_payload_len, cur_pos);
+ if (ret < 0) {
+ fprintf(stderr, "parse dns reponse resource record failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+udp_dns_decoder_entry(struct session *sess, int events, const struct packet *pkt,
+ struct dns_decoder_context *ctx)
+{
+ size_t udp_payload_len = 0;
+
+ const uint8_t *udp_payload =
+ session_get0_current_payload(sess, &udp_payload_len);
+ if (udp_payload_len < DNS_HEADER_SIZE) {
+ //Non dns packet
+ return 0;
+ }
+
+ struct dns_header dns_hdr = {0};
+ populate_dns_header(&dns_hdr, udp_payload);
+
+ int ret = validate_dns_header(&dns_hdr);
+ if (ret < 0) {
+ return 0;
+ }
+
+ uint8_t *dns_payload = udp_payload + DNS_HEADER_SIZE;
+ size_t dns_payload_len = udp_payload_len - DNS_HEADER_SIZE;
+
+ struct dns_message *msg = NULL;
+ if (0 == dns_hdr.qr) {
+ //query
+ msg = dns_message_new(DNS_MESSAGE_QUERY);
+ ret = parse_dns_query(&msg->query, &dns_hdr, dns_payload,
+ dns_payload_len);
+ if (ret < 0) {
+ dns_message_free(msg, NULL);
+ return -1;
+ }
+ } else {
+ //response
+ msg = dns_message_new(DNS_MESSAGE_RESPONSE);
+ ret = parse_dns_response(&msg->response, &dns_hdr, dns_payload,
+ dns_payload_len);
+ if (ret < 0) {
+ dns_message_free(msg, NULL);
+ return -1;
+ }
+ }
+
+ session_mq_publish_message(sess, ctx->topic_id, msg);
+
+ return 0;
+}
+
+static void
+dns_decoder_exdata_free(struct dns_decoder_exdata *ex_data)
+{
+ if (NULL == ex_data) {
+ return;
+ }
+
+ if (ex_data->cache_ptr != NULL) {
+ FREE(ex_data->cache_ptr);
+ }
+
+ FREE(ex_data);
+}
+
+static int
+tcp_dns_decoder_entry(struct session *sess, int events, const struct packet *pkt,
+ struct dns_decoder_context *ctx)
+{
+ struct dns_decoder_exdata *ex_data =
+ session_get_ex_data(sess, ctx->ex_data_idx);
+
+ if (events & SESS_EV_CLOSING) {
+ if (ex_data != NULL) {
+ dns_decoder_exdata_free(ex_data);
+ session_set_ex_data(sess, ctx->ex_data_idx, NULL);
+ }
+
+ return 0;
+ }
+
+ size_t tcp_payload_len = 0;
+ const uint8_t *tcp_payload =
+ session_get0_current_payload(sess, &tcp_payload_len);
+ if (0 == tcp_payload_len) {
+ return 0;
+ }
+
+ uint8_t *buff = NULL;
+ size_t buff_len = 0;
+
+ if (NULL == ex_data) {
+ if (1 == tcp_payload_len) {
+ //cache this byte
+ ex_data = CALLOC(struct dns_decoder_exdata, 1);
+ ex_data->one_byte_len = *(uint8_t *)tcp_payload;
+ session_set_ex_data(sess, ctx->ex_data_idx, ex_data);
+ return 0;
+ } else {
+ uint16_t dns_len = ntohs(*(uint16_t *)tcp_payload);
+ if (dns_len > (tcp_payload_len - 2)) {
+ ex_data = CALLOC(struct dns_decoder_exdata, 1);
+ ex_data->one_byte_len = -1;
+ ex_data->cache_ptr = CALLOC(char, dns_len);
+ ex_data->cache_len = tcp_payload_len - 2;
+ ex_data->cache_capacity = dns_len;
+ memcpy(ex_data->cache_ptr, tcp_payload, ex_data->cache_len);
+ session_set_ex_data(sess, ctx->ex_data_idx, ex_data);
+
+ return 0;
+ } else {
+ //goto dns parser
+ buff = tcp_payload + 2;
+ buff_len = tcp_payload_len - 2;
+ goto next;
+ }
+ }
+ } else {
+ // SESS_EV_PACKET
+ if (ex_data->one_byte_len == -1) {
+ if (tcp_payload_len < ex_data->cache_capacity - ex_data->cache_len) {
+ memcpy(ex_data->cache_ptr + ex_data->cache_len, tcp_payload,
+ tcp_payload_len);
+ ex_data->cache_len += tcp_payload_len;
+ return 0;
+ } else if (tcp_payload_len == ex_data->cache_capacity - ex_data->cache_len) {
+ memcpy(ex_data->cache_ptr + ex_data->cache_len, tcp_payload,
+ tcp_payload_len);
+ ex_data->cache_len += tcp_payload_len;
+ buff = ex_data->cache_ptr;
+ buff_len = ex_data->cache_len;
+ } else {
+ assert(0);
+ }
+ } else {
+ //
+ uint8_t tmp_len[2] = {0};
+ tmp_len[0] = ex_data->one_byte_len;
+ tmp_len[1] = *(uint8_t *)tcp_payload;
+ uint16_t dns_len = ntohs(*(uint16_t *)tmp_len);
+ if (dns_len > (tcp_payload_len - 1)) {
+ memcpy(ex_data->cache_ptr + ex_data->cache_len, tcp_payload + 1,
+ tcp_payload_len - 1);
+ ex_data->cache_len += tcp_payload_len - 1;
+ return 0;
+ } else {
+ buff = tcp_payload + 1;
+ buff_len = tcp_payload_len - 1;
+ }
+ }
+ }
+
+ struct dns_header dns_hdr = {0};
+next:
+ populate_dns_header(&dns_hdr, buff);
+
+ int ret = validate_dns_header(&dns_hdr);
+ if (ret < 0) {
+ return 0;
+ }
+
+ uint8_t *dns_payload = buff + DNS_HEADER_SIZE;
+ size_t dns_payload_len = buff_len - DNS_HEADER_SIZE;
+
+ struct dns_message *msg = NULL;
+ if (0 == dns_hdr.qr) {
+ //query
+ msg = dns_message_new(DNS_MESSAGE_QUERY);
+ ret = parse_dns_query(&msg->query, &dns_hdr, dns_payload,
+ dns_payload_len);
+ if (ret < 0) {
+ dns_message_free(msg, NULL);
+ return -1;
+ }
+ } else {
+ //response
+ msg = dns_message_new(DNS_MESSAGE_RESPONSE);
+ ret = parse_dns_response(&msg->response, &dns_hdr, dns_payload,
+ dns_payload_len);
+ if (ret < 0) {
+ dns_message_free(msg, NULL);
+ return -1;
+ }
+ }
+
+ session_mq_publish_message(sess, ctx->topic_id, msg);
+
+ return 0;
+}
+
+int dns_decoder_entry(struct session *sess, int events,
+ const struct packet *pkt, void *cb_arg)
+{
+ struct dns_decoder_context *ctx = (struct dns_decoder_context *)cb_arg;
+ uint64_t inner_flag = 0;
+
+ int ret = session_is_inner_most(sess, &inner_flag);
+ if (0 == ret) {
+ return 0;
+ }
+
+ enum session_addr_type sess_addr_type = SESSION_ADDR_TYPE_UNKNOWN;
+ struct session_addr *sess_addr = session_get0_addr(sess, &sess_addr_type);
+
+ if (sess_addr_type == SESSION_ADDR_TYPE_IPV4_TCP ||
+ sess_addr_type == SESSION_ADDR_TYPE_IPV4_UDP) {
+ if (ntohs(sess_addr->ipv4.dport) != 53 &&
+ ntohs(sess_addr->ipv4.sport) != 53) {
+ return 0;
+ }
+ } else if (sess_addr_type == SESSION_ADDR_TYPE_IPV6_TCP ||
+ sess_addr_type == SESSION_ADDR_TYPE_IPV6_UDP) {
+ if (ntohs(sess_addr->ipv6.dport) != 53 &&
+ ntohs(sess_addr->ipv6.sport) != 53) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+
+ if (session_get_type(sess) == SESSION_TYPE_TCP) {
+ // tcp
+ ret = tcp_dns_decoder_entry(sess, events, pkt, ctx);
+ } else {
+ // udp
+ ret = udp_dns_decoder_entry(sess, events, pkt, ctx);
+ }
+
+ return ret;
+}
+
+void *dns_decoder_init(struct stellar *st)
+{
+ int plugin_id = -1;
+ int topic_id = -1;
+ int ex_data_idx = -1;
+
+ struct dns_decoder_context *ctx = CALLOC(struct dns_decoder_context, 1);
+
+ ctx->st = st;
+ ex_data_idx = stellar_session_get_ex_new_index(st, "DNS_DECODER",
+ dns_decoder_ex_data_free,
+ NULL);
+ if (ex_data_idx < 0) {
+ goto failed;
+ }
+ ctx->ex_data_idx = ex_data_idx;
+
+ plugin_id = stellar_plugin_register(st, SESS_EV_TCP | SESS_EV_UDP,
+ dns_decoder_entry, ctx);
+ if (plugin_id < 0) {
+ goto failed;
+ }
+ ctx->plugin_id = plugin_id;
+
+ topic_id = session_mq_get_topic_id(st, dns_decoder_topic);
+ if (topic_id < 0) {
+ topic_id = session_mq_create_topic(st, dns_decoder_topic,
+ dns_message_free, NULL);
+ }
+ ctx->topic_id = topic_id;
+
+ printf("dns_decoder_init: ex_data_idx:%d, plugin_id:%d, topic_id:%d\n",
+ ctx->ex_data_idx, ctx->plugin_id, ctx->topic_id);
+
+ return ctx;
+
+failed:
+ _dns_decoder_context_free(ctx);
+ return NULL;
+}
+
+void dns_decoder_exit(void *decoder_ctx)
+{
+ if (NULL == decoder_ctx) {
+ return;
+ }
+
+ struct dns_decoder_context *ctx = (struct dns_decoder_context *)decoder_ctx;
+
+ _dns_decoder_context_free(ctx);
+} \ No newline at end of file
diff --git a/src/dns_decoder/dns_decoder.h b/src/dns_decoder/dns_decoder.h
new file mode 100644
index 0000000..5ee1fdc
--- /dev/null
+++ b/src/dns_decoder/dns_decoder.h
@@ -0,0 +1,271 @@
+/*
+**********************************************************************************************
+* File: dns_decoder.h
+* Description: dns decoder api
+* Authors: Liu WenTan <[email protected]>
+* Date: 2024-02-07
+* Copyright: (c) Since 2022 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#ifndef _DNS_DECODER_H_
+#define _DNS_DECODER_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdint.h>
+#include <linux/limits.h>
+#include <stddef.h>
+
+#define DNS_NAME_MAX (NAME_MAX + 1)
+#define HINFO_NAME_MAX (40 + 1) /* https://www.ietf.org/rfc/rfc1010.txt */
+
+enum dns_message_type {
+ DNS_MESSAGE_QUERY,
+ DNS_MESSAGE_RESPONSE,
+ DNS_MESSAGE_MAX
+};
+
+/* RR(resource record) type, defined in https://www.ietf.org/rfc/rfc1035.txt */
+enum dns_rr_type {
+ DNS_RR_TYPE_A = 1,
+ DNS_RR_TYPE_NS,
+ DNS_RR_TYPE_MD, /* Obsolete - use MX, rfc973 */
+ DNS_RR_TYPE_MF, /* Obsolete - use MX, rfc973 */
+ DNS_RR_TYPE_CNAME,
+ DNS_RR_TYPE_SOA,
+ DNS_RR_TYPE_MB, /* EXPERIMENTAL, rfc883,rfc2505 */
+ DNS_RR_TYPE_MG, /* EXPERIMENTAL, rfc883,rfc2505 */
+ DNS_RR_TYPE_MR, /* EXPERIMENTAL, rfc883 */
+ DNS_RR_TYPE_NULL, /* EXPERIMENTAL, rfc1035 */
+ DNS_RR_TYPE_WKS, /* Not to be relied upon, rfc1123 */
+ DNS_RR_TYPE_PTR,
+ DNS_RR_TYPE_HINFO,
+ DNS_RR_TYPE_MINFO,
+ DNS_RR_TYPE_MX,
+ DNS_RR_TYPE_TXT,
+ DNS_RR_TYPE_RP,
+ DNS_RR_TYPE_ISDN = 20,
+ DNS_RR_TYPE_AAAA = 28, /* dns_ipv6 */
+ DNS_RR_TYPE_SRV = 33,
+ DNS_RR_TYPE_DNAME = 39,
+ DNS_RR_TYPE_OPT = 41,
+ DNS_RR_TYPE_DS = 43,
+ DNS_RR_TYPE_RRSIG = 46,
+ DNS_RR_TYPE_NSEC,
+ DNS_RR_TYPE_DNSKEY,
+ DNS_RR_TYPE_NSEC3 = 50,
+ DNS_RR_TYPE_NSEC3PARAM,
+ DNS_RR_TYPE_HTTPS = 65,
+ DNS_RR_TYPE_AXFR = 252,
+ DNS_RR_TYPE_MAILB,
+ DNS_RR_TYPE_MAILA, /* Obsolete - see MX */
+ DNS_RR_TYPE_ANY,
+ DNS_RR_TYPE_DLV = 32769, /* DSNSEC Lokkaside Validation */
+ DNS_RR_TYPE_UNKNOWN = 65534
+};
+
+struct dns_flag {
+ uint8_t qr:1;
+ uint8_t opcode:4;
+ uint8_t aa:1;
+ uint8_t tc:1;
+ uint8_t rd:1;
+ uint8_t ra:1;
+ uint8_t z:3;
+ uint8_t rcode:4;
+};
+
+struct dns_query_question {
+ uint16_t qtype;
+ uint16_t qclass;
+ uint8_t qname[DNS_NAME_MAX];
+};
+
+struct rdata_hinfo {
+ uint8_t os_len;
+ uint8_t cpu_len;
+ uint8_t cpu[HINFO_NAME_MAX];
+ uint8_t os[HINFO_NAME_MAX];
+};
+
+struct rdata_minfo {
+ uint8_t rmailbx[DNS_NAME_MAX];
+ uint8_t emailbx[DNS_NAME_MAX];
+};
+
+struct rdata_mx {
+ uint16_t preference;
+ uint8_t exchange[DNS_NAME_MAX];
+};
+
+struct rdata_soa {
+ uint8_t mname[DNS_NAME_MAX];
+ uint8_t rname[DNS_NAME_MAX];
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ uint32_t minimum;
+};
+
+struct rdata_txt {
+ uint8_t txt[DNS_NAME_MAX];
+ uint8_t size;
+};
+
+struct rdata_rp {
+ uint8_t mailbox[DNS_NAME_MAX];
+ uint8_t txt_rr[DNS_NAME_MAX];
+};
+
+struct rdata_null {
+ uint8_t null[DNS_NAME_MAX];
+ uint8_t size;
+};
+
+struct rdata_wks {
+ uint8_t protocol;
+ uint32_t addr;
+ uint32_t size;
+ uint8_t *bitmap;
+};
+
+struct rdata_srv {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ uint8_t target[DNS_NAME_MAX];
+};
+
+struct rdata_ds {
+ uint16_t key_tag;
+ uint8_t algo;
+ uint8_t digest_type;
+ uint32_t digest_len;
+ uint8_t *digest;
+};
+
+struct rdata_rrsig {
+ uint16_t type_covered;
+ uint8_t algo;
+ uint8_t labels;
+ uint32_t original_ttl;
+ uint32_t sig_expiration;
+ uint32_t sig_inception;
+ uint32_t key_tag;
+ uint32_t signature_len;
+ uint8_t signer_name[DNS_NAME_MAX];
+ uint8_t *signature;
+};
+
+struct rdata_nsec {
+ uint16_t maps_len;
+ uint8_t next_domain[DNS_NAME_MAX];
+ uint8_t type_bit_maps[DNS_NAME_MAX];
+};
+
+struct rdata_dnskey {
+ uint16_t flags;
+ uint8_t protocol;
+ uint8_t algo;
+ uint32_t public_key_len;
+ uint8_t *public_key;
+};
+
+struct rdata_nsec3 {
+ uint8_t hash_algo;
+ uint8_t flags;
+ uint8_t salt_len;
+ uint8_t hash_len;
+ uint16_t iteration;
+ uint16_t maps_len;
+ uint8_t *salt_value;
+ uint8_t *next_hash_owner;
+ uint8_t type_bit_maps[DNS_NAME_MAX];
+};
+
+struct rdata_nsec3param {
+ uint8_t hash_algo;
+ uint8_t flags;
+ uint8_t salt_len;
+ uint16_t iteration;
+ uint8_t *salt_value;
+};
+
+/* rr is short for resource record */
+struct dns_rr {
+ uint8_t name[DNS_NAME_MAX];
+ uint16_t type;
+ uint16_t rr_class;
+ uint32_t ttl; /* 1byte: extended RCODE; 1byte: version; 2bytes: Z(upper bit) if type is OPT */
+ uint16_t rdlength;
+ union {
+ uint8_t cname[DNS_NAME_MAX];
+ uint8_t mb[DNS_NAME_MAX];
+ uint8_t md[DNS_NAME_MAX];
+ uint8_t mf[DNS_NAME_MAX];
+ uint8_t mg[DNS_NAME_MAX];
+ uint8_t mr[DNS_NAME_MAX];
+ uint8_t ns[DNS_NAME_MAX];
+ uint8_t ptr[DNS_NAME_MAX];
+ uint8_t a[DNS_NAME_MAX];
+ uint8_t aaaa[DNS_NAME_MAX];
+ uint8_t dname[DNS_NAME_MAX];
+ uint8_t isdn[DNS_NAME_MAX];
+ uint8_t unknown_data[DNS_NAME_MAX];
+ struct rdata_hinfo hinfo;
+ struct rdata_minfo minfo;
+ struct rdata_mx mx;
+ struct rdata_soa soa;
+ struct rdata_txt txt;
+ struct rdata_rp rp;
+ struct rdata_null null;
+ struct rdata_wks wks;
+ struct rdata_srv srv;
+ struct rdata_ds ds;
+ struct rdata_rrsig rrsig;
+ struct rdata_nsec nsec;
+ struct rdata_dnskey dnskey;
+ struct rdata_nsec3 nsec3;
+ struct rdata_nsec3param nsec3param;
+ } rdata;
+};
+
+struct dns_query {
+ uint16_t transaction_id;
+ struct dns_flag flag;
+ uint16_t n_question;
+ struct dns_query_question query_question;
+};
+
+struct dns_response {
+ struct dns_query query;
+ uint16_t n_answer_rr;
+ uint16_t n_authority_rr;
+ uint16_t n_additional_rr;
+ struct dns_rr *answer_rr;
+ struct dns_rr *authority_rr;
+ struct dns_rr *additional_rr;
+};
+
+struct dns_message;
+
+enum dns_message_type dns_message_type(struct dns_message *msg);
+
+int dns_message_get_query(struct dns_message *msg, struct dns_query *query);
+
+int dns_message_get_response(struct dns_message *msg, struct dns_response *response);
+
+int dns_serialize_query(uint8_t **buff, size_t *buff_len, struct dns_query *query);
+
+int dns_serialize_response(uint8_t **buff, size_t *buff_len, struct dns_response *response);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index b7beeaa..2df4ad3 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -11,6 +11,7 @@ target_link_libraries(
)
add_subdirectory(http_decoder)
+add_subdirectory(dns_decoder)
include(GoogleTest)
gtest_discover_tests(gtest_stellar) \ No newline at end of file
diff --git a/test/dns_decoder/CMakeLists.txt b/test/dns_decoder/CMakeLists.txt
new file mode 100644
index 0000000..b9477ce
--- /dev/null
+++ b/test/dns_decoder/CMakeLists.txt
@@ -0,0 +1,44 @@
+set(DECODER_NAME dns_decoder)
+
+add_library(${DECODER_NAME}_test SHARED dns_decoder_gtest.cpp)
+add_dependencies(${DECODER_NAME}_test ${DECODER_NAME})
+target_link_libraries(${DECODER_NAME}_test MESA_prof_load cjson)
+set_target_properties(${DECODER_NAME}_test PROPERTIES PREFIX "")
+
+set(TEST_RUN_DIR ${CMAKE_CURRENT_BINARY_DIR}/sapp)
+set(TEST_MAIN ${TEST_RUN_DIR}/plugin_test_main)
+
+# assemble test env
+add_test(NAME INSTALL_TEST_MAIN COMMAND sh -c "rpm -i ${CMAKE_CURRENT_SOURCE_DIR}/test_env/sapp4.el8.x86_64.rpm --prefix=${CMAKE_CURRENT_BINARY_DIR}/sapp --force --nodeps")
+
+add_test(NAME COPY_TEST_MAIN COMMAND sh -c "cp ${TEST_RUN_DIR}/tools/plugin_test_main ${TEST_RUN_DIR}/plugin_test_main")
+add_test(NAME COPY_CONF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/tsgconf/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/tsg_l7_protocol.conf ${TEST_RUN_DIR}/tsgconf/tsg_l7_protocol.conf")
+add_test(NAME COPY_SPEC COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/stellar_plugin/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/spec.toml ${TEST_RUN_DIR}/stellar_plugin/spec.toml")
+add_test(NAME COPY_CONFLIST COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plug/ && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/conflist.inf ${TEST_RUN_DIR}/plug/conflist.inf")
+add_test(NAME COPY_INF COMMAND sh -c "mkdir -p ${TEST_RUN_DIR}/plug/stellar_on_sapp && cp ${CMAKE_CURRENT_SOURCE_DIR}/test_env/start_loader.inf ${TEST_RUN_DIR}/plug/stellar_on_sapp/start_loader.inf")
+
+# update config files
+add_test(NAME UPDATE_SAPP_LOG COMMAND bash -c "sed -i 's/sapp_log.fatal/sapp_log.info/' ${TEST_RUN_DIR}/etc/sapp_log.conf")
+add_test(NAME UPDATE_SAPP_SYN_MODE COMMAND bash -c "sed -i 's/syn_mandatory=1/syn_mandatory=0/' ${TEST_RUN_DIR}/etc/sapp.toml")
+add_test(NAME UPDATE_SAPP_REORDER COMMAND bash -c "sed -i 's/reorder_pkt_max=32/reorder_pkt_max=5/' ${TEST_RUN_DIR}/etc/sapp.toml")
+
+# update plugin to be tested
+add_test(NAME UPDATE_STELLAR_ON_SAPP_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/src/stellar_on_sapp/stellar_on_sapp.so ${TEST_RUN_DIR}/plug/stellar_on_sapp/stellar_on_sapp.so")
+add_test(NAME UPDATE_PLUG_SO COMMAND sh -c "cp ${CMAKE_BINARY_DIR}/src/${DECODER_NAME}/${DECODER_NAME}.so ${TEST_RUN_DIR}/stellar_plugin/${DECODER_NAME}.so")
+add_test(NAME UPDATE_TEST_SO COMMAND sh -c "cp ${CMAKE_CURRENT_BINARY_DIR}/${DECODER_NAME}_test.so ${TEST_RUN_DIR}/stellar_plugin/${DECODER_NAME}_test.so")
+
+set_tests_properties(INSTALL_TEST_MAIN COPY_TEST_MAIN COPY_CONF COPY_SPEC COPY_CONFLIST COPY_INF
+ UPDATE_SAPP_LOG UPDATE_SAPP_SYN_MODE UPDATE_SAPP_REORDER
+ UPDATE_STELLAR_ON_SAPP_SO UPDATE_PLUG_SO UPDATE_TEST_SO
+ PROPERTIES FIXTURES_SETUP TestFixture)
+
+# run tests
+add_test(NAME DNS_UDP_SIMPLE_TEST COMMAND ${TEST_MAIN} ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/dns_udp_simple.json
+ -f "find ${CMAKE_CURRENT_SOURCE_DIR}/dns_pcap/ -name dns_udp_simple.pcap|sort -V" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
+add_test(NAME DNS_TCP_SIMPLE_TEST COMMAND ${TEST_MAIN} ${CMAKE_CURRENT_SOURCE_DIR}/test_result_json/dns_tcp_simple.json
+ -f "find ${CMAKE_CURRENT_SOURCE_DIR}/dns_pcap/ -name dns_tcp_simple.pcap|sort -V" WORKING_DIRECTORY ${TEST_RUN_DIR})
+
+set_tests_properties(DNS_UDP_SIMPLE_TEST
+ DNS_TCP_SIMPLE_TEST
+ PROPERTIES FIXTURES_REQUIRED TestFixture) \ No newline at end of file
diff --git a/test/dns_decoder/dns_decoder_gtest.cpp b/test/dns_decoder/dns_decoder_gtest.cpp
new file mode 100644
index 0000000..6e3a9c4
--- /dev/null
+++ b/test/dns_decoder/dns_decoder_gtest.cpp
@@ -0,0 +1,280 @@
+/*
+**********************************************************************************************
+* File: dns_decoder_gtest.cpp
+* Description:
+* Authors: Liu WenTan <[email protected]>
+* Date: 2023-12-15
+* Copyright: (c) Since 2023 Geedge Networks, Ltd. All rights reserved.
+***********************************************************************************************
+*/
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "../../src/dns_decoder/dns_decoder.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+
+#include "cJSON.h"
+
+#include "stellar/utils.h"
+#include "stellar/stellar.h"
+#include "stellar/session_exdata.h"
+#include "stellar/session_mq.h"
+
+int commit_test_result_json(cJSON *node, const char *name);
+}
+#endif
+
+#define DD_IPV4 4
+#define DD_IPV6 6
+
+#define MAX_KEY_STR_LEN 2048
+
+static int g_result_count = 1;
+int g_topic_id = 0;
+
+typedef void rr_printer_fn(uint8_t *in_data, size_t in_data_len,
+ char *out_data, size_t out_data_len);
+
+struct rr_printer {
+ int rr_type;
+ rr_printer_fn *fn;
+};
+
+void print_rr_a(uint8_t *in_data, size_t in_data_len,
+ char *out_data, size_t out_data_len)
+{
+ inet_ntop(AF_INET, in_data, out_data, out_data_len);
+}
+
+void print_rr_aaaa(uint8_t *in_data, size_t in_data_len,
+ char *out_data, size_t out_data_len)
+{
+ inet_ntop(AF_INET6, in_data, out_data, out_data_len);
+}
+
+void print_rr_str(uint8_t *in_data, size_t in_data_len,
+ char *out_data, size_t out_data_len)
+{
+ memcpy(out_data, in_data, out_data_len);
+}
+
+void print_rr_soa(uint8_t *in_data, size_t in_data_len,
+ char *out_data, size_t out_data_len)
+{
+ memcpy(out_data, in_data, out_data_len);
+}
+
+struct rr_printer rr_printers[] = {
+ {-1, NULL},
+ {DNS_RR_TYPE_A, print_rr_a},
+ {DNS_RR_TYPE_NS, print_rr_str},
+ {DNS_RR_TYPE_MD, NULL},
+ {DNS_RR_TYPE_MF, NULL},
+ {DNS_RR_TYPE_CNAME, NULL},
+ {DNS_RR_TYPE_SOA, print_rr_soa},
+ {DNS_RR_TYPE_MB, NULL},
+ {DNS_RR_TYPE_MG, NULL},
+ {DNS_RR_TYPE_MR, NULL},
+ {DNS_RR_TYPE_NULL, NULL},
+ {DNS_RR_TYPE_WKS, NULL},
+ {DNS_RR_TYPE_PTR, NULL},
+ {DNS_RR_TYPE_HINFO, NULL},
+ {DNS_RR_TYPE_MINFO, NULL},
+ {DNS_RR_TYPE_MX, print_rr_str},
+ {DNS_RR_TYPE_AAAA, print_rr_aaaa}
+};
+
+static int
+dns_field_to_json(cJSON *object, const char *key, char *val, size_t val_len)
+{
+ if (NULL == object || NULL == key || NULL == val || 0 == val_len) {
+ return -1;
+ }
+
+ char *tmp = CALLOC(char, val_len + 1);
+ memcpy(tmp, val, val_len);
+ cJSON_AddStringToObject(object, key, tmp);
+ FREE(tmp);
+
+ return 0;
+}
+
+static void dns_query_to_json(cJSON *ctx, struct dns_query *query)
+{
+ char buff[64] = {0};
+ sprintf(buff, "%x", query->transaction_id);
+ dns_field_to_json(ctx, "transaction_id", buff, strlen(buff));
+
+ memset(buff, 0, sizeof(buff));
+ sprintf(buff, "%s", "query");
+ dns_field_to_json(ctx, "dns_type", buff, strlen(buff));
+
+ memset(buff, 0, sizeof(buff));
+ sprintf(buff, "%s", query->query_question.qname);
+ dns_field_to_json(ctx, "qname", buff, strlen(buff));
+}
+
+static void rr_data_to_buff(struct dns_rr *dns_rr, char *buff, size_t buff_len)
+{
+ char tmp_buff[1024] = {0};
+
+ switch (dns_rr->type) {
+ case DNS_RR_TYPE_CNAME:
+ memcpy(buff, dns_rr->rdata.cname, buff_len);
+ break;
+ case DNS_RR_TYPE_HINFO:
+ sprintf(tmp_buff, "%c%c%s%s", dns_rr->rdata.hinfo.os_len,
+ dns_rr->rdata.hinfo.cpu_len, dns_rr->rdata.hinfo.cpu,
+ dns_rr->rdata.hinfo.os);
+ memcpy(buff, tmp_buff, buff_len);
+ break;
+ case DNS_RR_TYPE_MB:
+ memcpy(buff, dns_rr->rdata.mb, buff_len);
+ break;
+ case DNS_RR_TYPE_MD:
+ memcpy(buff, dns_rr->rdata.md, buff_len);
+ break;
+ case DNS_RR_TYPE_MF:
+ memcpy(buff, dns_rr->rdata.mf, buff_len);
+ break;
+ case DNS_RR_TYPE_MG:
+ memcpy(buff, dns_rr->rdata.mg, buff_len);
+ break;
+ case DNS_RR_TYPE_MR:
+ memcpy(buff, dns_rr->rdata.mr, buff_len);
+ break;
+ case DNS_RR_TYPE_NS:
+ memcpy(buff, dns_rr->rdata.ns, buff_len);
+ break;
+ case DNS_RR_TYPE_MINFO:
+ sprintf(tmp_buff, "%s%s", dns_rr->rdata.minfo.rmailbx,
+ dns_rr->rdata.minfo.emailbx);
+ memcpy(buff, tmp_buff, buff_len);
+ break;
+ case DNS_RR_TYPE_SOA:
+ sprintf(tmp_buff, "%s%s%u%u%u%u%u", dns_rr->rdata.soa.mname,
+ dns_rr->rdata.soa.rname, dns_rr->rdata.soa.serial,
+ dns_rr->rdata.soa.refresh, dns_rr->rdata.soa.retry,
+ dns_rr->rdata.soa.expire, dns_rr->rdata.soa.minimum);
+ memcpy(buff, tmp_buff, buff_len);
+ break;
+ case DNS_RR_TYPE_A:
+ inet_ntop(AF_INET, dns_rr->rdata.a, tmp_buff, 1024);
+ memcpy(buff, tmp_buff, strlen(tmp_buff));
+ break;
+ }
+}
+
+static void dns_response_to_json(cJSON *ctx, struct dns_response *response)
+{
+ char key[64] = {0};
+ char buff[64] = {0};
+
+ sprintf(buff, "%s", "response");
+ dns_field_to_json(ctx, "dns_type", buff, strlen(buff));
+
+ size_t printer_num = sizeof(rr_printers) / sizeof(struct rr_printer);
+
+ for (size_t i = 0; i < response->n_answer_rr; i++) {
+ memset(key, 0, sizeof(key));
+ memset(buff, 0, sizeof(buff));
+
+ sprintf(key, "ans_rr%zu", i);
+ rr_data_to_buff(&response->answer_rr[i], buff, sizeof(buff));
+ dns_field_to_json(ctx, key, buff, sizeof(buff));
+ }
+
+ for (size_t i = 0; i < response->n_authority_rr; i++) {
+ memset(key, 0, sizeof(key));
+ memset(buff, 0, sizeof(buff));
+
+ sprintf(key, "auth_rr%zu", i);
+ rr_data_to_buff(&response->authority_rr[i], buff, sizeof(buff));
+ dns_field_to_json(ctx, key, buff, strlen(buff));
+ }
+
+ for (size_t i = 0; i < response->n_additional_rr; i++) {
+ memset(key, 0, sizeof(key));
+ memset(buff, 0, sizeof(buff));
+
+ sprintf(key, "addt_rr%zu", i);
+ rr_data_to_buff(&response->additional_rr[i], buff, sizeof(buff));
+ dns_field_to_json(ctx, key, buff, strlen(buff));
+ }
+}
+
+static int
+dns_decoder_test_entry(struct session *sess, int topic_id, const void *data,
+ void *cb_arg)
+{
+ int exdata_idx = 0;
+ struct dns_message *msg = (struct dns_message *)data;
+
+ cJSON *json = cJSON_CreateObject();
+ cJSON_AddStringToObject(json, "Tuple4", session_get0_readable_addr(sess));
+
+ enum dns_message_type type = dns_message_type(msg);
+ struct dns_query query;
+ struct dns_response response;
+
+ switch (type) {
+ case DNS_MESSAGE_QUERY:
+ dns_message_get_query(msg, &query);
+ dns_query_to_json(json, &query);
+ break;
+ case DNS_MESSAGE_RESPONSE:
+ dns_message_get_response(msg, &response);
+ dns_response_to_json(json, &response);
+ break;
+ default:
+ break;
+ }
+
+ char result_name[MAX_KEY_STR_LEN] = {0};
+ sprintf(result_name, "DNS_DECODER_RESULT_%d", g_result_count);
+ commit_test_result_json(json, result_name);
+ g_result_count++;
+
+ return 0;
+}
+
+static void
+dns_decoder_test_exdata_free(struct session *sess, int idx, void *ex_ptr, void *arg)
+{
+ if (ex_ptr != NULL) {
+ cJSON_Delete((cJSON *)ex_ptr);
+ }
+}
+
+extern "C" void *dns_decoder_test_init(struct stellar *st)
+{
+ g_topic_id = session_mq_get_topic_id(st, "DNS_DECODER_MESSAGE");
+ if (g_topic_id < 0) {
+ printf("[%s:%d]: can't get dns_decoder topic id !!!\n",
+ __FUNCTION__, __LINE__);
+ exit(-1);
+ }
+
+ session_mq_subscribe_topic(st, g_topic_id, dns_decoder_test_entry, NULL);
+ printf("dns_decoder_test_init OK!\n");
+
+ return NULL;
+
+}
+
+extern "C" void dns_decoder_test_exit(void *test_ctx)
+{
+ if (test_ctx != NULL) {
+ FREE(test_ctx);
+ }
+
+ printf("dns_decoder_test_exit OK!\n");
+} \ No newline at end of file
diff --git a/test/dns_decoder/dns_pcap/dns_tcp_simple.pcap b/test/dns_decoder/dns_pcap/dns_tcp_simple.pcap
new file mode 100644
index 0000000..c1ff33a
--- /dev/null
+++ b/test/dns_decoder/dns_pcap/dns_tcp_simple.pcap
Binary files differ
diff --git a/test/dns_decoder/dns_pcap/dns_udp_simple.pcap b/test/dns_decoder/dns_pcap/dns_udp_simple.pcap
new file mode 100644
index 0000000..95ac41d
--- /dev/null
+++ b/test/dns_decoder/dns_pcap/dns_udp_simple.pcap
Binary files differ
diff --git a/test/dns_decoder/test_env/conflist.inf b/test/dns_decoder/test_env/conflist.inf
new file mode 100644
index 0000000..2e8144d
--- /dev/null
+++ b/test/dns_decoder/test_env/conflist.inf
@@ -0,0 +1,9 @@
+[platform]
+./plug/stellar_on_sapp/start_loader.inf
+
+
+[protocol]
+
+
+[business]
+#./plug/stellar_on_sapp/defer_loader.inf
diff --git a/test/dns_decoder/test_env/sapp4.el8.x86_64.rpm b/test/dns_decoder/test_env/sapp4.el8.x86_64.rpm
new file mode 100644
index 0000000..e43fe2f
--- /dev/null
+++ b/test/dns_decoder/test_env/sapp4.el8.x86_64.rpm
Binary files differ
diff --git a/test/dns_decoder/test_env/spec.toml b/test/dns_decoder/test_env/spec.toml
new file mode 100644
index 0000000..5b90d1d
--- /dev/null
+++ b/test/dns_decoder/test_env/spec.toml
@@ -0,0 +1,12 @@
+# stellar_plugin.toml
+#
+
+[[plugin]]
+path = "./stellar_plugin/dns_decoder.so"
+init = "dns_decoder_init"
+exit = "dns_decoder_exit"
+
+[[plugin]]
+path = "./stellar_plugin/dns_decoder_test.so"
+init = "dns_decoder_test_init"
+exit = "dns_decoder_test_exit" \ No newline at end of file
diff --git a/test/dns_decoder/test_env/start_loader.inf b/test/dns_decoder/test_env/start_loader.inf
new file mode 100644
index 0000000..89b2f94
--- /dev/null
+++ b/test/dns_decoder/test_env/start_loader.inf
@@ -0,0 +1,17 @@
+[PLUGINFO]
+PLUGNAME=stellar_start_loader
+SO_PATH=./plug/stellar_on_sapp/stellar_on_sapp.so
+INIT_FUNC=STELLAR_START_LOADER_INIT
+DESTROY_FUNC=STELLAR_START_LOADER_EXIT
+
+#[TCP_ALL]
+#FUNC_FLAG=ALL
+#FUNC_NAME=stellar_on_sapp_tcpall_entry
+
+[TCP]
+FUNC_FLAG=ALL
+FUNC_NAME=stellar_on_sapp_tcp_entry
+
+[UDP]
+FUNC_FLAG=ALL
+FUNC_NAME=stellar_on_sapp_udp_entry \ No newline at end of file
diff --git a/test/dns_decoder/test_env/tsg_l7_protocol.conf b/test/dns_decoder/test_env/tsg_l7_protocol.conf
new file mode 100644
index 0000000..1075a8f
--- /dev/null
+++ b/test/dns_decoder/test_env/tsg_l7_protocol.conf
@@ -0,0 +1,57 @@
+#TYPE:1:UCHAR,2:USHORT,3:USTRING,4:ULOG,5:USTRING,6:FILE,7:UBASE64,8:PACKET
+#TYPE FIELD VALUE
+STRING UNCATEGORIZED 8000
+#STRING UNCATEGORIZED 8001
+#STRING UNKNOWN_OTHER 8002
+STRING DNS 32
+STRING FTP 45
+STRING FTPS 751
+STRING HTTP 67
+STRING HTTPS 68
+STRING ICMP 70
+STRING IKE 8003
+STRING MAIL 8004
+STRING IMAP 75
+STRING IMAPS 76
+STRING IPSEC 85
+STRING XMPP 94
+STRING L2TP 98
+STRING NTP 137
+STRING POP3 147
+STRING POP3S 148
+STRING PPTP 153
+STRING QUIC 2521
+STRING SIP 182
+STRING SMB 185
+STRING SMTP 186
+STRING SMTPS 187
+STRING SPDY 1469
+STRING SSH 198
+STRING SSL 199
+STRING SOCKS 8005
+STRING TELNET 209
+STRING DHCP 29
+STRING RADIUS 158
+STRING OPENVPN 336
+STRING STUN 201
+STRING TEREDO 555
+STRING DTLS 1291
+STRING DoH 8006
+STRING ISAKMP 92
+STRING MDNS 3835
+STRING NETBIOS 129
+STRING NETFLOW 130
+STRING RDP 150
+STRING RTCP 174
+STRING RTP 175
+STRING SLP 8007
+STRING SNMP 190
+STRING SSDP 197
+STRING TFTP 211
+STRING BJNP 2481
+STRING LDAP 100
+STRING RTMP 337
+STRING RTSP 176
+STRING ESNI 8008
+STRING QQ 156
+STRING WeChat 1296
diff --git a/test/dns_decoder/test_result_json/dns_tcp_simple.json b/test/dns_decoder/test_result_json/dns_tcp_simple.json
new file mode 100644
index 0000000..5271b83
--- /dev/null
+++ b/test/dns_decoder/test_result_json/dns_tcp_simple.json
@@ -0,0 +1,58 @@
+[
+ {
+ "Tuple4": "10.180.156.141.49342>10.2.95.39.53",
+ "transaction_id": "5e63",
+ "dns_type": "query",
+ "qname": "google.com",
+ "name": "DNS_DECODER_RESULT_1"
+ },
+ {
+ "Tuple4": "10.180.156.141.49342>10.2.95.39.53",
+ "dns_type": "response",
+ "ans_rr0": "74.125.228.46",
+ "ans_rr1": "74.125.228.32",
+ "ans_rr2": "74.125.228.33",
+ "ans_rr3": "74.125.228.34",
+ "ans_rr4": "74.125.228.35",
+ "ans_rr5": "74.125.228.36",
+ "ans_rr6": "74.125.228.37",
+ "ans_rr7": "74.125.228.38",
+ "ans_rr8": "74.125.228.39",
+ "ans_rr9": "74.125.228.40",
+ "ans_rr10": "74.125.228.41",
+ "auth_rr0": "ns4.google.com",
+ "auth_rr1": "ns2.google.com",
+ "auth_rr2": "ns3.google.com",
+ "auth_rr3": "ns1.google.com",
+ "addt_rr0": "216.239.32.10",
+ "addt_rr1": "216.239.36.10",
+ "addt_rr2": "216.239.38.10",
+ "addt_rr3": "216.239.34.10",
+ "name": "DNS_DECODER_RESULT_2"
+ },
+ {
+ "Tuple4": "10.180.156.141.49343>10.2.95.39.53",
+ "transaction_id": "e2ec",
+ "dns_type": "query",
+ "qname": "aol.com",
+ "name": "DNS_DECODER_RESULT_3"
+ },
+ {
+ "Tuple4": "10.180.156.141.49343>10.2.95.39.53",
+ "dns_type": "response",
+ "ans_rr0": "205.188.100.58",
+ "ans_rr1": "205.188.101.58",
+ "ans_rr2": "207.200.74.38",
+ "ans_rr3": "64.12.79.57",
+ "ans_rr4": "64.12.89.186",
+ "auth_rr0": "dns-01.ns.aol.com",
+ "auth_rr1": "dns-02.ns.aol.com",
+ "auth_rr2": "dns-06.ns.aol.com",
+ "auth_rr3": "dns-07.ns.aol.com",
+ "addt_rr0": "64.236.1.107",
+ "addt_rr1": "207.200.73.80",
+ "addt_rr2": "64.12.51.132",
+ "addt_rr3": "205.188.157.232",
+ "name": "DNS_DECODER_RESULT_4"
+ }
+] \ No newline at end of file
diff --git a/test/dns_decoder/test_result_json/dns_udp_simple.json b/test/dns_decoder/test_result_json/dns_udp_simple.json
new file mode 100644
index 0000000..232785c
--- /dev/null
+++ b/test/dns_decoder/test_result_json/dns_udp_simple.json
@@ -0,0 +1,25 @@
+[
+ {
+ "Tuple4": "192.168.1.52.54585>8.8.8.8.53",
+ "transaction_id": "3",
+ "dns_type": "query",
+ "qname": "google.com",
+ "name": "DNS_DECODER_RESULT_1"
+ },
+ {
+ "Tuple4": "192.168.1.52.54585>8.8.8.8.53",
+ "dns_type": "response",
+ "ans_rr0": "74.125.236.35",
+ "ans_rr1": "74.125.236.37",
+ "ans_rr2": "74.125.236.39",
+ "ans_rr3": "74.125.236.32",
+ "ans_rr4": "74.125.236.40",
+ "ans_rr5": "74.125.236.33",
+ "ans_rr6": "74.125.236.41",
+ "ans_rr7": "74.125.236.34",
+ "ans_rr8": "74.125.236.36",
+ "ans_rr9": "74.125.236.46",
+ "ans_rr10": "74.125.236.38",
+ "name": "DNS_DECODER_RESULT_2"
+ }
+] \ No newline at end of file
diff --git a/test/http_decoder/test_env/spec.toml b/test/http_decoder/test_env/spec.toml
index 626f85b..b8c7eb9 100644
--- a/test/http_decoder/test_env/spec.toml
+++ b/test/http_decoder/test_env/spec.toml
@@ -8,4 +8,4 @@ exit = "http_decoder_exit"
[[plugin]]
path = "./stellar_plugin/http_decoder_test.so"
init = "http_decoder_test_init"
-exit = "http_decoder_test_exit"
+exit = "http_decoder_test_exit" \ No newline at end of file