CSを起動せずとも手軽にrconを操作したくなったので調べてみた。
http://pelipalvelimet.serverit.fi/ssc/Tools/rcon/?C=S;O=A
hlrcon.exeなるものを見つけた。でもソースコードがみつからない。
内部でどう動いてるかわからんものを使いたくないです!
ソースを開示するニダ!
というわけで、hlrcon.exeを使って、何を送ってるのかなどの挙動を調べて、
Perlで書き起こしてみた。OOPっぽく。動作は自分のCS1.6鯖で確認。
ソースが見にくいのはいつものことでござる。
HLRCON.pm
package HLRCON; use Socket; sub new{ my $self = shift; my $hash={ addr => '127.0.0.1', port => '27015', pass => '', comm => 'status', rcon_message => '', error_msg => '', debug => 0, buffer => 1024*256, }; if(ref($_[0]) eq 'HASH'){ %$hash = (%$hash, %{$_[0]}); } return bless($hash, $self); } sub addr{ my $self =shift; if($_[0]){ $self->{addr}=$_[0]; } return $self; } sub port{ my $self =shift; if($_[0]){ $self->{port}=$_[0]; } return $self; } sub pass{ my $self =shift; if($_[0]){ $self->{pass}=$_[0]; } return $self; } sub comm{ my $self =shift; if($_[0]){ $self->{comm}=$_[0]; } return $self; } sub getmsg{ my $self =shift; return $self->{rcon_message}; } sub geterr{ my $self =shift; return $self->{error_message}; } sub exec{ my $self = shift; my $S; socket($S, PF_INET, SOCK_DGRAM, getprotobyname('udp') ) || ( $self->{error_message} ="failed: create socket." && return 0 ); connect($S, sockaddr_in($self->{port}, inet_aton($self->{addr}))) || ( $self->{error_message} ="failed: connection socket." && return 0 ); $self->{rcon_message} = ''; if($self->{debug}){ print " address: $self->{addr}\n"; print " port: $self->{port}\n"; print "password: $self->{pass}\n"; print " command: $self->{comm}\n"; } eval{ my $send_msg = ''; my $recv_msg = ''; local $SIG{'ALRM'} = sub { die 'timeout: connection timeout.' }; alarm 5; select($S); local $|=1; select(STDOUT); send($S, "\xFF\xFF\xFF\xFFchallenge rcon\n", 0); recv($S, $recv_msg , $self->{buffer}, 0); my $challenge_rcon = substr($recv_msg , index($recv_msg, 'rcon ')+5 , -2); # chomp($challenge_rcon); if( !$challenge_rcon ){ die 'failed: get challenge code.'; } send($S, "\xFF\xFF\xFF\xFFrcon $challenge_rcon \"$self->{pass}\" $self->{comm}", 0); recv($S, $recv_msg , $self->{buffer}, 0); $self->{rcon_message} = substr($recv_msg, 5); alarm 0; }; if($@){ alarm 0; $self->{error_message}="$@"; close $S || close $S || ($self->{error_message}="failed: close socket."); $self->{rcon_message}=''; return 0; } alarm 0; close $S || close $S || ($self->{error_message}="failed: close socket."); return 1; } 1;
使い方の例。
use HLRCON; # HLRCON.pmを同じフォルダに置く my $rcon = new HLRCON({addr=>'123.45.67.89', pass=>'asdf', port=>27015}); $rcon->comm('status')->exec(); print $rcon->getmsg(); $rcon->comm('stats')->exec(); print $rcon->getmsg(); $rcon->addr('abc.net')->pass('aaaa')->comm('status')->exec(); print $rcon->getmsg();
設定はコンストラクタ生成時にハッシュリファレンスで定義してもいいし、
途中でセッターを使って変更もできる。
実行時はexecしてやれば、getmsgでコマンドの実行結果が取得できる仕組みです。
WEBアプリにしやすい感じに仕上がったかな。
execの返り値を、コマンドの実行結果にしようと思ったが、
任意で取得するほうがいいかな、とか思ったり。
でもエラーが起きたらgeterrでエラー内容を参照できるのだから、
execで普通に返してもよかった気がする。
rconを実行する仕組みは、
まず、UDP通信で、
"rcon challenge"っていうコードを要求すると、よくわからない値が返ってくる。
その返ってきた値をくっつけてコマンドを送るという、単純な仕組みだった。
それにしても久々にPerlでsocketを使った。
C言語でも実装できるかな、って思ってやりはじめたけど、
結構難航してます。winsockとか仕様わかんねえww
てかC言語のUDP通信の情報少なすぎるwwww
そしてMinGWにはsys/socket.hは無いようだ。不便だなぁ。
LinuxでもWindowsでも使えるようにしたいから、
ifdefとかendifを書いたりしなきゃならないっぽい。
まぁいいや明日やろう。