# Simple script that read message from STDIN and test it on rspamd server
# using specified command.
#
-# Usage: rspamc.pl [-c conf_file] [command]
+# Usage: rspamc.pl [-c conf_file] [command] [-s statfile]
#
# By default rspamc.pl would read ./rspamd.conf and default command is SYMBOLS
'command' => 'SYMBOLS',
'host' => 'localhost',
'port' => '11333',
- 'is_unix' => 0,
+ 'is_unix' => 0,
+ 'password' => '',
+ 'control' => 0,
+ 'statfile' => '',
);
+
sub usage {
- return "Usage: rspamc.pl [-c conf_file] [command]";
+ return "Usage: rspamc.pl [-c conf_file] [-s statfile] [command]";
}
-while (my $param = shift) {
- if ($param eq '-c') {
- my $value = shift;
- if ($value) {
- if (-r $value) {
- $cfg{'conf_file'} = $value;
+# Load rspamd config params
+sub parse_config {
+ my ($is_ctrl) = @_;
+
+ open CONF, "< $cfg{'conf_file'}" or die "config file $cfg{'conf_file'} cannot be opened";
+
+ my $ctrl = 0;
+ while (<CONF>) {
+ if ($_ =~ /control\s*{/i) {
+ $ctrl = 1;
+ }
+ if ($ctrl && $_ =~ /}/) {
+ $ctrl = 0;
+ }
+ if (((!$is_ctrl && !$ctrl) || ($ctrl && $is_ctrl))
+ && $_ =~ /^\s*bind_socket\s*=\s*((([^:]+):(\d+))|(\/\S*))/i) {
+ if ($3 && $4) {
+ $cfg{'host'} = $3;
+ $cfg{'port'} = $4;
+ $cfg{'is_unix'} = 0;
}
else {
- die "config file $value is not readable";
+ $cfg{'host'} = $5;
+ $cfg{'is_unix'} = 1;
}
}
- else {
- die usage();
+ if ($ctrl && $is_ctrl && $_ =~ /^\s*password\s*=\s*"(\S+)"/) {
+ $cfg{'password'} = $1;
}
}
- elsif ($param =~ /(SYMBOLS|SCAN|PROCESS|CHECK|REPORT_IFSPAM|REPORT)/i) {
- $cfg{'command'} = $1;
- }
+
+ close CONF;
+
}
-open CONF, "< $cfg{'conf_file'}" or die "config file $cfg{'conf_file'} cannot be opened";
+sub connect_socket {
+ my $sock;
-my $ctrl = 0;
-while (<CONF>) {
- if ($_ =~ /control\s*{/i) {
- $ctrl = 1;
- }
- if ($ctrl && $_ =~ /}/) {
- $ctrl = 0;
+ if ($cfg{'is_unix'}) {
+ socket ($sock, PF_UNIX, SOCK_STREAM, 0) or die "cannot create unix socket";
+ my $sun = sockaddr_un($cfg{'host'});
+ connect ($sock, $sun) or die "cannot connect to socket $cfg{'host'}";
}
- if (!$ctrl && $_ =~ /^\s*bind_socket\s*=\s*((([^:]+):(\d+))|(\/\S*))/i) {
- if ($3 && $4) {
- $cfg{'host'} = $3;
- $cfg{'port'} = $4;
- $cfg{'is_unix'} = 0;
+ else {
+ my $proto = getprotobyname('tcp');
+ my $sin;
+ socket ($sock, PF_INET, SOCK_STREAM, $proto) or die "cannot create tcp socket";
+ if (inet_aton ($cfg{'host'})) {
+ $sin = sockaddr_in ($cfg{'port'}, inet_aton($cfg{'host'}));
}
else {
- $cfg{'host'} = $5;
- $cfg{'is_unix'} = 1;
+ my $addr = gethostbyname($cfg{'host'});
+ if (!$addr) {
+ die "cannot resolve $cfg{'host'}";
+ }
+ $sin = sockaddr_in ($cfg{'port'}, $addr);
}
- last;
+
+ connect ($sock, $sin) or die "cannot connect to socket $cfg{'host'}:$cfg{'port'}";
}
+ return $sock;
}
-close CONF;
+# Currently just read stdin for user's message and pass it to rspamd
+sub do_rspamc_command {
+ my ($sock) = @_;
+
+ my $input;
+ while (defined (my $line = <>)) {
+ $input .= $line;
+ }
+
+ print "Sending ". length ($input) ." bytes...\n";
-if ($cfg{'is_unix'}) {
- my $proto = getprotobyname('tcp');
- socket (SOCK, PF_UNIX, SOCK_STREAM, $proto) or die "cannot create unix socket";
- my $sun = sockaddr_un($cfg{'host'});
- connect (SOCK, $sun) or die "cannot connect to socket $cfg{'host'}";
+ syswrite $sock, "$cfg{'command'} RSPAMC/1.0 $CRLF";
+ syswrite $sock, "Content-Length: " . length ($input) . $CRLF . $CRLF;
+ syswrite $sock, $input;
+ syswrite $sock, $CRLF;
+ while (<$sock>) {
+ print $_;
+ }
}
-else {
- my $proto = getprotobyname('tcp');
- my $sin;
- socket (SOCK, PF_INET, SOCK_STREAM, $proto) or die "cannot create tcp socket";
- if (inet_aton ($cfg{'host'})) {
- $sin = sockaddr_in ($cfg{'port'}, inet_aton($cfg{'host'}));
+
+sub do_ctrl_auth {
+ my ($sock) = @_;
+
+ syswrite $sock, "password $cfg{'password'}" . $CRLF;
+ if (defined (my $reply = <$sock>)) {
+ my $end = <$sock>;
+ if ($reply =~ /^password accepted/) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+sub do_control_command {
+ my ($sock) = @_;
+
+ # Read greeting first
+ if (defined (my $greeting = <$sock>)) {
+ if ($greeting !~ /^Rspamd version/) {
+ die "not rspamd greeting line $greeting";
+ }
+ }
+ if ($cfg{'command'} =~ /^learn$/i) {
+ my $input;
+ die "statfile is not specified to learn command" if !$cfg{'statfile'};
+
+ while (defined (my $line = <>)) {
+ $input .= $line;
+ }
+
+ if (do_ctrl_auth ($sock)) {
+ my $len = length ($input);
+ print "Sending $len bytes...\n";
+ syswrite $sock, "learn $cfg{'statfile'} $len" . $CRLF;
+ syswrite $sock, $input . $CRLF;
+ if (defined (my $reply = <$sock>)) {
+ if ($reply =~ /^learn ok/) {
+ print "Learn succeed\n";
+ }
+ else {
+ print "Learn failed\n";
+ }
+ }
+ }
+ else {
+ print "Authentication failed\n";
+ }
+ }
+ elsif ($cfg{'command'} =~ /(reload|shutdown)/i) {
+ if (do_ctrl_auth ($sock)) {
+ syswrite $sock, $cfg{'command'} . $CRLF;
+ while (defined (my $line = <$sock>)) {
+ last if $line =~ /^END/;
+ print $line;
+ }
+ }
+ else {
+ print "Authentication failed\n";
+ }
}
else {
- my $addr = gethostbyname($cfg{'host'});
- if (!$addr) {
- die "cannot resolve $cfg{'host'}";
+ syswrite $sock, $cfg{'command'} . $CRLF;
+ while (defined (my $line = <$sock>)) {
+ last if $line =~ /^END/;
+ print $line;
}
- $sin = sockaddr_in ($cfg{'port'}, $addr);
}
-
- connect (SOCK, $sin) or die "cannot connect to socket $cfg{'host'}:$cfg{'port'}";
}
-my $input;
-while (defined (my $line = <>)) {
- $input .= $line;
+while (my $param = shift) {
+ if ($param eq '-c') {
+ my $value = shift;
+ if ($value) {
+ if (-r $value) {
+ $cfg{'conf_file'} = $value;
+ }
+ else {
+ die "config file $value is not readable";
+ }
+ }
+ else {
+ die usage();
+ }
+ }
+ elsif ($param eq '-s') {
+ my $value = shift;
+ if ($value) {
+ $cfg{'statfile'} = $value;
+ }
+ else {
+ die usage();
+ }
+ }
+ elsif ($param =~ /(SYMBOLS|SCAN|PROCESS|CHECK|REPORT_IFSPAM|REPORT)/i) {
+ $cfg{'command'} = $1;
+ $cfg{'control'} = 0;
+ }
+ elsif ($param =~ /(STAT|LEARN|SHUTDOWN|RELOAD|UPTIME)/i) {
+ $cfg{'command'} = $1;
+ $cfg{'control'} = 1;
+ }
+ else {
+ die usage();
+ }
}
-print "Sending ". length ($input) ." bytes...\n";
+parse_config ($cfg{'control'});
+my $sock = connect_socket ();
-syswrite SOCK, "$cfg{'command'} RSPAMC/1.0 $CRLF";
-syswrite SOCK, "Content-Length: " . length ($input) . $CRLF . $CRLF;
-syswrite SOCK, $input;
-syswrite SOCK, $CRLF;
-while (<SOCK>) {
- print $_;
+if ($cfg{'control'}) {
+ do_control_command ($sock);
}
-close SOCK;
+else {
+ do_rspamc_command ($sock);
+}
+
+close ($sock);
#include "classifiers/classifiers.h"
#define CRLF "\r\n"
+#define END "END" CRLF
enum command_type {
COMMAND_PASSWORD,
}
break;
case COMMAND_QUIT:
- session->state = STATE_QUIT;
- r = snprintf (out_buf, sizeof (out_buf), "bye" CRLF);
- bufferevent_write (session->bev, out_buf, r);
+ session->state = STATE_QUIT;
break;
case COMMAND_RELOAD:
if (check_auth (cmd, session)) {
session->worker->srv->stat->connections_count);
r += snprintf (out_buf + r, sizeof (out_buf) - r, "Control connections count: %u" CRLF,
session->worker->srv->stat->control_connections_count);
- r += snprintf (out_buf + r, sizeof (out_buf) - r, "-- end of stats report" CRLF);
bufferevent_write (session->bev, out_buf, r);
}
break;
/* If uptime more than 2 hours, print as a number of days. */
if (uptime >= 2 * 3600) {
days = uptime / 86400;
- hours = (uptime % 3600) / 60;
- minutes = (uptime % 60) / 60;
+ hours = uptime / 3600 - days * 86400;
+ minutes = uptime / 60 - hours * 3600 - days * 86400;
r = snprintf (out_buf, sizeof (out_buf), "%d day%s %d hour%s %d minute%s" CRLF,
days, days > 1 ? "s" : " ",
hours, hours > 1 ? "s" : " ",
/* Else print the minutes and seconds. */
else {
hours = uptime / 3600;
- minutes = (uptime % 60) / 60;
+ minutes = uptime / 60 - hours * 3600;
r = snprintf (out_buf, sizeof (out_buf), "%d hour%s %d minite%s %d second%s" CRLF,
hours, hours > 1 ? "s" : " ",
minutes, minutes > 1 ? "s" : " ",
session->state = STATE_REPLY;
}
if (session->state != STATE_LEARN) {
+ bufferevent_write (bev, END, sizeof (END) - 1);
bufferevent_enable (bev, EV_WRITE);
}
g_strfreev (params);
}
+ else {
+ bufferevent_enable (bev, EV_WRITE);
+ }
if (s != NULL) {
free (s);
}