/* DNSdbd (general purpose version) * * by: vade79/v9 v9@fakehalo.org (fakehalo/realhalo) * * (currently only tested on linux) * * compile: * gcc -lpthread -lmysqlclient thisfile.c -o thisfile * * note: * this takes a little over 1k/s bandwidth. (25 packets/sec) * * setup: (/usr/bin/mysql -u root -p) * CREATE DATABASE dnsdb; * GRANT ALL PRIVILEGES ON dnsdb.* TO dnsdb@'%' IDENTIFIED BY 'pass' WITH GRANT OPTION; * FLUSH PRIVILEGES; * (you will also need to change the SQL_ DEFINEs below to match the server and password) * * example use: * # echo 68.0.0.1 > ~/.dnsdb-cache * # ./dnsdbd * * example data fetching: (/usr/bin/mysql -u dnsdb -p) * use dnsdb; * SELECT ip FROM db WHERE rsp>7 ORDER BY date DESC; * * note: * amplification is saved in a 8bit int to avoid using unneeded space/double, * it equals (amp/10), ie. 25 = 2.5. */ #ifndef _ISOC99_SOURCE #define _ISOC99_SOURCE #endif #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DNSDBD_VERSION "1.0.1" /* CHANGE THESE VALUES TO WHATEVER SQL SERVER IS USED. */ #define SQL_SERVER "localhost" #define SQL_DB "dnsdb" #define SQL_USER "dnsdb" #define SQL_PASS "pass" #define RESOLV_DOMAIN "x" /* must NOT exist. */ #define LPORT 7979 #define PACKETS_PER_SEC 25 #define PACKET_DELAY (1000000 / PACKETS_PER_SEC) #define FALL_BACK (30 * PACKETS_PER_SEC) /* 30 second fallback. */ #define MAX_HOSTNAME 48 /* change this in DNSDB_TABLE too, and others. */ #define MIN_RESPONSE 2 /* minimum number of dups to keep track of. */ #define DNSDB_TABLE \ "CREATE TABLE IF NOT EXISTS db (" \ " id INT unsigned NOT NULL default 0," \ " rsp SMALLINT unsigned NOT NULL default 0," \ " amp TINYINT unsigned NOT NULL default 0," \ " lag TINYINT unsigned NOT NULL default 0," \ " date DATETIME NOT NULL default '0000-00-00 00:00:00'," \ " ip VARCHAR(15) NOT NULL default '?'," \ " name VARCHAR(48) NOT NULL default '?'," \ " PRIMARY KEY (id)," \ " INDEX (rsp)," \ " INDEX (date)" \ ")" /* GLOBALS. */ unsigned char daemon=0,block_i=0,nexit=0,gat=0,idle=0,wait_db=0; signed int sd=0; unsigned int ip_i=0; char *cachefile,*nptr; pid_t dpid=0; MYSQL *sql_con; /* GENERAL "NON-BOGON" /8 IP-RANGES LISTED BY THE IANA TABLE. */ /* ALL OF 169, 172, AND 192 ARE NOT ALL RESERVED, JUST LAZY. */ /* static unsigned char a_blocks[]={3,4,6,8,9,11,12,13,14,15,16,17,18,19,20,21, 22,24,25,26,28,29,30,32,33,34,35,38,40,41,43,44,45,46,47,48,51,52,53,54,55, 56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,80,81,82,83, 84,85,86,87,88,89,90,91,124,125,126,128,129,130,131,132,133,134,135,136,137, 138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156, 157,158,159,160,161,162,163,164,165,166,167,168,170,171,188,189,190,191,193, 194,195,196,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213, 214,215,216,217,218,219,220,221,222}; */ /* CURRENTLY ONLY CHECKING ONE A CLASS AT A TIME. */ static unsigned char a_blocks[]={68}; /* PROTOTYPES. */ void dnsdbd_init(void); unsigned int get_last_ip(void); void write_last_ip(struct in_addr); void dns_scan_t(void); void dns_get_answers_t(void); unsigned int init_ip(void); unsigned int dns_inc_ip(void); unsigned char set_a(unsigned char); char *dns_converthost(char *); unsigned int r_32(unsigned int); unsigned char db_init(void); void db_printf(char *,...); void print_msg(unsigned char,char *,...); void sig_handler(signed int); /* INIT. */ int main(signed int argc,char **argv){ signal(SIGINT,sig_handler); /* FOREGROUND OR BACKGROUND. */ if(argc>1&&!strcasecmp(argv[1],"daemon"))daemon=1; /* BANNER. */ else printf("\nDNSDBD[v"DNSDBD_VERSION"]: DNSDB spider daemon, by: v9@fakehalo." "us.\n(run \"%s daemon\", to place into the background)\n\n",argv[0]); /* FOREGROUND/BACKGROUND SWITCH. */ switch((dpid=(daemon?fork():0))){ case -1: print_msg(1,"fork() failed."); break; case 0: dnsdbd_init(); _exit(0); break; default: printf("%d\n",dpid); break; } exit(0); } /* MAIN ROUTINE. */ void dnsdbd_init(void){ unsigned int i=0; pthread_t pt_a,pt_s; struct in_addr show_ip; struct passwd *p; /* SET GLOBAL "~/.dnsdb-cache" PATH. */ if(!(p=getpwuid(getuid()))&&(i=strlen(p->pw_dir))){ if(!(cachefile=(char *)malloc(13+1))) print_msg(1,"malloc() failed."); memset(cachefile,0,(13+1)); strcpy(cachefile,"/.dnsdb-cache"); } else{ if(!(cachefile=(char *)malloc(i+13+1))) print_msg(1,"malloc() failed."); memset(cachefile,0,(i+13+1)); sprintf(cachefile,"%s/.dnsdb-cache",p->pw_dir); } /* BEGIN INITIALIZATION. */ print_msg(0,"bringing up MySQL connection to \"%s\"...",SQL_SERVER); while(db_init()){ print_msg(0,"MySQL connection \"%s\" failed... (wait)",SQL_SERVER); sleep(5); } print_msg(0,"creating (if not exists) 'db' table..."); db_printf(DNSDB_TABLE); if((show_ip.s_addr=ip_i=get_last_ip())) print_msg(0,"using cached IP from \"%s\"...",cachefile); else{ print_msg(0,"no usable IP from \"%s\"...",cachefile); show_ip.s_addr=init_ip(); } if(((ip_i>>24)&0xff)!=1) print_msg(1,"the starting ip must start with a 1... (xxx.xxx.xxx.1)"); print_msg(0,"setting initial base IP to \"%s\"...",inet_ntoa(show_ip)); print_msg(0,"deleting any non-DUP MySQL entries..."); db_printf("DELETE FROM db WHERE rsp<%u",MIN_RESPONSE); print_msg(0,"deleting any MySQL entries in the starting /24..."); db_printf("DELETE FROM db WHERE id>%u AND id<%u",r_32(ip_i),r_32(ip_i)+254); print_msg(0,"bringing dns_get_answers_t() thread up..."); if(pthread_create(&pt_a,0,(void*(*)(void *))dns_get_answers_t,0)) print_msg(1,"thread creation failed."); print_msg(0,"waiting for confirmation..."); while(!gat&&!nexit)sleep(1); if(!nexit){ print_msg(0,"bringing dns_scan_t() thread up..."); if(pthread_create(&pt_s,0,(void*(*)(void *))dns_scan_t,0)) print_msg(1,"thread creation failed."); /* GOOD TO KNOW, AS ITS COMPILED IN AND SHOWN NOWHERES ELSE. */ print_msg(0,"sending %u(%.2fk/s) packets/requests per second...", PACKETS_PER_SEC,((float)((20+8+18+strlen(RESOLV_DOMAIN))*PACKETS_PER_SEC) /1024)); print_msg(0,"idle, collecting replies... (hit ENTER for current ip)"); while(!nexit){ /* MIGHT AS WELL HAVE A WAY TO SHOW THE CURRENT IP LOCATION. */ if(getchar()=='\n'){ show_ip.s_addr=ip_i; print_msg(0,"current ip: %s",inet_ntoa(show_ip)); } } } return; } /* GET IP FROM "~/.dnsdb-cache" ON START. */ unsigned int get_last_ip(void){ unsigned int i=0,r=0,a=0,b=0,c=0,d=0; char ipbuf[16]; FILE *f; if(!cachefile||!strlen(cachefile))return(0); memset(ipbuf,0,16); if(!(f=fopen(cachefile,"r")))return(0); fgets(ipbuf,15,f); fclose(f); if(strlen(ipbuf)<1)return(0); i=sscanf(ipbuf,"%u.%u.%u.%u",&a,&b,&c,&d); if(i!=4)return(0); r=(d<<24); r+=(c<<16); r+=(b<<8); r+=a; return(r); } /* WRITE NEW IP TO "~/.dnsdb-cache" AT EVERY /24. */ void write_last_ip(struct in_addr ipaddr){ FILE *f; if(!cachefile||!strlen(cachefile))return; if(!(f=fopen(cachefile,"w")))return; fprintf(f,"%s\n",inet_ntoa(ipaddr)); fclose(f); return; } /* THREAD FOR DNS SCANNING. */ void dns_scan_t(void){ socklen_t sas=0; char *p; struct sockaddr_in sad; sas=sizeof(struct sockaddr_in); memset((char *)&sad,0,sas); sad.sin_family=AF_INET; sad.sin_port=htons(53); if(!(p=(char *)malloc(18+strlen(RESOLV_DOMAIN)+1))) print_msg(1,"malloc() failed."); sprintf(p,"%c%c%c%c%c%c%c%c%c%c%c%c%s%c%c%c%c%c",0,1,1,0,0,1,0,0,0,0,0,0, dns_converthost(RESOLV_DOMAIN),0,0,1,0,1); while(!nexit){ /* RELATED TO NO SQL CONNECION. */ while(idle)sleep(1); dns_inc_ip(); sad.sin_addr.s_addr=ip_i; sendto(sd,p,(18+strlen(RESOLV_DOMAIN)),0,(struct sockaddr *)&sad, sizeof(struct sockaddr_in)); if(((ip_i>>24)&0xff)==1){ /* CLEAN OUT NON-DUPS. (FALL_BACK) */ db_printf("DELETE FROM db WHERE (id<%u OR id>%u) AND rsp<%u", r_32(ip_i)-FALL_BACK-1,r_32(ip_i),MIN_RESPONSE); /* CLEAN OUT THE NEXT /24. */ db_printf("DELETE FROM db WHERE id>%u AND id<%u", r_32(ip_i),r_32(ip_i)+254); /* SAVE LOCATION AT THE BEGINNING OF EACH /24. */ write_last_ip(sad.sin_addr); } usleep(PACKET_DELAY); } free(p); pthread_exit(0); return; } /* THREAD TO LISTEN FOR ANSWERS FROM dns_scan_t(). */ void dns_get_answers_t(void){ unsigned short i=0,j=0,k=0,amp=0,lag=0; unsigned int ip_r=0; unsigned char ubuf[512]; char hn[MAX_HOSTNAME+1]; char *ptr; socklen_t sas=0; struct hostent *h; struct sockaddr_in mad; sas=sizeof(struct sockaddr_in); memset((char *)&mad,0,sas); mad.sin_family=AF_INET; mad.sin_port=htons(LPORT); mad.sin_addr.s_addr=htonl(INADDR_ANY); if((sd=socket(AF_INET,SOCK_DGRAM,0))<0)print_msg(1,"socket() failed."); if(bind(sd,(struct sockaddr *)&mad,sas)<0)print_msg(1,"bind() failed."); gat=1; while(!nexit){ /* RELATED TO NO SQL CONNECION. */ while(idle)sleep(1); memset(ubuf,0xff,511); if(recvfrom(sd,(char *)&ubuf,511,0,(struct sockaddr *)&mad,&sas)>=0){ /* REPLY SOURCE ADDRESS MUST BE FROM AT LEAST "FALL_BACK" IPS. */ /* THIS IS TO PREVENT SPOOFED PACKETS FROM TAINTING THE DATABASE. */ if((lag=(r_32(ip_i)-r_32(mad.sin_addr.s_addr)))h_name; else ptr="?"; memset(hn,0,MAX_HOSTNAME+1); if((k=strlen(ptr))>MAX_HOSTNAME){ strcpy(hn,"..."); for(j=MAX_HOSTNAME;j>2;j--) hn[j]=ptr[k--]; } else strcpy(hn,ptr); /* REVERSE NETWORK IP FORMAT, MORE USES FOR SEARCHING BACKWARDS. */ ip_r=r_32(mad.sin_addr.s_addr); /* THIS IS TO MAX(25.5x) THE LIMITIATION INSTEAD OF LOOPING OVER. */ amp=(unsigned int) /* ... */ (((float)(20+8+i)/(float)(20+8+18+strlen(RESOLV_DOMAIN)))*10); if(amp>255)amp=255; /* THIS IS TO MAX(255) THE LIMITIATION INSTEAD OF LOOPING OVER. */ lag=(lag/PACKETS_PER_SEC); if(lag>255)lag=255; /* VERBOSE/DEBUG. */ print_msg(0,".-FOUND: %s",inet_ntoa(mad.sin_addr)); print_msg(0,"| ID: %u ** AMP: %.1fx ** LAG: %u",ip_r,((float)amp/10.0),lag); print_msg(0,"| NAME: %s",hn); print_msg(0,"`---------------------------------------------------"); /* THIS IS HACKY AS IT GETS. MY MYSQL IS OLD, NO "ON DUPLICATE KEY"... */ db_printf("SET @rsp=0"); db_printf("SELECT @rsp:=rsp FROM db WHERE id=%u",ip_r); db_printf("REPLACE INTO db VALUES(%u,(@rsp+1),%u,%u,NOW(),'%s','%s')", ip_r,amp,lag,inet_ntoa(mad.sin_addr),hn); } } } close(sd); pthread_exit(0); return; } /* CREATES INITIAL RANDOM "NON-BOGON" IP. */ unsigned int init_ip(void){ struct timeval tv; if(!ip_i){ gettimeofday(&tv,0); srand(tv.tv_usec); ip_i=(1<<24); ip_i+=((int)(255.0*rand()/(RAND_MAX+1.0))<<16); ip_i+=((int)(255.0*rand()/(RAND_MAX+1.0))<<8); block_i=(int)(((double)sizeof(a_blocks))*rand()/(RAND_MAX+1.0)); ip_i+=a_blocks[block_i]; } return(ip_i); } /* INCREASES IP FOR SCANNING. */ unsigned int dns_inc_ip(void){ if(((ip_i>>24)&0xff)>=254&&((ip_i>>16)&0xff)==255&&((ip_i>>8)&0xff)==255) ip_i=a_blocks[set_a(block_i)]; if(((ip_i>>24)&0xff)>=254&&((ip_i>>16)&0xff)==255) ip_i+=((1<<24)+(1<<16)+(1<<8)); if(((ip_i>>24)&0xff)>=254) ip_i+=((3<<24)+(1<<16)); else ip_i+=(1<<24); return(ip_i); } /* skip the bogon ip blocks. */ unsigned char set_a(unsigned char n){ unsigned char r=n; if(r>=(sizeof(a_blocks)-1))r=0; else r++; return(r); } /* MAGICAL DOT-2-NUMERIC DNS FORMAT. */ char *dns_converthost(char *host){ unsigned char i=0,j=0,k=0; unsigned int hs=0; char *nshost; hs=strlen(host); if(!(nshost=(char *)malloc(hs+2)))print_msg(1,"malloc() failed."); memset(nshost,0,(hs+2)); if(hs>255)strcpy(nshost,"x"); else{ for(i=0;i>8)&0xff)<<16); r+=((int)((i>>16)&0xff)<<8); r+=((int)((i>>24)&0xff)); return(r); } /* MAKE INITIAL CONNECTION TO SQL SERVER. */ unsigned char db_init(void){ if(!(sql_con=mysql_init(NULL))){ print_msg(0,"[SQL] mysql_init() failed."); return(1); } if(!mysql_real_connect(sql_con,SQL_SERVER,SQL_USER,SQL_PASS,SQL_DB,0,NULL,0)){ print_msg(0,"[SQL] %s",mysql_error(sql_con)); return(1); } return(0); } /* SQL PINGER AND QUERY SENDER. */ void db_printf(char *fmt,...){ char buf[1024]; va_list ap; MYSQL_RES *sql_res; memset(buf,0,1024); va_start(ap,fmt); vsnprintf(buf,1023,fmt,ap); va_end(ap); while(idle||wait_db)usleep(100000); wait_db=1; if(mysql_ping(sql_con)){ print_msg(0,"[SQL] (mysql_ping) %s",mysql_error(sql_con)); idle=1; while(db_init())sleep(1); print_msg(0,"[SQL] connection re-established."); idle=0; } if(mysql_query(sql_con,buf)){ print_msg(0,"[SQL] (mysql_query) %s",mysql_error(sql_con)); return; } /* DON'T NEED TO READ RESPONSE DATA FOR ANYTHING AS OF NOW. */ if((sql_res=mysql_use_result(sql_con))) mysql_free_result(sql_res); wait_db=0; return; } /* PRINTS ERRORS AND NOTICES. (formatted) */ void print_msg(unsigned char e,char *fmt,...){ char buf[1024],tbuf[16]; time_t ttt; struct tm *ttm; va_list ap; if(nexit)return; else if(!daemon){ memset(buf,0,1024); va_start(ap,fmt); vsnprintf(buf,1023,fmt,ap); va_end(ap); ttt=time(0); ttm=localtime(&ttt); strftime(tbuf,15,"%I:%M:%S%p",ttm); printf("%s : (%s) %s\n",(e?"ERROR":"NOTICE"),tbuf,buf); } if(e)exit(e); return; } /* ALL-PURPOSE SIGNAL HANDLER. */ void sig_handler(signed int sig){ if(sig==SIGINT){ signal(SIGINT,SIG_IGN); print_msg(0,"SIGINT caught, user aborted!"); nexit=1; exit(0); } }