From 92af4c05c05ceac76292441bb6f251badfb34958 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Tue, 5 Apr 2022 21:21:33 +0800 Subject: [PATCH] dns: support domain compress. --- src/dns.c | 886 ++++++++++++++++++++++++++++++------------------- src/dns.h | 35 +- src/dns_conf.c | 4 +- src/util.c | 3 +- 4 files changed, 570 insertions(+), 358 deletions(-) diff --git a/src/dns.c b/src/dns.c index f402063..1ce6d6a 100644 --- a/src/dns.c +++ b/src/dns.c @@ -94,6 +94,217 @@ static unsigned int _dns_read_int(unsigned char **buffer) return value; } +static inline int _dns_left_len(struct dns_context *context) +{ + return context->maxsize - (context->ptr - context->data); +} + +static int _dns_get_domain_from_packet(unsigned char *packet, int packet_size, unsigned char **domain_ptr, char *output, int size) +{ + int output_len = 0; + int copy_len = 0; + int len = 0; + unsigned char *ptr = (unsigned char*)*domain_ptr; + int is_compressed = 0; + int ptr_jump = 0; + + /*[len]string[len]string...[0]0 */ + while (1) { + if (ptr >= packet + packet_size || ptr < packet || output_len >= size - 1 || ptr_jump > 4) { + return -1; + } + + len = *ptr; + if (len == 0) { + *output = 0; + ptr++; + break; + } + + /* compressed domain */ + if (len >= 0xC0) { + if ((ptr + 2) > (packet + packet_size)) { + return -1; + } + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1| OFFSET | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + /* read offset */ + len = _dns_read_short(&ptr) & 0x3FFF; + if (is_compressed == 0) { + *domain_ptr = ptr; + } + + ptr = packet + len; + if (ptr > packet + packet_size) { + tlog(TLOG_DEBUG, "length is not enough %u:%ld, %p, %p", packet_size, (long)(ptr - packet), + *domain_ptr, packet); + return -1; + } + is_compressed = 1; + ptr_jump++; + continue; + } + + ptr_jump = 0; + + /* change [len] to '.' */ + if (output_len > 0) { + *output = '.'; + output++; + output_len += 1; + } + + if (ptr > packet + packet_size) { + tlog(TLOG_DEBUG, "length is not enough %u:%ld, %p, %p", packet_size, (long)(ptr - packet), + *domain_ptr, packet); + return -1; + } + + ptr++; + if (output_len < size - 1) { + /* copy sub string */ + copy_len = (len < size - output_len) ? len : size - 1 - output_len; + if ((ptr + copy_len) > (packet + packet_size)) { + tlog(TLOG_DEBUG, "length is not enough %u:%ld, %p, %p", packet_size, (long)(ptr - packet), + *domain_ptr, packet); + return -1; + } + memcpy(output, ptr, copy_len); + } + + ptr += len; + output += len; + output_len += len; + } + + if (is_compressed == 0) { + *domain_ptr = ptr; + } + + return 0; +} + +static int _dns_decode_domain(struct dns_context *context, char *output, int size) +{ + return _dns_get_domain_from_packet(context->data, context->maxsize, &(context->ptr), output, size); +} + +unsigned int dict_hash(const char *s) +{ + unsigned int hashval; + for (hashval = 0; *s != '\0'; s++) + hashval = *s + 31 * hashval; + return hashval; +} + +int _dns_add_domain_dict(struct dns_context *context, unsigned int hash, int pos) { + struct dns_packet_dict *dict = context->namedict; + + if (dict->dict_count >= DNS_PACKET_DICT_SIZE) { + return -1; + } + + if (hash == 0) { + return -1; + } + + if (pos >= context->maxsize) { + return -1; + } + + int index = dict->dict_count; + dict->names[index].hash = hash; + dict->names[index].pos = pos; + dict->dict_count++; + + return 0; +} + +int _dns_get_domain_offset(struct dns_context *context, const char *domain) { + int i = 0; + + char domain_check[DNS_MAX_CNAME_LEN]; + struct dns_packet_dict *dict = context->namedict; + + if (*domain == '\0') { + return -1; + } + + unsigned int hash = dict_hash(domain); + for (i = 0; i < dict->dict_count; i++) { + if (dict->names[i].hash != hash) { + continue; + } + + unsigned char *domain_check_ptr = dict->names[i].pos + context->data; + if (_dns_get_domain_from_packet(context->data, context->maxsize, &domain_check_ptr, domain_check, DNS_MAX_CNAME_LEN) !=0) { + return -1; + } + + return dict->names[i].pos; + } + + _dns_add_domain_dict(context, hash, context->ptr - 1 - context->data); + return -1; +} + +static int _dns_encode_domain(struct dns_context *context, char *domain) +{ + int num = 0; + int total_len = 0; + unsigned char *ptr_num = context->ptr++; + int dict_offset = 0; + + dict_offset = _dns_get_domain_offset(context, domain); + total_len++; + + /*[len]string[len]string...[0]0 */ + while (_dns_left_len(context) > 1 && *domain != 0) { + total_len++; + if (dict_offset >= 0) { + int offset = 0xc000 | dict_offset; + _dns_write_short(&ptr_num, offset); + context->ptr++; + dict_offset = -1; + ptr_num = NULL; + return total_len; + } + + if (*domain == '.') { + *ptr_num = num; + num = 0; + ptr_num = context->ptr; + domain++; + context->ptr++; + dict_offset = _dns_get_domain_offset(context, domain); + continue; + } + *context->ptr = *domain; + num++; + context->ptr++; + domain++; + } + + *ptr_num = num; + + if (total_len > 1) { + /* if domain is '\0', [domain] is '\0' */ + *(context->ptr) = 0; + total_len++; + context->ptr++; + } + + if (_dns_left_len(context) <= 0) { + return -1; + } + + return total_len; +} + /* iterator get rrs begin */ struct dns_rrs *dns_get_rrs_start(struct dns_packet *packet, dns_rr_type type, int *count) { @@ -146,19 +357,32 @@ struct dns_rrs *dns_get_rrs_next(struct dns_packet *packet, struct dns_rrs *rrs) return (struct dns_rrs *)(packet->data + rrs->next); } +static void _dns_init_context_by_rrs(struct dns_rrs *rrs, struct dns_context *context) { + context->packet =rrs->packet; + context->data = rrs->packet->data; + context->ptr = rrs->data; + context->namedict = &rrs->packet->namedict; + context->maxsize = rrs->data - rrs->packet->data + rrs->len; +} + /* iterator add rrs begin */ -static unsigned char *_dns_add_rrs_start(struct dns_packet *packet, int *maxlen) +static int _dns_add_rrs_start(struct dns_packet *packet, struct dns_context *context) { struct dns_rrs *rrs; unsigned char *end = packet->data + packet->len; - rrs = (struct dns_rrs *)end; - *maxlen = packet->size - packet->len - sizeof(*packet) - sizeof(*rrs); - if (*maxlen <= 0) { - /* if size exceeds max packet size, return NULL */ - return NULL; + if ((packet->len + sizeof(*rrs)) >= packet->size) { + return -1; } - return rrs->data; + rrs = (struct dns_rrs *)end; + + context->ptr = rrs->data; + context->packet = packet; + context->maxsize = packet->size - sizeof(*packet); + context->data = packet->data; + context->namedict = &packet->namedict; + + return 0; } /* iterator add rrs end */ @@ -214,6 +438,7 @@ static int _dns_rr_add_end(struct dns_packet *packet, int type, dns_type_t rtype } /* update rrs head info */ + rrs->packet = packet; rrs->len = len; rrs->type = rtype; rrs->next = DNS_RR_END; @@ -224,86 +449,47 @@ static int _dns_rr_add_end(struct dns_packet *packet, int type, dns_type_t rtype return 0; } -static inline int _dns_data_left_len(struct dns_data_context *data_context) +static int _dns_add_qr_head(struct dns_context *context, char *domain, int qtype, int qclass) { - /* check whether data length out of bound */ - return data_context->maxsize - (data_context->ptr - data_context->data); -} - -static int _dns_add_qr_head(struct dns_data_context *data_context, char *domain, int qtype, int qclass) -{ - /* question head */ - /* |domain | - * |qtype | qclass | - */ - while (1) { - if (_dns_data_left_len(data_context) < 4) { - return -1; - } - *data_context->ptr = *domain; - if (*domain == '\0') { - data_context->ptr++; - break; - } - data_context->ptr++; - domain++; - } - - if (_dns_data_left_len(data_context) < 4) { + int ret = _dns_encode_domain(context, domain); + if (ret < 0) { return -1; } - _dns_write_short(&data_context->ptr, qtype); - _dns_write_short(&data_context->ptr, qclass); + if (_dns_left_len(context) < 4) { + return -1; + } + + _dns_write_short(&context->ptr, qtype); + _dns_write_short(&context->ptr, qclass); + + return ret + 4; +} + +static int _dns_get_qr_head(struct dns_context *context, char *domain, int maxsize, int *qtype, int *qclass) +{ + int ret = 0; + + if (domain == NULL || context == NULL) { + return -1; + } + + ret = _dns_decode_domain(context, domain, maxsize); + if (ret < 0) { + return -1; + } + + if (_dns_left_len(context) < 4) { + return -1; + } + + *qtype = _dns_read_short(&context->ptr); + *qclass = _dns_read_short(&context->ptr); return 0; } -static int _dns_get_qr_head(struct dns_data_context *data_context, char *domain, int maxsize, int *qtype, int *qclass) -{ - int i; - int is_read_all = 0; - - if (domain == NULL || data_context == NULL) { - return -1; - } - /* question head */ - /* |domain | - * |qtype | qclass | - */ - for (i = 0; i < maxsize - 1; i++) { - if (_dns_data_left_len(data_context) < 4) { - return -1; - } - *domain = *data_context->ptr; - if (*data_context->ptr == '\0') { - domain++; - data_context->ptr++; - i++; - is_read_all = 1; - break; - } - *domain = *data_context->ptr; - domain++; - data_context->ptr++; - } - - *domain = '\0'; - if (is_read_all == 0) { - return -1; - } - - if (_dns_data_left_len(data_context) < 4) { - return -1; - } - - *qtype = _dns_read_short(&data_context->ptr); - *qclass = _dns_read_short(&data_context->ptr); - - return 0; -} - -static int _dns_add_rr_head(struct dns_data_context *data_context, char *domain, int qtype, int qclass, int ttl, +static int _dns_add_rr_head(struct dns_context *context, char *domain, int qtype, int qclass, int ttl, int rr_len) { int len = 0; @@ -314,22 +500,22 @@ static int _dns_add_rr_head(struct dns_data_context *data_context, char *domain, * | ttl | * | rrlen | rrdata | */ - len = _dns_add_qr_head(data_context, domain, qtype, qclass); + len = _dns_add_qr_head(context, domain, qtype, qclass); if (len < 0) { return -1; } - if (_dns_data_left_len(data_context) < 6) { + if (_dns_left_len(context) < 6) { return -1; } - _dns_write_int(&data_context->ptr, ttl); - _dns_write_short(&data_context->ptr, rr_len); + _dns_write_int(&context->ptr, ttl); + _dns_write_short(&context->ptr, rr_len); - return 0; + return len + 6; } -static int _dns_get_rr_head(struct dns_data_context *data_context, char *domain, int maxsize, int *qtype, int *qclass, +static int _dns_get_rr_head(struct dns_context *context, char *domain, int maxsize, int *qtype, int *qclass, int *ttl, int *rr_len) { int len = 0; @@ -340,14 +526,14 @@ static int _dns_get_rr_head(struct dns_data_context *data_context, char *domain, * | ttl | * | rrlen | rrdata | */ - len = _dns_get_qr_head(data_context, domain, maxsize, qtype, qclass); + len = _dns_get_qr_head(context, domain, maxsize, qtype, qclass); - if (_dns_data_left_len(data_context) < 6) { + if (_dns_left_len(context) < 6) { return -1; } - *ttl = _dns_read_int(&data_context->ptr); - *rr_len = _dns_read_short(&data_context->ptr); + *ttl = _dns_read_int(&context->ptr); + *rr_len = _dns_read_short(&context->ptr); return len; } @@ -355,9 +541,9 @@ static int _dns_get_rr_head(struct dns_data_context *data_context, char *domain, static int _dns_add_RAW(struct dns_packet *packet, dns_rr_type rrtype, dns_type_t rtype, char *domain, int ttl, void *raw, int raw_len) { - int maxlen = 0; int len = 0; - struct dns_data_context data_context; + struct dns_context context; + int ret; /* resource record */ /* |domain | @@ -365,29 +551,25 @@ static int _dns_add_RAW(struct dns_packet *packet, dns_rr_type rrtype, dns_type_ * | ttl | * | rrlen | rrdata | */ - unsigned char *data = _dns_add_rrs_start(packet, &maxlen); - if (data == NULL) { + ret = _dns_add_rrs_start(packet, &context); + if (ret < 0) { return -1; } - if (raw_len >= maxlen) { - return -1; - } - - data_context.data = data; - data_context.ptr = data; - data_context.maxsize = maxlen; - /* add rr head */ - len = _dns_add_rr_head(&data_context, domain, rtype, DNS_C_IN, ttl, raw_len); + len = _dns_add_rr_head(&context, domain, rtype, DNS_C_IN, ttl, raw_len); if (len < 0) { return -1; } + if (_dns_left_len(&context) < raw_len) { + return -1; + } + /* add rr data */ - memcpy(data_context.ptr, raw, raw_len); - data_context.ptr += raw_len; - len = data_context.ptr - data_context.data; + memcpy(context.ptr, raw, raw_len); + context.ptr += raw_len; + len += raw_len; return _dns_rr_add_end(packet, rrtype, rtype, len); } @@ -398,7 +580,7 @@ static int _dns_get_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl int qclass = 0; int rr_len = 0; int ret = 0; - struct dns_data_context data_context; + struct dns_context context; /* resource record head */ /* |domain | @@ -406,14 +588,10 @@ static int _dns_get_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl * | ttl | * | rrlen | rrdata | */ - unsigned char *data = rrs->data; - - data_context.data = data; - data_context.ptr = data; - data_context.maxsize = rrs->len; + _dns_init_context_by_rrs(rrs, &context); /* get rr head */ - ret = _dns_get_rr_head(&data_context, domain, maxsize, &qtype, &qclass, ttl, &rr_len); + ret = _dns_get_rr_head(&context, domain, maxsize, &qtype, &qclass, ttl, &rr_len); if (ret < 0) { return -1; } @@ -423,8 +601,8 @@ static int _dns_get_RAW(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl } /* get rr data */ - memcpy(raw, data_context.ptr, rr_len); - data_context.ptr += rr_len; + memcpy(raw, context.ptr, rr_len); + context.ptr += rr_len; *raw_len = rr_len; return 0; @@ -459,9 +637,9 @@ static int __attribute__((unused)) _dns_add_OPT(struct dns_packet *packet, dns_r { // TODO - int maxlen = 0; + int ret = 0; int len = 0; - struct dns_data_context data_context; + struct dns_context context; int total_len = sizeof(*opt) + opt->length; int ttl = 0; @@ -476,31 +654,27 @@ static int __attribute__((unused)) _dns_add_OPT(struct dns_packet *packet, dns_r / / +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ */ - unsigned char *data = _dns_add_rrs_start(packet, &maxlen); - if (data == NULL) { + ret = _dns_add_rrs_start(packet, &context); + if (ret < 0) { return -1; } - if (total_len > maxlen) { + if (_dns_left_len(&context) < total_len) { return -1; } - data_context.data = data; - data_context.ptr = data; - data_context.maxsize = maxlen; - ttl = (opt_code << 16) | opt_len; /* add rr head */ - len = _dns_add_rr_head(&data_context, "", type, DNS_C_IN, ttl, total_len); + len = _dns_add_rr_head(&context, "", type, DNS_C_IN, ttl, total_len); if (len < 0) { return -1; } /* add rr data */ - memcpy(data_context.ptr, opt, total_len); - data_context.ptr += total_len; - len = data_context.ptr - data_context.data; + memcpy(context.ptr, opt, total_len); + context.ptr += total_len; + len = context.ptr - context.data - packet->len; return _dns_rr_add_end(packet, type, DNS_T_OPT, len); } @@ -514,18 +688,15 @@ static int __attribute__((unused)) _dns_get_OPT(struct dns_rrs *rrs, unsigned sh int qclass = 0; int rr_len = 0; int ret = 0; - struct dns_data_context data_context; + struct dns_context context; char domain[DNS_MAX_CNAME_LEN]; int maxsize = DNS_MAX_CNAME_LEN; int ttl = 0; - unsigned char *data = rrs->data; - data_context.data = data; - data_context.ptr = data; - data_context.maxsize = rrs->len; + _dns_init_context_by_rrs(rrs, &context); /* get rr head */ - ret = _dns_get_rr_head(&data_context, domain, maxsize, &qtype, &qclass, &ttl, &rr_len); + ret = _dns_get_rr_head(&context, domain, maxsize, &qtype, &qclass, &ttl, &rr_len); if (ret < 0) { return -1; } @@ -537,8 +708,8 @@ static int __attribute__((unused)) _dns_get_OPT(struct dns_rrs *rrs, unsigned sh /* get rr data */ *opt_code = ttl >> 16; *opt_len = ttl & 0xFFFF; - memcpy(opt, data_context.ptr, rr_len); - data_context.ptr += rr_len; + memcpy(opt, context.ptr, rr_len); + context.ptr += rr_len; *opt_maxlen = rr_len; return 0; @@ -792,47 +963,32 @@ int dns_get_OPT_TCP_KEEYALIVE(struct dns_rrs *rrs, unsigned short *opt_code, uns int dns_add_domain(struct dns_packet *packet, char *domain, int qtype, int qclass) { int len = 0; - int maxlen = 0; - unsigned char *data = _dns_add_rrs_start(packet, &maxlen); - struct dns_data_context data_context; + int ret = 0; + struct dns_context context; - if (data == NULL) { + ret = _dns_add_rrs_start(packet, &context); + if (ret < 0) { return -1; } - data_context.data = data; - data_context.ptr = data; - data_context.maxsize = maxlen; - - len = _dns_add_qr_head(&data_context, domain, qtype, qclass); + len = _dns_add_qr_head(&context, domain, qtype, qclass); if (len < 0) { return -1; } - len = data_context.ptr - data_context.data; - return _dns_rr_add_end(packet, DNS_RRS_QD, DNS_T_CNAME, len); } int dns_get_domain(struct dns_rrs *rrs, char *domain, int maxsize, int *qtype, int *qclass) { - struct dns_data_context data_context; - unsigned char *data = rrs->data; + struct dns_context context; if (rrs->type != DNS_T_CNAME) { return -1; } - data_context.data = data; - data_context.ptr = data; - data_context.maxsize = rrs->len; - - return _dns_get_qr_head(&data_context, domain, maxsize, qtype, qclass); -} - -static inline int _dns_left_len(struct dns_context *context) -{ - return context->maxsize - (context->ptr - context->data); + _dns_init_context_by_rrs(rrs, &context); + return _dns_get_qr_head(&context, domain, maxsize, qtype, qclass); } static int _dns_decode_head(struct dns_context *context) @@ -921,126 +1077,6 @@ static int _dns_encode_head_count(struct dns_context *context) return len; } -static int _dns_decode_domain(struct dns_context *context, char *output, int size) -{ - int output_len = 0; - int copy_len = 0; - int len = 0; - unsigned char *ptr = context->ptr; - int is_compressed = 0; - int ptr_jump = 0; - - /*[len]string[len]string...[0]0 */ - while (1) { - if (ptr >= context->data + context->maxsize || ptr < context->data || output_len >= size - 1 || ptr_jump > 4) { - return -1; - } - - len = *ptr; - if (len == 0) { - *output = 0; - ptr++; - break; - } - - /* compressed domain */ - if (len >= 0xC0) { - if ((ptr + 2) > (context->data + context->maxsize)) { - return -1; - } - /* - 0 1 2 3 4 5 6 7 8 9 A B C D E F - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | 1 1| OFFSET | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - */ - /* read offset */ - len = _dns_read_short(&ptr) & 0x3FFF; - if (is_compressed == 0) { - context->ptr = ptr; - } - ptr = context->data + len; - if (ptr > context->data + context->maxsize) { - tlog(TLOG_DEBUG, "length is not enough %u:%ld, %p, %p", context->maxsize, (long)(ptr - context->data), - context->ptr, context->data); - return -1; - } - is_compressed = 1; - ptr_jump++; - continue; - } - - ptr_jump = 0; - - /* change [len] to '.' */ - if (output_len > 0) { - *output = '.'; - output++; - output_len += 1; - } - - if (ptr > context->data + context->maxsize) { - tlog(TLOG_DEBUG, "length is not enough %u:%ld, %p, %p", context->maxsize, (long)(ptr - context->data), - context->ptr, context->data); - return -1; - } - - ptr++; - if (output_len < size - 1) { - /* copy sub string */ - copy_len = (len < size - output_len) ? len : size - 1 - output_len; - if ((ptr + copy_len) > (context->data + context->maxsize)) { - tlog(TLOG_DEBUG, "length is not enough %u:%ld, %p, %p", context->maxsize, (long)(ptr - context->data), - context->ptr, context->data); - return -1; - } - memcpy(output, ptr, copy_len); - } - - ptr += len; - output += len; - output_len += len; - } - - if (is_compressed == 0) { - context->ptr = ptr; - } - - return 0; -} - -static int _dns_encode_domain(struct dns_context *context, char *domain) -{ - int num = 0; - int total_len = 0; - unsigned char *ptr_num = context->ptr++; - - /*[len]string[len]string...[0]0 */ - while (_dns_left_len(context) > 1 && *domain != 0) { - if (*domain == '.') { - *ptr_num = num; - num = 0; - ptr_num = context->ptr; - domain++; - context->ptr++; - continue; - } - *context->ptr = *domain; - num++; - context->ptr++; - domain++; - total_len++; - } - - *ptr_num = num; - if (total_len > 0) { - /* if domain is '\0', [domain] is '\0' */ - *(context->ptr) = 0; - context->ptr++; - } - return 0; -} - static int _dns_decode_qr_head(struct dns_context *context, char *domain, int domain_size, int *qtype, int *qclass) { int ret = 0; @@ -1114,7 +1150,7 @@ static int _dns_decode_rr_head(struct dns_context *context, char *domain, int do return 0; } -static int _dns_encode_rr_head(struct dns_context *context, char *domain, int qtype, int qclass, int ttl, int rr_len) +static int _dns_encode_rr_head(struct dns_context *context, char *domain, int qtype, int qclass, int ttl, int rr_len, unsigned char **rr_len_ptr) { int ret = 0; ret = _dns_encode_qr_head(context, domain, qtype, qclass); @@ -1127,6 +1163,9 @@ static int _dns_encode_rr_head(struct dns_context *context, char *domain, int qt } _dns_write_int(&context->ptr, ttl); + if (rr_len_ptr) { + *rr_len_ptr = context->ptr; + } _dns_write_short(&context->ptr, rr_len); return 0; @@ -1140,7 +1179,8 @@ static int _dns_encode_raw(struct dns_context *context, struct dns_rrs *rrs) int ttl = 0; char domain[DNS_MAX_CNAME_LEN]; int rr_len; - struct dns_data_context data_context; + unsigned char *rr_len_ptr = NULL; + struct dns_context data_context; /* 0 1 2 3 4 5 6 7 8 9 A B C D E F +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ @@ -1162,17 +1202,13 @@ static int _dns_encode_raw(struct dns_context *context, struct dns_rrs *rrs) / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ - - data_context.data = rrs->data; - data_context.ptr = rrs->data; - data_context.maxsize = rrs->len; - + _dns_init_context_by_rrs(rrs, &data_context); ret = _dns_get_rr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass, &ttl, &rr_len); if (ret < 0) { return -1; } - ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len); + ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len, &rr_len_ptr); if (ret < 0) { return -1; } @@ -1218,24 +1254,18 @@ static int _dns_encode_CNAME(struct dns_context *context, struct dns_rrs *rrs) int ttl = 0; char domain[DNS_MAX_CNAME_LEN]; int rr_len; - struct dns_data_context data_context; - - data_context.data = rrs->data; - data_context.ptr = rrs->data; - data_context.maxsize = rrs->len; + unsigned char *rr_len_ptr = NULL; + struct dns_context data_context; + _dns_init_context_by_rrs(rrs, &data_context); ret = _dns_get_rr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass, &ttl, &rr_len); if (ret < 0) { return -1; } /* when code domain, len must plus 1, because of length at the begining */ - rr_len++; - if (rr_len > rrs->len) { - return -1; - } - - ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len); + rr_len = 1; + ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len, &rr_len_ptr); if (ret < 0) { return -1; } @@ -1244,9 +1274,14 @@ static int _dns_encode_CNAME(struct dns_context *context, struct dns_rrs *rrs) if (ret < 0) { return -1; } - + rr_len += ret; data_context.ptr += strnlen((char *)(data_context.ptr), DNS_MAX_CNAME_LEN) + 1; + if (rr_len > rrs->len) { + return -1; + } + _dns_write_short(&rr_len_ptr, ret); + return 0; } @@ -1283,35 +1318,28 @@ static int _dns_encode_SOA(struct dns_context *context, struct dns_rrs *rrs) int qclass = 0; int ttl = 0; char domain[DNS_MAX_CNAME_LEN]; - int rr_len; - struct dns_data_context data_context; - - data_context.data = rrs->data; - data_context.ptr = rrs->data; - data_context.maxsize = rrs->len; + int rr_len = 0; + unsigned char *rr_len_ptr = NULL; + struct dns_context data_context; + _dns_init_context_by_rrs(rrs, &data_context); ret = _dns_get_rr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass, &ttl, &rr_len); if (ret < 0) { return -1; } - /* when code two domain, len must plus 2, because of length at the begining */ - rr_len += 2; - if (rr_len > rrs->len) { - return -1; - } - - ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len); + ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len, &rr_len_ptr); if (ret < 0) { return -1; } + rr_len = 0; /* mname */ ret = _dns_encode_domain(context, (char *)data_context.ptr); if (ret < 0) { return -1; } - + rr_len += ret; data_context.ptr += strnlen((char *)(data_context.ptr), DNS_MAX_CNAME_LEN) + 1; /* rname */ @@ -1320,8 +1348,14 @@ static int _dns_encode_SOA(struct dns_context *context, struct dns_rrs *rrs) return -1; } + rr_len += ret; data_context.ptr += strnlen((char *)(data_context.ptr), DNS_MAX_CNAME_LEN) + 1; + if (rr_len > rrs->len) { + return -1; + } + rr_len += 20; + _dns_write_short(&rr_len_ptr, rr_len); if (_dns_left_len(context) < 20) { return -1; } @@ -1407,15 +1441,12 @@ static int _dns_encode_OPT(struct dns_context *context, struct dns_rrs *rrs) int opt_code = 0; int qclass = 0; char domain[DNS_MAX_CNAME_LEN]; - struct dns_data_context data_context; + struct dns_context data_context; int rr_len = 0; int ttl; struct dns_opt *dns_opt = NULL; - data_context.data = rrs->data; - data_context.ptr = rrs->data; - data_context.maxsize = rrs->len; - + _dns_init_context_by_rrs(rrs, &data_context); ret = _dns_get_rr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &opt_code, &qclass, &ttl, &rr_len); if (ret < 0) { return -1; @@ -1452,14 +1483,11 @@ static int _dns_get_opts_data_len(struct dns_packet *packet, struct dns_rrs *rrs int ttl; int ret; char domain[DNS_MAX_CNAME_LEN]; - struct dns_data_context data_context; + struct dns_context data_context; int rr_len = 0; for (i = 0; i < count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { - data_context.data = rrs->data; - data_context.ptr = rrs->data; - data_context.maxsize = rrs->len; - + _dns_init_context_by_rrs(rrs, &data_context); ret = _dns_get_rr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &opt_code, &qclass, &ttl, &rr_len); if (ret < 0) { return -1; @@ -1479,6 +1507,7 @@ static int _dns_encode_opts(struct dns_packet *packet, struct dns_context *conte unsigned int rcode = 0; int rr_len = 0; int payloadsize = packet->payloadsize; + unsigned char *rr_len_ptr = NULL; rr_len = _dns_get_opts_data_len(packet, rrs, count); if (rr_len < 0) { @@ -1489,7 +1518,7 @@ static int _dns_encode_opts(struct dns_packet *packet, struct dns_context *conte payloadsize = DNS_DEFAULT_PACKET_SIZE; } - ret = _dns_encode_rr_head(context, "", DNS_T_OPT, payloadsize, rcode, rr_len); + ret = _dns_encode_rr_head(context, "", DNS_T_OPT, payloadsize, rcode, rr_len, &rr_len_ptr); if (ret < 0) { return -1; } @@ -1727,33 +1756,47 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) return -1; } } break; - case DNS_T_OPT: { - unsigned char *opt_start = context->ptr; - ret = _dns_decode_opt(context, type, ttl, rr_len); + // case DNS_T_OPT: { + // unsigned char *opt_start = context->ptr; + // ret = _dns_decode_opt(context, type, ttl, rr_len); + // if (ret < 0) { + // tlog(TLOG_ERROR, "decode opt failed, %s", domain); + // return -1; + // } + + // if (context->ptr - opt_start != rr_len) { + // tlog(TLOG_ERROR, "opt length mismatch, %s\n", domain); + // return -1; + // } + + // dns_set_OPT_payload_size(packet, qclass); + // } break; + default: { + unsigned char raw_data[1024]; + if (_dns_left_len(context) < rr_len || rr_len >= sizeof(raw_data)) { + tlog(TLOG_DEBUG, "length mismatch\n"); + return -1; + } + + ret = _dns_decode_raw(context, raw_data, rr_len); if (ret < 0) { - tlog(TLOG_ERROR, "decode opt failed, %s", domain); + tlog(TLOG_ERROR, "decode A failed, %s", domain); return -1; } - if (context->ptr - opt_start != rr_len) { - tlog(TLOG_ERROR, "opt length mitchmatch, %s\n", domain); + ret = _dns_add_RAW(packet, type, qtype, domain, ttl, raw_data, rr_len); + if (ret < 0) { + tlog(TLOG_ERROR, "add raw failed, %s", domain); return -1; } - - dns_set_OPT_payload_size(packet, qclass); - } break; - default: - if (_dns_left_len(context) < rr_len) { - tlog(TLOG_DEBUG, "length mitchmatch\n"); - return -1; - } - context->ptr += rr_len; + tlog(TLOG_DEBUG, "DNS type = %d not supported", qtype); break; } + } if (context->ptr - start != rr_len) { - tlog(TLOG_ERROR, "length mitchmatch , %s, %ld:%d", domain, (long)(context->ptr - start), rr_len); + tlog(TLOG_ERROR, "length mismatch, %s, %ld:%d", domain, (long)(context->ptr - start), rr_len); return -1; } @@ -1766,12 +1809,9 @@ static int _dns_encode_qd(struct dns_context *context, struct dns_rrs *rrs) int qtype = 0; int qclass = 0; char domain[DNS_MAX_CNAME_LEN]; - struct dns_data_context data_context; - - data_context.data = rrs->data; - data_context.ptr = rrs->data; - data_context.maxsize = rrs->len; + struct dns_context data_context; + _dns_init_context_by_rrs(rrs, &data_context); ret = _dns_get_qr_head(&data_context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass); if (ret < 0) { return -1; @@ -1810,6 +1850,10 @@ static int _dns_encode_an(struct dns_context *context, struct dns_rrs *rrs) } break; default: + ret = _dns_encode_raw(context, rrs); + if (ret < 0) { + return -1; + } break; } @@ -1965,6 +2009,7 @@ int dns_decode(struct dns_packet *packet, int maxsize, unsigned char *data, int context.packet = packet; context.ptr = data; context.maxsize = size; + context.namedict = &packet->namedict; ret = dns_packet_init(packet, maxsize, head); if (ret != 0) { @@ -1982,6 +2027,8 @@ int dns_decode(struct dns_packet *packet, int maxsize, unsigned char *data, int return -1; } + packet->size = context.ptr - context.data + sizeof(*packet); + return 0; } @@ -1989,12 +2036,15 @@ int dns_encode(unsigned char *data, int size, struct dns_packet *packet) { int ret = 0; struct dns_context context; + struct dns_packet_dict namedict; memset(&context, 0, sizeof(context)); + memset(&namedict, 0, sizeof(namedict)); context.data = data; context.packet = packet; context.ptr = data; context.maxsize = size; + context.namedict = &namedict; ret = _dns_encode_head(&context); if (ret < 0) { @@ -2014,8 +2064,146 @@ int dns_encode(unsigned char *data, int size, struct dns_packet *packet) return context.ptr - context.data; } +static int _dns_update_an(struct dns_context *context, dns_rr_type type, struct dns_update_param *param) +{ + int ret; + int qtype = 0; + int qclass = 0; + int ttl; + int rr_len = 0; + char domain[DNS_MAX_CNAME_LEN]; + unsigned char *start; + + /* decode rr head */ + ret = _dns_decode_rr_head(context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass, &ttl, &rr_len); + if (ret < 0) { + tlog(TLOG_DEBUG, "decode head failed."); + return -1; + } + + start = context->ptr; + switch (qtype) { + case DNS_T_OPT: + break; + default: { + unsigned char *ttl_ptr = start - sizeof(int) - sizeof(short); + _dns_write_int(&ttl_ptr, param->ip_ttl); + } break; + } + context->ptr += rr_len; + if (context->ptr - start != rr_len) { + tlog(TLOG_ERROR, "length mismatch , %s, %ld:%d", domain, (long)(context->ptr - start), rr_len); + return -1; + } + + return 0; +} + + +static int _dns_update_body(struct dns_context *context, struct dns_update_param *param) +{ + struct dns_packet *packet = context->packet; + struct dns_head *head = &packet->head; + int i = 0; + int ret = 0; + int count = 0; + + count = head->qdcount; + head->qdcount = 0; + for (i = 0; i < count; i++) { + char domain[DNS_MAX_CNAME_LEN]; + int qtype; + int qclass; + int len; + len = _dns_decode_qr_head(context, domain, DNS_MAX_CNAME_LEN, &qtype, &qclass); + if (len < 0) { + tlog(TLOG_DEBUG, "update qd failed."); + return -1; + } + } + + count = head->ancount; + head->ancount = 0; + for (i = 0; i < count; i++) { + ret = _dns_update_an(context, DNS_RRS_AN, param); + if (ret < 0) { + tlog(TLOG_DEBUG, "update an failed."); + return -1; + } + } + + count = head->nscount; + head->nscount = 0; + for (i = 0; i < count; i++) { + ret = _dns_update_an(context, DNS_RRS_NS, param); + if (ret < 0) { + tlog(TLOG_DEBUG, "update ns failed."); + return -1; + } + } + + count = head->nrcount; + head->nrcount = 0; + for (i = 0; i < count; i++) { + ret = _dns_update_an(context, DNS_RRS_NR, param); + if (ret < 0) { + tlog(TLOG_DEBUG, "update nr failed."); + return -1; + } + } + + return 0; +} + +int _dns_update_id(unsigned char *data, int size, struct dns_update_param *param) { + + unsigned char *ptr = data; + _dns_write_short(&ptr, param->id); + return 0; +} + +int dns_packet_update(unsigned char *data, int size, struct dns_update_param *param) +{ + struct dns_packet packet; + int maxsize = sizeof(packet); + struct dns_head *head = &packet.head; + struct dns_context context; + int ret = 0; + + memset(&context, 0, sizeof(context)); + memset(&packet, 0, sizeof(packet)); + + context.data = data; + context.packet = &packet; + context.ptr = data; + context.maxsize = size; + + ret = dns_packet_init(&packet, maxsize, head); + if (ret != 0) { + return -1; + } + + ret = _dns_decode_head(&context); + if (ret < 0) { + return -1; + } + + ret = _dns_update_id(data, size, param); + if (ret < 0) { + return -1; + } + + ret = _dns_update_body(&context, param); + if (ret < 0) { + tlog(TLOG_DEBUG, "decode body failed.\n"); + return -1; + } + + return 0; +} + #if 0 -static void dns_debug(void) +void dns_debug(void) { unsigned char data[1024]; int len; @@ -2042,8 +2230,14 @@ static void dns_debug(void) tlog(TLOG_ERROR, "encode failed."); } - fd = open("dns-cmp.bin", O_CREAT | O_TRUNC | O_RDWR); + fd = open("dns-cmp.bin", O_CREAT | O_TRUNC | O_RDWR, 0660); write(fd, data, len); close(fd); + + packet = (struct dns_packet *)buff; + if (dns_decode(packet, 4096, data, len) != 0) { + tlog(TLOG_ERROR, "decode failed.\n"); + } + } #endif diff --git a/src/dns.h b/src/dns.h index 592800a..66ab48c 100644 --- a/src/dns.h +++ b/src/dns.h @@ -113,11 +113,15 @@ struct dns_head { unsigned short nrcount; /* number of addititional resource entries */ } __attribute__((packed, aligned(2))); -struct dns_rrs { - unsigned short next; - unsigned short len; - dns_type_t type; - unsigned char data[0]; +#define DNS_PACKET_DICT_SIZE 16 +struct dns_packet_dict_item { + unsigned pos; + unsigned int hash; +}; + +struct dns_packet_dict { + short dict_count; + struct dns_packet_dict_item names[DNS_PACKET_DICT_SIZE]; }; /* packet haed */ @@ -130,21 +134,24 @@ struct dns_packet { unsigned short optcount; unsigned short optional; unsigned short payloadsize; + struct dns_packet_dict namedict; int size; int len; unsigned char data[0]; }; -/* RRS encode/decode context */ -struct dns_data_context { - unsigned char *data; - unsigned char *ptr; - unsigned int maxsize; +struct dns_rrs { + struct dns_packet *packet; + unsigned short next; + unsigned short len; + dns_type_t type; + unsigned char data[0]; }; /* packet encode/decode context */ struct dns_context { struct dns_packet *packet; + struct dns_packet_dict *namedict; unsigned char *data; unsigned int maxsize; unsigned char *ptr; @@ -234,4 +241,12 @@ int dns_encode(unsigned char *data, int size, struct dns_packet *packet); int dns_packet_init(struct dns_packet *packet, int size, struct dns_head *head); +struct dns_update_param { + int id; + int ip_ttl; + int cname_ttl; +}; + +int dns_packet_update(unsigned char *data, int size, struct dns_update_param *param); + #endif diff --git a/src/dns_conf.c b/src/dns_conf.c index 09cd724..7b08f20 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -1176,11 +1176,13 @@ static int _config_iplist_rule(char *subnet, enum address_rule rule) static int _config_qtype_soa(void *data, int argc, char *argv[]) { struct dns_qtype_soa_list *soa_list; + int i = 0; + if (argc <= 1) { return -1; } - for (int i = 1; i < argc; i++) { + for (i = 1; i < argc; i++) { soa_list = malloc(sizeof(*soa_list)); if (soa_list == NULL) { tlog(TLOG_ERROR, "cannot malloc memory"); diff --git a/src/util.c b/src/util.c index a481116..1a03837 100644 --- a/src/util.c +++ b/src/util.c @@ -1049,6 +1049,7 @@ void print_stack(void) { const size_t max_buffer = 30; void *buffer[max_buffer]; + int idx = 0; struct backtrace_state state = {buffer, buffer + max_buffer}; _Unwind_Backtrace(unwind_callback, &state); @@ -1058,7 +1059,7 @@ void print_stack(void) } tlog(TLOG_FATAL, "Stack:"); - for (int idx = 0; idx < frame_num; ++idx) { + for (idx = 0; idx < frame_num; ++idx) { const void *addr = buffer[idx]; const char *symbol = "";