一款简单易用的 DNS 发包工具

时间:2022-11-08 08:54:25

1)执行make,编译
2)修改client.conf
3)根据需要修改要发送的域名 改query_domain
4)根据需要修改发包数量,时间间隔等 改start.sh
5)执行./start.sh
6)查看 ./start.sh print
7)关闭 ./start.sh kill

容易犯的错误
1)client.conf 配置没有改对。源ip和目的ip不能相同。一般来说目的比源大
2)域名后面没有加. 只支持A
3)start.sh路径不对

Makefile

senddns : senddns.o
gcc -g -o senddns senddns.c
.PHONY : clean
clean :
rm -f *.o senddns

startdns.sh

if [ "kill" = $1 ]
then
killall senddns
elif [ "print" = $1 ]
then
ps -ef|grep senddns|grep -v grep
else
for((i=1;i<=1;i++))
do
/root/senddns/senddns @client.conf 100 1000000 1000&
done
fi

client.conf

#start source ip
--start_sip 192.168.100.58

#stop source ip
--end_sip 192.168.100.60

#exclude source IP list, space to separate
--exclude_sip 192.168.100.50 192.168.100.51 192.168.100.52 192.168.100.100

#start source port, must be more than 1024
--start_sport 2000

#stop source port, must be less than 65535
--end_sport 60000

#direction IP
--targetip 192.168.100.100

#direction port
--targetport 53

#query domain list file
--queryfile query_domain.txt

query_domain.txt

www.sabadeda.com. A
bbs.sabadeda.com. A
edu.paidai.com. A
news.paidai.com. A
z.paidai.com. A
x.paidai.com. A
my.paidai.com. A
bbs.paidai.com. A

senddns.c

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int debug_flag = 0;
void sig_proc(int signum);

