aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xrspamc.pl240
-rw-r--r--src/controller.c16
-rw-r--r--src/fstring.c16
-rw-r--r--src/fstring.h6
-rw-r--r--src/statfile.c6
-rw-r--r--src/tokenizers/osb.c1
-rw-r--r--src/tokenizers/tokenizers.c6
7 files changed, 222 insertions, 69 deletions
diff --git a/rspamc.pl b/rspamc.pl
index ee203b85a..46940987c 100755
--- a/rspamc.pl
+++ b/rspamc.pl
@@ -3,7 +3,7 @@
# 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
@@ -14,95 +14,215 @@ my %cfg = (
'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);
diff --git a/src/controller.c b/src/controller.c
index fb14cf8bb..56eeb042a 100644
--- a/src/controller.c
+++ b/src/controller.c
@@ -26,6 +26,7 @@
#include "classifiers/classifiers.h"
#define CRLF "\r\n"
+#define END "END" CRLF
enum command_type {
COMMAND_PASSWORD,
@@ -149,9 +150,7 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
}
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)) {
@@ -170,7 +169,6 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
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;
@@ -187,8 +185,8 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
/* 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" : " ",
@@ -201,7 +199,7 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
/* 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" : " ",
@@ -371,10 +369,14 @@ read_socket (struct bufferevent *bev, void *arg)
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);
}
diff --git a/src/fstring.c b/src/fstring.c
index 2935fe8e6..b2008c047 100644
--- a/src/fstring.c
+++ b/src/fstring.c
@@ -1,4 +1,5 @@
#include <stdlib.h>
+#include <string.h>
#include "fstring.h"
@@ -155,6 +156,21 @@ fstrcat (f_str_t *dest, f_str_t *src)
}
/*
+ * Make copy of string to 0-terminated string
+ */
+char*
+fstrcstr (f_str_t *str, memory_pool_t *pool)
+{
+ char *res;
+ res = memory_pool_alloc (pool, str->len + 1);
+
+ memcpy (res, str->begin, str->len);
+ res[str->len] = 0;
+
+ return res;
+}
+
+/*
* Push one character to fstr
*/
int
diff --git a/src/fstring.h b/src/fstring.h
index 6840d9088..896cd8dcf 100644
--- a/src/fstring.h
+++ b/src/fstring.h
@@ -93,4 +93,10 @@ f_str_t* fstrgrow (memory_pool_t *pool, f_str_t *orig, size_t newlen);
*/
uint32_t fstrhash (f_str_t *str);
+
+/*
+ * Make copy of string to 0-terminated string
+ */
+char* fstrcstr (f_str_t *str, memory_pool_t *pool);
+
#endif
diff --git a/src/statfile.c b/src/statfile.c
index c0a2a2487..830bc5960 100644
--- a/src/statfile.c
+++ b/src/statfile.c
@@ -337,6 +337,12 @@ statfile_pool_set_block (statfile_pool_t *pool, char *filename, uint32_t h1, uin
msg_debug ("statfile_pool_set_block: chain %u is full, starting expire", blocknum);
break;
}
+ /* First try to find block in chain */
+ if (block->hash1 == h1 && block->hash2 == h2) {
+ block->last_access = now - (time_t)header->create_time;
+ block->value = value;
+ return;
+ }
/* Check whether we have a free block in chain */
if (block->hash1 == 0 && block->hash2 == 0) {
/* Write new block here */
diff --git a/src/tokenizers/osb.c b/src/tokenizers/osb.c
index afd2febd8..451644675 100644
--- a/src/tokenizers/osb.c
+++ b/src/tokenizers/osb.c
@@ -43,6 +43,7 @@ osb_tokenize_text (struct tokenizer *tokenizer, memory_pool_t *pool, f_str_t *in
hashpipe[i] = hashpipe[i - 1];
}
hashpipe[0] = fstrhash (&token);
+ msg_debug ("osb_tokenize_text: text token %s, hash: %d", fstrcstr (&token, pool), hashpipe[0]);
for (i = 1; i < FEATURE_WINDOW_SIZE; i ++) {
h1 = hashpipe[0]* primes[0] + hashpipe[i] * primes[i<<1];
diff --git a/src/tokenizers/tokenizers.c b/src/tokenizers/tokenizers.c
index 280ebd477..f0481e00d 100644
--- a/src/tokenizers/tokenizers.c
+++ b/src/tokenizers/tokenizers.c
@@ -60,11 +60,13 @@ get_next_word (f_str_t *buf, f_str_t *token)
pos = token->begin;
/* Skip non graph symbols */
- while (remain-- && !g_ascii_isgraph (*pos ++)) {
+ while (remain-- && !g_ascii_isgraph (*pos)) {
token->begin ++;
+ pos ++;
}
- while (remain-- && g_ascii_isgraph (*pos ++)) {
+ while (remain-- && g_ascii_isgraph (*pos)) {
token->len ++;
+ pos ++;
}
if (token->len == 0) {