2012-02-23 18:01:09 +01:00
# include <algorithm>
2011-12-20 05:20:50 +01:00
# include <pthread.h>
2012-01-01 19:18:56 +01:00
# include <stdio.h>
# include <stdlib.h>
# include <getopt.h>
2011-12-20 05:20:50 +01:00
# include "bitcoin.h"
# include "db.h"
2012-01-01 19:18:56 +01:00
# define NTHREADS 24
2011-12-20 09:31:28 +01:00
2011-12-20 05:20:50 +01:00
using namespace std ;
2012-01-01 19:18:56 +01:00
class CDnsSeedOpts {
public :
int nThreads ;
int nPort ;
2012-05-04 01:15:49 +02:00
int nDnsThreads ;
2012-01-01 19:18:56 +01:00
const char * mbox ;
const char * ns ;
const char * host ;
2012-05-04 01:15:49 +02:00
CDnsSeedOpts ( ) : nThreads ( 24 ) , nDnsThreads ( 24 ) , nPort ( 53 ) , mbox ( NULL ) , ns ( NULL ) , host ( NULL ) { }
2012-01-01 19:18:56 +01:00
void ParseCommandLine ( int argc , char * * argv ) {
2012-01-01 23:23:21 +01:00
static const char * help = " Bitcoin-seeder \n "
" Usage: %s -h <host> -n <ns> [-m <mbox>] [-t <threads>] [-p <port>] \n "
" \n "
" Options: \n "
" -h <host> Hostname of the DNS seed \n "
" -n <ns> Hostname of the nameserver \n "
" -m <mbox> E-Mail address reported in SOA records \n "
" -t <threads> Number of crawlers to run in parallel (default 24) \n "
2012-05-04 01:15:49 +02:00
" -d <threads> Number of DNS server threads (default 24) \n "
2012-01-01 23:23:21 +01:00
" -p <port> UDP port to listen on (default 53) \n "
" -?, --help Show this text \n "
" \n " ;
bool showHelp = false ;
2012-01-01 19:18:56 +01:00
while ( 1 ) {
static struct option long_options [ ] = {
{ " host " , required_argument , 0 , ' h ' } ,
{ " ns " , required_argument , 0 , ' n ' } ,
{ " mbox " , required_argument , 0 , ' m ' } ,
{ " threads " , required_argument , 0 , ' t ' } ,
2012-05-04 01:15:49 +02:00
{ " dnsthreads " , required_argument , 0 , ' d ' } ,
2012-01-01 19:18:56 +01:00
{ " port " , required_argument , 0 , ' p ' } ,
2012-01-01 23:23:21 +01:00
{ " help " , no_argument , 0 , ' h ' } ,
2012-01-01 19:18:56 +01:00
{ 0 , 0 , 0 , 0 }
} ;
int option_index = 0 ;
2012-05-04 01:15:49 +02:00
int c = getopt_long ( argc , argv , " h:n:m:t:p:d: " , long_options , & option_index ) ;
2012-01-01 19:18:56 +01:00
if ( c = = - 1 ) break ;
switch ( c ) {
case ' h ' : {
host = optarg ;
break ;
}
case ' m ' : {
mbox = optarg ;
break ;
}
case ' n ' : {
ns = optarg ;
break ;
}
case ' t ' : {
int n = strtol ( optarg , NULL , 10 ) ;
if ( n > 0 & & n < 1000 ) nThreads = n ;
2012-01-01 23:23:21 +01:00
break ;
2012-01-01 19:18:56 +01:00
}
2012-05-04 01:15:49 +02:00
case ' d ' : {
int n = strtol ( optarg , NULL , 10 ) ;
if ( n > 0 & & n < 1000 ) nDnsThreads = n ;
break ;
}
2012-01-01 19:18:56 +01:00
case ' p ' : {
int p = strtol ( optarg , NULL , 10 ) ;
if ( p > 0 & & p < 65536 ) nPort = p ;
2012-01-01 23:23:21 +01:00
break ;
}
case ' ? ' : {
showHelp = true ;
break ;
2012-01-01 19:18:56 +01:00
}
}
}
2012-02-27 03:31:34 +01:00
if ( host ! = NULL & & ns = = NULL ) showHelp = true ;
2012-01-01 23:23:21 +01:00
if ( showHelp ) fprintf ( stderr , help , argv [ 0 ] ) ;
2012-01-01 19:18:56 +01:00
}
} ;
2011-12-20 05:20:50 +01:00
extern " C " {
2011-12-20 14:29:21 +01:00
# include "dns.h"
2011-12-20 05:20:50 +01:00
}
CAddrDb db ;
extern " C " void * ThreadCrawler ( void * data ) {
do {
2012-05-25 15:41:27 +02:00
CService ip ;
2011-12-20 06:42:48 +01:00
int wait = 5 ;
if ( ! db . Get ( ip , wait ) ) {
2011-12-20 09:31:28 +01:00
wait * = 1000 ;
wait + = rand ( ) % ( 500 * NTHREADS ) ;
Sleep ( wait ) ;
2011-12-20 05:20:50 +01:00
continue ;
}
int ban = 0 ;
vector < CAddress > addr ;
2011-12-23 02:43:32 +01:00
int clientV = 0 ;
2012-02-23 18:01:09 +01:00
std : : string clientSV ;
bool ret = TestNode ( ip , ban , clientV , clientSV , addr ) ;
2011-12-20 05:20:50 +01:00
db . Add ( addr ) ;
if ( ret ) {
2012-02-23 18:01:09 +01:00
db . Good ( ip , clientV , clientSV ) ;
2011-12-20 05:20:50 +01:00
} else {
db . Bad ( ip , ban ) ;
}
} while ( 1 ) ;
}
2012-05-25 15:41:27 +02:00
extern " C " int GetIPList ( void * thread , addr_t * addr , int max , int ipv4 , int ipv6 ) ;
2012-05-04 00:08:26 +02:00
2012-05-04 01:15:49 +02:00
class CDnsThread {
public :
dns_opt_t dns_opt ;
2012-05-25 15:41:27 +02:00
vector < addr_t > cache ;
int nIPv4 , nIPv6 ;
2012-05-04 01:15:49 +02:00
time_t cacheTime ;
unsigned int cacheHits ;
uint64_t dbQueries ;
2012-05-25 15:41:27 +02:00
void cacheHit ( bool force = false ) {
static bool nets [ NET_MAX ] = { } ;
if ( ! nets [ NET_IPV4 ] ) {
nets [ NET_IPV4 ] = true ;
nets [ NET_IPV6 ] = true ;
}
2012-05-04 01:15:49 +02:00
time_t now = time ( NULL ) ;
cacheHits + + ;
if ( force | | cacheHits > ( cache . size ( ) * cache . size ( ) / 400 ) | | ( cacheHits * cacheHits > cache . size ( ) / 20 & & ( now - cacheTime > 5 ) ) ) {
2012-05-25 15:41:27 +02:00
set < CNetAddr > ips ;
db . GetIPs ( ips , 1000 , nets ) ;
2012-05-04 01:15:49 +02:00
dbQueries + + ;
cache . clear ( ) ;
2012-05-25 15:41:27 +02:00
nIPv4 = 0 ;
nIPv6 = 0 ;
2012-05-04 01:15:49 +02:00
cache . reserve ( ips . size ( ) ) ;
2012-05-25 15:41:27 +02:00
for ( set < CNetAddr > : : iterator it = ips . begin ( ) ; it ! = ips . end ( ) ; it + + ) {
2012-05-04 01:15:49 +02:00
struct in_addr addr ;
2012-05-25 15:41:27 +02:00
struct in6_addr addr6 ;
2012-05-04 01:15:49 +02:00
if ( ( * it ) . GetInAddr ( & addr ) ) {
2012-05-25 15:41:27 +02:00
addr_t a ;
a . v = 4 ;
memcpy ( & a . data . v4 , & addr , 4 ) ;
cache . push_back ( a ) ;
nIPv4 + + ;
# ifdef USE_IPV6
} else if ( ( * it ) . GetIn6Addr ( & addr6 ) ) {
addr_t a ;
a . v = 6 ;
memcpy ( & a . data . v6 , & addr6 , 16 ) ;
cache . push_back ( a ) ;
nIPv6 + + ;
# endif
2012-05-04 01:15:49 +02:00
}
2012-05-04 00:08:26 +02:00
}
2012-05-04 01:15:49 +02:00
cacheHits = 0 ;
cacheTime = now ;
2011-12-20 22:18:13 +01:00
}
2012-05-04 01:15:49 +02:00
}
CDnsThread ( CDnsSeedOpts * opts ) {
dns_opt . host = opts - > host ;
dns_opt . ns = opts - > ns ;
dns_opt . mbox = opts - > mbox ;
dns_opt . datattl = 60 ;
dns_opt . nsttl = 40000 ;
dns_opt . cb = GetIPList ;
dns_opt . port = opts - > nPort ;
dns_opt . nRequests = 0 ;
cache . clear ( ) ;
cache . reserve ( 1000 ) ;
cacheTime = 0 ;
2012-05-04 00:08:26 +02:00
cacheHits = 0 ;
2012-05-04 01:15:49 +02:00
dbQueries = 0 ;
2012-05-25 15:41:27 +02:00
nIPv4 = 0 ;
nIPv6 = 0 ;
cacheHit ( true ) ;
2012-05-04 00:08:26 +02:00
}
2012-05-04 01:15:49 +02:00
void run ( ) {
dnsserver ( & dns_opt ) ;
}
} ;
2012-05-25 15:41:27 +02:00
extern " C " int GetIPList ( void * data , addr_t * addr , int max , int ipv4 , int ipv6 ) {
2012-05-04 01:15:49 +02:00
CDnsThread * thread = ( CDnsThread * ) data ;
2012-05-25 15:41:27 +02:00
thread - > cacheHit ( ) ;
unsigned int size = ( ipv4 ? thread - > nIPv4 : 0 ) + ( ipv6 ? thread - > nIPv6 : 0 ) ;
2012-05-04 01:15:49 +02:00
if ( max > size )
max = size ;
2012-05-25 15:41:27 +02:00
int i = 0 ;
while ( i < max ) {
2012-05-04 01:15:49 +02:00
int j = i + ( rand ( ) % ( size - i ) ) ;
2012-05-25 15:41:27 +02:00
do {
bool ok = ( ipv4 & & thread - > cache [ j ] . v = = 4 ) | |
( ipv6 & & thread - > cache [ j ] . v = = 6 ) ;
if ( ok ) break ;
j = i + ( ( j - i + 1 ) % ( size - i ) ) ;
} while ( 1 ) ;
2012-05-04 01:15:49 +02:00
addr [ i ] = thread - > cache [ j ] ;
thread - > cache [ j ] = thread - > cache [ i ] ;
thread - > cache [ i ] = addr [ i ] ;
2011-12-20 22:18:13 +01:00
}
2012-05-04 00:08:26 +02:00
return max ;
2011-12-20 05:20:50 +01:00
}
2012-05-04 01:15:49 +02:00
vector < CDnsThread * > dnsThread ;
2011-12-26 01:04:24 +01:00
2012-01-01 19:18:56 +01:00
extern " C " void * ThreadDNS ( void * arg ) {
2012-05-04 01:15:49 +02:00
CDnsThread * thread = ( CDnsThread * ) arg ;
thread - > run ( ) ;
2011-12-20 05:20:50 +01:00
}
2012-02-23 18:01:09 +01:00
int StatCompare ( const CAddrReport & a , const CAddrReport & b ) {
if ( a . uptime [ 4 ] = = b . uptime [ 4 ] ) {
if ( a . uptime [ 3 ] = = b . uptime [ 3 ] ) {
return a . clientVersion > b . clientVersion ;
} else {
return a . uptime [ 3 ] > b . uptime [ 3 ] ;
}
} else {
return a . uptime [ 4 ] > b . uptime [ 4 ] ;
}
}
2011-12-20 08:35:22 +01:00
extern " C " void * ThreadDumper ( void * ) {
do {
Sleep ( 100000 ) ;
{
FILE * f = fopen ( " dnsseed.dat " , " w+ " ) ;
if ( f ) {
CAutoFile cf ( f ) ;
cf < < db ;
}
2012-02-23 18:01:09 +01:00
FILE * d = fopen ( " dnsseed.dump " , " w " ) ;
vector < CAddrReport > v = db . GetAll ( ) ;
sort ( v . begin ( ) , v . end ( ) , StatCompare ) ;
2012-02-24 00:57:06 +01:00
fprintf ( d , " # address \t %%(2h) \t %%(8h) \t %%(1d) \t %%(7d) \t %%(30d) \t version \n " ) ;
2012-05-04 00:08:26 +02:00
double stat [ 5 ] = { 0 , 0 , 0 , 0 , 0 } ;
2012-02-23 18:01:09 +01:00
for ( vector < CAddrReport > : : const_iterator it = v . begin ( ) ; it < v . end ( ) ; it + + ) {
CAddrReport rep = * it ;
fprintf ( d , " %s \t %.2f%% \t %.2f%% \t %.2f%% \t %.2f%% \t %.2f%% \t %i \" %s \" \n " , rep . ip . ToString ( ) . c_str ( ) , 100.0 * rep . uptime [ 0 ] , 100.0 * rep . uptime [ 1 ] , 100.0 * rep . uptime [ 2 ] , 100.0 * rep . uptime [ 3 ] , 100.0 * rep . uptime [ 4 ] , rep . clientVersion , rep . clientSubVersion . c_str ( ) ) ;
2012-05-04 00:08:26 +02:00
stat [ 0 ] + = rep . uptime [ 0 ] ;
stat [ 1 ] + = rep . uptime [ 1 ] ;
stat [ 2 ] + = rep . uptime [ 2 ] ;
stat [ 3 ] + = rep . uptime [ 3 ] ;
stat [ 4 ] + = rep . uptime [ 4 ] ;
2012-02-23 18:01:09 +01:00
}
fclose ( d ) ;
2012-05-04 00:08:26 +02:00
FILE * ff = fopen ( " dnsstats.log " , " a " ) ;
fprintf ( ff , " %llu %g %g %g %g %g \n " , ( unsigned long long ) ( time ( NULL ) ) , stat [ 0 ] , stat [ 1 ] , stat [ 2 ] , stat [ 3 ] , stat [ 4 ] ) ;
fclose ( ff ) ;
2011-12-20 08:35:22 +01:00
}
} while ( 1 ) ;
}
2011-12-26 01:04:24 +01:00
extern " C " void * ThreadStats ( void * ) {
do {
2012-02-23 18:01:09 +01:00
char c [ 256 ] ;
time_t tim = time ( NULL ) ;
struct tm * tmp = localtime ( & tim ) ;
strftime ( c , 256 , " [%y-%m-%d %H:%M:%S] " , tmp ) ;
2011-12-26 01:04:24 +01:00
CAddrDbStats stats ;
db . GetStats ( stats ) ;
2011-12-26 15:53:22 +01:00
printf ( " \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b \b " ) ;
2012-05-04 01:15:49 +02:00
uint64_t requests = 0 ;
uint64_t queries = 0 ;
for ( unsigned int i = 0 ; i < dnsThread . size ( ) ; i + + ) {
requests + = dnsThread [ i ] - > dns_opt . nRequests ;
queries + = dnsThread [ i ] - > dbQueries ;
}
printf ( " %s %i/%i available (%i tried in %is, %i new, %i active), %i banned; %llu DNS requests, %llu db queries " , c , stats . nGood , stats . nAvail , stats . nTracked , stats . nAge , stats . nNew , stats . nAvail - stats . nTracked - stats . nNew , stats . nBanned , ( unsigned long long ) requests , ( unsigned long long ) queries ) ;
2011-12-26 15:53:22 +01:00
Sleep ( 1000 ) ;
2011-12-26 01:04:24 +01:00
} while ( 1 ) ;
}
static const string seeds [ ] = { " dnsseed.bluematt.me " , " bitseed.xf2.org " , " dnsseed.bitcoin.dashjr.org " , " seed.bitcoin.sipa.be " } ;
extern " C " void * ThreadSeeder ( void * ) {
do {
for ( int i = 0 ; i < sizeof ( seeds ) / sizeof ( seeds [ 0 ] ) ; i + + ) {
2012-05-25 15:41:27 +02:00
vector < CNetAddr > ips ;
2011-12-26 01:04:24 +01:00
LookupHost ( seeds [ i ] . c_str ( ) , ips ) ;
2012-05-25 15:41:27 +02:00
for ( vector < CNetAddr > : : iterator it = ips . begin ( ) ; it ! = ips . end ( ) ; it + + ) {
db . Add ( CService ( * it , 8333 ) , true ) ;
2011-12-26 01:04:24 +01:00
}
}
Sleep ( 1800000 ) ;
} while ( 1 ) ;
}
2012-01-01 19:18:56 +01:00
int main ( int argc , char * * argv ) {
2011-12-26 15:53:22 +01:00
setbuf ( stdout , NULL ) ;
2012-01-01 19:18:56 +01:00
CDnsSeedOpts opts ;
opts . ParseCommandLine ( argc , argv ) ;
2012-02-24 01:05:38 +01:00
bool fDNS = true ;
2012-01-01 19:18:56 +01:00
if ( ! opts . ns ) {
2012-02-24 01:05:38 +01:00
printf ( " No nameserver set. Not starting DNS server. \n " ) ;
fDNS = false ;
2012-01-01 19:18:56 +01:00
}
2012-02-24 01:05:38 +01:00
if ( fDNS & & ! opts . host ) {
2012-01-01 19:18:56 +01:00
fprintf ( stderr , " No hostname set. Please use -h. \n " ) ;
2012-01-01 23:23:21 +01:00
exit ( 1 ) ;
2012-01-01 19:18:56 +01:00
}
2011-12-20 08:35:22 +01:00
FILE * f = fopen ( " dnsseed.dat " , " r " ) ;
if ( f ) {
2012-01-01 19:18:56 +01:00
printf ( " Loading dnsseed.dat... " ) ;
2011-12-20 08:35:22 +01:00
CAutoFile cf ( f ) ;
cf > > db ;
2012-02-23 18:01:09 +01:00
// db.banned.clear();
2012-01-01 19:18:56 +01:00
printf ( " done \n " ) ;
2011-12-20 05:20:50 +01:00
}
2012-01-01 19:18:56 +01:00
pthread_t threadDns , threadSeed , threadDump , threadStats ;
printf ( " Starting seeder... " ) ;
pthread_create ( & threadSeed , NULL , ThreadSeeder , NULL ) ;
printf ( " done \n " ) ;
printf ( " Starting %i crawler threads... " , opts . nThreads ) ;
for ( int i = 0 ; i < opts . nThreads ; i + + ) {
pthread_t thread ;
pthread_create ( & thread , NULL , ThreadCrawler , NULL ) ;
2011-12-20 05:20:50 +01:00
}
2012-01-01 19:18:56 +01:00
printf ( " done \n " ) ;
pthread_create ( & threadDump , NULL , ThreadDumper , NULL ) ;
2012-02-24 01:05:38 +01:00
if ( fDNS ) {
2012-05-04 01:15:49 +02:00
printf ( " Starting %i DNS threads for %s on %s (port %i)... " , opts . nDnsThreads , opts . host , opts . ns , opts . nPort ) ;
dnsThread . clear ( ) ;
for ( int i = 0 ; i < opts . nDnsThreads ; i + + ) {
dnsThread . push_back ( new CDnsThread ( & opts ) ) ;
pthread_create ( & threadDns , NULL , ThreadDNS , dnsThread [ i ] ) ;
printf ( " . " ) ;
Sleep ( 20 ) ;
}
2012-02-24 01:05:38 +01:00
printf ( " done \n " ) ;
}
2012-01-01 19:18:56 +01:00
pthread_create ( & threadStats , NULL , ThreadStats , NULL ) ;
void * res ;
2012-02-24 01:05:38 +01:00
pthread_join ( threadDump , & res ) ;
2011-12-20 05:20:50 +01:00
return 0 ;
}