浏览代码

dns-debug: support record fail packet for debugging

Nick Peng 3 年之前
父节点
当前提交
ac042e8bee
共有 10 个文件被更改,包括 363 次插入56 次删除
  1. 1 41
      src/dns.c
  2. 10 6
      src/dns_client.c
  3. 7 0
      src/dns_conf.c
  4. 4 0
      src/dns_conf.h
  5. 6 2
      src/dns_server.c
  6. 5 1
      src/smartdns.c
  7. 40 3
      src/tlog.c
  8. 6 0
      src/tlog.h
  9. 280 3
      src/util.c
  10. 4 0
      src/util.h

+ 1 - 41
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

+ 10 - 6
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));

+ 7 - 0
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:

+ 4 - 0
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);

+ 6 - 2
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;
 	}
 

+ 5 - 1
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;

+ 40 - 3
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);
 

+ 6 - 0
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);

+ 280 - 3
src/util.c

@@ -34,12 +34,13 @@
 #include <openssl/crypto.h>
 #include <openssl/ssl.h>
 #include <pthread.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
-#include <signal.h>
 #include <sys/prctl.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <sys/time.h>
 #include <sys/types.h>
 #include <time.h>
 #include <unistd.h>
@@ -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

+ 4 - 0
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 */