aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2011-01-31 20:33:12 +0300
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2011-01-31 20:33:12 +0300
commit2a383446ab6254777693544e5acda2a805947f81 (patch)
treeb929dc0b151cfda62f0c1256673ede9d8cdf5de0 /lib
parentbc15f7bcfbe45c51917d153b8174e6b76b5e00bb (diff)
downloadrspamd-2a383446ab6254777693544e5acda2a805947f81.tar.gz
rspamd-2a383446ab6254777693544e5acda2a805947f81.zip
* Add C client for rspamd that is using librspamdclient
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt2
-rw-r--r--lib/librspamdclient.c230
2 files changed, 193 insertions, 39 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index e57d18a2e..f625b2833 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,5 +1,5 @@
# Librspamd
-SET(LIBRSPAMDSRC librspamdclient.c ../src/util.c ../src/upstream.c)
+SET(LIBRSPAMDSRC librspamdclient.c ../src/util.c ../src/upstream.c ../src/mem_pool.c)
ADD_LIBRARY(rspamdclient SHARED ${LIBRSPAMDSRC})
ADD_LIBRARY(rspamdclient_static STATIC ${LIBRSPAMDSRC})
diff --git a/lib/librspamdclient.c b/lib/librspamdclient.c
index 8fa280440..d7568eb62 100644
--- a/lib/librspamdclient.c
+++ b/lib/librspamdclient.c
@@ -92,7 +92,7 @@ symbol_free_func (gpointer arg)
}
static struct rspamd_connection *
-rspamd_connect_random_server (guint16 port, GError **err)
+rspamd_connect_random_server (gboolean is_control, GError **err)
{
struct rspamd_server *selected = NULL;
struct rspamd_connection *new;
@@ -122,7 +122,9 @@ rspamd_connect_random_server (guint16 port, GError **err)
new->server = selected;
new->connection_time = now;
/* Create socket */
- new->socket = make_tcp_socket (&selected->addr, port, FALSE, TRUE);
+ new->socket = make_tcp_socket (&selected->addr,
+ is_control ? selected->controller_port : selected->client_port,
+ FALSE, TRUE);
if (new->socket == -1) {
goto err;
}
@@ -159,13 +161,27 @@ rspamd_create_metric (const gchar *begin, guint len)
return new;
}
+static struct rspamd_result *
+rspamd_create_result (struct rspamd_connection *c)
+{
+ struct rspamd_result *new;
+
+ new = g_malloc (sizeof (struct rspamd_result));
+ new->conn = c;
+ new->headers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ new->metrics = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, metric_free_func);
+ new->is_ok = FALSE;
+
+ return new;
+}
+
/*
* Parse line like RSPAMD/{version} {code} {message}
*/
static gboolean
-parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err)
+parse_rspamd_first_line (struct rspamd_connection *conn, guint len, GError **err)
{
- gchar *b = c->in_buf->str + sizeof("RSPAMD/") - 1, *p;
+ gchar *b = conn->in_buf->str + sizeof("RSPAMD/") - 1, *p, *c;
guint remain = len - sizeof("RSPAMD/") + 1, state = 0, next_state;
p = b;
@@ -187,6 +203,9 @@ parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err)
if (g_ascii_isspace (*p)) {
state = 99;
next_state = 2;
+ if (*c == '0') {
+ conn->result->is_ok = TRUE;
+ }
}
else if (!g_ascii_isdigit (*p)) {
goto err;
@@ -207,6 +226,7 @@ parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err)
/* Skip spaces */
if (!g_ascii_isspace (*p)) {
state = next_state;
+ c = p;
}
else {
p ++;
@@ -219,12 +239,13 @@ parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err)
goto err;
}
+ return TRUE;
err:
if (*err == NULL) {
*err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid protocol line: %*s at pos: %d",
- len, b, (int)(p - b));
+ remain, b, (int)(p - b));
}
- upstream_fail (&c->server->up, c->connection_time);
+ upstream_fail (&conn->server->up, conn->connection_time);
return FALSE;
}
@@ -255,7 +276,7 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er
}
else {
/* Create new metric */
- new = rspamd_create_metric (c, p - c - 1);
+ new = rspamd_create_metric (c, p - c);
if (g_hash_table_lookup (conn->result->metrics, new->name) != NULL) {
/* Duplicate metric */
metric_free_func (new);
@@ -272,13 +293,13 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er
case 1:
/* Read boolean result */
if (*p == ';') {
- if (p - c > sizeof("Skip")) {
+ if (p - c >= sizeof("Skip")) {
if (memcmp (c, "Skip", p - c - 1) == 0) {
new->is_skipped = TRUE;
}
- state = 99;
- next_state = 2;
}
+ state = 99;
+ next_state = 2;
}
p ++;
break;
@@ -297,18 +318,18 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er
break;
case 3:
/* Read / */
- if (*p != '/') {
- goto err;
- }
- else if (g_ascii_isspace (*p)) {
+ if (g_ascii_isspace (*p)) {
state = 99;
next_state = 4;
}
+ else if (*p != '/') {
+ goto err;
+ }
p ++;
break;
case 4:
/* Read required score */
- if (g_ascii_isspace (*p)) {
+ if (g_ascii_isspace (*p) || p - b == remain - 1) {
new->required_score = strtod (c, &err_str);
if (*err_str != *p) {
/* Invalid score */
@@ -321,13 +342,13 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er
break;
case 5:
/* Read / if it exists */
- if (*p != '/') {
- goto err;
- }
- else if (g_ascii_isspace (*p)) {
+ if (g_ascii_isspace (*p)) {
state = 99;
next_state = 6;
}
+ else if (*p != '/') {
+ goto err;
+ }
p ++;
break;
case 6:
@@ -358,11 +379,12 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er
if (state != 99) {
goto err;
}
+ return TRUE;
err:
if (*err == NULL) {
- *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid metric line: %*s at pos: %d",
- len, b, (int)(p - b));
+ *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid metric line: %*s at pos: %d, state: %d",
+ remain, b, (int)(p - b), state);
}
upstream_fail (&conn->server->up, conn->connection_time);
return FALSE;
@@ -395,9 +417,9 @@ parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **er
}
else {
/* Create new symbol */
- sym = g_malloc (p - c);
- sym[p - c - 1] = '\0';
- memcpy (sym, c, p - c - 1);
+ sym = g_malloc (p - c + 1);
+ sym[p - c] = '\0';
+ memcpy (sym, c, p - c);
if (g_hash_table_lookup (conn->cur_metric->symbols, sym) != NULL) {
/* Duplicate symbol */
@@ -405,6 +427,7 @@ parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **er
goto err;
}
new = g_malloc0 (sizeof (struct rspamd_symbol));
+ new->name = sym;
g_hash_table_insert (conn->cur_metric->symbols, sym, new);
state = 99;
if (*p == '(') {
@@ -467,11 +490,12 @@ parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **er
if (state != 99) {
goto err;
}
+ return TRUE;
err:
if (*err == NULL) {
*err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid symbol line: %*s at pos: %d",
- len, b, (int)(p - b));
+ remain, b, (int)(p - b));
}
upstream_fail (&conn->server->up, conn->connection_time);
return FALSE;
@@ -494,18 +518,23 @@ parse_rspamd_action_line (struct rspamd_connection *conn, guint len, GError **er
/* Read action */
if (g_ascii_isspace (*p)) {
state = 99;
- next_state = 0;
+ next_state = 1;
+ }
+ else {
+ state = 1;
}
- else if (p - b == remain - 1) {
+ break;
+ case 1:
+ if (p - b == remain - 1) {
if (p - c <= 1) {
/* Empty action name */
goto err;
}
else {
/* Create new action */
- sym = g_malloc (p - c + 1);
- sym[p - c] = '\0';
- memcpy (sym, c, p - c);
+ sym = g_malloc (p - c + 2);
+ sym[p - c + 1] = '\0';
+ memcpy (sym, c, p - c + 1);
conn->cur_metric->action = sym;
state = 99;
@@ -529,11 +558,12 @@ parse_rspamd_action_line (struct rspamd_connection *conn, guint len, GError **er
if (state != 99) {
goto err;
}
+ return TRUE;
err:
if (*err == NULL) {
*err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid action line: %*s at pos: %d",
- len, b, (int)(p - b));
+ remain, b, (int)(p - b));
}
upstream_fail (&conn->server->up, conn->connection_time);
return FALSE;
@@ -561,9 +591,9 @@ parse_rspamd_header_line (struct rspamd_connection *conn, guint len, GError **er
}
else {
/* Create header name */
- hname = g_malloc (p - c);
- hname[p - c - 1] = '\0';
- memcpy (hname, c, p - c - 1);
+ hname = g_malloc (p - c + 1);
+ hname[p - c] = '\0';
+ memcpy (hname, c, p - c);
next_state = 1;
state = 99;
}
@@ -603,11 +633,12 @@ parse_rspamd_header_line (struct rspamd_connection *conn, guint len, GError **er
if (state != 99) {
goto err;
}
+ return TRUE;
err:
if (*err == NULL) {
*err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid header line: %*s at pos: %d",
- len, b, (int)(p - b));
+ remain, b, (int)(p - b));
}
if (hname) {
g_free (hname);
@@ -682,6 +713,7 @@ read_rspamd_reply_line (struct rspamd_connection *c, GError **err)
}
/* Move remaining buffer to the begin of string */
c->in_buf = g_string_erase (c->in_buf, 0, len);
+ len = 0;
}
else {
return FALSE;
@@ -706,12 +738,13 @@ read_rspamd_reply_line (struct rspamd_connection *c, GError **err)
/* Read new data to a string */
if ((r = read (c->socket,
c->in_buf->str + c->in_buf->len,
- c->in_buf->allocated_len - c->in_buf->len)) < 0) {
+ c->in_buf->allocated_len - c->in_buf->len)) > 0) {
/* Try to parse remaining data */
- return parse_rspamd_reply_line (c, c->in_buf->len, err);
+ c->in_buf->len += r;
+ return read_rspamd_reply_line (c, err);
}
- return TRUE;
+ return FALSE;
}
/*
@@ -721,6 +754,7 @@ static gboolean
rspamd_sendfile (gint sock, gint fd, GError **err)
{
+ /* Make socket blocking for further operations */
make_socket_blocking (sock);
#ifdef HAVE_SENDFILE
# if defined(FREEBSD) || defined(DARWIN)
@@ -769,6 +803,38 @@ err:
return FALSE;
}
+static gboolean
+rspamd_send_normal_command (struct rspamd_connection *c, const gchar *command,
+ gsize clen, GHashTable *headers, GError **err)
+{
+ static gchar outbuf[16384];
+ GHashTableIter it;
+ gpointer key, value;
+ gint r;
+
+ /* Write command */
+ r = rspamd_snprintf (outbuf, sizeof (outbuf), "%s RSPAMC/1.2\r\n", command);
+ r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "Content-Length: %uz\r\n", clen);
+ /* Iterate through headers */
+ if (headers != NULL) {
+ g_hash_table_iter_init (&it, headers);
+ while (g_hash_table_iter_next (&it, &key, &value)) {
+ r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "%s: %s\r\n", key, value);
+ }
+ }
+ r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "\r\n");
+
+ if ((r = write (c->socket, outbuf, r)) == -1) {
+ if (*err == NULL) {
+ *err = g_error_new (G_RSPAMD_ERROR, errno, "Write error: %s",
+ strerror (errno));
+ }
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
rspamd_free_connection (struct rspamd_connection *c)
{
@@ -856,8 +922,44 @@ rspamd_set_timeout (guint connect_timeout, guint read_timeout)
struct rspamd_result *
rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GError **err)
{
+ struct rspamd_connection *c;
+ struct rspamd_result *res = NULL;
+
g_assert (client != NULL);
+ /* Connect to server */
+ c = rspamd_connect_random_server (FALSE, err);
+
+ if (c == NULL) {
+ return NULL;
+ }
+
+ /* Set socket blocking for writing */
+ make_socket_blocking (c->socket);
+ /* Send command */
+ if (!rspamd_send_normal_command (c, "SYMBOLS", length, headers, err)) {
+ return NULL;
+ }
+
+ /* Send message */
+ if (write (c->socket, message, length) == -1) {
+ if (*err == NULL) {
+ *err = g_error_new (G_RSPAMD_ERROR, errno, "Write error: %s",
+ strerror (errno));
+ }
+ return NULL;
+ }
+
+ /* Create result structure */
+ res = rspamd_create_result (c);
+ c->result = res;
+ /* Restore non-blocking mode for reading operations */
+ make_socket_nonblocking (c->socket);
+
+ /* Read result cycle */
+ while (read_rspamd_reply_line (c, err));
+
+ return res;
}
/*
@@ -866,8 +968,19 @@ rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GE
struct rspamd_result *
rspamd_scan_file (const guchar *filename, GHashTable *headers, GError **err)
{
+ gint fd;
g_assert (client != NULL);
+ /* Open file */
+ if ((fd = open (filename, O_RDONLY | O_CLOEXEC)) == -1) {
+ if (*err == NULL) {
+ *err = g_error_new (G_RSPAMD_ERROR, errno, "Open error for file %s: %s",
+ filename, strerror (errno));
+ }
+ return NULL;
+ }
+
+ return rspamd_scan_fd (fd, headers, err);
}
/*
@@ -876,8 +989,49 @@ rspamd_scan_file (const guchar *filename, GHashTable *headers, GError **err)
struct rspamd_result *
rspamd_scan_fd (int fd, GHashTable *headers, GError **err)
{
+ struct rspamd_connection *c;
+ struct rspamd_result *res = NULL;
+ struct stat st;
+
g_assert (client != NULL);
+ /* Connect to server */
+ c = rspamd_connect_random_server (FALSE, err);
+
+ if (c == NULL) {
+ return NULL;
+ }
+
+ /* Get length */
+ if (fstat (fd, &st) == -1) {
+ if (*err == NULL) {
+ *err = g_error_new (G_RSPAMD_ERROR, errno, "Stat error: %s",
+ strerror (errno));
+ }
+ return NULL;
+ }
+ /* Set socket blocking for writing */
+ make_socket_blocking (c->socket);
+ /* Send command */
+ if (!rspamd_send_normal_command (c, "SYMBOLS", (gsize)st.st_size, headers, err)) {
+ return NULL;
+ }
+
+ /* Send message */
+ if (!rspamd_sendfile (c->socket, fd, err)) {
+ return NULL;
+ }
+
+ /* Create result structure */
+ res = rspamd_create_result (c);
+ c->result = res;
+ /* Restore non-blocking mode for reading operations */
+ make_socket_nonblocking (c->socket);
+
+ /* Read result cycle */
+ while (read_rspamd_reply_line (c, err));
+
+ return res;
}
/*