This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
tango-tfe/plugin/business/doh/src/dns.cpp

1250 lines
32 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// git version 5effe725
#include <stdio.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "dns.h"
#define DNS_ERROR_LOG(msg) printf("%s, %s:%d", msg, __FILE__, __LINE__)
/////////////////////////////////////////////////////////////////////////////////
// compress/decompress API
/////////////////////////////////////////////////////////////////////////////////
static int dns_compress_rr_str(u_char *domain, int domain_len, u_char *result)
{
int section_len = 0;
int result_pos = 1;
int domain_pos = 0;
if (domain_len < 0 || domain_len > DNS_MAX_NAME + 1 || '.' == domain[0] || '.' == domain[domain_len - 1])
{
return -1;
}
while ((domain[domain_pos] != '\n') || (domain[domain_pos] != '\0'))
{
section_len = 0;
while ((domain[domain_pos] != '.') && (domain[domain_pos] != '\n') && (domain[domain_pos] != '\0'))
{
result[result_pos] = domain[domain_pos];
result_pos++;
domain_pos++;
section_len++;
}
result[result_pos - section_len - 1] = section_len;
if ((domain[domain_pos] == '\n') || (domain[domain_pos] == '\0'))
break;
result_pos++;
domain_pos++;
}
result[result_pos] = '\0';
if (result_pos >= domain_len)
{
return result_pos + 1;
}
else
{
return result_pos;
}
return 0;
}
static int dns_name_compress(u_char *name, int name_len, char *payload)
{
int used_len = 0;
u_int16_t compress_len = 0;
u_char compress_name[DNS_MAX_NAME + 1] = {0};
if (name_len)
{
compress_len = dns_compress_rr_str(name, name_len, compress_name);
if (compress_len <= 0)
{
DNS_ERROR_LOG("dns_compress_rr_str()");
return -1;
}
NS_SETLEN(compress_name, compress_len, payload, used_len);
}
else
{
NS_SET8(0, payload, used_len);
}
return used_len;
}
static int dns_name_decompress(char *msg, u_char **ptr, u_char *buf, int buflen, char *end)
{
u_char *p = NULL;
int index = 0, len = 0;
int np = 0, tot_len = 0;
p = *ptr;
*ptr = NULL;
index = 0;
np = 0;
while (1)
{
if (0 == p[0])
{
break;
}
if (0x0c0 == (p[0] & 0x0c0))
{
if (p + 2 > (u_char *)end)
return -1;
len = ((p[0] & 0x03f) << 8) + p[1];
if (NULL == *ptr)
{
tot_len += 2;
*ptr = p + 2;
}
p = (u_char *)msg + len;
if (p > (u_char *)end)
return -1;
/* too many pointers. */
if (np++ > 16)
return -1;
continue;
}
len = p[0];
p++;
tot_len++;
if (p + len > (u_char *)end)
return -1;
if (index + len >= buflen - 1)
return -1;
memcpy(buf + index, p, len);
index += len;
buf[index++] = '.';
p += len;
tot_len += len;
}
if (NULL == *ptr)
{
*ptr = p + 1;
tot_len++;
}
/*
* omit last '.'
*/
if (index > 0)
{
buf[index - 1] = '\0';
}
else
{
buf[0] = '\0';
}
return tot_len;
}
/////////////////////////////////////////////////////////////////////////////////
// get API
/////////////////////////////////////////////////////////////////////////////////
static void get_dns_hdr_info(dns_hdr_t *dns_hdr, char *payload)
{
dns_hdr_t *tmp = ((dns_hdr_t *)payload);
memset(dns_hdr, 0, sizeof(dns_hdr_t));
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 void get_rr_type_nsec3(char **ptr, nsec3_t *nsec3, char *end)
{
NS_GET8(nsec3->hash_algo, *ptr);
NS_GET8(nsec3->flags, *ptr);
NS_GET16(nsec3->iteration, *ptr);
NS_GET8(nsec3->salt_len, *ptr);
nsec3->salt_value = *(u_char **)ptr;
*ptr += nsec3->salt_len; /* jump salt_value */
NS_GET8(nsec3->hash_len, *ptr);
nsec3->next_hash_owner = *(u_char **)ptr;
*ptr += nsec3->hash_len; /* jump next_hash_owner */
}
// unused
__attribute__((unused))static int get_rr_signer(u_char **ptr, u_char *buf, int buflen, char *end)
{
u_char *p = NULL;
int len = 0, i = 0;
p = *ptr;
if (0 == p[0])
{
len = strlen("Root");
memcpy(buf, "Root", len);
buf[len] = '\0';
*ptr += 1;
return 1;
}
p = *ptr;
*ptr = NULL;
len = 0;
while (1)
{
len++;
if (0x03 == p[0])
{
p += 1;
continue;
}
buf[i++] = p[0];
p += 1;
if (0 == p[0])
{
len++;
p += 1;
break;
}
}
*ptr = p;
buf[i] = '\0';
return len;
}
static void get_rr_type_rrsig(char **ptr, rrsig_t *rrsig, char *end)
{
NS_GET16(rrsig->type_covered, *ptr);
NS_GET8(rrsig->algo, *ptr);
NS_GET8(rrsig->labels, *ptr);
NS_GET32(rrsig->original_ttl, *ptr);
NS_GET32(rrsig->sig_expiration, *ptr);
NS_GET32(rrsig->sig_inception, *ptr);
NS_GET16(rrsig->key_tag, *ptr);
}
static int get_rr_type_wks(char **ptr, wks_t *wks, char *end)
{
if (*ptr + 4 > end)
return -1;
NS_GET32(wks->addr, *ptr);
if (*ptr + 1 > end)
return -1;
NS_GET8(wks->protocol, *ptr);
wks->bitmap = *(u_char **)ptr;
return 0;
}
static void get_rr_type_info(char **ptr, hinfo_t *hinfo, char *end)
{
int len = 0;
NS_GET8(hinfo->cpu_len, *ptr);
len = MIN(hinfo->cpu_len, sizeof(DNS_HINFO_MAX_CPU - 1));
memcpy((char *)hinfo->cpu, *ptr, len);
hinfo->cpu[len] = '\0';
*ptr += hinfo->cpu_len;
hinfo->cpu_len = len;
NS_GET8(hinfo->os_len, *ptr);
len = MIN(hinfo->os_len, sizeof(DNS_HINFO_MAX_OS - 1));
memcpy((char *)hinfo->os, *ptr, len);
hinfo->os[len] = '\0';
*ptr += hinfo->os_len;
hinfo->os_len = len;
}
static int get_rr_type_soa(char *msg, char **ptr, soa_t *soa, char *end)
{
if (0 >= dns_name_decompress(msg, (u_char **)ptr, soa->mname, sizeof(soa->mname), end))
return -1;
if (0 >= dns_name_decompress(msg, (u_char **)ptr, soa->rname, sizeof(soa->rname), end))
return -1;
if (*ptr + 4 > end)
return -1;
NS_GET32(soa->serial, *ptr);
if (*ptr + 4 > end)
return -1;
NS_GET32(soa->refresh, *ptr);
if (*ptr + 4 > end)
return -1;
NS_GET32(soa->retry, *ptr);
if (*ptr + 4 > end)
return -1;
NS_GET32(soa->expire, *ptr);
if (*ptr + 4 > end)
return -1;
NS_GET32(soa->minimum, *ptr);
return 0;
}
static int get_rr_common_field(char *msg, char **ptr, dns_rr_t *rr, char *end)
{
char *p = NULL;
if (*ptr == NULL)
{
return -1;
}
dns_name_decompress(msg, (u_char **)ptr, rr->name, DNS_MAX_NAME + 1, end);
#if 0
if(0 >= dns_name_decompress(msg, (u_char**)ptr, rr->name, DNS_MAX_NAME+1, end))
{
return -1;
}
#endif
if (*ptr == NULL || *ptr + 2 > end)
{
return -1;
}
NS_GET16(rr->type, *ptr);
if (*ptr == NULL || *ptr + 2 > end)
{
return -1;
}
NS_GET16(rr->rr_class, *ptr);
if (DNS_CLASS_UNKNOWN == rr->rr_class)
{
return -1;
}
if (*ptr == NULL || *ptr + 4 > end)
{
return -1;
}
NS_GET32(rr->ttl, *ptr);
if (*ptr == NULL || *ptr + 2 > end)
{
return -1;
}
NS_GET16(rr->rdlength, *ptr);
p = *ptr + rr->rdlength;
if (*ptr == NULL || p > end)
{
return -1;
}
return 0;
}
static int get_dns_query_question(char *msg, char **ptr, dns_query_question_t *q, char *end)
{
int used_len = 0;
if (0 >= dns_name_decompress(msg, (u_char **)ptr, q->qname, DNS_MAX_NAME + 1, end))
{
return -1;
}
used_len = strlen((char *)q->qname);
if (q->qname[used_len - 1] == '.')
return -2;
if (*ptr + 2 > end)
{
return -1;
}
NS_GET16(q->qtype, *ptr);
if (*ptr + 2 > end)
{
return -1;
}
NS_GET16(q->qclass, *ptr);
return 0;
}
static int get_one_resource_record(char *msg, char **ptr, dns_rr_t *rr, char *end)
{
u_int32_t len = 0, byte = 0;
u_char *original_ptr = NULL;
switch (rr->type)
{
case DNS_TYPE_CNAME:
original_ptr = (u_char *)*ptr;
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.cname, DNS_MAX_NAME + 1, end))
{
return 0;
}
break;
case DNS_TYPE_HINFO:
get_rr_type_info(ptr, &(rr->rdata.hinfo), end);
break;
case DNS_TYPE_MB:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mb, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_MD:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.md, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_MF:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mf, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_MG:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mg, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_MINFO:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.minfo.rmailbx, DNS_MAX_NAME + 1, end))
return 0;
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.minfo.emailbx, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_MR:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mr, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_MX:
if (*ptr + 2 > end)
return 0;
NS_GET16(rr->rdata.mx.preference, *ptr);
if (rr->rdlength - 2 < ((u_char *)*ptr)[0])
{
if (rr->rdlength < 2)
{
*ptr += rr->rdlength;
break;
}
len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 2); /* size=1byte */
memcpy(rr->rdata.mx.exchange, *ptr, len); /* error labels */
rr->rdata.mx.exchange[len] = '\0';
*ptr += rr->rdlength - 2;
}
else
{
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.mx.exchange, DNS_MAX_NAME + 1, end))
return 0;
}
break;
case DNS_TYPE_NS:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.ns, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_PTR:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.ptr, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_SOA:
original_ptr = (u_char *)*ptr;
if (0 != get_rr_type_soa(msg, ptr, &(rr->rdata.soa), end))
return 0;
if ((char *)original_ptr + rr->rdlength != *ptr)
{
*ptr = (char *)original_ptr + rr->rdlength;
}
break;
case DNS_TYPE_A:
if (*ptr + 4 > end)
return 0;
memcpy(rr->rdata.a, *ptr, NS_INT32SZ);
(*ptr) += NS_INT32SZ;
break;
case DNS_TYPE_AAAA:
if (*ptr + 16 > end)
return -1;
memcpy(rr->rdata.aaaa, *ptr, 16);
(*ptr) += 16;
break;
case DNS_TYPE_DNAME:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.dname, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_ISDN:
memcpy(rr->rdata.isdn, *ptr, sizeof(u_char));
(*ptr) += 1;
break;
case DNS_TYPE_TXT:
len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 1); /* size=1byte */
memcpy(rr->rdata.txt.txt, *ptr + 1, len);
rr->rdata.txt.size = len;
*ptr += rr->rdlength;
break;
case DNS_TYPE_RP:
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rp.mailbox, DNS_MAX_NAME + 1, end))
return 0;
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rp.txt_rr, DNS_MAX_NAME + 1, end))
return 0;
break;
case DNS_TYPE_NULL:
len = MIN(DNS_MAX_NAME - 1, rr->rdlength - 1); /* size=1byte */
memcpy(rr->rdata.null.null, *ptr + 1, len);
rr->rdata.null.size = len;
*ptr += rr->rdlength;
break;
case DNS_TYPE_WKS:
if (0 != get_rr_type_wks(ptr, &(rr->rdata.wks), end))
return 0;
rr->rdata.wks.size = rr->rdlength - 5;
*ptr += rr->rdlength - 5;
case DNS_TYPE_SRV:
NS_GET16(rr->rdata.srv.priority, *ptr);
NS_GET16(rr->rdata.srv.weight, *ptr);
NS_GET16(rr->rdata.srv.port, *ptr);
if (0 >= dns_name_decompress(msg, (u_char **)ptr, rr->rdata.srv.target, DNS_MAX_TARGET, end))
return 0;
break;
case DNS_TYPE_DS:
case DNS_TYPE_DLV:
if (*ptr + 4 > end)
return 0;
NS_GET16(rr->rdata.ds.key_tag, *ptr);
NS_GET8(rr->rdata.ds.algo, *ptr);
NS_GET8(rr->rdata.ds.digest_type, *ptr);
rr->rdata.ds.digest = *(u_char **)ptr;
rr->rdata.ds.digest_len = rr->rdlength - 4;
*ptr += rr->rdlength - 4;
break;
case DNS_TYPE_RRSIG:
if (*ptr + 18 > end)
return 0;
get_rr_type_rrsig(ptr, &(rr->rdata.rrsig), end);
len = dns_name_decompress(msg, (u_char **)ptr, rr->rdata.rrsig.signer_name, DNS_MAX_SIGNER_NAME, end);
if (len <= 0)
{
return -1;
}
rr->rdata.rrsig.signature = *(u_char **)ptr;
rr->rdata.rrsig.signature_len = rr->rdlength - 18 - len;
*ptr += rr->rdlength - 18 - len;
break;
case DNS_TYPE_NSEC:
original_ptr = (u_char *)*ptr;
len = dns_name_decompress(msg, (u_char **)ptr, rr->rdata.nsec.next_domain, DNS_MAX_OWNER, end);
if (len <= 0)
{
return -1;
}
if ((original_ptr + rr->rdlength != (u_char *)*ptr) && (*ptr != NULL))
{
NS_GET16(len, *ptr);
byte = MIN(DNS_MAX_MAPS - 1, len);
memcpy(rr->rdata.nsec.type_bit_maps, *ptr, byte);
rr->rdata.nsec.type_bit_maps[byte] = '\0';
*ptr += len;
rr->rdata.nsec.maps_temp_len = len;
rr->rdata.nsec.maps_len = len;
len = byte;
byte = ((u_char *)ptr)[0];
if ((byte & 0xFF) == 0xFF || byte == 128)
{
*ptr += 1; /* jump 0xFF */
byte = ((u_char *)ptr)[0];
*ptr += 1; /* jump 1 byte of len */
len = MIN(DNS_MAX_MAPS - 1 - len, byte);
memcpy(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_len, *ptr, len);
(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_len)[len] = '\0';
*ptr += byte; /* jump byte */
rr->rdata.nsec.maps_len += len;
}
}
break;
case DNS_TYPE_DNSKEY:
if (*ptr + 4 > end)
return 0;
NS_GET16(rr->rdata.dnskey.flags, *ptr);
NS_GET8(rr->rdata.dnskey.protocol, *ptr);
NS_GET8(rr->rdata.dnskey.algo, *ptr);
rr->rdata.dnskey.public_key = *(u_char **)ptr;
rr->rdata.dnskey.public_key_len = rr->rdlength - 4; /* sizeof(flags)+sizeof(protocol)+sizeof(algo) */
*ptr += rr->rdlength - 4; /* todo add log */
break;
case DNS_TYPE_NSEC3:
if (*ptr + 5 > end)
return 0;
original_ptr = (u_char *)*ptr;
get_rr_type_nsec3(ptr, &(rr->rdata.nsec3), end);
if ((original_ptr + rr->rdlength != (u_char *)*ptr) && (*ptr != NULL))
{
NS_GET16(len, *ptr);
byte = MIN(DNS_MAX_MAPS - 1, len);
memcpy(rr->rdata.nsec3.type_bit_maps, *ptr, byte);
rr->rdata.nsec3.type_bit_maps[byte] = '\0';
*ptr += len;
rr->rdata.nsec3.maps_temp_len = byte;
rr->rdata.nsec3.maps_len = byte;
len = byte;
byte = ((u_char *)*ptr)[0];
if ((byte & 0xFF) == 0xFF || byte == 128)
{
*ptr += 1; /* jump 0xFF */
byte = ((u_char *)*ptr)[0];
*ptr += 1; /* jump 1 byte of len */
len = MIN(DNS_MAX_MAPS - 1 - len, byte);
memcpy(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_len, *ptr, len);
(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_len)[len] = '\0';
*ptr += byte; /* jump byte */
rr->rdata.nsec3.maps_len += len;
}
}
break;
case DNS_TYPE_NSEC3PARAM:
NS_GET8(rr->rdata.nsec3param.hash_algo, *ptr);
NS_GET8(rr->rdata.nsec3param.flags, *ptr);
NS_GET16(rr->rdata.nsec3param.iteration, *ptr);
rr->rdata.nsec3param.salt_len = rr->rdlength - 4 - 1;
*ptr += 1;
rr->rdata.nsec3param.salt_value = *(u_char **)ptr;
*ptr += rr->rdlength - 5;
break;
case DNS_TYPE_OPT:
/* fail through */
case DNS_TYPE_UNKNOWN:
/* fail through */
default:
memcpy(rr->rdata.unknown_data, *ptr, rr->rdlength);
rr->rdata.unknown_data[rr->rdlength] = '\0';
(*ptr) += rr->rdlength;
// printf("No support respone type, type: %d", rr->type);
break;
}
return 1;
}
static int parse_resource_record(dns_info_t *dns_info, char *payload, int payload_len, char **cur_pos)
{
int i = 0;
dns_info->rr_count = dns_info->hdr_info.ancount + dns_info->hdr_info.adcount + dns_info->hdr_info.aucount;
if (dns_info->rr_count == 0)
{
return 0;
}
dns_info->rr_count = MIN(dns_info->rr_count, MAX_RR_NUM);
for (i = 0; i < dns_info->rr_count; i++)
{
if ((u_char *)*cur_pos >= (u_char *)payload + payload_len || (u_char *)*cur_pos < (u_char *)payload)
{
DNS_ERROR_LOG("parse_resource_record()");
return -1;
}
if (0 != get_rr_common_field(payload, cur_pos, &dns_info->rr[i], payload + payload_len))
{
if (dns_info->rr[i].rr_class == DNS_CLASS_UNKNOWN)
{
DNS_ERROR_LOG("get_rr_common_field()");
return -1; /* error packet */
}
else
{
DNS_ERROR_LOG("get_rr_common_field()");
dns_info->rr_count -= 1;
i -= 1;
continue;
}
}
if (dns_info->rr[i].rdlength == 0)
{
continue;
}
if (get_one_resource_record(payload, cur_pos, &dns_info->rr[i], payload + payload_len) != 1)
{
DNS_ERROR_LOG("get_one_resource_record()");
dns_info->rr_count -= 1;
i -= 1;
}
}
assert(i == dns_info->rr_count);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////
// set API
/////////////////////////////////////////////////////////////////////////////////
static void set_dns_hdr_info(dns_hdr_t *src, char *payload)
{
dns_hdr_t *dst = ((dns_hdr_t *)payload);
memset(dst, 0, sizeof(dns_hdr_t));
dst->qr = src->qr;
dst->opcode = src->opcode;
dst->aa = src->aa;
dst->tc = src->tc;
dst->rd = src->rd;
dst->ra = src->ra;
dst->z = src->z;
dst->rcode = src->rcode;
dst->id = htons(src->id);
dst->qdcount = htons(src->qdcount); // 16bits: QDCOUNT: number of questions
dst->ancount = htons(src->ancount); // 16bits: ANCOUNT: number of answer resource records
dst->aucount = htons(src->aucount); // 16bits: NSCOUNT: number of authority resource records
dst->adcount = htons(src->adcount); // 16bits: ARCOUNT: number of additional resource records
}
static int set_dns_pkt_rr_header(char *payload, int payload_len, dns_rr_t *rr, int rdlength, char **rr_header_length_ptr)
{
int ret;
int used_len = 0;
if (rr->name)
{
ret = dns_name_compress(rr->name, strlen((char *)rr->name), payload);
if (ret == -1)
{
return -1;
}
used_len += ret;
}
else
{
NS_SET16(0xc00c, payload, used_len);
}
NS_SET16(rr->type, payload, used_len);
NS_SET16(rr->rr_class, payload, used_len);
NS_SET32(rr->ttl, payload, used_len);
*rr_header_length_ptr = payload + used_len;
NS_SET16(rr->rdlength, payload, used_len);
return used_len;
}
static void reset_rr_header_length(char **rr_length_ptr, int rdlength)
{
u_int16_t seg_16 = 0;
seg_16 = htons(rdlength);
memset(*rr_length_ptr, 0, sizeof(seg_16));
memcpy(*rr_length_ptr, &seg_16, sizeof(seg_16));
}
/////////////////////////////////////////////////////////////////////////////////
// cheat API
/////////////////////////////////////////////////////////////////////////////////
static void set_cheat_pkt_header(dns_hdr_t *dns_hdr)
{
dns_hdr->qr = 1; // 1bit: Response
dns_hdr->opcode = 0; // 4bits: Query
dns_hdr->aa = 0; // 1bit: authoritative answer
dns_hdr->tc = 0; // 1bit: Not truncated
dns_hdr->rd = 1; // 1bit: Recursion Desired
dns_hdr->ra = 1; // 1bit: Recursion Available
dns_hdr->z = 0; // 3bits: Reserved for future use: Must be zero in all queries and responses
dns_hdr->rcode = 0; // 4bits: 0: No error condition
dns_hdr->id = htons(dns_hdr->id);
dns_hdr->qdcount = htons(dns_hdr->qdcount); // 16bits: QDCOUNT: number of questions
dns_hdr->ancount = htons(dns_hdr->ancount); // 16bits: ANCOUNT: number of answer resource records
dns_hdr->aucount = htons(dns_hdr->aucount); // 16bits: NSCOUNT: number of authority resource records
dns_hdr->adcount = htons(dns_hdr->adcount); // 16bits: ARCOUNT: number of additional resource records
}
static int set_cheat_pkt_question(char *payload, dns_query_question_t *query, int query_num)
{
int ret;
int used_len = 0;
/* ֻ<><D6BB><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
ret = dns_name_compress(query->qname, strlen((char *)query->qname), payload);
if (ret == -1)
{
return -1;
}
used_len += ret;
NS_SET16(query->qtype, payload, used_len);
NS_SET16(query->qclass, payload, used_len);
return used_len;
}
// NOTE <20><><EFBFBD><EFBFBD>ֵ TODO
static int set_cheat_pkt_rr_pdata_type_str(char *payload, cheat_pkt_opt_t *cheat_opt)
{
int used_len = 0;
u_int16_t q_class = 1;
u_int16_t compress_len = 0;
u_char compress_name[DNS_MAX_NAME + 1] = {0};
NS_SET16(0xc00c, payload, used_len);
NS_SET16(cheat_opt->res_type, payload, used_len);
NS_SET16(q_class, payload, used_len);
NS_SET32(cheat_opt->ttl, payload, used_len);
compress_len = dns_compress_rr_str(cheat_opt->res_info, cheat_opt->res_len, compress_name);
if (compress_len <= 0)
{
DNS_ERROR_LOG("dns_compress_rr_str()");
}
NS_SET16(compress_len, payload, used_len);
NS_SETLEN(compress_name, compress_len, payload, used_len);
return used_len;
}
static int set_cheat_pkt_rr_pdata_type_ip(char *payload, cheat_pkt_opt_t *cheat_opt)
{
int used_len = 0;
u_int16_t q_class = 1;
NS_SET16(0xc00c, payload, used_len);
NS_SET16(cheat_opt->res_type, payload, used_len);
NS_SET16(q_class, payload, used_len);
NS_SET32(cheat_opt->ttl, payload, used_len);
NS_SET16(cheat_opt->res_len, payload, used_len);
NS_SETLEN((char *)cheat_opt->res_info, cheat_opt->res_len, payload, used_len);
return used_len;
}
/////////////////////////////////////////////////////////////////////////////////
// public API
/////////////////////////////////////////////////////////////////////////////////
dns_info_t *dns_new(void)
{
dns_info_t *dns_info = (dns_info_t *)calloc(sizeof(dns_info_t), 1);
return dns_info;
}
void dns_free(dns_info_t *dns_info)
{
if (dns_info)
{
free(dns_info);
dns_info = NULL;
}
}
int dns_parser(dns_info_t *dns_info, char *in_buff, int buff_len)
{
char *cur_pos = NULL;
get_dns_hdr_info(&dns_info->hdr_info, in_buff);
cur_pos = in_buff + sizeof(dns_hdr_t);
if (0 == dns_info->hdr_info.qdcount || dns_info->hdr_info.qdcount > 1)
{
return -1;
}
if (0 != get_dns_query_question(in_buff, &cur_pos, &(dns_info->query_question), in_buff + buff_len))
{
DNS_ERROR_LOG("get_dns_query_question()");
return -1;
}
if (parse_resource_record(dns_info, in_buff, buff_len, &cur_pos) != 0)
{
DNS_ERROR_LOG("parse_resource_record()");
return -1;
}
return 0;
}
int dns_package(dns_info_t *dns_info, char *out_buff, int buff_size)
{
char *rr_header_length_ptr = NULL;
int i = 0;
int rr_count = 0;
dns_hdr_t *dns_hdr = &dns_info->hdr_info;
int ret = 0;
int used_len = 0;
dns_rr_t *rr;
set_dns_hdr_info(dns_hdr, out_buff);
used_len += sizeof(dns_hdr_t);
ret = set_cheat_pkt_question(out_buff + used_len, &dns_info->query_question, dns_hdr->qdcount);
if (ret < 0)
{
return -1;
}
used_len += ret;
rr_count = dns_hdr->adcount + dns_hdr->ancount + dns_hdr->aucount;
for (i = 0; i < rr_count; i++)
{
rr = &dns_info->rr[i];
ret = set_dns_pkt_rr_header(out_buff + used_len, buff_size, rr, 0, &rr_header_length_ptr);
if (ret == -1)
{
return -1;
}
used_len += ret;
if (rr->rdlength == 0)
{
continue;
}
int rr_header_used_len = used_len;
switch (rr->type)
{
case DNS_TYPE_A:
NS_SETLEN(rr->rdata.a, strlen((char *)rr->rdata.a), out_buff, used_len);
break;
case DNS_TYPE_AAAA:
NS_SETLEN(rr->rdata.aaaa, 16, out_buff, used_len);
break;
case DNS_TYPE_CNAME:
ret = dns_name_compress(rr->rdata.cname, strlen((char *)rr->rdata.cname), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MB:
ret = dns_name_compress(rr->rdata.mb, strlen((char *)rr->rdata.mb), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MD:
ret = dns_name_compress(rr->rdata.md, strlen((char *)rr->rdata.md), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MF:
ret = dns_name_compress(rr->rdata.mf, strlen((char *)rr->rdata.mf), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MG:
ret = dns_name_compress(rr->rdata.mg, strlen((char *)rr->rdata.mg), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MINFO:
ret = dns_name_compress(rr->rdata.minfo.rmailbx, strlen((char *)rr->rdata.minfo.rmailbx), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
ret = dns_name_compress(rr->rdata.minfo.emailbx, strlen((char *)rr->rdata.minfo.emailbx), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MR:
ret = dns_name_compress(rr->rdata.mr, strlen((char *)rr->rdata.mr), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_MX:
NS_SET16(rr->rdata.mx.preference, out_buff, used_len);
ret = dns_name_compress(rr->rdata.mx.exchange, strlen((char *)rr->rdata.mx.exchange), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_NS:
ret = dns_name_compress(rr->rdata.ns, strlen((char *)rr->rdata.ns), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_PTR:
ret = dns_name_compress(rr->rdata.ptr, strlen((char *)rr->rdata.ptr), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_SOA:
ret = dns_name_compress(rr->rdata.soa.mname, strlen((char *)rr->rdata.soa.mname), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
ret = dns_name_compress(rr->rdata.soa.rname, strlen((char *)rr->rdata.soa.rname), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
NS_SET32(rr->rdata.soa.serial, out_buff, used_len);
NS_SET32(rr->rdata.soa.refresh, out_buff, used_len);
NS_SET32(rr->rdata.soa.retry, out_buff, used_len);
NS_SET32(rr->rdata.soa.expire, out_buff, used_len);
NS_SET32(rr->rdata.soa.minimum, out_buff, used_len);
break;
case DNS_TYPE_DNAME:
ret = dns_name_compress(rr->rdata.dname, strlen((char *)rr->rdata.dname), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_ISDN:
NS_SETLEN(rr->rdata.isdn, strlen((char *)rr->rdata.isdn), out_buff, used_len);
break;
case DNS_TYPE_RP:
ret = dns_name_compress(rr->rdata.rp.mailbox, strlen((char *)rr->rdata.rp.mailbox), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
ret = dns_name_compress(rr->rdata.rp.txt_rr, strlen((char *)rr->rdata.rp.txt_rr), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_SRV:
NS_SET16(rr->rdata.srv.priority, out_buff, used_len);
NS_SET16(rr->rdata.srv.weight, out_buff, used_len);
NS_SET16(rr->rdata.srv.port, out_buff, used_len);
ret = dns_name_compress(rr->rdata.srv.target, strlen((char *)rr->rdata.srv.target), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
break;
case DNS_TYPE_RRSIG:
NS_SET16(rr->rdata.rrsig.type_covered, out_buff, used_len);
NS_SET8(rr->rdata.rrsig.algo, out_buff, used_len);
NS_SET8(rr->rdata.rrsig.labels, out_buff, used_len);
NS_SET32(rr->rdata.rrsig.original_ttl, out_buff, used_len);
NS_SET32(rr->rdata.rrsig.sig_expiration, out_buff, used_len);
NS_SET32(rr->rdata.rrsig.sig_inception, out_buff, used_len);
NS_SET16(rr->rdata.rrsig.key_tag, out_buff, used_len);
ret = dns_name_compress(rr->rdata.rrsig.signer_name, strlen((char *)rr->rdata.rrsig.signer_name), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
NS_SETLEN(rr->rdata.rrsig.signature, rr->rdata.rrsig.signature_len, out_buff, used_len);
break;
case DNS_TYPE_DNSKEY:
NS_SET16(rr->rdata.dnskey.flags, out_buff, used_len);
NS_SET8(rr->rdata.dnskey.protocol, out_buff, used_len);
NS_SET8(rr->rdata.dnskey.algo, out_buff, used_len);
NS_SETLEN(rr->rdata.dnskey.public_key, rr->rdata.dnskey.public_key_len, out_buff, used_len);
break;
case DNS_TYPE_NSEC3PARAM:
NS_SET8(rr->rdata.nsec3param.hash_algo, out_buff, used_len);
NS_SET8(rr->rdata.nsec3param.flags, out_buff, used_len);
NS_SET16(rr->rdata.nsec3param.iteration, out_buff, used_len);
NS_SET8(rr->rdata.nsec3param.salt_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec3param.salt_value, rr->rdata.nsec3param.salt_len, out_buff, used_len);
break;
case DNS_TYPE_DS:
/* fail through */
case DNS_TYPE_DLV:
NS_SET16(rr->rdata.ds.key_tag, out_buff, used_len);
NS_SET8(rr->rdata.ds.algo, out_buff, used_len);
NS_SET8(rr->rdata.ds.digest_type, out_buff, used_len);
NS_SETLEN(rr->rdata.ds.digest, rr->rdata.ds.digest_len, out_buff, used_len);
break;
case DNS_TYPE_TXT:
NS_SET8(rr->rdata.txt.size, out_buff, used_len);
NS_SETLEN(rr->rdata.txt.txt, rr->rdata.txt.size, out_buff, used_len);
break;
case DNS_TYPE_NULL:
NS_SET8(rr->rdata.null.size, out_buff, used_len);
NS_SETLEN(rr->rdata.null.null, rr->rdata.null.size, out_buff, used_len);
break;
case DNS_TYPE_WKS:
NS_SET32(rr->rdata.wks.addr, out_buff, used_len);
NS_SET8(rr->rdata.wks.protocol, out_buff, used_len);
NS_SETLEN(rr->rdata.wks.bitmap, rr->rdata.wks.size, out_buff, used_len);
break;
case DNS_TYPE_HINFO:
NS_SET8(rr->rdata.hinfo.cpu_len, out_buff, used_len);
NS_SETLEN(rr->rdata.hinfo.cpu, rr->rdata.hinfo.cpu_len, out_buff, used_len);
NS_SET8(rr->rdata.hinfo.os_len, out_buff, used_len);
NS_SETLEN(rr->rdata.hinfo.os, rr->rdata.hinfo.os_len, out_buff, used_len);
break;
case DNS_TYPE_NSEC:
ret = dns_name_compress(rr->rdata.nsec.next_domain, strlen((char *)rr->rdata.nsec.next_domain), out_buff + used_len);
if (ret == -1)
{
return -1;
}
used_len += ret;
if (rr->rdata.nsec.maps_len)
{
NS_SET16(rr->rdata.nsec.maps_temp_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec.type_bit_maps, rr->rdata.nsec.maps_temp_len, out_buff, used_len);
if (rr->rdata.nsec.maps_len > rr->rdata.nsec.maps_temp_len)
{
NS_SET8(0xFF, out_buff, used_len);
NS_SET8(rr->rdata.nsec.maps_len - rr->rdata.nsec.maps_temp_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec.type_bit_maps + rr->rdata.nsec.maps_temp_len, rr->rdata.nsec.maps_len - rr->rdata.nsec.maps_temp_len, out_buff, used_len);
}
}
break;
case DNS_TYPE_NSEC3:
NS_SET8(rr->rdata.nsec3.hash_algo, out_buff, used_len);
NS_SET8(rr->rdata.nsec3.flags, out_buff, used_len);
NS_SET16(rr->rdata.nsec3.iteration, out_buff, used_len);
NS_SET8(rr->rdata.nsec3.salt_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec3.salt_value, rr->rdata.nsec3.salt_len, out_buff, used_len);
NS_SET8(rr->rdata.nsec3.hash_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec3.next_hash_owner, rr->rdata.nsec3.hash_len, out_buff, used_len);
if (rr->rdata.nsec3.maps_len)
{
NS_SET16(rr->rdata.nsec3.maps_temp_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec3.type_bit_maps, rr->rdata.nsec3.maps_temp_len, out_buff, used_len);
if (rr->rdata.nsec3.maps_len > rr->rdata.nsec3.maps_temp_len)
{
NS_SET8(0xFF, out_buff, used_len);
NS_SET8(rr->rdata.nsec3.maps_len - rr->rdata.nsec3.maps_temp_len, out_buff, used_len);
NS_SETLEN(rr->rdata.nsec3.type_bit_maps + rr->rdata.nsec3.maps_temp_len, rr->rdata.nsec3.maps_len - rr->rdata.nsec3.maps_temp_len, out_buff, used_len);
}
}
break;
case DNS_TYPE_OPT:
/* fail through */
case DNS_TYPE_UNKNOWN:
/* fail through */
default:
NS_SETLEN(rr->rdata.unknown_data, rr->rdlength, out_buff, used_len);
break;
}
if (used_len - rr_header_used_len != rr->rdlength)
{
reset_rr_header_length(&rr_header_length_ptr, used_len - rr_header_used_len);
}
if (used_len > buff_size)
{
// printf("dns_package buff to short, %s, %d\n", __FILE__, __LINE__);
return -1;
}
}
return used_len;
}
int dns_cheat_response(dns_info_t *dns_info, cheat_pkt_opt_t *cheat_opt, int cheat_opt_num, char *out_buff, int buff_size)
{
int i = 0;
dns_hdr_t *dns_hdr = NULL;
int ret = 0, used_len = 0;
memset(out_buff, 0, buff_size);
dns_hdr = (dns_hdr_t *)out_buff;
dns_hdr->id = dns_info->hdr_info.id;
dns_hdr->qdcount = 1;
dns_hdr->ancount = cheat_opt_num;
used_len += sizeof(dns_hdr_t);
ret = set_cheat_pkt_question(out_buff + used_len, &dns_info->query_question, dns_hdr->qdcount);
if (ret < 0)
{
return -1;
}
used_len += ret;
for (i = 0; i < cheat_opt_num; i++)
{
switch (cheat_opt[i].res_type)
{
case DNS_TYPE_A:
case DNS_TYPE_AAAA:
used_len += set_cheat_pkt_rr_pdata_type_ip(out_buff + used_len, &cheat_opt[i]);
break;
case DNS_TYPE_CNAME:
case DNS_TYPE_NS:
used_len += set_cheat_pkt_rr_pdata_type_str(out_buff + used_len, &cheat_opt[i]);
break;
}
if (used_len > buff_size)
{
return -2;
}
}
set_cheat_pkt_header(dns_hdr);
return used_len;
}