From ac042e8bee72ae04b60b9dca2f0c1716b992cf5a Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sun, 31 Jul 2022 15:49:10 +0800 Subject: [PATCH] dns-debug: support record fail packet for debugging --- src/dns.c | 42 +------ src/dns_client.c | 16 ++- src/dns_conf.c | 7 ++ src/dns_conf.h | 4 + src/dns_server.c | 8 +- src/smartdns.c | 6 +- src/tlog.c | 43 ++++++- src/tlog.h | 6 + src/util.c | 283 ++++++++++++++++++++++++++++++++++++++++++++++- src/util.h | 4 + 10 files changed, 363 insertions(+), 56 deletions(-) diff --git a/src/dns.c b/src/dns.c index a974c7c..bea0c80 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1785,7 +1785,7 @@ static int _dns_decode_an(struct dns_context *context, dns_rr_type type) } if (context->ptr - opt_start != rr_len) { - tlog(TLOG_ERROR, "opt length mismatch, %s\n", domain); + tlog(TLOG_DEBUG, "opt length mismatch, %s\n", domain); return -1; } @@ -2223,43 +2223,3 @@ int dns_packet_update(unsigned char *data, int size, struct dns_update_param *pa return 0; } - -#if 0 -void dns_debug(void) -{ - unsigned char data[1024]; - ssize_t len; - char buff[4096]; - - int fd = open("dns.bin", O_RDWR); - if (fd < 0) { - return; - } - len = read(fd, data, 1024); - close(fd); - if (len < 0) { - return; - } - - struct dns_packet *packet = (struct dns_packet *)buff; - if (dns_decode(packet, 4096, data, len) != 0) { - tlog(TLOG_ERROR, "decode failed.\n"); - } - - memset(data, 0, sizeof(data)); - len = dns_encode(data, 1024, packet); - if (len < 0) { - tlog(TLOG_ERROR, "encode failed."); - } - - 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_client.c b/src/dns_client.c index 8d40436..321675f 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -574,7 +574,8 @@ errout: return -1; } -static int _dns_client_add_to_pending_group(const char *group_name, char *server_ip, int port, dns_server_type_t server_type) +static int _dns_client_add_to_pending_group(const char *group_name, char *server_ip, int port, + dns_server_type_t server_type) { struct dns_server_pending *item = NULL; struct dns_server_pending *tmp = NULL; @@ -621,8 +622,8 @@ errout: } /* add server to group */ -static int _dns_client_add_to_group_pending(const char *group_name, char *server_ip, int port, dns_server_type_t server_type, - int ispending) +static int _dns_client_add_to_group_pending(const char *group_name, char *server_ip, int port, + dns_server_type_t server_type, int ispending) { struct dns_server_info *server_info = NULL; @@ -1591,8 +1592,11 @@ static int _dns_client_recv(struct dns_server_info *server_info, unsigned char * len = dns_decode(packet, DNS_PACKSIZE, inpacket, inpacket_len); if (len != 0) { char host_name[DNS_MAX_CNAME_LEN]; - tlog(TLOG_WARN, "decode failed, packet len = %d, tc = %d, id = %d, from = %s\n", inpacket_len, packet->head.tc, + tlog(TLOG_INFO, "decode failed, packet len = %d, tc = %d, id = %d, from = %s\n", inpacket_len, packet->head.tc, packet->head.id, gethost_by_addr(host_name, sizeof(host_name), from)); + if (dns_save_fail_packet) { + dns_packet_save(dns_save_fail_packet_dir, "client", host_name, inpacket, inpacket_len); + } return -1; } @@ -1721,7 +1725,7 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info) fd = socket(server_info->ai_family, SOCK_STREAM, 0); if (fd < 0) { - tlog(TLOG_ERROR, "create socket failed."); + tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno)); goto errout; } @@ -1732,7 +1736,7 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info) /* enable tcp fast open */ if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &yes, sizeof(yes)) != 0) { - tlog(TLOG_DEBUG, "enable TCP fast open failed."); + tlog(TLOG_DEBUG, "enable TCP fast open failed, %s", strerror(errno)); } setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); diff --git a/src/dns_conf.c b/src/dns_conf.c index c54a056..7c85a61 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -130,6 +130,9 @@ int dns_conf_ipset_timeout_enable; char dns_conf_user[DNS_CONF_USRNAME_LEN]; +int dns_save_fail_packet; +char dns_save_fail_packet_dir[DNS_MAX_PATH]; + /* ECS */ struct dns_edns_client_subnet dns_conf_ipv4_ecs; struct dns_edns_client_subnet dns_conf_ipv6_ecs; @@ -1920,6 +1923,8 @@ static struct config_item _config_item[] = { CONF_STRING("ca-file", (char *)&dns_conf_ca_file, DNS_MAX_PATH), CONF_STRING("ca-path", (char *)&dns_conf_ca_path, DNS_MAX_PATH), CONF_STRING("user", (char *)&dns_conf_user, sizeof(dns_conf_user)), + CONF_YESNO("debug-save-fail-packet", &dns_save_fail_packet), + CONF_STRING("debug-save-fail-packet-dir", (char *)&dns_save_fail_packet_dir, sizeof(dns_save_fail_packet_dir)), CONF_CUSTOM("conf-file", config_addtional_file, NULL), CONF_END(), }; @@ -2068,6 +2073,8 @@ static int _dns_conf_load_pre(void) _dns_ping_cap_check(); + safe_strncpy(dns_save_fail_packet_dir, SMARTDNS_DEBUG_DIR, sizeof(dns_save_fail_packet_dir)); + return 0; errout: diff --git a/src/dns_conf.h b/src/dns_conf.h index 8e62b99..8915b98 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -52,6 +52,7 @@ extern "C" { #define SMARTDNS_LOG_FILE "/var/log/smartdns/smartdns.log" #define SMARTDNS_AUDIT_FILE "/var/log/smartdns/smartdns-audit.log" #define SMARTDNS_CACHE_FILE "/tmp/smartdns.cache" +#define SMARTDNS_DEBUG_DIR "/tmp/smartdns" enum domain_rule { DOMAIN_RULE_FLAGS = 0, @@ -321,6 +322,9 @@ extern struct dns_edns_client_subnet dns_conf_ipv6_ecs; extern char dns_conf_sni_proxy_ip[DNS_MAX_IPLEN]; +extern int dns_save_fail_packet; +extern char dns_save_fail_packet_dir[DNS_MAX_PATH]; + void dns_server_load_exit(void); int dns_server_load_conf(const char *file); diff --git a/src/dns_server.c b/src/dns_server.c index 84e15db..7270856 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -557,8 +557,8 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) tm.min, tm.sec, tm.usec / 1000); tlog_printf(dns_audit, "%s %s query %s, type %d, time %lums, speed: %.1fms, result %s\n", req_time, req_host, - request->domain, request->qtype, get_tick_count() - request->send_tick, ((float)request->ping_time) / 10, - req_result); + request->domain, request->qtype, get_tick_count() - request->send_tick, + ((float)request->ping_time) / 10, req_result); } static void _dns_rrs_result_log(struct dns_server_post_context *context, struct dns_ip_address *addr_map) @@ -3533,6 +3533,7 @@ static int _dns_server_process_cache_packet(struct dns_request *request, struct request->ping_time = dns_cache->info.speed; if (dns_decode(context.packet, context.packet_maxlen, cache_packet->data, cache_packet->head.size) != 0) { + tlog(TLOG_ERROR, "decode cache failed, %d, %d", context.packet_maxlen, context.inpacket_len); return -1; } @@ -4159,6 +4160,9 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in if (decode_len < 0) { tlog(TLOG_DEBUG, "decode failed.\n"); ret = RECV_ERROR_INVALID_PACKET; + if (dns_save_fail_packet) { + dns_packet_save(dns_save_fail_packet_dir, "server", name, inpacket, inpacket_len); + } goto errout; } diff --git a/src/smartdns.c b/src/smartdns.c index bfbdbb3..cd61858 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -524,7 +524,7 @@ int main(int argc, char *argv[]) sigemptyset(&empty_sigblock); sigprocmask(SIG_SETMASK, &empty_sigblock, NULL); - while ((opt = getopt(argc, argv, "fhc:p:Svx")) != -1) { + while ((opt = getopt(argc, argv, "fhc:p:SvxN:")) != -1) { switch (opt) { case 'f': is_forground = 1; @@ -545,6 +545,10 @@ int main(int argc, char *argv[]) _show_version(); return 0; break; +#ifdef DEBUG + case 'N': + return dns_packet_debug(optarg); +#endif case 'h': _help(); return 1; diff --git a/src/tlog.c b/src/tlog.c index c539dc3..0025cfd 100644 --- a/src/tlog.c +++ b/src/tlog.c @@ -79,6 +79,7 @@ struct tlog_log { int zip_pid; int multi_log; int logscreen; + int no_write_log; int segment_log; int max_line_size; @@ -216,7 +217,6 @@ static int _tlog_mkdir(const char *path) } if (mkdir(path_c, 0750) != 0) { - fprintf(stderr, "create directory %s failed, %s\n", path_c, strerror(errno)); return -1; } @@ -1130,6 +1130,10 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen) unused = write(STDOUT_FILENO, buff, bufflen); } + if (log->no_write_log) { + return 0; + } + /* if log file size exceeds threshold, start to compress */ if (log->multi_log && log->fd > 0) { log->filesize = lseek(log->fd, 0, SEEK_END); @@ -1160,7 +1164,15 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen) char logfile[PATH_MAX * 2]; if (_tlog_mkdir(log->logdir) != 0) { - fprintf(stderr, "create log dir %s failed.\n", log->logdir); + if (print_errmsg == 0) { + return -1; + } + print_errmsg = 0; + fprintf(stderr, "create log dir %s failed, %s\n", log->logdir, strerror(errno)); + if (errno == EACCES && log->logscreen == 0) { + fprintf(stderr, "no permission to write log file, output log to console\n"); + tlog_logscreen_only(log, 1); + } return -1; } snprintf(logfile, sizeof(logfile), "%s/%s", log->logdir, log->logname); @@ -1574,11 +1586,26 @@ static void _tlog_log_setlogscreen(struct tlog_log *log, int enable) log->logscreen = (enable != 0) ? 1 : 0; } +static void _tlog_log_setlogscreen_only(struct tlog_log *log, int enable) +{ + if (log == NULL) { + return; + } + + log->logscreen = (enable != 0) ? 1 : 0; + log->no_write_log = (enable != 0) ? 1 : 0; +} + void tlog_setlogscreen(int enable) { _tlog_log_setlogscreen(tlog.root, enable); } +void tlog_setlogscreen_only(int enable) +{ + _tlog_log_setlogscreen_only(tlog.root, enable); +} + int tlog_write_log(char *buff, int bufflen) { if (unlikely(tlog.root == NULL)) { @@ -1597,6 +1624,15 @@ void tlog_logscreen(tlog_log *log, int enable) _tlog_log_setlogscreen(log, enable); } +void tlog_logscreen_only(tlog_log *log, int enable) +{ + if (log == NULL) { + return; + } + + _tlog_log_setlogscreen_only(log, enable); +} + int tlog_reg_output_func(tlog_log *log, tlog_output_func output) { if (log == NULL) { @@ -1830,13 +1866,13 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize } tlog_reg_output_func(log, _tlog_root_write_log); + tlog.root = log; ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL); if (ret != 0) { fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno)); goto errout; } - tlog.root = log; if (flag & TLOG_SUPPORT_FORK) { pthread_atfork(&tlog_fork_prepare, &tlog_fork_parent, &tlog_fork_child); } @@ -1852,6 +1888,7 @@ errout: pthread_cond_destroy(&tlog.cond); pthread_mutex_destroy(&tlog.lock); tlog.run = 0; + tlog.root = NULL; _tlog_close(log, 1); diff --git a/src/tlog.h b/src/tlog.h index 574d2c0..fda31d3 100644 --- a/src/tlog.h +++ b/src/tlog.h @@ -104,6 +104,9 @@ extern void tlog_set_logfile(const char *logfile); /* enalbe log to screen */ extern void tlog_setlogscreen(int enable); +/* output log to screen only */ +extern void tlog_setlogscreen_only(int enable); + /* enalbe early log to screen */ extern void tlog_set_early_printf(int enable); @@ -184,6 +187,9 @@ extern int tlog_vprintf(tlog_log *log, const char *format, va_list ap); /* enalbe log to screen */ extern void tlog_logscreen(tlog_log *log, int enable); +/* enalbe log to screen only*/ +extern void tlog_logscreen_only(tlog_log *log, int enable); + /* register output callback */ typedef int (*tlog_output_func)(struct tlog_log *log, const char *buff, int bufflen); extern int tlog_reg_output_func(tlog_log *log, tlog_output_func output); diff --git a/src/util.c b/src/util.c index c3ef5fd..f77885d 100644 --- a/src/util.c +++ b/src/util.c @@ -34,12 +34,13 @@ #include #include #include +#include #include #include -#include #include #include #include +#include #include #include #include @@ -79,6 +80,8 @@ #define NETLINK_ALIGN(len) (((len) + 3) & ~(3)) #define BUFF_SZ 1024 +#define PACKET_BUF_SIZE 8192 +#define PACKET_MAGIC 0X11040918 struct ipset_netlink_attr { unsigned short len; @@ -641,7 +644,7 @@ unsigned char *SSL_SHA256(const unsigned char *d, size_t n, unsigned char *md) md = m; } - EVP_MD_CTX* ctx = EVP_MD_CTX_create(); + EVP_MD_CTX *ctx = EVP_MD_CTX_create(); if (ctx == NULL) { return NULL; } @@ -1159,7 +1162,7 @@ void bug_ext(const char *file, int line, const char *func, const char *errfmt, . int write_file(const char *filename, void *data, int data_len) { - int fd = open(filename, O_WRONLY|O_CREAT, 0644); + int fd = open(filename, O_WRONLY | O_CREAT, 0644); if (fd < 0) { return -1; } @@ -1178,3 +1181,277 @@ errout: return -1; } + +int dns_packet_save(const char *dir, const char *type, const char *from, const void *packet, int packet_len) +{ + char *data = NULL; + int data_len = 0; + char filename[BUFF_SZ]; + char time_s[BUFF_SZ]; + int ret = -1; + + struct tm *ptm; + struct tm tm; + struct timeval tmval; + struct stat sb; + + if (stat(dir, &sb) != 0) { + mkdir(dir, 0750); + } + + if (gettimeofday(&tmval, NULL) != 0) { + return -1; + } + + ptm = localtime_r(&tmval.tv_sec, &tm); + if (ptm == NULL) { + return -1; + } + + ret = snprintf(time_s, sizeof(time_s) - 1, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d", ptm->tm_year + 1900, + ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, (int)(tmval.tv_usec / 1000)); + ret = snprintf(filename, sizeof(filename) - 1, "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%.1d.packet", dir, type, + ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec, + (int)(tmval.tv_usec / 100000)); + + data = malloc(PACKET_BUF_SIZE); + if (data == NULL) { + return -1; + } + + data_len = snprintf(data, PACKET_BUF_SIZE, + "type: %s\n" + "from: %s\n" + "time: %s\n" + "packet-len: %d\n", + type, from, time_s, packet_len); + if (data_len <= 0 || data_len >= PACKET_BUF_SIZE) { + goto out; + } + + data[data_len] = 0; + data_len++; + uint32_t magic = htonl(PACKET_MAGIC); + memcpy(data + data_len, &magic, sizeof(magic)); + data_len += sizeof(magic); + int len_in_h = htonl(packet_len); + memcpy(data + data_len, &len_in_h, sizeof(len_in_h)); + data_len += 4; + memcpy(data + data_len, packet, packet_len); + data_len += packet_len; + + ret = write_file(filename, data, data_len); + if (ret != 0) { + goto out; + } + + ret = 0; +out: + if (data) { + free(data); + } + + return ret; +} + +#ifdef DEBUG +struct _dns_read_packet_info { + int data_len; + int message_len; + char *message; + int packet_len; + uint8_t *packet; + uint8_t data[0]; +}; + +static struct _dns_read_packet_info *_dns_read_packet_file(const char *packet_file) +{ + struct _dns_read_packet_info *info = NULL; + int fd = 0; + int len = 0; + int message_len = 0; + uint8_t *ptr = NULL; + + info = malloc(sizeof(struct _dns_read_packet_info) + PACKET_BUF_SIZE); + fd = open(packet_file, O_RDONLY); + if (fd < 0) { + printf("open file %s failed, %s\n", packet_file, strerror(errno)); + goto errout; + } + + len = read(fd, info->data, PACKET_BUF_SIZE); + if (len < 0) { + printf("read file %s failed, %s\n", packet_file, strerror(errno)); + goto errout; + } + + message_len = strnlen((char *)info->data, PACKET_BUF_SIZE); + if (message_len >= 512 || message_len >= len) { + printf("invalid packet file, bad message len\n"); + goto errout; + } + + info->message_len = message_len; + info->message = (char *)info->data; + + ptr = info->data + message_len + 1; + uint32_t magic = 0; + if (ptr - (uint8_t *)info + sizeof(magic) >= (size_t)len) { + printf("invalid packet file, magic length is invalid.\n"); + goto errout; + } + + memcpy(&magic, ptr, sizeof(magic)); + if (magic != htonl(PACKET_MAGIC)) { + printf("invalid packet file, bad magic\n"); + goto errout; + } + ptr += sizeof(magic); + + uint32_t packet_len = 0; + if (ptr - info->data + sizeof(packet_len) >= (size_t)len) { + printf("invalid packet file, packet length is invalid.\n"); + goto errout; + } + + memcpy(&packet_len, ptr, sizeof(packet_len)); + packet_len = ntohl(packet_len); + ptr += sizeof(packet_len); + if (packet_len != (size_t)len - (ptr - info->data)) { + printf("invalid packet file, packet length is invalid\n"); + goto errout; + } + + info->packet_len = packet_len; + info->packet = ptr; + + close(fd); + return info; +errout: + + if (fd > 0) { + close(fd); + } + + if (info) { + free(info); + } + + return NULL; +} + +static int _dns_debug_display(struct dns_packet *packet) +{ + int i = 0; + int j = 0; + int ttl = 0; + struct dns_rrs *rrs = NULL; + int rr_count = 0; + char req_host[MAX_IP_LEN]; + + for (j = 1; j < DNS_RRS_END; j++) { + rrs = dns_get_rrs_start(packet, j, &rr_count); + printf("section: %d\n", j); + for (i = 0; i < rr_count && rrs; i++, rrs = dns_get_rrs_next(packet, rrs)) { + switch (rrs->type) { + case DNS_T_A: { + unsigned char addr[4]; + char name[DNS_MAX_CNAME_LEN] = {0}; + /* get A result */ + dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); + req_host[0] = '\0'; + inet_ntop(AF_INET, addr, req_host, sizeof(req_host)); + printf("domain: %s A: %s TTL: %d\n", name, req_host, ttl); + } break; + case DNS_T_AAAA: { + unsigned char addr[16]; + char name[DNS_MAX_CNAME_LEN] = {0}; + dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); + req_host[0] = '\0'; + 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_NS: { + char cname[DNS_MAX_CNAME_LEN]; + char name[DNS_MAX_CNAME_LEN] = {0}; + dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); + printf("domain: %s TTL: %d NS: %s\n", name, ttl, cname); + } break; + case DNS_T_CNAME: { + char cname[DNS_MAX_CNAME_LEN]; + char name[DNS_MAX_CNAME_LEN] = {0}; + if (dns_conf_force_no_cname) { + continue; + } + + dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); + printf("domain: %s TTL: %d CNAME: %s\n", name, ttl, cname); + } break; + case DNS_T_SOA: { + char name[DNS_MAX_CNAME_LEN] = {0}; + struct dns_soa soa; + dns_get_SOA(rrs, name, 128, &ttl, &soa); + printf("domain: %s SOA: mname: %s, rname: %s, serial: %d, refresh: %d, retry: %d, expire: " + "%d, minimum: %d", + name, soa.mname, soa.rname, soa.serial, soa.refresh, soa.retry, soa.expire, soa.minimum); + } break; + default: + break; + } + } + printf("\n"); + } + + return 0; +} + +int dns_packet_debug(const char *packet_file) +{ + struct _dns_read_packet_info *info = NULL; + char buff[DNS_PACKSIZE]; + + tlog_setlogscreen_only(1); + tlog_setlevel(TLOG_DEBUG); + + info = _dns_read_packet_file(packet_file); + if (info == NULL) { + goto errout; + } + + const char *send_env = getenv("SMARTDNS_DEBUG_SEND"); + if (send_env != NULL) { + char ip[32]; + int port = 53; + if (parse_ip(send_env, ip, &port) == 0) { + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd > 0) { + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_port = htons(port); + server.sin_addr.s_addr = inet_addr(ip); + sendto(sockfd, info->packet, info->packet_len, 0, (struct sockaddr *)&server, sizeof(server)); + close(sockfd); + } + } + } + + struct dns_packet *packet = (struct dns_packet *)buff; + if (dns_decode(packet, DNS_PACKSIZE, info->packet, info->packet_len) != 0) { + printf("decode failed.\n"); + goto errout; + } + + _dns_debug_display(packet); + + free(info); + return 0; + +errout: + if (info) { + free(info); + } + + return -1; +} + +#endif \ No newline at end of file diff --git a/src/util.h b/src/util.h index 81a5787..4f1c2be 100644 --- a/src/util.h +++ b/src/util.h @@ -126,6 +126,10 @@ void print_stack(void); int write_file(const char *filename, void *data, int data_len); +int dns_packet_save(const char *dir, const char *type, const char *from, const void *packet, int packet_len); + +int dns_packet_debug(const char *packet_file); + #ifdef __cplusplus } #endif /*__cplusplus */