* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */#include "rspamd_control.h"
+ */
+
+#include "config.h"
+#include "rspamd.h"
+#include "rspamd_control.h"
+#include "http.h"
+#include "unix-std.h"
+#include "utlist.h"
+
+static struct timeval io_timeout = {
+ .tv_sec = 30,
+ .tv_usec = 0
+};
+
+struct rspamd_control_reply_elt {
+ struct rspamd_control_reply reply;
+ struct event io_ev;
+ struct event tm_ev;
+ struct rspamd_worker *wrk;
+ struct rspamd_control_reply_elt *prev, *next;
+};
+
+struct rspamd_control_session {
+ gint fd;
+ struct rspamd_main *rspamd_main;
+ struct rspamd_http_connection *conn;
+ struct rspamd_control_command cmd;
+ struct rspamd_control_reply_elt *replies;
+ gboolean is_reply;
+};
+
+static const struct rspamd_control_cmd_match {
+ rspamd_ftok_t name;
+ enum rspamd_control_type type;
+} cmd_matches[] = {
+ {
+ .name = {
+ .begin = "/stat",
+ .len = 5
+ },
+ .type = RSPAMD_CONTROL_STAT
+ },
+ {
+ .name = {
+ .begin = "/reload",
+ .len = 7
+ },
+ .type = RSPAMD_CONTROL_RELOAD
+ },
+};
+
+void
+rspamd_control_send_error (struct rspamd_control_session *session,
+ gint code, const gchar *error_msg, ...)
+{
+ struct rspamd_http_message *msg;
+ va_list args;
+
+ msg = rspamd_http_new_message (HTTP_RESPONSE);
+
+ va_start (args, error_msg);
+ msg->status = rspamd_fstring_sized_new (128);
+ rspamd_vprintf_fstring (&msg->status, error_msg, args);
+ va_end (args);
+
+ msg->date = time (NULL);
+ msg->code = code;
+ msg->body = rspamd_fstring_sized_new (128);
+ rspamd_printf_fstring (&msg->body, "{\"error\":\"%V\"}", msg->status);
+ rspamd_http_connection_reset (session->conn);
+ rspamd_http_connection_write_message (session->conn,
+ msg,
+ NULL,
+ "application/json",
+ session,
+ session->fd,
+ &io_timeout,
+ session->rspamd_main->ev_base);
+}
+
+static void
+rspamd_control_send_ucl (struct rspamd_control_session *session,
+ ucl_object_t *obj)
+{
+ struct rspamd_http_message *msg;
+
+ msg = rspamd_http_new_message (HTTP_RESPONSE);
+ msg->date = time (NULL);
+ msg->code = 200;
+ msg->body = rspamd_fstring_sized_new (BUFSIZ);
+ rspamd_ucl_emit_fstring (obj, UCL_EMIT_JSON_COMPACT, &msg->body);
+ rspamd_http_connection_reset (session->conn);
+ rspamd_http_connection_write_message (session->conn,
+ msg,
+ NULL,
+ "application/json",
+ session,
+ session->fd,
+ &io_timeout,
+ session->rspamd_main->ev_base);
+}
+
+static void
+rspamd_control_connection_close (struct rspamd_control_session *session)
+{
+ struct rspamd_control_reply_elt *elt, *telt;
+
+ DL_FOREACH_SAFE (session->replies, elt, telt) {
+ g_slice_free1 (sizeof (*elt), elt);
+ }
+
+ rspamd_http_connection_unref (session->conn);
+ close (session->fd);
+ g_slice_free1 (sizeof (*session), session);
+}
+
+static void
+rspamd_control_error_handler (struct rspamd_http_connection *conn, GError *err)
+{
+ struct rspamd_control_session *session = conn->ud;
+
+ if (!session->is_reply) {
+ msg_info ("abnormally closing control connection: %e", err);
+ session->is_reply = TRUE;
+ rspamd_control_send_error (session, err->code, "%s", err->message);
+ }
+ else {
+ rspamd_control_connection_close (session);
+ }
+}
+
+static gint
+rspamd_control_finish_hadler (struct rspamd_http_connection *conn,
+ struct rspamd_http_message *msg)
+{
+ struct rspamd_control_session *session = conn->ud;
+ rspamd_ftok_t srch;
+ guint i;
+ gboolean found = FALSE;
+
+ if (!session->is_reply) {
+ if (msg->url == NULL) {
+ rspamd_control_connection_close (session);
+
+ return 0;
+ }
+
+ srch.begin = msg->url->str;
+ srch.len = msg->url->len;
+
+ session->is_reply = TRUE;
+
+ for (i = 0; i < G_N_ELEMENTS (cmd_matches); i++) {
+ if (rspamd_ftok_casecmp (&srch, &cmd_matches[i].name) == 0) {
+ session->cmd.type = cmd_matches[i].type;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ rspamd_control_send_error (session, 404, "Command not defined");
+ }
+ else {
+ rspamd_control_send_error (session, 500, "Not implemented yet");
+ }
+ }
+ else {
+ rspamd_control_connection_close (session);
+ }
+
+
+ return 0;
+}
+
+void
+rspamd_control_process_client_socket (struct rspamd_main *rspamd_main,
+ gint fd)
+{
+ struct rspamd_control_session *session;
+
+ session = g_slice_alloc0 (sizeof (*session));
+
+ session->fd = fd;
+ session->conn = rspamd_http_connection_new (NULL, rspamd_control_error_handler,
+ rspamd_control_finish_hadler, 0, RSPAMD_HTTP_SERVER, NULL);
+ session->rspamd_main = rspamd_main;
+ rspamd_http_connection_read_message (session->conn, session, session->fd,
+ &io_timeout, rspamd_main->ev_base);
+}
+
+void
+rspamd_control_worker_add_default_handler (struct rspamd_worker *worker)
+{
+
+}
+
+/**
+ * Register custom handler for a specific control command for this worker
+ */
+void
+rspamd_control_worker_add_cmd_handler (struct rspamd_worker *worker,
+ enum rspamd_control_type type,
+ rspamd_worker_control_handler handler,
+ gpointer ud)
+{
+
+}
#ifndef RSPAMD_RSPAMD_CONTROL_H
#define RSPAMD_RSPAMD_CONTROL_H
+#include "config.h"
+struct rspamd_main;
+struct rspamd_worker;
+
+enum rspamd_control_type {
+ RSPAMD_CONTROL_STAT = 0,
+ RSPAMD_CONTROL_RELOAD,
+ RSPAMD_CONTROL_MAX
+};
+
+struct rspamd_control_command {
+ enum rspamd_control_type type;
+ union {
+ struct {
+ guint unused;
+ } stat;
+ struct {
+ guint unused;
+ } reload;
+ } cmd;
+};
+
+struct rspamd_control_reply {
+ enum rspamd_control_type type;
+ union {
+ struct {
+ guint conns;
+ guint64 uptime;
+ } stat;
+ struct {
+ guint status;
+ } reload;
+ } reply;
+};
+
+typedef gboolean (*rspamd_worker_control_handler) (struct rspamd_main *rspamd_main,
+ struct rspamd_worker *worker, gint fd,
+ struct rspamd_control_command *cmd,
+ gpointer ud);
+
+/**
+ * Process client socket connection
+ */
+void rspamd_control_process_client_socket (struct rspamd_main *rspamd_main,
+ gint fd);
+
+/**
+ * Register default handlers for a worker
+ */
+void rspamd_control_worker_add_default_handler (struct rspamd_worker *worker);
+
+/**
+ * Register custom handler for a specific control command for this worker
+ */
+void rspamd_control_worker_add_cmd_handler (struct rspamd_worker *worker,
+ enum rspamd_control_type type,
+ rspamd_worker_control_handler handler,
+ gpointer ud);
#endif