You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

asn.pl 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #!/usr/bin/env perl
  2. use warnings;
  3. use strict;
  4. use Pod::Usage;
  5. use Getopt::Long;
  6. use File::Fetch;
  7. use LWP::Simple;
  8. use PerlIO::gzip;
  9. use File::Basename;
  10. use Net::MRT;
  11. use URI;
  12. use Data::Dumper;
  13. $LWP::Simple::ua->show_progress(1);
  14. $Net::MRT::USE_RFC4760 = -1;
  15. my %config = (
  16. asn_sources => [
  17. 'ftp://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest',
  18. 'ftp://ftp.ripe.net/ripe/stats/delegated-ripencc-latest',
  19. 'ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest',
  20. 'ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest',
  21. 'ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest'
  22. ],
  23. bgp_sources => ['http://data.ris.ripe.net/rrc00/latest-bview.gz']
  24. );
  25. my $download_asn = 0;
  26. my $download_bgp = 0;
  27. my $download_target = "./";
  28. my $help = 0;
  29. my $man = 0;
  30. my $v4 = 1;
  31. my $v6 = 0;
  32. my $parse = 1;
  33. my $v4_zone = "asn.rspamd.com";
  34. my $v6_zone = "asn6.rspamd.com";
  35. my $v4_file = "asn.zone";
  36. my $v6_file = "asn6.zone";
  37. GetOptions(
  38. "download-asn" => \$download_asn,
  39. "download-bgp" => \$download_bgp,
  40. "4!" => \$v4,
  41. "6" => \$v6,
  42. "parse!" => \$parse,
  43. "target=s" => \$download_target,
  44. "zone-v4=s" => \$v4_zone,
  45. "zone-v6=s" => \$v6_zone,
  46. "file-v4=s" => \$v4_file,
  47. "file-v6=s" => \$v6_file,
  48. "help|?" => \$help,
  49. "man" => \$man
  50. ) or pod2usage(2);
  51. pod2usage(1) if $help;
  52. pod2usage( -exitval => 0, -verbose => 2 ) if $man;
  53. sub download_file {
  54. my ($u) = @_;
  55. print "Fetching $u\n";
  56. my $ff = File::Fetch->new( uri => $u );
  57. my $where = $ff->fetch( to => $download_target ) or die $ff->error;
  58. return $where;
  59. }
  60. if ($download_asn) {
  61. foreach my $u ( @{ $config{'asn_sources'} } ) {
  62. download_file($u);
  63. }
  64. }
  65. if ($download_bgp) {
  66. foreach my $u ( @{ $config{'bgp_sources'} } ) {
  67. download_file($u);
  68. }
  69. }
  70. if ( !$parse ) {
  71. exit 0;
  72. }
  73. my $v4_fh;
  74. my $v6_fh;
  75. if ($v4) {
  76. open( $v4_fh, ">", $v4_file ) or die "Cannot open $v4_file for writing: $!";
  77. print $v4_fh
  78. "\$SOA 43200 ns1.$v4_zone support.rspamd.com 0 600 300 86400 300\n";
  79. print $v4_fh "\$NS 43200 ns1.$v4_zone\n";
  80. }
  81. if ($v6) {
  82. open( $v6_fh, ">", $v6_file ) or die "Cannot open $v6_file for writing: $!";
  83. print $v6_fh
  84. "\$SOA 43200 ns1.$v6_zone support.rspamd.com 0 600 300 86400 300\n";
  85. print $v6_fh "\$NS 43200 ns1.$v6_zone\n";
  86. }
  87. # Now load BGP data
  88. my $networks = {};
  89. foreach my $u ( @{ $config{'bgp_sources'} } ) {
  90. my $parsed = URI->new($u);
  91. my $fname = $download_target . '/' . basename( $parsed->path );
  92. open( my $fh, "<:gzip", $fname )
  93. or die "Cannot open $fname: $!";
  94. while ( my $dd = eval { Net::MRT::mrt_read_next($fh) } ) {
  95. if ( $dd->{'prefix'} && $dd->{'bits'} ) {
  96. next if $dd->{'subtype'} == 2 and !$v4;
  97. next if $dd->{'subtype'} == 4 and !$v6;
  98. my $entry = $dd->{'entries'}->[0];
  99. my $net = $dd->{'prefix'} . '/' . $dd->{'bits'};
  100. if ( $entry && $entry->{'AS_PATH'} ) {
  101. my $as = $entry->{'AS_PATH'}->[-1];
  102. if ( !$networks->{$as} ) {
  103. if ( $dd->{'subtype'} == 2 ) {
  104. $networks->{$as} = { nets_v4 => [$net], nets_v6 => [] };
  105. }
  106. else {
  107. $networks->{$as} = { nets_v6 => [$net], nets_v4 => [] };
  108. }
  109. }
  110. else {
  111. if ( $dd->{'subtype'} == 2 ) {
  112. push @{ $networks->{$as}->{'nets_v4'} }, $net;
  113. }
  114. else {
  115. push @{ $networks->{$as}->{'nets_v6'} }, $net;
  116. }
  117. }
  118. }
  119. }
  120. }
  121. }
  122. # Now roughly detect countries
  123. foreach my $u ( @{ $config{'asn_sources'} } ) {
  124. my $parsed = URI->new($u);
  125. my $fname = $download_target . '/' . basename( $parsed->path );
  126. open( my $fh, "<", $fname ) or die "Cannot open $fname: $!";
  127. while (<$fh>) {
  128. next if /^\#/;
  129. chomp;
  130. my @elts = split /\|/;
  131. if ( $elts[2] eq 'asn' && $elts[3] ne '*' ) {
  132. my $as_start = int( $elts[3] );
  133. my $as_end = $as_start + int( $elts[4] );
  134. for ( my $as = $as_start ; $as < $as_end ; $as++ ) {
  135. if ( $networks->{"$as"} ) {
  136. $networks->{"$as"}->{'country'} = $elts[1];
  137. $networks->{"$as"}->{'rir'} = $elts[0];
  138. }
  139. }
  140. }
  141. }
  142. }
  143. while ( my ( $k, $v ) = each( %{$networks} ) ) {
  144. if ($v4) {
  145. foreach my $n ( @{ $v->{'nets_v4'} } ) {
  146. # "15169 | 8.8.8.0/24 | US | arin |" for 8.8.8.8
  147. if ( $v->{'country'} ) {
  148. printf $v4_fh "%s %s|%s|%s|%s|\n", $n, $k, $n, $v->{'country'}, $v->{'rir'};
  149. }
  150. else {
  151. printf $v4_fh "%s %s|%s|%s|%s|\n", $n, $k, $n, 'UN', 'UN';
  152. }
  153. }
  154. }
  155. if ($v6) {
  156. foreach my $n ( @{ $v->{'nets_v6'} } ) {
  157. # "15169 | 8.8.8.0/24 | US | arin |" for 8.8.8.8
  158. if ( $v->{'country'} ) {
  159. printf $v6_fh "%s %s|%s|%s|%s|\n", $n, $k, $n, $v->{'country'}, $v->{'rir'};
  160. }
  161. else {
  162. printf $v6_fh "%s %s|%s|%s|%s|\n", $n, $k, $n, 'UN', 'UN';
  163. }
  164. }
  165. }
  166. }
  167. __END__
  168. =head1 NAME
  169. asn.pl - download and parse ASN data for Rspamd
  170. =head1 SYNOPSIS
  171. asn.pl [options]
  172. Options:
  173. --download-asn Download ASN data from RIR
  174. --download-bgp Download GeoIP data from Maxmind
  175. --target Where to download files (default: current dir)
  176. --help Brief help message
  177. --man Full documentation
  178. =head1 OPTIONS
  179. =over 8
  180. =item B<--download-asn>
  181. Download ASN data from RIR.
  182. =item B<--download-bgp>
  183. Download GeoIP data from Ripe
  184. =item B<--target>
  185. Specifies where to download files.
  186. =item B<--help>
  187. Print a brief help message and exits.
  188. =item B<--man>
  189. Prints the manual page and exits.
  190. =back
  191. =head1 DESCRIPTION
  192. B<asn.pl> is intended to download ASN data and GeoIP data and create a rbldnsd zone.
  193. =cut