From 61a6e676bc331f38a091af8294f3e9a51c45d8b1 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Thu, 7 Dec 2023 22:27:29 +0800 Subject: [PATCH] dns: support parser SRV record. --- src/dns.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ src/dns.h | 5 ++ src/dns_client.c | 2 +- src/util.c | 18 ++++++ 4 files changed, 170 insertions(+), 1 deletion(-) diff --git a/src/dns.c b/src/dns.c index e17675c..d4c2606 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1104,6 +1104,60 @@ int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *timeout) return 0; } +int dns_add_SRV(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, int priority, int weight, + int port, const char *target) +{ + unsigned char data[DNS_MAX_CNAME_LEN]; + unsigned char *data_ptr = data; + + int target_len = 0; + if (target == NULL) { + target = ""; + } + + target_len = strnlen(target, DNS_MAX_CNAME_LEN) + 1; + memcpy(data_ptr, &priority, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + memcpy(data_ptr, &weight, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + memcpy(data_ptr, &port, sizeof(unsigned short)); + data_ptr += sizeof(unsigned short); + if (data_ptr - data + target_len >= DNS_MAX_CNAME_LEN) { + return -1; + } + + safe_strncpy((char *)data_ptr, target, target_len); + data_ptr += target_len; + + return _dns_add_RAW(packet, type, DNS_T_SRV, domain, ttl, data, data_ptr - data); +} + +int dns_get_SRV(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned short *priority, + unsigned short *weight, unsigned short *port, char *target, int target_size) +{ + unsigned char data[DNS_MAX_CNAME_LEN]; + unsigned char *ptr = data; + int len = sizeof(data); + + if (_dns_get_RAW(rrs, domain, maxsize, ttl, data, &len) != 0) { + return -1; + } + + if (len < 6) { + return -1; + } + + memcpy(priority, ptr, sizeof(unsigned short)); + ptr += sizeof(unsigned short); + memcpy(weight, ptr, sizeof(unsigned short)); + ptr += sizeof(unsigned short); + memcpy(port, ptr, sizeof(unsigned short)); + ptr += sizeof(unsigned short); + safe_strncpy(target, (char *)ptr, target_size); + + return 0; +} + int dns_add_HTTPS_start(struct dns_rr_nested *svcparam_buffer, struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, int priority, const char *target) { @@ -1613,6 +1667,26 @@ static int _dns_encode_CNAME(struct dns_context *context, struct dns_rrs *rrs) return 0; } +static int _dns_decode_SRV(struct dns_context *context, unsigned short *priority, unsigned short *weight, + unsigned short *port, char *target, int target_size) +{ + int ret = 0; + + if (_dns_left_len(context) < 6) { + return -1; + } + + *priority = _dns_read_short(&context->ptr); + *weight = _dns_read_short(&context->ptr); + *port = _dns_read_short(&context->ptr); + + ret = _dns_decode_domain(context, target, target_size); + if (ret < 0) { + return -1; + } + return 0; +} + static int _dns_decode_SOA(struct dns_context *context, struct dns_soa *soa) { int ret = 0; @@ -1702,6 +1776,54 @@ static int _dns_encode_SOA(struct dns_context *context, struct dns_rrs *rrs) return 0; } +static int _dns_encode_SRV(struct dns_context *context, struct dns_rrs *rrs) +{ + int ret = 0; + int qtype = 0; + int qclass = 0; + int ttl = 0; + char domain[DNS_MAX_CNAME_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; + } + + ret = _dns_encode_rr_head(context, domain, qtype, qclass, ttl, rr_len, &rr_len_ptr); + if (ret < 0) { + return -1; + } + + rr_len = 0; + + if (_dns_left_len(context) < 6) { + return -1; + } + + _dns_write_short(&context->ptr, *(unsigned short *)data_context.ptr); + data_context.ptr += 2; + _dns_write_short(&context->ptr, *(unsigned short *)data_context.ptr); + data_context.ptr += 2; + _dns_write_short(&context->ptr, *(unsigned short *)data_context.ptr); + data_context.ptr += 2; + rr_len += 6; + + 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; + + _dns_write_short(&rr_len_ptr, rr_len); + + return 0; +} + static int _dns_decode_opt_ecs(struct dns_context *context, struct dns_opt_ecs *ecs, int opt_len) { int len = 0; @@ -2277,6 +2399,24 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) return -1; } } break; + case DNS_T_SRV: { + unsigned short priority = 0; + unsigned short weight = 0; + unsigned short port = 0; + char target[DNS_MAX_CNAME_LEN]; + + ret = _dns_decode_SRV(context, &priority, &weight, &port, target, DNS_MAX_CNAME_LEN); + if (ret < 0) { + tlog(TLOG_DEBUG, "decode SRV failed, %s", domain); + return -1; + } + + ret = dns_add_SRV(packet, type, domain, ttl, priority, weight, port, target); + if (ret < 0) { + tlog(TLOG_DEBUG, "add SRV failed, %s", domain); + return -1; + } + } break; case DNS_T_OPT: { unsigned char *opt_start = context->ptr; ret = _dns_decode_opt(context, type, ttl, rr_len); @@ -2393,6 +2533,12 @@ static int _dns_encode_an(struct dns_context *context, struct dns_rrs *rrs) return -1; } break; + case DNS_T_SRV: + ret = _dns_encode_SRV(context, rrs); + if (ret < 0) { + return -1; + } + break; case DNS_T_HTTPS: ret = _dns_encode_HTTPS(context, rrs); if (ret < 0) { diff --git a/src/dns.h b/src/dns.h index 7cca5e6..c6cfec9 100644 --- a/src/dns.h +++ b/src/dns.h @@ -285,6 +285,11 @@ int dns_get_OPT_ECS(struct dns_rrs *rrs, struct dns_opt_ecs *ecs); int dns_add_OPT_TCP_KEEPALIVE(struct dns_packet *packet, unsigned short timeout); int dns_get_OPT_TCP_KEEPALIVE(struct dns_rrs *rrs, unsigned short *timeout); +int dns_add_SRV(struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, int priority, int weight, + int port, const char *target); +int dns_get_SRV(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, unsigned short *priority, + unsigned short *weight, unsigned short *port, char *target, int target_size); + /* the key must be added in orders, or dig will report FORMERR */ int dns_add_HTTPS_start(struct dns_rr_nested *svcparam_buffer, struct dns_packet *packet, dns_rr_type type, const char *domain, int ttl, int priority, const char *target); diff --git a/src/dns_client.c b/src/dns_client.c index 739e22c..d9c310f 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -3494,7 +3494,7 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, } } total_server++; - tlog(TLOG_DEBUG, "send query to server %s", server_info->ip); + tlog(TLOG_DEBUG, "send query to server %s:%d", server_info->ip, server_info->port); if (server_info->fd <= 0) { ret = _dns_client_create_socket(server_info); if (ret != 0) { diff --git a/src/util.c b/src/util.c index 91b9348..fe16d35 100644 --- a/src/util.c +++ b/src/util.c @@ -1913,6 +1913,24 @@ static int _dns_debug_display(struct dns_packet *packet) inet_ntop(AF_INET6, addr, req_host, sizeof(req_host)); printf("domain: %s AAAA: %s TTL:%d\n", name, req_host, ttl); } break; + case DNS_T_SRV: { + unsigned short priority = 0; + unsigned short weight = 0; + unsigned short port = 0; + int ret = 0; + + char name[DNS_MAX_CNAME_LEN] = {0}; + char target[DNS_MAX_CNAME_LEN]; + + ret = dns_get_SRV(rrs, name, DNS_MAX_CNAME_LEN, &ttl, &priority, &weight, &port, target, DNS_MAX_CNAME_LEN); + if (ret < 0) { + tlog(TLOG_DEBUG, "decode SRV failed, %s", name); + return -1; + } + + printf("domain: %s SRV: %s TTL: %d priority: %d weight: %d port: %d\n", name, target, ttl, priority, + weight, port); + } break; case DNS_T_HTTPS: { char name[DNS_MAX_CNAME_LEN] = {0}; char target[DNS_MAX_CNAME_LEN] = {0};