#define LOG_DEBUG(fmt, args...) \
do { if (debug_flag) printf(fmt,##args); } while (0)


typedef struct ip_hdr
{
unsigned char h_verlen;
unsigned char tos;
unsigned short total_len;
unsigned short ident;
unsigned short frag_and_flags;
unsigned char ttl;
unsigned char proto;
unsigned short checksum;
unsigned int sourceIP;
unsigned int destIP;
}IP_HEADER;

typedef struct udp_hdr
{
unsigned short uh_sport;
unsigned short uh_dport;
unsigned short uh_length;
unsigned short uh_checksum;
}UDP_HEADER;

typedef struct usd_hdr
{
unsigned long saddr;
unsigned long daddr;
char mbz;
char ptcl;
unsigned short udpl;
}USD_HEADER;

typedef struct dns
{
unsigned short tid;
unsigned short flags;
unsigned short queries;
unsigned short answers;
unsigned short auth;
unsigned short additional;
}DNS_HEADER;

typedef struct query
{
char * name;
unsigned short type;
unsigned short class;
}QUERY_HEADER;

unsigned long long sleeptime, starttime, outcount = 0, max_pkts=0;
int pkt_then_sleep = 0;
FILE *fp_query = NULL;

struct send_conf {
unsigned int threadnum;
unsigned int start_sip;
unsigned int end_sip;
unsigned int exclude_sip_num;
unsigned int exclude_sip_list[1024];
unsigned int start_sport;
unsigned int end_sport;
unsigned int targetip;
unsigned int targetport;
unsigned char query_file[1024];
} send_config;

IP_HEADER IpHeader;
UDP_HEADER UdpHeader;
USD_HEADER UsdHeader;
DNS_HEADER DnsHeader;
QUERY_HEADER QueryHeader;
char query_domain[1024] = {0};

unsigned short CheckSum(unsigned short * buffer, int size)
{
unsigned long cksum = 0;

while (size > 1)
{
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size)
{
cksum += *(unsigned char *) buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);

return (unsigned short) (~cksum);
}

void MySleep(unsigned int micro_second)
{
struct timeval t_timeval;

t_timeval.tv_sec = 0;
t_timeval.tv_usec = micro_second;

select( 0, NULL, NULL, NULL, &t_timeval );
}

void PaddingQuery( char *buffer, char *domain_name)
{
char *tmp = (char *)malloc(strlen(domain_name)+1);
if( NULL == tmp )
{
fprintf( stderr, "malloc for query error: %s\n", strerror(errno) );
exit(-1);
}
memset( tmp, 0, strlen(domain_name)+1 );
memset(query_domain, 0, sizeof(query_domain));

strncpy(tmp, domain_name, strlen(domain_name));
strncpy(query_domain, tmp, sizeof(query_domain));

int length_pos = 0;
int loop_num = 1;

char *token = strtok( tmp, "." );
while( NULL != token )
{
if( loop_num == 1 )
{
length_pos = 0;
memset( buffer, strlen(token), 1 );
strcpy( buffer+length_pos+1, token );

length_pos = length_pos + strlen(token) + 1;
}
else
{
memset( buffer+length_pos, strlen(token), 1 );
strcpy( buffer+length_pos+1, token );

length_pos = length_pos + strlen(token) + 1;
}

token = strtok( NULL, "." );
loop_num ++;
}

free(tmp);
}

void Init( char *buffer, int buffer_size, struct send_conf *sndcfg, char *domain_name)
{

// whole udp packet except ip header and usd_header
int i;
int sip;
int total_packet_len = buffer_size;
int total_len;
static int srcip_cnt = 0;

// udp packet with usd_header
int udp_with_usd_len = total_packet_len - sizeof(IP_HEADER) + sizeof(USD_HEADER);

char *udp_packet = (char *)malloc( udp_with_usd_len );
if( NULL == udp_packet )
{
fprintf( stderr, "malloc udp packet error: %s\n", strerror(errno) );
exit(-1);
}
memset( udp_packet, 0, udp_with_usd_len );

IpHeader.h_verlen = (4<<4 | sizeof(IpHeader)/sizeof(unsigned int));
IpHeader.tos = 0;
IpHeader.total_len = htons( total_packet_len );
IpHeader.ident = rand() % 30000 + 9876;
IpHeader.frag_and_flags = 0x0000;
IpHeader.ttl = 64;
IpHeader.proto = IPPROTO_UDP;
IpHeader.checksum = 0x0000;

// 1.1.1.1 ----------- 250.250.250.250
// IpHeader.sourceIP = htonl(rand( ) % 4193909242 + 16843009);

sip = sndcfg->start_sip + srcip_cnt%(sndcfg->end_sip - sndcfg->start_sip);

get_sip_again:

for (i = 0; i < sndcfg->exclude_sip_num; i++) {
if (sip == sndcfg->exclude_sip_list[i]) {
srcip_cnt++;
if (sip < sndcfg->end_sip) {
sip++;
} else {
sip = sndcfg->start_sip;
}
goto get_sip_again;
}
}

srcip_cnt++;

IpHeader.sourceIP = htonl(sip);
IpHeader.destIP = htonl(sndcfg->targetip);

UdpHeader.uh_sport = htons(sndcfg->start_sport + outcount%(sndcfg->end_sport - sndcfg->start_sport));
UdpHeader.uh_dport = htons(sndcfg->targetport);

UdpHeader.uh_length = htons(total_packet_len - sizeof(IP_HEADER) );
UdpHeader.uh_checksum = 0x0000;

UsdHeader.saddr = IpHeader.sourceIP;
UsdHeader.daddr = IpHeader.destIP;
UsdHeader.mbz = 0x00;
UsdHeader.ptcl = IPPROTO_UDP;
UsdHeader.udpl = UdpHeader.uh_length;

DnsHeader.tid = rand() % 40000 + 12345;
DnsHeader.flags = 0x0001;
DnsHeader.queries = 0x0100;
DnsHeader.answers = 0x0000;
DnsHeader.auth = 0x0000;
DnsHeader.additional = 0x0000;

QueryHeader.type = 0x0100;
QueryHeader.class = 0x0100;

memcpy( (void*)buffer, (void*)&IpHeader, sizeof(IpHeader) );
IpHeader.checksum = CheckSum( (unsigned short *) buffer, sizeof(IpHeader) );
memcpy( (void*)buffer, (void*)&IpHeader, sizeof(IpHeader) );

memcpy( udp_packet, (void*)&UsdHeader, sizeof(UsdHeader) );

memcpy( udp_packet+sizeof(UsdHeader), &UdpHeader, sizeof(UdpHeader) );

memcpy( udp_packet+sizeof(UsdHeader)+sizeof(UdpHeader), &DnsHeader, sizeof(DnsHeader) );

PaddingQuery( udp_packet+sizeof(UsdHeader)+sizeof(UdpHeader)+sizeof(DnsHeader), domain_name );

memcpy( udp_packet+sizeof(UsdHeader)+sizeof(UdpHeader)+sizeof(DnsHeader)+strlen(domain_name)+1, ((char*)&QueryHeader)+sizeof(char *), sizeof(QueryHeader)-sizeof(char*) );

UdpHeader.uh_checksum = CheckSum( (unsigned short *)udp_packet, udp_with_usd_len );
memcpy( udp_packet+sizeof(UsdHeader), &UdpHeader, sizeof(UdpHeader) );

memcpy( buffer+sizeof(IpHeader), udp_packet+sizeof(UsdHeader), udp_with_usd_len - sizeof(USD_HEADER) );

free( udp_packet );
}

void one_pkt_info_show(int outcount, int srcip, int sport, int dstip, int dport, char *domain)
{
struct in_addr saddr,daddr;
char sip[64] = {0};
char dip[64] = {0};

memset(&saddr, 0, sizeof(saddr));
memset(&daddr, 0, sizeof(daddr));

printf("send dns pkts------------ %d -------------------\n", outcount );

saddr.s_addr = srcip;
daddr.s_addr = dstip;
strncpy(sip, inet_ntoa(saddr), 16);
strncpy(dip, inet_ntoa(daddr), 16);
printf("%s[%d] ==> %s[%d] %s\n\n", sip, sport, dip, dport, domain);
}

int parse_query_file(FILE *fp, struct send_conf *sndcfg, char *domain_name, int size)
{
char *p;
char *token;
char *saveptr;
char readline[4096] = {0};

read_again:
if (fgets(readline, sizeof(readline), fp) != NULL) {
p = readline;
while(*p == ' ' || *p == '\t') p++;
if (*p == '#') {
goto read_again;
}

if ((token = strtok_r(p, " ", &saveptr)) != NULL) {
strncpy(domain_name, token, size);
p = NULL;
}
} else {
rewind(fp);
goto read_again;
}

}

void Flood(struct send_conf *sndcfg)
{
int ret;
int sock;
int flag = 1;
char domain_name[1024];
int packet_buff_len = 2048;
int total_packet_len;

// sizeof(char *) means the length of "name" field in the query header.
// 2 means, 0x00 and the length of host, 0x03www0x06google0x03com0x00

char *buffer = (char *)malloc(packet_buff_len);
if( NULL == buffer )
{
fprintf( stderr, "malloc memory for packet error.\n" );
return;
}

struct sockaddr_in sa;
memset( &sa, 0, sizeof(struct sockaddr_in) );
sa.sin_family = AF_INET;
sa.sin_port = htons(sndcfg->targetport);
sa.sin_addr.s_addr = htonl(sndcfg->targetip);

if( (sock = socket(PF_INET, SOCK_RAW, IPPROTO_UDP)) < 0 )
{
fprintf( stderr, "create socket error: %s\n", strerror(errno) );
free(buffer);
return;
}

if( setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (int *)&flag, sizeof(flag)) < 0 )
{
fprintf( stderr, "setsockopt error: %s\n", strerror(errno) );
free(buffer);
return;
}

fp_query = fopen(sndcfg->query_file, "r");
if (fp_query == NULL) {
fprintf(stderr, "can not open file: %s\n", sndcfg->query_file);
return;
}

int number = 0;
if( sleeptime == 0 )
{
while( 1 )
{
memset( (void *)buffer, 0, sizeof(buffer) );
memset(domain_name, 0, sizeof(domain_name));
parse_query_file(fp_query, sndcfg, domain_name, sizeof(domain_name));

total_packet_len = sizeof(IP_HEADER) + sizeof(UDP_HEADER) + sizeof(DNS_HEADER) + sizeof(QUERY_HEADER) + strlen(domain_name) + 2 - sizeof(char *);
Init( buffer, total_packet_len, sndcfg, domain_name);

ret = sendto( sock, buffer, total_packet_len, 0, (struct sockaddr *)&sa, sizeof(struct sockaddr_in) );
if (ret == -1) {
fprintf(stderr, "sento error: %s\n", strerror(errno));
continue;
}
outcount ++;
if (debug_flag) {
one_pkt_info_show(outcount, IpHeader.sourceIP, ntohs(UdpHeader.uh_sport),
IpHeader.destIP, ntohs(UdpHeader.uh_dport), query_domain);
}
if (max_pkts != 0 && outcount >= max_pkts) {
sig_proc(0);
exit(0);
}
}
}

else
{
while( 1 )
{

memset( (void *)buffer, 0, sizeof(buffer) );
memset(domain_name, 0, sizeof(domain_name));
parse_query_file(fp_query, sndcfg, domain_name, sizeof(domain_name));

total_packet_len = sizeof(IP_HEADER) + sizeof(UDP_HEADER) + sizeof(DNS_HEADER) + sizeof(QUERY_HEADER) + strlen(domain_name) + 2 - sizeof(char *);
Init( buffer, total_packet_len, sndcfg, domain_name);

ret = sendto( sock, buffer, total_packet_len, 0, (struct sockaddr *)&sa, sizeof(struct sockaddr_in) );
if (ret == -1) {
fprintf(stderr, "sento error: %s\n", strerror(errno));
continue;
}

outcount ++;
number ++;

if (debug_flag) {
one_pkt_info_show(outcount, IpHeader.sourceIP, ntohs(UdpHeader.uh_sport),
IpHeader.destIP, ntohs(UdpHeader.uh_dport), query_domain);
}

if (max_pkts != 0 && outcount >= max_pkts) {
sig_proc(0);
exit(0);
}

if( number == pkt_then_sleep )
{
MySleep( sleeptime );
number = 0;
}
}
}

free( buffer );
return;
}

void sig_proc(int signum)
{
int end_time = 0;
char *lang_env;

end_time=time(NULL);

lang_env = getenv("LANG");
if (!lang_env) {
printf("getenv LANG not Found\n");
return;
}

if (!strncmp(lang_env, "zh_CN.UTF-8", strlen("zh_CN.UTF-8"))) {
printf("\n ********** 统计信息( %d ) *************\n", signum);
printf(" 总发包数: %llu\n",outcount);
printf(" 运行时间:  %llu\n",end_time - starttime);
printf(" 平均每秒发包数:   %llu\n",outcount/(end_time - starttime));
printf(" **************************************\n");
} else {
printf("\n ******* statistics( %d ) *************\n", signum);
printf(" packets sent: %llu\n",outcount);
printf(" seconds active: %llu\n",end_time - starttime);
printf(" average packet/second: %llu\n",outcount/(end_time - starttime));
printf(" **************************************\n");
}

if (fp_query) {
fclose(fp_query);
}

exit(1);
}

int usage(void)
{
char *lang_env;

lang_env = getenv("LANG");
if (!lang_env) {
printf("getenv LANG not Found\n");
return -1;
}

if (!strncmp(lang_env, "zh_CN.UTF-8", strlen("zh_CN.UTF-8"))) {
fprintf(stderr,"\n%s @config_file <pkt_then_sleep> <sleep_time> <max_pkts> [debug] \n", "senddns");
fprintf(stderr, "DNS发包模拟程序\n");
fprintf(stderr, "参数说明: \n"
"\t @config_file: 配置文件\n"
"\t <pkt_then_sleep>: 每次发送的包数后休眠\n"
"\t <sleep_time>: 休眠时间(微秒)\n"
"\t <max_pkts>: 发送总包数(0代表无穷大,可通过信号打断退出)\n"
"\t [debug]: 调试信息输出开关\n"
);
} else {
fprintf(stderr,"\n%s @config_file <pkt_then_sleep> <sleep_time> <max_pkts> [debug] \n", "senddns");
fprintf(stderr, "DNS send packets monitor\n");
fprintf(stderr, "args note: \n"
"\t @config_file: config file\n"
"\t <pkt_then_sleep>: send packets number before sleep interval\n"
"\t <sleep_time>: sleep time(microseconds)\n"
"\t <max_pkts>: send total packets(0 is enough big, can be signal interrupt then exit)\n"
"\t [debug]: debug switch\n"
);
}
}

void set_sig( )
{
signal(SIGHUP,&sig_proc);
signal(SIGINT,&sig_proc);
signal(SIGQUIT,&sig_proc);
signal(SIGILL,&sig_proc);
signal(SIGABRT,&sig_proc);
signal(SIGFPE,&sig_proc);
signal(SIGSEGV,&sig_proc);
signal(SIGPIPE,&sig_proc);
signal(SIGALRM,&sig_proc);
signal(SIGTERM,&sig_proc);
signal(SIGUSR1,&sig_proc);
signal(SIGUSR2,&sig_proc);
signal(SIGCHLD,&sig_proc);
signal(SIGCONT,&sig_proc);
signal(SIGTSTP,&sig_proc);
signal(SIGTTIN,&sig_proc);
signal(SIGTTOU,&sig_proc);
}


int parse_conf_file(char *conf_name)
{
int ret;
int i;
FILE *fp = NULL;
char *p = NULL;
char readline[4096] = {0};
char *saveptr = NULL;
char *token;
int ip;
int port;
int threadnum;
int len;

conf_name += 1; /* skip @ */
fp = fopen(conf_name, "r");
if (fp == NULL) {
fprintf(stderr, "can not open %s file: %s\n", conf_name, strerror(errno));
return -1;
}

while (fgets(readline, sizeof(readline), fp) != NULL) {
p = readline;
while (*p == ' ' || *p == '\t' ) p++;
if (*p == '#') continue;

if (!strncmp(p, "--threadnum", strlen("--threadnum"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
threadnum = atoi(p);
if (threadnum <= 0 || threadnum > 32) {
fprintf(stderr, "--threadnum must be range in [1~32]: %s\n", p);
return -1;
}
send_config.threadnum = threadnum;
} else if (!strncmp(p, "--start_sip", strlen("--start_sip"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
ip = inet_addr(p);
if (ip == INADDR_NONE) {
fprintf(stderr, "--start_sip format error: %s\n", p);
return -1;
}
send_config.start_sip = ntohl(ip);

} else if (!strncmp(p, "--end_sip", strlen("--end_sip"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
ip = inet_addr(p);
if (ip == INADDR_NONE) {
fprintf(stderr, "--end_sip format error: %s\n", p);
return -1;
}
send_config.end_sip = ntohl(ip);

} else if (!strncmp(p, "--exclude_sip", strlen("--exclude_sip"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
i = 0;
send_config.exclude_sip_num = 0;
while ((token = strtok_r(p, " ", &saveptr)) != NULL) {
ip = inet_addr(token);
if (ip == INADDR_NONE) {
fprintf(stderr, "--exclude_sip format error: %s\n", p);
return -1;
}
send_config.exclude_sip_num++;
send_config.exclude_sip_list[i] = ntohl(ip);
i++;
p = NULL;
}
} else if (!strncmp(p, "--start_sport", strlen("--start_sport"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
port = atoi(p);
if (port <= 1024) {
fprintf(stderr, "--start_sport must be big than 1024: %s\n", p);
return -1;
}
send_config.start_sport = atoi(p);

} else if (!strncmp(p, "--end_sport", strlen("--end_sport"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
port = atoi(p);
if (port > 65535) {
fprintf(stderr, "--end_sport must be less than 65536: %s\n", p);
return -1;
}
send_config.end_sport = atoi(p);

} else if (!strncmp(p, "--targetip", strlen("--targetip"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
ip = inet_addr(p);
if (ip == INADDR_NONE) {
fprintf(stderr, "--targetip format error: %s\n", p);
return -1;
}
send_config.targetip = ntohl(ip);

} else if (!strncmp(p, "--targetport", strlen("--targetport"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
send_config.targetport = atoi(p);

} else if (!strncmp(p, "--queryfile", strlen("--queryfile"))) {
p = strchr(p, ' ');
while (*p == ' ' || *p == '\t' ) p++;
len = strlen(p);
if (p[len-1] == '\n') {
p[len-1] = '\0';
}
ret = access(p, F_OK);
if (ret != 0) {
fprintf(stderr, "can not access file: %s, ERR: %s\n", p, strerror(errno));
return -1;
}
strncpy(send_config.query_file, p, sizeof(send_config.query_file));
}

memset(readline, 0, sizeof(readline));

}

fclose(fp);

return 0;
}


int main(int argc,char *argv[])
{
int i;
int ret;
char dst_ip[20] = { 0 };

if( argc != 5 && argc != 6 )
{
usage();
return -1;
}

if (argc == 6 && !strcmp(argv[5], "debug")) {
debug_flag = 1;
}

if (argv[1][0] != '@') {
usage();
return -1;
}

ret = parse_conf_file(argv[1]);
if (ret != 0) {
fprintf(stderr, "parse config file error: %d\n", ret);
return ret;
}

pkt_then_sleep = atoi(argv[2]);
if( pkt_then_sleep == 0 )
{
printf( "pkt_then_sleep error.\n" );
return -1;
}

sleeptime = atoi(argv[3]);
max_pkts = atoi(argv[4]);
starttime = time(NULL);
while(time(NULL) == starttime) usleep(1000);

srand((unsigned) time(NULL));

set_sig( );
Flood(&send_config);

return 0;
}