factored the source into smaller files covering single functionality
authorChristian Thaeter <ct@pipapo.org>
Fri, 12 Oct 2007 09:17:59 +0000 (11:17 +0200)
committerChristian Thaeter <ct@pipapo.org>
Fri, 12 Oct 2007 09:17:59 +0000 (11:17 +0200)
Makefile.am
src/rxpd.c [deleted file]
src/rxpd.h
src/rxpd_base.c [new file with mode: 0644]
src/rxpd_buffer.c [new file with mode: 0644]
src/rxpd_connection.c [new file with mode: 0644]
src/rxpd_connection_cmd.c [new file with mode: 0644]
src/rxpd_file.c [new file with mode: 0644]
src/rxpd_rule.c [new file with mode: 0644]
src/rxpd_socket.c [new file with mode: 0644]

index be5515a..02e95ab 100644 (file)
@@ -24,11 +24,16 @@ bin_PROGRAMS = rxpd
 rxpd_CFLAGS = $(CFLAGS) -std=gnu99 -Wall -Wextra
 rxpd_LDADD = -levent
 
 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
diff --git a/src/rxpd.c b/src/rxpd.c
deleted file mode 100644 (file)
index 7688c8b..0000000
+++ /dev/null
@@ -1,1050 +0,0 @@
-/*
-    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);
-}
index 0635c7c..649e0a4 100644 (file)
@@ -206,6 +206,9 @@ rxpd_buffer_init (struct rxpd_buffer* self, struct rxpd_connection* conn);
 char*
 rxpd_buffer_readline (struct rxpd_buffer* self, int again);
 
 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)
 {
 inline static enum rxpd_buffer_state_e
 rxpd_buffer_state (struct rxpd_buffer* self)
 {
diff --git a/src/rxpd_base.c b/src/rxpd_base.c
new file mode 100644 (file)
index 0000000..60566f0
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+    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;
+}
+
diff --git a/src/rxpd_buffer.c b/src/rxpd_buffer.c
new file mode 100644 (file)
index 0000000..8f54bc0
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+    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;
+}
+
+
+
diff --git a/src/rxpd_connection.c b/src/rxpd_connection.c
new file mode 100644 (file)
index 0000000..00acbff
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+    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
+    }
+}
+
diff --git a/src/rxpd_connection_cmd.c b/src/rxpd_connection_cmd.c
new file mode 100644 (file)
index 0000000..9aa3558
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+    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);
+}
diff --git a/src/rxpd_file.c b/src/rxpd_file.c
new file mode 100644 (file)
index 0000000..0a78c72
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+    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);
+}
+
+
diff --git a/src/rxpd_rule.c b/src/rxpd_rule.c
new file mode 100644 (file)
index 0000000..2877742
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+    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);
+    }
+}
+
+
diff --git a/src/rxpd_socket.c b/src/rxpd_socket.c
new file mode 100644 (file)
index 0000000..add7f7b
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+    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);
+}
+