SyntaxHighlighter

2012年1月14日土曜日

IO::Socket で HTTP クライアント

WEBフォームからデータ登録する処理を自動化したいとき。 1件ずつしか登録できないシステムに大量に登録したい場合、1件1件入力するのがめんどくさいですよね。 勿論システム側をいじることができるならそのほうが良いのですが、利用者としての権限しかなければクライアントを作成することになります。
perl で HTTP クライアントといえば LWP モジュールを使うのが一般的ですがあえて IO::Socket での実装サンプル。 ASCII テキストのコマンドでやりとりするタイプのプロトコルならなんでもこれだけでいけちゃうのが便利ですねぇ。

以下のサンプルは

  • プロトコルはHTTP(×HTTPS)
  • 対象はIDと名前と電話番号を登録するだけの単純なアプリ
    ※name値はクエリから推測してください・・・。
  • 登録が成功した場合表示されるページにはsuccessという文字列が含まれている
  • 一括登録したいデータはカンマ区切りのテキストファイルとして用意(引数として渡す)
という前提です。

#!/usr/bin/perl

use strict;
use IO::Socket::INET;

# setting
my $host = '192.168.1.10';
my $port = 80;
my $baseuri = '/test.cgi';
my $pwd = 'hogehoge';
my $method = 'POST';

# get filename
my $filename = shift;
while (!-f $filename) {
 print "File not found. Enter correct filename: ";
 $filename = ;
 chomp $filename;
}

# open
my $sock = IO::Socket::INET->new(PeerAddr => "$host:$port", Proto => 'tcp') or die "connection failed.\n";
select($sock); $| = 1; select(STDOUT);
open FH,$filename or die;
my @data = ;
close FH;
for (my $i = 0; $data[$i]; $i++) {
 next if $data[$i] =~ /^\s*$/;
 chomp $data[$i];
 my ($id,$name,$phone) = split(/,/,$data[$i]);

 # フォーマットチェックめんどくさいから略

 # encode
 $name =~ s/([^\w ])/'%' . unpack('H2', $1)/eg;
 $name =~ tr/ /+/;

 # create query
 my $query = "flag=regist&id=$id&name=$name&number=$phone&password=$pwd";

 # regist
 print $sock "$method $baseuri";
 if ($method eq 'GET') { print $sock "?$query"; }
 print $sock " HTTP/1.1\n";
 print $sock "Host: $host\n";
 if ($i != $#data) {
  print $sock "Connection: Keep-Alive\n";
 } else {
  print $sock "Connection: Close\n";
 }
 if ($method eq 'POST') {
  print $sock "Content-Type: application/x-www-form-urlencoded\n";
  print $sock "Content-Length: " . length($query) . "\n";
  print $sock "\n$query\n\n";
 } else {
  print $sock "\n";
 }

 # check result
 my $success = 0;
 while (<$sock>) {
  if (index($_,'success') != -1) {
   $success = 1;
   last;
  }
 }
 print ($success?"登録成功\n":"登録失敗\n");
}
$sock->close();

exit;

実際作成するときには、フォームのソースを見る+登録の流れのパケットキャプチャをしてどのような値が送信されているか調査して実現可否を判断することになります。ログインが必要なものでもログイン後のセッション管理が単純なものであれば実装は難しくないです(Cookie関連のヘッダをちゃんと処理すれば良い)。CAPTCHA(画像に表示されている歪んだ字を入力させるやつ)は無理。

2012年1月11日水曜日

CentOSとlibiconv

CentOS(少なくとも5.4)には libiconv が入っているパッケージがない・・・!衝撃。

$ wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
$ tar zxvf libiconv-1.14.tar.gz
$ cd libiconv-1.14
$ ./configure
$ make
$ su -
# make install

2012年1月9日月曜日

IO::Socket でサーバ実装

perl でサーバ作ることはないと思いますが。ただのエコーサーバの習作メモ。

#!/usr/local/bin/perl

use strict;
use IO::Socket;

my $port = 5000;

# MAIN
my $listenSock = IO::Socket::INET->new(
 LocalPort => $port,
 Listen => SOMAXCONN,
 Proto => 'tcp',
 Reuse => 1
) or die "bind $port failed.\n";

print "listen start.\n";

while (1) {
 my $newSock = $listenSock->accept;
 if (my $pid = fork){
  #親プロセス
  $newSock->close;
 } else {
  #子プロセス
  $listenSock->close;
  #出力バッファを無効化
  select($newSock);
  $| = 1;
  select(STDOUT);
  #メイン。exitかquit打たれるまでechoするだけ。
  my $buf;
  while (uc($buf) ne 'EXIT' and uc($buf) ne 'QUIT'){
   $buf = <$newSock>;
   $buf =~ tr/\r\n//d;
   print $newSock "$buf\n";
  }
  print $newSock "Bye.\n";
  $newSock->close();
  exit;
 }
}