Srs tcp_table(5) daemon.
authorPierre Habouzit <madcoder@debian.org>
Wed, 29 Aug 2007 16:51:58 +0000 (18:51 +0200)
committerPierre Habouzit <madcoder@debian.org>
Wed, 29 Aug 2007 16:51:58 +0000 (18:51 +0200)
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
Makefile
common.c [new file with mode: 0644]
common.h
common.ld [new file with mode: 0644]
postlicyd.c
srsd.c

index 7ef024d..8811918 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -40,11 +40,11 @@ TESTS    = tst-rbl
 
 GENERATED = tokens.h tokens.c
 
-postlicyd_SOURCES = str.c buffer.c daemon.c rbl.c postfix.c \
-                   postlicyd.c $(GENERATED)
+postlicyd_SOURCES = common.c str.c buffer.c daemon.c rbl.c postfix.c \
+                   $(GENERATED) postlicyd.c
 postlicyd_LIBADD = -lpthread
 
-srsd_SOURCES = str.c daemon.c srsd.c
+srsd_SOURCES = common.c buffer.c str.c daemon.c srsd.c
 srsd_LIBADD = -lsrs2
 
 tst-rbl_SOURCES = tst-rbl.c
@@ -82,8 +82,8 @@ headers:
 
 .SECONDEXPANSION:
 
-$(PROGRAMS) $(TESTS): $$(patsubst %.c,.%.o,$$($$@_SOURCES)) Makefile
-       $(CC) -o $@ $(CFLAGS) $($@_CFLAGS) $(filter %.o,$^) $(LDFLAGS) $($@_LIBADD) $(filter %.a,$^)
+$(PROGRAMS) $(TESTS): $$(patsubst %.c,.%.o,$$($$@_SOURCES)) Makefile common.ld
+       $(CC) -o $@ $(CFLAGS) $($@_CFLAGS) $(filter %.ld,$^) $(filter %.o,$^) $(LDFLAGS) $($@_LIBADD) $(filter %.a,$^)
 
 -include $(foreach p,$(PROGRAMS) $(TESTS),$(patsubst %.c,.%.dep,$(filter %.c,$($p_SOURCES))))
 
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..520e77f
--- /dev/null
+++ b/common.c
@@ -0,0 +1,91 @@
+/******************************************************************************/
+/*          postlicyd: a postfix policy daemon with a lot of features         */
+/*          ~~~~~~~~~                                                         */
+/*  ________________________________________________________________________  */
+/*                                                                            */
+/*  Redistribution and use in source and binary forms, with or without        */
+/*  modification, are permitted provided that the following conditions        */
+/*  are met:                                                                  */
+/*                                                                            */
+/*  1. Redistributions of source code must retain the above copyright         */
+/*     notice, this list of conditions and the following disclaimer.          */
+/*  2. Redistributions in binary form must reproduce the above copyright      */
+/*     notice, this list of conditions and the following disclaimer in the    */
+/*     documentation and/or other materials provided with the distribution.   */
+/*  3. The names of its contributors may not be used to endorse or promote    */
+/*     products derived from this software without specific prior written     */
+/*     permission.                                                            */
+/*                                                                            */
+/*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
+/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
+/*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
+/*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
+/*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
+/*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
+/*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
+/*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
+/*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
+/*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
+/*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
+/******************************************************************************/
+
+/*
+ * Copyright © 2007 Pierre Habouzit
+ */
+
+#include "common.h"
+
+sig_atomic_t cleanexit = false;
+sig_atomic_t sigint    = false;
+sig_atomic_t sighup    = false;
+
+void common_sighandler(int sig)
+{
+    static time_t lastintr = 0;
+    time_t now = time(NULL);
+
+    switch (sig) {
+      case SIGINT:
+        if (sigint) {
+            if (now - lastintr >= 1)
+                break;
+        } else {
+            lastintr = now;
+            sigint   = true;
+        }
+        return;
+
+      case SIGTERM:
+        break;
+
+      case SIGHUP:
+        sighup = true;
+        return;
+
+      default:
+        return;
+    }
+
+    syslog(LOG_ERR, "Killed...");
+    exit(-1);
+}
+
+void common_initialize(void)
+{
+    extern initcall_t __madinit_start, __madinit_end;
+
+    initcall_t *call_p = &__madinit_start;
+    while (call_p < &__madinit_end) {
+        (*call_p++)();
+    }
+}
+
+void common_shutdown(void)
+{
+    extern exitcall_t __madexit_start, __madexit_end;
+
+    exitcall_t *call_p = &__madexit_end;
+    while (call_p > &__madexit_start) {
+        (*--call_p)();
+    }
+}
index 6a885d9..75825eb 100644 (file)
--- a/common.h
+++ b/common.h
@@ -39,6 +39,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <pthread.h>
+#include <signal.h>
 #include <stdbool.h>
 #include <stdbool.h>
 #include <stdio.h>
         syslog(LOG_ERR, "%s:%d:%s: %s: %m",             \
                __FILE__, __LINE__, __func__, fun)
 
+typedef int  (*initcall_t)(void);
+typedef void (*exitcall_t)(void);
+
+#define __init __attribute__((__used__,__section__(".mad.init")))
+#define __exit __attribute__((__used__,__section__(".mad.exit")))
+
+#define module_init(fn)  static initcall_t __init_##fn __init = fn;
+#define module_exit(fn)  static exitcall_t __exit_##fn __exit = fn;
+
+/* common.c */
+extern sig_atomic_t cleanexit;
+extern sig_atomic_t sigint;
+extern sig_atomic_t sighup;
+
+void common_sighandler(int sig);
+void common_initialize(void);
+void common_shutdown(void);
+
 #endif
diff --git a/common.ld b/common.ld
new file mode 100644 (file)
index 0000000..3ef8a1b
--- /dev/null
+++ b/common.ld
@@ -0,0 +1,11 @@
+SECTIONS {
+    .data : {
+        __madinit_start = . ;
+        *( .mad.init )
+        __madinit_end = . ;
+
+        __madexit_start = . ;
+        *( .mad.exit )
+        __madexit_end = . ;
+    }
+}
index e9eb1c2..d7878d3 100644 (file)
 
 #include "common.h"
 
-static sig_atomic_t cleanexit = false;
-static sig_atomic_t sigint    = false;
-static volatile int nbthreads = 0;
+volatile int nbthreads = 0;
 
-static void main_sighandler(int sig)
-{
-    static time_t lastintr = 0;
-    time_t now = time(NULL);
-
-    switch (sig) {
-      case SIGINT:
-        if (sigint) {
-            if (now - lastintr >= 1)
-                break;
-        } else {
-            lastintr = now;
-            sigint   = true;
-        }
-        return;
-
-      case SIGTERM:
-        break;
-
-      default:
-        return;
-    }
-
-    syslog(LOG_ERR, "Killed...");
-    exit(-1);
-}
-
-static void main_initialize(void)
+static int main_initialize(void)
 {
     openlog("postlicyd", LOG_PID, LOG_MAIL);
     signal(SIGPIPE, SIG_IGN);
-    signal(SIGINT,  &main_sighandler);
-    signal(SIGTERM, &main_sighandler);
+    signal(SIGINT,  &common_sighandler);
+    signal(SIGTERM, &common_sighandler);
     syslog(LOG_INFO, "Starting...");
+    return 0;
 }
 
 void *job_run(void *_fd)
@@ -118,15 +90,17 @@ static void main_shutdown(void)
     closelog();
 }
 
+module_init(main_initialize);
+module_exit(main_shutdown);
+
 int main(void)
 {
-    if (atexit(main_shutdown)) {
+    if (atexit(common_shutdown)) {
         fputs("Cannot hook my atexit function, quitting !\n", stderr);
         return EXIT_FAILURE;
     }
 
-    main_initialize();
+    common_initialize();
     main_loop();
-    main_shutdown();
     return EXIT_SUCCESS;
 }
diff --git a/srsd.c b/srsd.c
index 7d25046..cd5c125 100644 (file)
--- a/srsd.c
+++ b/srsd.c
  */
 
 #include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/epoll.h>
 #include <sys/stat.h>
 
 #include <srs2.h>
 
 #include "common.h"
+#include "daemon.h"
 #include "mem.h"
+#include "buffer.h"
 
-static srs_t *srs = NULL;
+#define DEFAULT_ENCODER_PORT    10000
+#define DEFAULT_DECODER_PORT    10001
+#define __tostr(x)  #x
+#define STR(x)      __tostr(x)
 
-static int read_sfile(const char *sfile)
+/* srs encoder/decoder/listener worker {{{ */
+
+typedef struct srsd_t {
+    unsigned listener : 1;
+    unsigned decoder  : 1;
+    unsigned watchwr  : 1;
+    int fd;
+    buffer_t ibuf;
+    buffer_t obuf;
+} srsd_t;
+
+static srsd_t *srsd_new(void)
 {
-    FILE *f;
+    srsd_t *srsd = p_new(srsd_t, 1);
+    srsd->fd = -1;
+    return srsd;
+}
+
+static void srsd_delete(srsd_t **srsd)
+{
+    if (*srsd) {
+        if ((*srsd)->fd >= 0)
+            close((*srsd)->fd);
+        buffer_wipe(&(*srsd)->ibuf);
+        buffer_wipe(&(*srsd)->obuf);
+        p_delete(srsd);
+    }
+}
+
+int process_srs(srs_t *srs, const char *domain, srsd_t *srsd)
+{
+    while (srsd->ibuf.len > 4) {
+        char buf[BUFSIZ], *p, *q, *nl;
+        int err;
+
+        if (strncmp("get ", srsd->ibuf.data, 4)) {
+            syslog(LOG_ERR, "bad request, not starting with \"get \"");
+            return -1;
+        }
+
+        nl = strchr(srsd->ibuf.data + 4, '\n');
+        if (!nl)
+            return 0;
+
+        for (p = srsd->ibuf.data + 4; p < nl && isspace(*p); p++);
+        for (q = nl++; q >= p && isspace(*q); *q-- = '\0');
+
+        if (p == q) {
+            syslog(LOG_WARNING, "empty request");
+            goto skip;
+        }
+
+        if (srsd->decoder) {
+            err = srs_reverse(srs, buf, ssizeof(buf), p);
+        } else {
+            err = srs_forward(srs, buf, ssizeof(buf), p, domain);
+        }
+
+        if (err == 0) {
+            buffer_addstr(&srsd->obuf, "200 ");
+            buffer_addstr(&srsd->obuf, buf);
+            buffer_addstr(&srsd->obuf, "\r\n");
+        } else {
+            switch (SRS_ERROR_TYPE(err)) {
+              case SRS_ERRTYPE_SRS:
+              case SRS_ERRTYPE_SYNTAX:
+                buffer_addstr(&srsd->obuf, "500 ");
+                break;
+              default:
+                buffer_addstr(&srsd->obuf, "400 ");
+                break;
+            }
+            buffer_addstr(&srsd->obuf, srs_strerror(err));
+            buffer_addstr(&srsd->obuf, "\r\n");
+        }
+
+      skip:
+        buffer_consume(&srsd->ibuf, nl - srsd->ibuf.data);
+    }
+
+    return 0;
+}
+
+int start_listener(int epollfd, int port, bool decoder)
+{
+    struct sockaddr_in addr = {
+        .sin_family = AF_INET,
+        .sin_addr   = { htonl(INADDR_LOOPBACK) },
+    };
+    struct epoll_event evt = { .events = EPOLLIN };
+    srsd_t *tmp;
+    int sock;
+
+    addr.sin_port = htons(port);
+    sock = tcp_listen((const struct sockaddr *)&addr, sizeof(addr));
+    if (sock < 0) {
+        return -1;
+    }
+
+    evt.data.ptr  = tmp = srsd_new();
+    tmp->fd       = sock;
+    tmp->decoder  = decoder;
+    tmp->listener = true;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &evt) < 0) {
+        UNIXERR("epoll_ctl");
+        return -1;
+    }
+    return 0;
+}
+
+/* }}} */
+/* administrivia {{{ */
+
+static int main_initialize(void)
+{
+    openlog("srsd", LOG_PID, LOG_MAIL);
+    signal(SIGPIPE, SIG_IGN);
+    signal(SIGINT,  &common_sighandler);
+    signal(SIGTERM, &common_sighandler);
+    signal(SIGHUP,  &common_sighandler);
+    syslog(LOG_INFO, "Starting...");
+    return 0;
+}
+
+static void main_shutdown(void)
+{
+    syslog(LOG_INFO, cleanexit ? "Stopping..." : "Unclean exit...");
+    closelog();
+}
+
+module_init(main_initialize);
+module_exit(main_shutdown);
+
+void usage(void)
+{
+    fputs("usage: srsd [ -e <port> ] [ -d <port> ] domain secrets\n"
+          "\n"
+          "    -e <port>    port to listen to for encoding requests\n"
+          "                 (default: "STR(DEFAULT_ENCODER_PORT)")\n"
+          "    -d <port>    port to listen to for decoding requests\n"
+          "                 (default: "STR(DEFAULT_DECODER_PORT)")\n"
+         , stderr);
+}
+
+/* }}} */
+
+int main_loop(srs_t *srs, const char *domain, int port_enc, int port_dec)
+{
+    int exitcode = EXIT_SUCCESS;
+    int epollfd = epoll_create(128);
+
+    if (epollfd < 0) {
+        UNIXERR("epoll_create");
+        exitcode = EXIT_FAILURE;
+        goto error;
+    }
+
+    if (start_listener(epollfd, port_enc, false) < 0)
+        return EXIT_FAILURE;
+    if (start_listener(epollfd, port_dec, true) < 0)
+        return EXIT_FAILURE;
+
+    while (!sigint) {
+        struct epoll_event evts[1024];
+        int n;
+
+        n = epoll_wait(epollfd, evts, countof(evts), -1);
+        if (n < 0) {
+            if (errno != EAGAIN && errno != EINTR) {
+                UNIXERR("epoll_wait");
+                exitcode = EXIT_FAILURE;
+                break;
+            }
+            continue;
+        }
+
+        while (--n >= 0) {
+            srsd_t *srsd = evts[n].data.ptr;
+
+            if (srsd->listener) {
+                struct epoll_event evt = { .events = EPOLLIN };
+                srsd_t *tmp;
+                int sock;
+
+                sock = accept(srsd->fd, NULL, NULL);
+                if (sock < 0) {
+                    UNIXERR("accept");
+                    continue;
+                }
+
+                evt.data.ptr = tmp = srsd_new();
+                tmp->decoder = srsd->decoder;
+                tmp->fd      = sock;
+                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &evt) < 0) {
+                    UNIXERR("epoll_ctl");
+                    srsd_delete(&tmp);
+                    close(sock);
+                }
+                continue;
+            }
+
+            if (evts[n].events & EPOLLIN) {
+                int res = buffer_read(&srsd->ibuf, srsd->fd, -1);
+
+                if ((res < 0 && errno != EINTR && errno != EAGAIN)
+                ||  res == 0)
+                {
+                    srsd_delete(&srsd);
+                    continue;
+                }
+
+                if (process_srs(srs, domain, srsd) < 0) {
+                    srsd_delete(&srsd);
+                    continue;
+                }
+            }
+
+            if ((evts[n].events & EPOLLOUT) && srsd->obuf.len) {
+                int res = write(srsd->fd, srsd->obuf.data, srsd->obuf.len);
+
+                if (res < 0 && errno != EINTR && errno != EAGAIN) {
+                    srsd_delete(&srsd);
+                    continue;
+                }
+
+                if (res > 0) {
+                    buffer_consume(&srsd->obuf, res);
+                }
+            }
+
+            if (srsd->watchwr == !srsd->obuf.len) {
+                struct epoll_event evt = {
+                    .events   = EPOLLIN | (srsd->obuf.len ? EPOLLOUT : 0),
+                    .data.ptr = srsd,
+                };
+                if (epoll_ctl(epollfd, EPOLL_CTL_MOD, srsd->fd, &evt) < 0) {
+                    UNIXERR("epoll_ctl");
+                    srsd_delete(&srsd);
+                    continue;
+                }
+                srsd->watchwr = srsd->obuf.len != 0;
+            }
+        }
+    }
+
+    close(epollfd);
+
+  error:
+    cleanexit = true;
+    return exitcode;
+}
+
+static srs_t *srs_read_secrets(const char *sfile)
+{
+    srs_t *srs;
     char buf[BUFSIZ];
-    srs_t *newsrs;
+    FILE *f;
+    int lineno = 0;
 
     f = fopen(sfile, "r");
     if (!f) {
         UNIXERR("fopen");
-        return -1;
+        return NULL;
     }
 
-    newsrs = srs_new();
+    srs = srs_new();
 
     while (fgets(buf, sizeof(buf), f)) {
         int n = strlen(buf);
 
-        if (buf[n - 1] != '\n')
+        ++lineno;
+        if (buf[n - 1] != '\n') {
+            syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno);
             goto error;
-        while (n > 0 && isspace((unsigned char)buf[n - 1]))
-            buf[--n] = '\0';
-        if (n > 0)
-            srs_add_secret(newsrs, buf);
+        }
+
+        srs_add_secret(srs, buf);
     }
-    fclose(f);
 
-    if (srs) {
-        srs_free(srs);
+    if (!lineno) {
+        syslog(LOG_CRIT, "%s: empty file, no secrets", sfile);
+        goto error;
     }
-    srs = newsrs;
-    return 0;
 
-  error:
     fclose(f);
-    srs_free(newsrs);
-    return -1;
-}
+    return srs;
 
-static void help(void)
-{
-    puts(
-            "Usage: srs-c [ -r | -d domain ] -f sfile -e sender\n"
-            "Perform an SRS encoding / decoding\n"
-            "\n"
-            "    -r          perform an SRS decoding\n"
-            "    -d domain   use that domain (required for encoding)\n"
-            "\n"
-            "    -f sfile    secret file for decoding.  the first line is taken if -s omitted\n"
-            "\n"
-            "    -e sender   the sender address we want to encode/decode\n"
-          );
-    exit(1);
+  error:
+    fclose(f);
+    srs_free(srs);
+    return NULL;
 }
 
 int main(int argc, char *argv[])
 {
-    char *res    = NULL;
-    char *domain = NULL;
-    char *sender = NULL;
-    char *sfile  = NULL;
-
-    int    opt   = 0;
-    bool   rev   = false;
-    int    err   = 0;
-
-    while ((opt = getopt(argc, argv, "d:e:f:r")) != -1) {
-        switch (opt) {
-            case 'd': domain = optarg;  break;
-            case 'e': sender = optarg;  break;
-            case 'f': sfile  = optarg;  break;
-            case 'r': rev    = true;    break;
-        }
-    }
+    int port_enc = DEFAULT_ENCODER_PORT;
+    int port_dec = DEFAULT_DECODER_PORT;
+
+    srs_t *srs;
 
-    if (!sender || !sfile || !(rev||domain)) {
-        help();
+    if (atexit(common_shutdown)) {
+        fputs("Cannot hook my atexit function, quitting !\n", stderr);
+        return EXIT_FAILURE;
     }
+    common_initialize();
 
-    if (read_sfile(sfile) < 0)
-        return -1;
+    for (int c = 0; (c = getopt(argc, argv, "he:d:")) >= 0; ) {
+        switch (c) {
+          case 'e':
+            port_enc = atoi(optarg);
+            break;
+          case 'd':
+            port_dec = atoi(optarg);
+            break;
+          default:
+            usage();
+            return EXIT_FAILURE;
+        }
+    }
 
-    if (rev) {
-        err = srs_reverse_alloc(srs, &res, sender);
-    } else {
-        err = srs_forward_alloc(srs, &res, sender, domain);
+    if (argc - optind != 2) {
+        usage();
+        return EXIT_FAILURE;
     }
 
-    if (res == NULL) {
-        fprintf(stderr, "%s\n", srs_strerror(err));
-        return -1;
+    srs = srs_read_secrets(argv[optind + 1]);
+    if (!srs) {
+        return EXIT_FAILURE;
     }
-    puts(res);
-    return 0;
+
+    return main_loop(srs, argv[optind], port_enc, port_dec);
 }