rxpd_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Wextra
rxpd_LDADD = -levent
-rxpd_SOURCES = \
- $(rxpd_srcdir)/main.c \
- $(rxpd_srcdir)/psplay.c \
- $(rxpd_srcdir)/rxpd.c \
- $(rxpd_srcdir)/llist.h \
- $(rxpd_srcdir)/psplay.h \
- $(rxpd_srcdir)/rxpd.h
-
+rxpd_SOURCES = \
+ $(rxpd_srcdir)/rxpd.h \
+ $(rxpd_srcdir)/main.c \
+ $(rxpd_srcdir)/psplay.c \
+ $(rxpd_srcdir)/llist.h \
+ $(rxpd_srcdir)/psplay.h \
+ $(rxpd_srcdir)/rxpd_base.c \
+ $(rxpd_srcdir)/rxpd_buffer.c \
+ $(rxpd_srcdir)/rxpd_connection.c \
+ $(rxpd_srcdir)/rxpd_connection_cmd.c \
+ $(rxpd_srcdir)/rxpd_file.c \
+ $(rxpd_srcdir)/rxpd_rule.c \
+ $(rxpd_srcdir)/rxpd_socket.c
+++ /dev/null
-/*
- rxpd.c - regex policy daemon
-
- Copyright (C)
- 2007, Christian Thaeter <ct@pipapo.org>
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "rxpd.h"
-
-static struct rxpd_base global_base;
-
-struct rxpd_base*
-rxpd_init (struct event_base* eventbase)
-{
- if (global_base.basedir)
- return NULL;
-
- global_base.basedir = NULL;
-
- global_base.verbosity = LOG_WARNING;
- global_base.daemonize = 0;
- global_base.regflags = 0;
- global_base.policy = NULL;
-
- if (!eventbase)
- rxpd_die ("no eventbase provided");
-
- global_base.eventbase = eventbase;
-
- psplay_init_root (&global_base.files, rxpd_file_cmp, rxpd_file_delete);
- llist_init (&global_base.sockets);
-
- rxpd_log (&global_base, LOG_DEBUG, PACKAGE_NAME" initialized\n");
- return &global_base;
-}
-
-
-void
-rxpd_destroy (void)
-{
- if (global_base.basedir)
- {
- free (global_base.basedir);
- psplay_destroy_root (&global_base.files);
- LLIST_WHILE_HEAD (&global_base.sockets, n)
- {
- struct rxpd_socket* socket = (struct rxpd_socket*)n;
- rxpd_socket_delete (socket);
- }
- }
-}
-
-void
-rxpd_log (struct rxpd_base* self, int level, const char* fmt, ...)
-{
- va_list ap;
- va_start (ap, fmt);
- if (level <= (self?self->verbosity:LOG_DEBUG))
- {
- if (!self || self->daemonize)
- vsyslog (level, fmt, ap);
- vfprintf (stderr, fmt, ap);
- }
- va_end (ap);
-}
-
-void
-rxpd_die (const char* fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vsyslog (LOG_EMERG, fmt, ap);
- vfprintf (stderr, fmt, ap);
- va_end (ap);
- abort ();
-}
-
-void*
-rxpd_malloc (size_t size)
-{
- void* r;
- r = malloc (size);
- if (!r)
- rxpd_die ("Out of Memeory\n");
- return r;
-}
-
-char*
-rxpd_strdup (const char* str)
-{
- char* r;
- r = strdup (str);
- if (!r)
- rxpd_die ("Out of Memeory\n");
- return r;
-}
-
-//
-struct rxpd_rule*
-rxpd_rule_new (const char* buf)
-{
- struct rxpd_rule* self = rxpd_malloc (sizeof (struct rxpd_rule));
-
- llist_init (&self->node);
-
- if (*buf != '#')
- {
- int err;
- char* rxstart = strchr (buf, ':');
-
- if (!rxstart)
- self->string = rxpd_strdup ("#ERROR: Syntax error, line was neither a comment nor a rule");
- else
- {
- // TODO regflags from base
- err = regcomp (&self->rx, rxstart+1, REG_EXTENDED|REG_ICASE|REG_NOSUB);
-
- if (!err)
- self->string = rxpd_strdup (buf);
- else
- {
- regfree (&self->rx);
- char ebuf[256];
- size_t len = regerror (err, NULL, ebuf, 256);
- self->string = rxpd_malloc (len + strlen(buf) + 14);
- strcpy (self->string, "#ERROR: ");
- strcat (self->string, ebuf);
- strcat (self->string, " in '");
- strcat (self->string, buf);
- strcat (self->string, "'");
- }
- }
- }
- else
- self->string = rxpd_strdup (buf);
-
- return self;
-}
-
-void
-rxpd_rule_delete (struct rxpd_rule* rule)
-{
- if (rule)
- {
- llist_unlink (&rule->node);
- if (rule->string[0] != '#')
- regfree (&rule->rx);
- free (rule->string);
- free(rule);
- }
-}
-
-
-//
-struct rxpd_file*
-rxpd_file_new (struct rxpd_base* base, const char* filename)
-{
- char buf[4096];
- struct rxpd_file* self = NULL;
-
- if (!filename ||
- strcspn(filename, RXPD_FILE_ILG_CHARS) != strlen (filename) ||
- strlen (filename) + strlen (base->basedir) > 4095)
- {
- rxpd_log (base, LOG_ERR, "illegal filename: '%s'\n", filename?filename:"");
- return NULL;
- }
-
- strcpy (buf, base->basedir);
- strcat (buf, filename);
- filename = rxpd_strdup (buf);
-
- self = rxpd_malloc (sizeof (struct rxpd_file));
- self->filename = filename;
- self->base = base;
- const char* basename = strrchr (filename, '/');
- if (basename)
- ++basename;
- else
- basename = filename;
- psplay_init (&self->node, basename);
- llist_init (&self->rules);
-
- psplay_insert (&base->files, &self->node);
-
- rxpd_log (base, LOG_INFO, "new file: '%s'\n", filename);
- return self;
-}
-
-void
-rxpd_file_delete (PSplay f)
-{
- if (f)
- {
- struct rxpd_file* file = (struct rxpd_file*)f;
- LLIST_WHILE_HEAD (&file->rules, n)
- {
- struct rxpd_rule* node = (struct rxpd_rule*)n;
- rxpd_rule_delete (node);
- }
- free ((void*)file->filename);
- free (f);
- }
-}
-
-int
-rxpd_file_load (struct rxpd_file* self)
-{
- FILE* f = fopen (self->filename, "r");
- // TODO error handling
- if (f)
- {
- /* First purge old rules */
- LLIST_WHILE_HEAD (&self->rules, n)
- {
- struct rxpd_rule* node = (struct rxpd_rule*)n;
- rxpd_rule_delete (node);
- }
-
- // TODO test excess line length = error
- char buf[4096];
-
- rxpd_log (self->base, LOG_NOTICE, "loading '%s'\n", self->filename);
-
- while (fgets (buf, 4096, f))
- {
- size_t last = strlen(buf);
- if (buf[last-1] == '\n')
- buf[last-1] = '\0';
-
- struct rxpd_rule* rule;
- rule = rxpd_rule_new (buf);
- if (!rule)
- abort();
-
- rxpd_log (self->base, LOG_DEBUG, "new rule '%s'\n", rule->string);
-
- llist_insert_tail (&self->rules, &rule->node);
- }
-
- fclose (f);
- return 1;
- }
- else
- {
- rxpd_log (self->base, LOG_ERR, "failed loading '%s'\n", self->filename);
- return 0;
- }
-}
-
-int
-rxpd_file_save (struct rxpd_file* self)
-{
- FILE* f = fopen (self->filename, "w");
- // TODO error handling
- if (f)
- {
- LLIST_FOREACH (&self->rules, n)
- {
- struct rxpd_rule* node = (struct rxpd_rule*)n;
- fprintf (f, "%s\n", node->string);
- }
-
- fclose (f);
- rxpd_log (self->base, LOG_NOTICE, "saved '%s'\n", self->filename);
- return 1;
- }
- else
- {
- rxpd_log (self->base, LOG_ERR, "failed saving '%s'\n", self->filename);
- return 0;
- }
-}
-
-int
-rxpd_file_cmp (const void* A, const void* B)
-{
- return strcmp (A, B);
-}
-
-
-//
-struct rxpd_socket*
-rxpd_socket_new_tcp4 (struct rxpd_base* base, const char* addr, unsigned short port)
-{
- struct rxpd_socket* self;
- self = rxpd_malloc (sizeof (struct rxpd_socket));
-
- self->base = base;
-
- llist_init (&self->node);
-
- // TODO all abort() shall become rxpd_die
- self->fd = socket (PF_INET, SOCK_STREAM, 0);
- if (self->fd == -1)
- abort ();
-
- struct sockaddr_in listen_addr;
- memset (&listen_addr, 0, sizeof (listen_addr));
-
- listen_addr.sin_family = AF_INET;
- if (addr)
- {
- if (inet_aton (addr, &listen_addr.sin_addr) == 0)
- abort();
- }
- else
- listen_addr.sin_addr.s_addr = INADDR_ANY;
-
- listen_addr.sin_port = htons(port);
-
- if (bind (self->fd, (struct sockaddr*)&listen_addr, sizeof (listen_addr)) == -1)
- abort();
-
- static int yes = 1;
- if (setsockopt (self->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
- abort ();
-
- if (listen (self->fd, 20) == -1)
- abort ();
-
- self->rxpd_socket_addr = rxpd_socket_tcp4addr;
-
- event_set (&self->ev, self->fd, EV_READ, rxpd_socket_accept, self);
- llist_insert_tail (&base->sockets, &self->node);
-
- rxpd_log (base, LOG_INFO, "Listening on tcp4:%d\n", port);
- return self;
-}
-
-int
-rxpd_socket_tcp4addr (struct rxpd_connection* conn, char* dst, const char* pfx, size_t size)
-{
- struct sockaddr_in peer;
- socklen_t len = sizeof (peer);
- getpeername (conn->fd, (struct sockaddr*)&peer, &len);
-
- char* addr;
- addr = inet_ntoa (peer.sin_addr);
- if (sizeof (":tcp4:") + strlen (pfx) + strlen (addr) > size)
- return 0;
-
- strcat (dst, pfx);
- strcat (dst, ":tcp4:");
- strcat (dst, addr);
- return 1;
-}
-
-
-
-void
-rxpd_socket_delete (struct rxpd_socket* self)
-{
- if (self)
- {
- event_del (&self->ev);
- llist_unlink (&self->node);
- close (self->fd);
- }
- free (self);
-}
-
-struct rxpd_socket*
-rxpd_socket_schedule (struct rxpd_socket* self)
-{
- if (self)
- {
- event_add (&self->ev, NULL);
- }
- return self;
-}
-
-struct rxpd_socket*
-rxpd_socket_suspend (struct rxpd_socket* self)
-{
- if (self)
- {
- event_del (&self->ev);
- }
- return self;
-}
-
-void
-rxpd_socket_accept (int fd, short event, void* ptr)
-{
- (void) event;
- struct rxpd_socket* self = ptr;
-
- struct rxpd_connection* conn =
- rxpd_connection_new (self, fd);
-
- rxpd_connection_schedule (conn);
- rxpd_socket_schedule (self);
-}
-
-///
-
-struct rxpd_buffer*
-rxpd_buffer_init (struct rxpd_buffer* self, struct rxpd_connection* conn)
-{
- self->conn = conn;
- self->state = RXPD_OK;
- self->eol = self->eob = self->buffer;
- self->buffer [4095] = '\0';
- return self;
-}
-
-
-char*
-rxpd_buffer_readline (struct rxpd_buffer* self, int again)
-{
- int fd = self->conn->fd;
-
- if (self->eol < self->eob)
- {
- //there was a line pending, shift buffer left
- memmove (self->buffer, self->eol+1, self->eob - self->eol - 1);
- self->eob = (char*)(self->eob - (self->eol - self->buffer + 1));
- self->eol = self->buffer;
- // TODO handle \r's
- }
-
- if (!again && self->state == RXPD_OK) // we only read when again is 0, first iteration
- {
- ssize_t r = 0;
- do
- {
- r = read(fd, self->eob, 4095 - (self->eob - self->buffer));
- }
- while (r == -1 && errno == EINTR);
-
- if (r != -1)
- {
-
- if (r == 0)
- {
- shutdown (fd, SHUT_RD);
- self->state = RXPD_EOF;
- }
-
- self->eob += r;
- }
- else
- self->state = RXPD_ERROR;
- }
-
- // find next newline, terminate string there
- for (char* i = self->buffer; i < self->eob; ++i)
- {
- if (*i == '\n')
- {
- *i = '\0';
- self->eol = i;
- break;
- }
- }
-
- // TODO handle buffer overfulls
-
- return (self->eob == self->buffer) ? NULL : self->buffer;
-}
-
-/*
-void
-rxpd_buffer_write(int fd, short event, void* ptr)
-{
- struct rxpd_buffer* self = (struct rxpd_buffer*) ptr;
-
- ssize_t n = write(int fd, const void *buf, size_t count);
-
-}
-*/
-
-int
-rxpd_buffer_printf (struct rxpd_buffer* self, const char* fmt, ...)
-{
- // for now we do a blocking write, needs to be fixed some day
- va_list ap;
- va_start(ap, fmt);
- int n = vsnprintf (self->buffer, 4096, fmt, ap);
- va_end(ap);
-
- write (self->conn->fd, self->buffer, n);
-
- if (n>4095)
- return 0;
-
- return 1;
-}
-
-
-
-
-///
-
-struct rxpd_connection*
-rxpd_connection_new (struct rxpd_socket* socket, int fd)
-{
- struct rxpd_connection* self;
- self = rxpd_malloc (sizeof (struct rxpd_connection));
-
- self->fd = accept (fd, NULL, 0);
- if (self->fd == -1)
- abort ();
-
- self->socket = socket;
- self->file = NULL;
- self->tmp_str = NULL;
- llist_init (&self->tmp_list);
-
- rxpd_buffer_init (&self->in, self);
- rxpd_buffer_init (&self->out, self);
-
- event_set (&self->ev, self->fd, EV_READ, rxpd_connection_parse_cmd, self);
-
- // TODO more info
- rxpd_log (socket->base, LOG_INFO, "incoming connection\n");
- return self;
-}
-
-void
-rxpd_connection_delete (struct rxpd_connection* self)
-{
- if (self)
- {
- event_del (&self->ev);
- close (self->fd);
- free (self->tmp_str);
- LLIST_WHILE_HEAD (&self->tmp_list, n)
- {
- struct rxpd_rule* node = (struct rxpd_rule*)n;
- rxpd_rule_delete (node);
- }
- }
- free (self);
-}
-
-struct rxpd_connection*
-rxpd_connection_schedule (struct rxpd_connection* self)
-{
- if (self)
- {
- event_add (&self->ev, NULL);
- }
- return self;
-}
-
-int
-rxpd_connection_check_policy (struct rxpd_connection* self, char* line)
-{
- struct rxpd_base* base = self->socket->base;
- if (base->policy)
- {
- char buf[256];
- buf[0] = '\0';
-
- if (!self->socket->rxpd_socket_addr (self, buf, line, 256))
- {
- rxpd_log (base, LOG_ERR, "policy line too long\n");
- return 0;
- }
-
- rxpd_log (base, LOG_DEBUG, "policy check '%s'\n", buf);
-
- char* match = NULL;
- LLIST_FOREACH (&base->policy->rules, n)
- {
- struct rxpd_rule* rule = (struct rxpd_rule*)n;
- if (rule->string[0] != '#')
- {
- if (regexec (&rule->rx, buf, 0, NULL, 0) == 0)
- {
- match = rule->string;
- break;
- }
- }
- }
-
- if (!match || strncmp("ACCEPT:", match, sizeof("ACCEPT:")-1) != 0)
- {
- rxpd_log (base, LOG_WARNING, "access denied '%s'\n", buf);
- return 0;
- }
- }
- return 1;
-}
-void
-rxpd_connection_parse_cmd (int fd, short event, void* ptr)
-{
- (void) event;
-
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
- struct rxpd_base* base = self->socket->base;
-
- char* line;
- line = rxpd_buffer_readline (&self->in, 0);
-
- if (!line)
- {
- rxpd_log (base, LOG_ERR, "no data\n");
- rxpd_buffer_printf (&self->out, "#ERROR: no data\n");
- close (fd);
- return;
- }
-
- rxpd_log (base, LOG_DEBUG, "parse command '%s'\n", line);
-
- static const struct cmd_table
- {
- enum rxpd_cmd_e nr;
- const char* cmd;
- size_t sz;
- } cmds[] =
- {
-#define RXPD_CMD(cmd) {RXPD_CMD_##cmd, #cmd":", sizeof (#cmd)},
- RXPD_COMMANDS
-#undef RXPD_CMD
- {0, NULL, 0}
- };
-
- const struct cmd_table* i;
- for (i = cmds; i->cmd; ++i)
- if (strncmp (line, i->cmd, i->sz) == 0)
- break;
- if (!i->cmd)
- {
- rxpd_log (base, LOG_ERR, "no command\n");
- rxpd_buffer_printf (&self->out, "#ERROR: no command\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (!rxpd_connection_check_policy (self, line))
- {
- rxpd_buffer_printf (&self->out, "#ERROR: access denied\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (line[i->sz])
- {
- // rulename provided
- self->file = (struct rxpd_file*) psplay_find (&base->files, &line[i->sz]);
-
- if (!self->file)
- {
- self->file = rxpd_file_new (base, &line[i->sz]);
- if (!self->file)
- {
- rxpd_log (base, LOG_ERR, "illeagal filename\n");
- rxpd_buffer_printf (&self->out, "#ERROR: illegal filename\n");
- rxpd_connection_delete (self);
- return;
- }
- }
- }
-
- // dispatch
- switch (i->nr)
- {
-#define RXPD_CMD(cmd) \
-case RXPD_CMD_##cmd: \
- event_set (&self->ev, self->fd, EV_READ, rxpd_connection_cmd_##cmd, self); \
- rxpd_connection_cmd_##cmd (fd, 0, ptr); \
- break;
- RXPD_COMMANDS
-#undef RXPD_CMD
- }
-}
-
-
-
-void
-rxpd_connection_cmd_CHECK (int fd, short event, void* ptr)
-{
- (void) fd;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (event == EV_READ)
- {
- int again = -1;
- char* line;
- while ((line = rxpd_buffer_readline (&self->in, ++again)))
- {
- if (*line == '\0')
- {
- rxpd_buffer_printf (&self->out, "#OK:\n");
- }
- else
- {
- LLIST_FOREACH (&self->file->rules, n)
- {
- struct rxpd_rule* rule = (struct rxpd_rule*)n;
- if (rule->string[0] != '#')
- {
- if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
- {
- rxpd_buffer_printf (&self->out, "%s\n", rule->string);
- break;
- }
- }
- }
- }
- }
- }
- else if (!self->file)
- {
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (rxpd_buffer_state (&self->in) == RXPD_OK)
- rxpd_connection_schedule (self);
- else
- {
- if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
- rxpd_buffer_printf (&self->out, "#ERROR:\n");
- rxpd_connection_delete (self);
- }
-}
-
-
-
-static void
-rxpd_connection_APPEND_PREPEND_helper (short event, void* ptr, int do_append)
-{
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (event == EV_READ)
- {
- int again = -1;
- char* line;
-
- while ((line = rxpd_buffer_readline (&self->in, ++again)))
- {
- if (*line)
- {
- struct rxpd_rule* rule;
- rule = rxpd_rule_new (line);
- if (!rule)
- abort();
-
- llist_insert_tail (&self->tmp_list, &rule->node);
- }
- else goto finish; /* move along, look elsewhere! This goto is not harmful and saves some code. */
- }
- }
- else if (!event && !self->file)
- {
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (rxpd_buffer_state (&self->in) == RXPD_OK)
- rxpd_connection_schedule (self);
- else
- {
- // TODO should also print error when any rule compilation failed, use tmp_str to save case?
- if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
- rxpd_buffer_printf (&self->out, "#ERROR:\n");
- else
- {
- finish:
- rxpd_buffer_printf (&self->out, "#OK:\n");
- }
-
- if (do_append)
- llist_insertlist_prev (&self->file->rules, &self->tmp_list);
- else
- llist_insertlist_next (&self->file->rules, &self->tmp_list);
- rxpd_connection_delete (self);
- }
-}
-
-void
-rxpd_connection_cmd_APPEND (int fd, short event, void* ptr)
-{
- (void) fd;
- rxpd_connection_APPEND_PREPEND_helper (event, ptr, 1);
-}
-
-void
-rxpd_connection_cmd_PREPEND (int fd, short event, void* ptr)
-{
- (void) fd;
- rxpd_connection_APPEND_PREPEND_helper (event, ptr, 0);
-}
-
-
-void
-rxpd_connection_cmd_REMOVE (int fd, short event, void* ptr)
-{
- (void) fd;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (event == EV_READ)
- {
- int again = -1;
- char* line;
- while ((line = rxpd_buffer_readline (&self->in, ++again)))
- {
- LLIST_FOREACH (&self->file->rules, n)
- {
- struct rxpd_rule* rule = (struct rxpd_rule*)n;
- if (strcmp (rule->string, line) == 0)
- {
- LList tmp = llist_prev (n);
- rxpd_rule_delete (rule);
- n = tmp;
- rxpd_buffer_printf (&self->out, "#OK:\n");
- goto done;
- }
- }
- rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
- done:
- ;
- }
- }
- else if (!self->file)
- {
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (rxpd_buffer_state (&self->in) == RXPD_OK)
- rxpd_connection_schedule (self);
- else
- {
- if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
- rxpd_buffer_printf (&self->out, "#ERROR:\n");
- rxpd_connection_delete (self);
- }
-}
-
-
-static int
-rxpd_connection_do_REPLACE (struct rxpd_connection* self)
-{
- struct rxpd_rule* rule;
-
- LLIST_FOREACH (&self->file->rules, n)
- {
- rule = (struct rxpd_rule*)n;
- if (strcmp (rule->string, self->tmp_str) == 0)
- goto found;
- }
- return 0;
- found:
- llist_insertlist_next (&rule->node, &self->tmp_list);
- rxpd_rule_delete (rule);
- free (self->tmp_str);
- self->tmp_str = NULL;
- return 1;
-}
-
-void
-rxpd_connection_cmd_REPLACE (int fd, short event, void* ptr)
-{
- (void) fd;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (event == EV_READ)
- {
- int again = -1;
- char* line;
- while ((line = rxpd_buffer_readline (&self->in, ++again)))
- {
- if (self->tmp_str)
- {
- if (*line)
- {
- struct rxpd_rule* rule;
- rule = rxpd_rule_new (line);
- if (!rule)
- abort();
-
- llist_insert_tail (&self->tmp_list, &rule->node);
- }
- /* TODO handle empty lines? */
- }
- else
- self->tmp_str = rxpd_strdup (line);
- }
- }
- else if (!self->file)
- {
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (rxpd_buffer_state (&self->in) == RXPD_OK)
- rxpd_connection_schedule (self);
- else
- {
- if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
- rxpd_buffer_printf (&self->out, "#ERROR:\n");
- else
- {
- if (rxpd_connection_do_REPLACE (self))
- rxpd_buffer_printf (&self->out, "#OK:\n");
- else
- rxpd_buffer_printf (&self->out, "#ERROR: rule matching '%s'\n", self->tmp_str);
- }
-
- rxpd_connection_delete (self);
- }
-}
-
-void
-rxpd_connection_cmd_LOAD (int fd, short event, void* ptr)
-{
- (void) fd;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (!event)
- {
- if (self->file)
- {
- if (rxpd_file_load (self->file))
- {
- rxpd_buffer_printf (&self->out, "#OK:\n");
- }
- else
- {
- rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
- }
- }
- else
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- }
-}
-
-void
-rxpd_connection_cmd_SAVE (int fd, short event, void* ptr)
-{
- (void) fd;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (!event)
- {
- if (self->file)
- {
- if (rxpd_file_save (self->file))
- {
- rxpd_buffer_printf (&self->out, "#OK:\n");
- }
- else
- {
- rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
- }
- }
- else
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- }
-}
-
-void
-rxpd_connection_cmd_DUMP (int fd, short event, void* ptr)
-{
- (void) fd;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
-
- if (!event && !self->file)
- {
- rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
- rxpd_connection_delete (self);
- return;
- }
-
- if (llist_is_empty (&self->file->rules))
- rxpd_buffer_printf (&self->out, "#OK:\n");
- else
- {
- LLIST_FOREACH (&self->file->rules, n)
- {
- struct rxpd_rule* rule = (struct rxpd_rule*)n;
- rxpd_buffer_printf (&self->out, "%s\n", rule->string);
- }
- }
-
- rxpd_connection_delete (self);
-}
-
-
-static psplay_delete_t
-walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
-{
- (void) level;
- struct rxpd_file* file = (struct rxpd_file*) node;
- struct rxpd_connection* conn = (struct rxpd_connection*) data;
-
- if (which == PSPLAY_INORDER)
- rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
-
- return PSPLAY_CONT;
-}
-
-
-void
-rxpd_connection_cmd_LIST (int fd, short event, void* ptr)
-{
- (void) fd;
- (void) event;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
- struct rxpd_base* base = self->socket->base;
-
- if (psplay_isempty_root (&base->files))
- rxpd_buffer_printf (&self->out, "#OK:\n");
- else
- psplay_walk (&base->files, NULL, walk_LIST, 0, ptr);
-
- rxpd_connection_delete (self);
-}
-
-void
-rxpd_connection_cmd_SHUTDOWN (int fd, short event, void* ptr)
-{
- (void) fd;
- (void) event;
- struct rxpd_connection* self = (struct rxpd_connection*) ptr;
- struct rxpd_base* base = self->socket->base;
- // destroy all sockets
- LLIST_WHILE_HEAD (&base->sockets, n)
- {
- struct rxpd_socket* socket = (struct rxpd_socket*)n;
- rxpd_socket_delete (socket);
- }
- rxpd_buffer_printf (&self->out, "#OK:\n");
- rxpd_connection_delete (self);
-}
char*
rxpd_buffer_readline (struct rxpd_buffer* self, int again);
+int
+rxpd_buffer_printf (struct rxpd_buffer* self, const char* fmt, ...);
+
inline static enum rxpd_buffer_state_e
rxpd_buffer_state (struct rxpd_buffer* self)
{
--- /dev/null
+/*
+ rxpd_base.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+static struct rxpd_base global_base;
+
+struct rxpd_base*
+rxpd_init (struct event_base* eventbase)
+{
+ if (global_base.basedir)
+ return NULL;
+
+ global_base.basedir = NULL;
+
+ global_base.verbosity = LOG_WARNING;
+ global_base.daemonize = 0;
+ global_base.regflags = 0;
+ global_base.policy = NULL;
+
+ if (!eventbase)
+ rxpd_die ("no eventbase provided");
+
+ global_base.eventbase = eventbase;
+
+ psplay_init_root (&global_base.files, rxpd_file_cmp, rxpd_file_delete);
+ llist_init (&global_base.sockets);
+
+ rxpd_log (&global_base, LOG_DEBUG, PACKAGE_NAME" initialized\n");
+ return &global_base;
+}
+
+
+void
+rxpd_destroy (void)
+{
+ if (global_base.basedir)
+ {
+ free (global_base.basedir);
+ psplay_destroy_root (&global_base.files);
+ LLIST_WHILE_HEAD (&global_base.sockets, n)
+ {
+ struct rxpd_socket* socket = (struct rxpd_socket*)n;
+ rxpd_socket_delete (socket);
+ }
+ }
+}
+
+void
+rxpd_log (struct rxpd_base* self, int level, const char* fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ if (level <= (self?self->verbosity:LOG_DEBUG))
+ {
+ if (!self || self->daemonize)
+ vsyslog (level, fmt, ap);
+ vfprintf (stderr, fmt, ap);
+ }
+ va_end (ap);
+}
+
+void
+rxpd_die (const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsyslog (LOG_EMERG, fmt, ap);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ abort ();
+}
+
+void*
+rxpd_malloc (size_t size)
+{
+ void* r;
+ r = malloc (size);
+ if (!r)
+ rxpd_die ("Out of Memeory\n");
+ return r;
+}
+
+char*
+rxpd_strdup (const char* str)
+{
+ char* r;
+ r = strdup (str);
+ if (!r)
+ rxpd_die ("Out of Memeory\n");
+ return r;
+}
+
--- /dev/null
+/*
+ rxpd_buffer.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+struct rxpd_buffer*
+rxpd_buffer_init (struct rxpd_buffer* self, struct rxpd_connection* conn)
+{
+ self->conn = conn;
+ self->state = RXPD_OK;
+ self->eol = self->eob = self->buffer;
+ self->buffer [4095] = '\0';
+ return self;
+}
+
+
+char*
+rxpd_buffer_readline (struct rxpd_buffer* self, int again)
+{
+ int fd = self->conn->fd;
+
+ if (self->eol < self->eob)
+ {
+ //there was a line pending, shift buffer left
+ memmove (self->buffer, self->eol+1, self->eob - self->eol - 1);
+ self->eob = (char*)(self->eob - (self->eol - self->buffer + 1));
+ self->eol = self->buffer;
+ // TODO handle \r's
+ }
+
+ if (!again && self->state == RXPD_OK) // we only read when again is 0, first iteration
+ {
+ ssize_t r = 0;
+ do
+ {
+ r = read(fd, self->eob, 4095 - (self->eob - self->buffer));
+ }
+ while (r == -1 && errno == EINTR);
+
+ if (r != -1)
+ {
+
+ if (r == 0)
+ {
+ shutdown (fd, SHUT_RD);
+ self->state = RXPD_EOF;
+ }
+
+ self->eob += r;
+ }
+ else
+ self->state = RXPD_ERROR;
+ }
+
+ // find next newline, terminate string there
+ for (char* i = self->buffer; i < self->eob; ++i)
+ {
+ if (*i == '\n')
+ {
+ *i = '\0';
+ self->eol = i;
+ break;
+ }
+ }
+
+ // TODO handle buffer overfulls
+
+ return (self->eob == self->buffer) ? NULL : self->buffer;
+}
+
+/*
+void
+rxpd_buffer_write(int fd, short event, void* ptr)
+{
+ struct rxpd_buffer* self = (struct rxpd_buffer*) ptr;
+
+ ssize_t n = write(int fd, const void *buf, size_t count);
+
+}
+*/
+
+int
+rxpd_buffer_printf (struct rxpd_buffer* self, const char* fmt, ...)
+{
+ // for now we do a blocking write, needs to be fixed some day
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf (self->buffer, 4096, fmt, ap);
+ va_end(ap);
+
+ write (self->conn->fd, self->buffer, n);
+
+ if (n>4095)
+ return 0;
+
+ return 1;
+}
+
+
+
--- /dev/null
+/*
+ rxpd_connection.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+struct rxpd_connection*
+rxpd_connection_new (struct rxpd_socket* socket, int fd)
+{
+ struct rxpd_connection* self;
+ self = rxpd_malloc (sizeof (struct rxpd_connection));
+
+ self->fd = accept (fd, NULL, 0);
+ if (self->fd == -1)
+ abort ();
+
+ self->socket = socket;
+ self->file = NULL;
+ self->tmp_str = NULL;
+ llist_init (&self->tmp_list);
+
+ rxpd_buffer_init (&self->in, self);
+ rxpd_buffer_init (&self->out, self);
+
+ event_set (&self->ev, self->fd, EV_READ, rxpd_connection_parse_cmd, self);
+
+ // TODO more info
+ rxpd_log (socket->base, LOG_INFO, "incoming connection\n");
+ return self;
+}
+
+void
+rxpd_connection_delete (struct rxpd_connection* self)
+{
+ if (self)
+ {
+ event_del (&self->ev);
+ close (self->fd);
+ free (self->tmp_str);
+ LLIST_WHILE_HEAD (&self->tmp_list, n)
+ {
+ struct rxpd_rule* node = (struct rxpd_rule*)n;
+ rxpd_rule_delete (node);
+ }
+ }
+ free (self);
+}
+
+struct rxpd_connection*
+rxpd_connection_schedule (struct rxpd_connection* self)
+{
+ if (self)
+ {
+ event_add (&self->ev, NULL);
+ }
+ return self;
+}
+
+int
+rxpd_connection_check_policy (struct rxpd_connection* self, char* line)
+{
+ struct rxpd_base* base = self->socket->base;
+ if (base->policy)
+ {
+ char buf[256];
+ buf[0] = '\0';
+
+ if (!self->socket->rxpd_socket_addr (self, buf, line, 256))
+ {
+ rxpd_log (base, LOG_ERR, "policy line too long\n");
+ return 0;
+ }
+
+ rxpd_log (base, LOG_DEBUG, "policy check '%s'\n", buf);
+
+ char* match = NULL;
+ LLIST_FOREACH (&base->policy->rules, n)
+ {
+ struct rxpd_rule* rule = (struct rxpd_rule*)n;
+ if (rule->string[0] != '#')
+ {
+ if (regexec (&rule->rx, buf, 0, NULL, 0) == 0)
+ {
+ match = rule->string;
+ break;
+ }
+ }
+ }
+
+ if (!match || strncmp("ACCEPT:", match, sizeof("ACCEPT:")-1) != 0)
+ {
+ rxpd_log (base, LOG_WARNING, "access denied '%s'\n", buf);
+ return 0;
+ }
+ }
+ return 1;
+}
+void
+rxpd_connection_parse_cmd (int fd, short event, void* ptr)
+{
+ (void) event;
+
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+ struct rxpd_base* base = self->socket->base;
+
+ char* line;
+ line = rxpd_buffer_readline (&self->in, 0);
+
+ if (!line)
+ {
+ rxpd_log (base, LOG_ERR, "no data\n");
+ rxpd_buffer_printf (&self->out, "#ERROR: no data\n");
+ close (fd);
+ return;
+ }
+
+ rxpd_log (base, LOG_DEBUG, "parse command '%s'\n", line);
+
+ static const struct cmd_table
+ {
+ enum rxpd_cmd_e nr;
+ const char* cmd;
+ size_t sz;
+ } cmds[] =
+ {
+#define RXPD_CMD(cmd) {RXPD_CMD_##cmd, #cmd":", sizeof (#cmd)},
+ RXPD_COMMANDS
+#undef RXPD_CMD
+ {0, NULL, 0}
+ };
+
+ const struct cmd_table* i;
+ for (i = cmds; i->cmd; ++i)
+ if (strncmp (line, i->cmd, i->sz) == 0)
+ break;
+ if (!i->cmd)
+ {
+ rxpd_log (base, LOG_ERR, "no command\n");
+ rxpd_buffer_printf (&self->out, "#ERROR: no command\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (!rxpd_connection_check_policy (self, line))
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: access denied\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (line[i->sz])
+ {
+ // rulename provided
+ self->file = (struct rxpd_file*) psplay_find (&base->files, &line[i->sz]);
+
+ if (!self->file)
+ {
+ self->file = rxpd_file_new (base, &line[i->sz]);
+ if (!self->file)
+ {
+ rxpd_log (base, LOG_ERR, "illeagal filename\n");
+ rxpd_buffer_printf (&self->out, "#ERROR: illegal filename\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+ }
+ }
+
+ // dispatch
+ switch (i->nr)
+ {
+#define RXPD_CMD(cmd) \
+case RXPD_CMD_##cmd: \
+ event_set (&self->ev, self->fd, EV_READ, rxpd_connection_cmd_##cmd, self); \
+ rxpd_connection_cmd_##cmd (fd, 0, ptr); \
+ break;
+ RXPD_COMMANDS
+#undef RXPD_CMD
+ }
+}
+
--- /dev/null
+/*
+ rxpd_connection_cmd.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+void
+rxpd_connection_cmd_CHECK (int fd, short event, void* ptr)
+{
+ (void) fd;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (event == EV_READ)
+ {
+ int again = -1;
+ char* line;
+ while ((line = rxpd_buffer_readline (&self->in, ++again)))
+ {
+ if (*line == '\0')
+ {
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ }
+ else
+ {
+ LLIST_FOREACH (&self->file->rules, n)
+ {
+ struct rxpd_rule* rule = (struct rxpd_rule*)n;
+ if (rule->string[0] != '#')
+ {
+ if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
+ {
+ rxpd_buffer_printf (&self->out, "%s\n", rule->string);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (!self->file)
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (rxpd_buffer_state (&self->in) == RXPD_OK)
+ rxpd_connection_schedule (self);
+ else
+ {
+ if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
+ rxpd_buffer_printf (&self->out, "#ERROR:\n");
+ rxpd_connection_delete (self);
+ }
+}
+
+
+
+static void
+rxpd_connection_APPEND_PREPEND_helper (short event, void* ptr, int do_append)
+{
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (event == EV_READ)
+ {
+ int again = -1;
+ char* line;
+
+ while ((line = rxpd_buffer_readline (&self->in, ++again)))
+ {
+ if (*line)
+ {
+ struct rxpd_rule* rule;
+ rule = rxpd_rule_new (line);
+ if (!rule)
+ abort();
+
+ llist_insert_tail (&self->tmp_list, &rule->node);
+ }
+ else goto finish; /* move along, look elsewhere! This goto is not harmful and saves some code. */
+ }
+ }
+ else if (!event && !self->file)
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (rxpd_buffer_state (&self->in) == RXPD_OK)
+ rxpd_connection_schedule (self);
+ else
+ {
+ // TODO should also print error when any rule compilation failed, use tmp_str to save case?
+ if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
+ rxpd_buffer_printf (&self->out, "#ERROR:\n");
+ else
+ {
+ finish:
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ }
+
+ if (do_append)
+ llist_insertlist_prev (&self->file->rules, &self->tmp_list);
+ else
+ llist_insertlist_next (&self->file->rules, &self->tmp_list);
+ rxpd_connection_delete (self);
+ }
+}
+
+void
+rxpd_connection_cmd_APPEND (int fd, short event, void* ptr)
+{
+ (void) fd;
+ rxpd_connection_APPEND_PREPEND_helper (event, ptr, 1);
+}
+
+void
+rxpd_connection_cmd_PREPEND (int fd, short event, void* ptr)
+{
+ (void) fd;
+ rxpd_connection_APPEND_PREPEND_helper (event, ptr, 0);
+}
+
+
+void
+rxpd_connection_cmd_REMOVE (int fd, short event, void* ptr)
+{
+ (void) fd;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (event == EV_READ)
+ {
+ int again = -1;
+ char* line;
+ while ((line = rxpd_buffer_readline (&self->in, ++again)))
+ {
+ LLIST_FOREACH (&self->file->rules, n)
+ {
+ struct rxpd_rule* rule = (struct rxpd_rule*)n;
+ if (strcmp (rule->string, line) == 0)
+ {
+ LList tmp = llist_prev (n);
+ rxpd_rule_delete (rule);
+ n = tmp;
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ goto done;
+ }
+ }
+ rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
+ done:
+ ;
+ }
+ }
+ else if (!self->file)
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (rxpd_buffer_state (&self->in) == RXPD_OK)
+ rxpd_connection_schedule (self);
+ else
+ {
+ if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
+ rxpd_buffer_printf (&self->out, "#ERROR:\n");
+ rxpd_connection_delete (self);
+ }
+}
+
+
+static int
+rxpd_connection_do_REPLACE (struct rxpd_connection* self)
+{
+ struct rxpd_rule* rule;
+
+ LLIST_FOREACH (&self->file->rules, n)
+ {
+ rule = (struct rxpd_rule*)n;
+ if (strcmp (rule->string, self->tmp_str) == 0)
+ goto found;
+ }
+ return 0;
+ found:
+ llist_insertlist_next (&rule->node, &self->tmp_list);
+ rxpd_rule_delete (rule);
+ free (self->tmp_str);
+ self->tmp_str = NULL;
+ return 1;
+}
+
+void
+rxpd_connection_cmd_REPLACE (int fd, short event, void* ptr)
+{
+ (void) fd;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (event == EV_READ)
+ {
+ int again = -1;
+ char* line;
+ while ((line = rxpd_buffer_readline (&self->in, ++again)))
+ {
+ if (self->tmp_str)
+ {
+ if (*line)
+ {
+ struct rxpd_rule* rule;
+ rule = rxpd_rule_new (line);
+ if (!rule)
+ abort();
+
+ llist_insert_tail (&self->tmp_list, &rule->node);
+ }
+ /* TODO handle empty lines? */
+ }
+ else
+ self->tmp_str = rxpd_strdup (line);
+ }
+ }
+ else if (!self->file)
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (rxpd_buffer_state (&self->in) == RXPD_OK)
+ rxpd_connection_schedule (self);
+ else
+ {
+ if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
+ rxpd_buffer_printf (&self->out, "#ERROR:\n");
+ else
+ {
+ if (rxpd_connection_do_REPLACE (self))
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ else
+ rxpd_buffer_printf (&self->out, "#ERROR: rule matching '%s'\n", self->tmp_str);
+ }
+
+ rxpd_connection_delete (self);
+ }
+}
+
+void
+rxpd_connection_cmd_LOAD (int fd, short event, void* ptr)
+{
+ (void) fd;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (!event)
+ {
+ if (self->file)
+ {
+ if (rxpd_file_load (self->file))
+ {
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ }
+ else
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
+ }
+ }
+ else
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ }
+}
+
+void
+rxpd_connection_cmd_SAVE (int fd, short event, void* ptr)
+{
+ (void) fd;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (!event)
+ {
+ if (self->file)
+ {
+ if (rxpd_file_save (self->file))
+ {
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ }
+ else
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
+ }
+ }
+ else
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ }
+}
+
+void
+rxpd_connection_cmd_DUMP (int fd, short event, void* ptr)
+{
+ (void) fd;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+
+ if (!event && !self->file)
+ {
+ rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
+ rxpd_connection_delete (self);
+ return;
+ }
+
+ if (llist_is_empty (&self->file->rules))
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ else
+ {
+ LLIST_FOREACH (&self->file->rules, n)
+ {
+ struct rxpd_rule* rule = (struct rxpd_rule*)n;
+ rxpd_buffer_printf (&self->out, "%s\n", rule->string);
+ }
+ }
+
+ rxpd_connection_delete (self);
+}
+
+
+static psplay_delete_t
+walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
+{
+ (void) level;
+ struct rxpd_file* file = (struct rxpd_file*) node;
+ struct rxpd_connection* conn = (struct rxpd_connection*) data;
+
+ if (which == PSPLAY_INORDER)
+ rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
+
+ return PSPLAY_CONT;
+}
+
+
+void
+rxpd_connection_cmd_LIST (int fd, short event, void* ptr)
+{
+ (void) fd;
+ (void) event;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+ struct rxpd_base* base = self->socket->base;
+
+ if (psplay_isempty_root (&base->files))
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ else
+ psplay_walk (&base->files, NULL, walk_LIST, 0, ptr);
+
+ rxpd_connection_delete (self);
+}
+
+void
+rxpd_connection_cmd_SHUTDOWN (int fd, short event, void* ptr)
+{
+ (void) fd;
+ (void) event;
+ struct rxpd_connection* self = (struct rxpd_connection*) ptr;
+ struct rxpd_base* base = self->socket->base;
+ // destroy all sockets
+ LLIST_WHILE_HEAD (&base->sockets, n)
+ {
+ struct rxpd_socket* socket = (struct rxpd_socket*)n;
+ rxpd_socket_delete (socket);
+ }
+ rxpd_buffer_printf (&self->out, "#OK:\n");
+ rxpd_connection_delete (self);
+}
--- /dev/null
+/*
+ rxpd_file.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+struct rxpd_file*
+rxpd_file_new (struct rxpd_base* base, const char* filename)
+{
+ char buf[4096];
+ struct rxpd_file* self = NULL;
+
+ if (!filename ||
+ strcspn(filename, RXPD_FILE_ILG_CHARS) != strlen (filename) ||
+ strlen (filename) + strlen (base->basedir) > 4095)
+ {
+ rxpd_log (base, LOG_ERR, "illegal filename: '%s'\n", filename?filename:"");
+ return NULL;
+ }
+
+ strcpy (buf, base->basedir);
+ strcat (buf, filename);
+ filename = rxpd_strdup (buf);
+
+ self = rxpd_malloc (sizeof (struct rxpd_file));
+ self->filename = filename;
+ self->base = base;
+ const char* basename = strrchr (filename, '/');
+ if (basename)
+ ++basename;
+ else
+ basename = filename;
+ psplay_init (&self->node, basename);
+ llist_init (&self->rules);
+
+ psplay_insert (&base->files, &self->node);
+
+ rxpd_log (base, LOG_INFO, "new file: '%s'\n", filename);
+ return self;
+}
+
+void
+rxpd_file_delete (PSplay f)
+{
+ if (f)
+ {
+ struct rxpd_file* file = (struct rxpd_file*)f;
+ LLIST_WHILE_HEAD (&file->rules, n)
+ {
+ struct rxpd_rule* node = (struct rxpd_rule*)n;
+ rxpd_rule_delete (node);
+ }
+ free ((void*)file->filename);
+ free (f);
+ }
+}
+
+int
+rxpd_file_load (struct rxpd_file* self)
+{
+ FILE* f = fopen (self->filename, "r");
+ // TODO error handling
+ if (f)
+ {
+ /* First purge old rules */
+ LLIST_WHILE_HEAD (&self->rules, n)
+ {
+ struct rxpd_rule* node = (struct rxpd_rule*)n;
+ rxpd_rule_delete (node);
+ }
+
+ // TODO test excess line length = error
+ char buf[4096];
+
+ rxpd_log (self->base, LOG_NOTICE, "loading '%s'\n", self->filename);
+
+ while (fgets (buf, 4096, f))
+ {
+ size_t last = strlen(buf);
+ if (buf[last-1] == '\n')
+ buf[last-1] = '\0';
+
+ struct rxpd_rule* rule;
+ rule = rxpd_rule_new (buf);
+ if (!rule)
+ abort();
+
+ rxpd_log (self->base, LOG_DEBUG, "new rule '%s'\n", rule->string);
+
+ llist_insert_tail (&self->rules, &rule->node);
+ }
+
+ fclose (f);
+ return 1;
+ }
+ else
+ {
+ rxpd_log (self->base, LOG_ERR, "failed loading '%s'\n", self->filename);
+ return 0;
+ }
+}
+
+int
+rxpd_file_save (struct rxpd_file* self)
+{
+ FILE* f = fopen (self->filename, "w");
+ // TODO error handling
+ if (f)
+ {
+ LLIST_FOREACH (&self->rules, n)
+ {
+ struct rxpd_rule* node = (struct rxpd_rule*)n;
+ fprintf (f, "%s\n", node->string);
+ }
+
+ fclose (f);
+ rxpd_log (self->base, LOG_NOTICE, "saved '%s'\n", self->filename);
+ return 1;
+ }
+ else
+ {
+ rxpd_log (self->base, LOG_ERR, "failed saving '%s'\n", self->filename);
+ return 0;
+ }
+}
+
+int
+rxpd_file_cmp (const void* A, const void* B)
+{
+ return strcmp (A, B);
+}
+
+
--- /dev/null
+/*
+ rxpd_rule.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+struct rxpd_rule*
+rxpd_rule_new (const char* buf)
+{
+ struct rxpd_rule* self = rxpd_malloc (sizeof (struct rxpd_rule));
+
+ llist_init (&self->node);
+
+ if (*buf != '#')
+ {
+ int err;
+ char* rxstart = strchr (buf, ':');
+
+ if (!rxstart)
+ self->string = rxpd_strdup ("#ERROR: Syntax error, line was neither a comment nor a rule");
+ else
+ {
+ // TODO regflags from base
+ err = regcomp (&self->rx, rxstart+1, REG_EXTENDED|REG_ICASE|REG_NOSUB);
+
+ if (!err)
+ self->string = rxpd_strdup (buf);
+ else
+ {
+ regfree (&self->rx);
+ char ebuf[256];
+ size_t len = regerror (err, NULL, ebuf, 256);
+ self->string = rxpd_malloc (len + strlen(buf) + 14);
+ strcpy (self->string, "#ERROR: ");
+ strcat (self->string, ebuf);
+ strcat (self->string, " in '");
+ strcat (self->string, buf);
+ strcat (self->string, "'");
+ }
+ }
+ }
+ else
+ self->string = rxpd_strdup (buf);
+
+ return self;
+}
+
+void
+rxpd_rule_delete (struct rxpd_rule* rule)
+{
+ if (rule)
+ {
+ llist_unlink (&rule->node);
+ if (rule->string[0] != '#')
+ regfree (&rule->rx);
+ free (rule->string);
+ free(rule);
+ }
+}
+
+
--- /dev/null
+/*
+ rxpd_socket.c - regex policy daemon
+
+ Copyright (C)
+ 2007, Christian Thaeter <ct@pipapo.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "rxpd.h"
+
+struct rxpd_socket*
+rxpd_socket_new_tcp4 (struct rxpd_base* base, const char* addr, unsigned short port)
+{
+ struct rxpd_socket* self;
+ self = rxpd_malloc (sizeof (struct rxpd_socket));
+
+ self->base = base;
+
+ llist_init (&self->node);
+
+ // TODO all abort() shall become rxpd_die
+ self->fd = socket (PF_INET, SOCK_STREAM, 0);
+ if (self->fd == -1)
+ abort ();
+
+ struct sockaddr_in listen_addr;
+ memset (&listen_addr, 0, sizeof (listen_addr));
+
+ listen_addr.sin_family = AF_INET;
+ if (addr)
+ {
+ if (inet_aton (addr, &listen_addr.sin_addr) == 0)
+ abort();
+ }
+ else
+ listen_addr.sin_addr.s_addr = INADDR_ANY;
+
+ listen_addr.sin_port = htons(port);
+
+ if (bind (self->fd, (struct sockaddr*)&listen_addr, sizeof (listen_addr)) == -1)
+ abort();
+
+ static int yes = 1;
+ if (setsockopt (self->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
+ abort ();
+
+ if (listen (self->fd, 20) == -1)
+ abort ();
+
+ self->rxpd_socket_addr = rxpd_socket_tcp4addr;
+
+ event_set (&self->ev, self->fd, EV_READ, rxpd_socket_accept, self);
+ llist_insert_tail (&base->sockets, &self->node);
+
+ rxpd_log (base, LOG_INFO, "Listening on tcp4:%d\n", port);
+ return self;
+}
+
+int
+rxpd_socket_tcp4addr (struct rxpd_connection* conn, char* dst, const char* pfx, size_t size)
+{
+ struct sockaddr_in peer;
+ socklen_t len = sizeof (peer);
+ getpeername (conn->fd, (struct sockaddr*)&peer, &len);
+
+ char* addr;
+ addr = inet_ntoa (peer.sin_addr);
+ if (sizeof (":tcp4:") + strlen (pfx) + strlen (addr) > size)
+ return 0;
+
+ strcat (dst, pfx);
+ strcat (dst, ":tcp4:");
+ strcat (dst, addr);
+ return 1;
+}
+
+void
+rxpd_socket_delete (struct rxpd_socket* self)
+{
+ if (self)
+ {
+ event_del (&self->ev);
+ llist_unlink (&self->node);
+ close (self->fd);
+ }
+ free (self);
+}
+
+struct rxpd_socket*
+rxpd_socket_schedule (struct rxpd_socket* self)
+{
+ if (self)
+ {
+ event_add (&self->ev, NULL);
+ }
+ return self;
+}
+
+struct rxpd_socket*
+rxpd_socket_suspend (struct rxpd_socket* self)
+{
+ if (self)
+ {
+ event_del (&self->ev);
+ }
+ return self;
+}
+
+void
+rxpd_socket_accept (int fd, short event, void* ptr)
+{
+ (void) event;
+ struct rxpd_socket* self = ptr;
+
+ struct rxpd_connection* conn =
+ rxpd_connection_new (self, fd);
+
+ rxpd_connection_schedule (conn);
+ rxpd_socket_schedule (self);
+}
+