Advertisement
Guest User

Untitled

a guest
Oct 26th, 2016
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.82 KB | None | 0 0
  1. #!/usr/bin/perl
  2. # xtetsuji 2016/10/26
  3. # qqns.pl - qq.com の MX を qq.com の NS 群全部に問い合わせをして、その記録を取る
  4. #
  5. # 何も考えず雑に書いたのでかなり適当です
  6. # qq.com の NS のうち複数が返答しない場合があるらしく、その調査観察を行うためのスクリプトです
  7.  
  8. use v5.10;
  9. use strict;
  10. use warnings;
  11. use utf8;
  12.  
  13. use Getopt::Long qw(:config posix_default no_ignore_case bundling auto_help);
  14. use Pod::Usage qw(pod2usage);
  15. use Time::Piece;
  16. use Data::Dumper;
  17.  
  18.  
  19. use constant DEBUG => $ENV{DEBUG};
  20. use constant DAEMON_INTERVAL_SEC => 3600; # デーモンモード時のインターバル
  21. use constant COMMAND_TIMEOUT_SEC => 3; # host コマンドのタイムアウト秒
  22. use constant NS_QUERY_WAITING_SEC => 2; # 外部NS群に直接MXを聞きに行くときに入れる待ち秒数
  23.  
  24. GetOptions(
  25. \my %opt,
  26. "daemon|d", "domain|D=s", "log|l=s",
  27. );
  28.  
  29. my $domain = $opt{domain} || "qq.com";
  30. my $log_filename = $opt{log} || "./qqns.log";
  31.  
  32. ###
  33. ### main
  34. ###
  35. # 実際の本体は query()
  36. # どう実行させるか(スタンドアローン・デーモン)が違うところ
  37. if ( $opt{daemon} ) {
  38. print "daemon mode\n";
  39. while (1) {
  40. process_message("PROCESSING");
  41. query();
  42. } continue {
  43. process_message("WAITING");
  44. my $waiting_rest_sec = DAEMON_INTERVAL_SEC;
  45. while ($waiting_rest_sec-- >= 0 ) {
  46. process_message("WAITING: rest_sec=$waiting_rest_sec");
  47. sleep 1;
  48. }
  49. }
  50. } else {
  51. print "foreground mode\n";
  52. process_message("PROCESSING_FOREGROUND");
  53. query();
  54. }
  55.  
  56. print "process finish.\n" if DEBUG;
  57.  
  58. exit;
  59.  
  60. # host(COMMAND_LIST)
  61. # シェルの host コマンドを実行する
  62. # タイムアウトを設定する
  63. sub host {
  64. my @command = @_;
  65. unshift @command, 'host';
  66. my $pid = open my $pipe, '-|', @command;
  67. local $SIG{ALRM} = sub {
  68. print "timeout\n";
  69. kill INT => $pid;
  70. };
  71. alarm COMMAND_TIMEOUT_SEC;
  72. my $result = '';
  73. while(<$pipe>) {
  74. print; # for debug
  75. $result .= $_;
  76. last unless kill 0 => $pid;
  77. }
  78. alarm 0;
  79. close $pipe; # $? $! などが設定される
  80. return $result;
  81. }
  82.  
  83. # query()
  84. # メイン処理
  85. sub query {
  86. my $current_date = ymd();
  87. my $current_time = hms();
  88. my @all_ns_ips;
  89. for my $ns_server_hostname (nameservers($domain)) {
  90. print "ns> $ns_server_hostname\n" if DEBUG;
  91. for my $ns_server_ip (ipaddresses($ns_server_hostname)) {
  92. print "ns(ip)> $ns_server_ip\n" if DEBUG;
  93. push @all_ns_ips, $ns_server_ip;
  94. }
  95. }
  96.  
  97. mkdir for grep { !-d } @all_ns_ips;
  98.  
  99. my %ip_query_result;
  100. for my $ns_ip (@all_ns_ips) {
  101. #my $list = qx{host -t mx $domain $ns_ip};
  102. my $list = host(qw(-t mx), $domain, $ns_ip);
  103. ( my $mx_lines = $list ) =~ s/.*?Aliases:\s*//s;
  104. #$ip_query_result{$ns_ip} = $list;
  105. my $status = $? == 0 ? "SUCCESS" : "FAILED";
  106.  
  107. logging("querying $domain MX to NS $ns_ip is $status. MX server count is " . line_count($mx_lines));
  108.  
  109. print "host command status is $status\n";
  110. print "list lines count is " . line_count($mx_lines) . "\n";
  111. print "=== $ns_ip\n$list\n";
  112.  
  113.  
  114. my $filepath = "$ns_ip/$current_date\_$current_time";
  115. my $content = "=== $ns_ip $current_time";
  116. print_file($filepath => $content);
  117. print "===/ $ns_ip done\n";
  118. } continue {
  119. sleep NS_QUERY_WAITING_SEC;
  120. }
  121. }
  122.  
  123. sub nameservers {
  124. my $fqdn = shift;
  125. my $output = qx{host -t ns $fqdn};
  126. # //m での $ は、改行の直前かマッチ文字列の終端直前のこと
  127. my @result = $output =~ / name server (.*?)\.$/gm;
  128. return @result;
  129. }
  130.  
  131. sub ipaddresses {
  132. my $fqdn = shift;
  133. my $output = qx{host -t a $fqdn};
  134. my @result = $output =~ / has address (\d+\.\d+\.\d+\.\d+)$/gm;
  135. return @result;
  136. }
  137.  
  138. sub logging {
  139. my $line = shift;
  140. my $now_format = time_format_syslog(time);
  141. chomp $line;
  142. state $fh;
  143. open $fh, '>>', $log_filename if !defined $fh;
  144. print {$fh} "$now_format qqns.pl[$$] $line\n";
  145. }
  146.  
  147. sub process_message {
  148. state $CMDLINE = $0;
  149. my $status = shift or die "specify status";
  150. $0 = "perl $CMDLINE [$status]";
  151. }
  152.  
  153. sub time_format_syslog {
  154. my $time = shift;
  155. state $monname = [undef, qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)];
  156. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime $time;
  157. $mon++;
  158. return sprintf "%s %2d %02d:%02d:%02d", $monname->[$mon], $mday, $hour, $min, $sec;
  159. }
  160.  
  161. sub ymd {
  162. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
  163. $mon++;
  164. $year += 1900;
  165. return sprintf "%4d%02d%02d", $year, $mon, $mday;
  166. }
  167.  
  168. sub hms {
  169. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
  170. $mon++;
  171. $year += 1900;
  172. return sprintf "%02d%02d%02d", $hour, $min, $sec;
  173. }
  174.  
  175. sub line_count {
  176. my $list = shift;
  177. chomp $list;
  178. return scalar split /\n/, $list;
  179. }
  180.  
  181. sub print_file {
  182. my $file = shift;
  183. my $content = shift;
  184. open my $fh, '>>', $file or die;
  185. print {$fh} $content;
  186. close $fh;
  187. return;
  188. }
  189.  
  190. __END__
  191.  
  192. =pod
  193.  
  194. =head1 NAME
  195.  
  196. qqns.pl - qq.com NS checker
  197.  
  198. =head1 SYNOPSIS
  199.  
  200. qqns.pl [--daemon] [--domain=DOMAIN_NAME] [--log=FILENAME]
  201.  
  202. =head1 DESCRIPTIONS
  203.  
  204. qq.com が返す NS 群の中で応答を返さないものが結構あるようで、結果的に
  205. qq.com の MX レコードを引くことに失敗してしまうメールサーバがあるようだったので、
  206. そのような状態がいつ起こるのか調べるために書いたのがこのスクリプトです。
  207.  
  208. 単発だと、ドメイン→NSホスト名群→NS IPアドレス群→各 NS IP に対して最初のドメインの
  209. MXレコードを聞き回る、という動作になります。
  210. C<--daemon> オプション付きだと、これを1時間に一度実行して結果をログに出力する
  211. デーモンとなります。
  212.  
  213. =head1 OPTIONS
  214.  
  215. =over
  216.  
  217. =item --daemon
  218.  
  219. デーモンモード。プログラムは終了せず、標準では3600秒ごとに一連の問い合わせ処理を
  220. 試すモードになります。
  221.  
  222. 端末は離さないので、切り離して本当のデーモンっぽくするには disown などを併用して下さい。
  223.  
  224. =item --domain=DOMAIN_NAME
  225.  
  226. 問い合わせるドメイン名を指定します。オプションで指定しなければ qq.com が指定されたもの
  227. とみなします。
  228.  
  229. =item --log=FILENAME
  230.  
  231. 進捗出力のためのログファイルの出力先ファイル名を指定します。
  232.  
  233. ログ出力フォーマットは Syslog ライクですが、出力は Syslog エコシステムによらない
  234. 手製のものです。これは最小構成の CentOS の Perl でも安全なようにという配慮です
  235. (Sys::Syslog すら無い可能性がある)。
  236.  
  237. =back
  238.  
  239. =head1 AUTHOR
  240.  
  241. OGATA Tetsuji E<lt>tetsuji.ogata@gmail.comE<lt>
  242.  
  243. =cut
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement