From 2472ae9bd2a2a22a278ffe586fd381e70c881e8d Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 29 Aug 2007 18:51:58 +0200 Subject: [PATCH] Srs tcp_table(5) daemon. Signed-off-by: Pierre Habouzit --- Makefile | 10 +- common.c | 91 +++++++++++++ common.h | 19 +++ common.ld | 11 ++ postlicyd.c | 46 ++----- srsd.c | 373 +++++++++++++++++++++++++++++++++++++++++++--------- 6 files changed, 445 insertions(+), 105 deletions(-) create mode 100644 common.c create mode 100644 common.ld diff --git a/Makefile b/Makefile index 7ef024d..8811918 100644 --- 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 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)(); + } +} diff --git a/common.h b/common.h index 6a885d9..75825eb 100644 --- a/common.h +++ b/common.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -53,4 +54,22 @@ 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 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 = . ; + } +} diff --git a/postlicyd.c b/postlicyd.c index e9eb1c2..d7878d3 100644 --- a/postlicyd.c +++ b/postlicyd.c @@ -39,44 +39,16 @@ #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 --- a/srsd.c +++ b/srsd.c @@ -34,106 +34,351 @@ */ #include +#include +#include #include #include #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 ] [ -d ] domain secrets\n" + "\n" + " -e port to listen to for encoding requests\n" + " (default: "STR(DEFAULT_ENCODER_PORT)")\n" + " -d 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); } -- 2.20.1