X-Git-Url: http://git.madism.org/?a=blobdiff_plain;f=pfix-srsd%2Fmain-srsd.c;fp=pfix-srsd%2Fmain-srsd.c;h=a8e6ce935f244f695f6829faf00efdbe14112f52;hb=8977d252fc44ae953df4bde4a2f1c5895beea4e0;hp=0000000000000000000000000000000000000000;hpb=063fa644df8a1bc2f0d76027dae5d7d0d8ce4f47;p=apps%2Fpfixtools.git diff --git a/pfix-srsd/main-srsd.c b/pfix-srsd/main-srsd.c new file mode 100644 index 0000000..a8e6ce9 --- /dev/null +++ b/pfix-srsd/main-srsd.c @@ -0,0 +1,325 @@ +/******************************************************************************/ +/* pfixtools: a collection of postfix related tools */ +/* ~~~~~~~~~ */ +/* ________________________________________________________________________ */ +/* */ +/* 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 © 2005-2007 Pierre Habouzit + * Copyright © 2008 Florent Bruneau + */ + +#include "common.h" + +#include + +#include "epoll.h" +#include "mem.h" +#include "buffer.h" +#include "server.h" + +#define DAEMON_NAME "pfix-srsd" +#define DEFAULT_ENCODER_PORT 10001 +#define DEFAULT_DECODER_PORT 10002 +#define RUNAS_USER "nobody" +#define RUNAS_GROUP "nogroup" + +DECLARE_MAIN + +typedef struct srs_config_t { + srs_t* srs; + const char* domain; +} srs_config_t; + + +/* Server {{{1 + */ + +static const char* const decoder_ptr = "decoder"; +static const char* const encoder_ptr = "encoder"; + +static void *srsd_new_decoder(void) +{ + return (void*)decoder_ptr; +} + +static void *srsd_new_encoder(void) +{ + return (void*)encoder_ptr; +} + +static void *srsd_starter(server_t *server) +{ + return server->data; +} + +int start_listener(int port, bool decoder) +{ + return start_server(port, decoder ? srsd_new_decoder : srsd_new_encoder, NULL); +} + + +/* Processing {{{1 + */ + +void urldecode(char *s, char *end) +{ + char *p = s; + + while (*p) { + if (*p == '%' && end - p >= 3) { + int h = (hexval(p[1]) << 4) | hexval(p[2]); + + if (h >= 0) { + *s++ = h; + p += 3; + continue; + } + } + + *s++ = *p++; + } + *s++ = '\0'; +} + +int process_srs(server_t *srsd, void* vconfig) +{ + srs_config_t* config = vconfig; + int res = buffer_read(&srsd->ibuf, srsd->fd, -1); + + if ((res < 0 && errno != EINTR && errno != EAGAIN) || res == 0) + return -1; + + while (srsd->ibuf.len > 4) { + char buf[BUFSIZ], *p, *q, *nl; + int err; + + nl = strchr(srsd->ibuf.data + 4, '\n'); + if (!nl) { + if (srsd->ibuf.len > BUFSIZ) { + syslog(LOG_ERR, "unreasonnable amount of data without a \\n"); + return -1; + } + if (srsd->obuf.len) { + epoll_modify(srsd->fd, EPOLLIN | EPOLLOUT, srsd); + } + return 0; + } + + if (strncmp("get ", srsd->ibuf.data, 4)) { + syslog(LOG_ERR, "bad request, not starting with \"get \""); + return -1; + } + + for (p = srsd->ibuf.data + 4; p < nl && isspace(*p); p++); + for (q = nl++; q >= p && isspace(*q); *q-- = '\0'); + + if (p == q) { + buffer_addstr(&srsd->obuf, "400 empty request ???\n"); + syslog(LOG_WARNING, "empty request"); + goto skip; + } + + urldecode(p, q); + + if (srsd->data == (void*)decoder_ptr) { + err = srs_reverse(config->srs, buf, ssizeof(buf), p); + } else { + err = srs_forward(config->srs, buf, ssizeof(buf), p, config->domain); + } + + if (err == 0) { + buffer_addstr(&srsd->obuf, "200 "); + buffer_addstr(&srsd->obuf, buf); + } 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_addch(&srsd->obuf, '\n'); + + skip: + buffer_consume(&srsd->ibuf, nl - srsd->ibuf.data); + } + if (srsd->obuf.len) { + epoll_modify(srsd->fd, EPOLLIN | EPOLLOUT, srsd); + } + return 0; +} + + +/* config {{{1 + */ + +static srs_config_t config = { + .srs = NULL, + .domain = NULL +}; + +/** overload srs_free since the lib is not properly maintained. + */ +inline void srs_free(srs_t* srs) +{ + int i; + for (i = 0; i < srs->numsecrets; i++) { + memset(srs->secrets[i], 0, strlen(srs->secrets[i])); + free(srs->secrets[i]); + srs->secrets[i] = '\0'; + } + if (srs->secrets) { + free(srs->secrets); + } + free(srs); +} + +static void config_shutdown(void) +{ + if (config.srs) { + srs_free(config.srs); + config.srs = NULL; + } +} + +module_exit(config_shutdown); + +static srs_t *srs_read_secrets(const char *sfile) +{ + srs_t *srs; + char buf[BUFSIZ]; + FILE *f; + int lineno = 0; + + f = fopen(sfile, "r"); + if (!f) { + UNIXERR("fopen"); + return NULL; + } + + srs = srs_new(); + + while (fgets(buf, sizeof(buf), f)) { + int n = strlen(buf); + + ++lineno; + if (n == sizeof(buf) - 1 && buf[n - 1] != '\n') { + syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno); + goto error; + } + m_strrtrim(buf); + srs_add_secret(srs, skipspaces(buf)); + } + + if (!lineno) { + syslog(LOG_CRIT, "%s: empty file, no secrets", sfile); + goto error; + } + + fclose(f); + return srs; + + error: + fclose(f); + srs_free(srs); + return NULL; +} + +/* administrivia {{{1 + */ + +void usage(void) +{ + fputs("usage: "DAEMON_NAME" [options] domain secrets\n" + "\n" + "Options:\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" + " -p file to write our pid to\n" + " -u unsafe mode: don't drop privilegies\n" + " -f stay in foreground\n" + , stderr); +} + +/* }}} + */ + +int main(int argc, char *argv[]) +{ + bool unsafe = false; + bool daemonize = true; + int port_enc = DEFAULT_ENCODER_PORT; + int port_dec = DEFAULT_DECODER_PORT; + const char *pidfile = NULL; + + for (int c = 0; (c = getopt(argc, argv, "hfu" "e:d:p:")) >= 0; ) { + switch (c) { + case 'e': + port_enc = atoi(optarg); + break; + case 'f': + daemonize = false; + break; + case 'd': + port_dec = atoi(optarg); + break; + case 'p': + pidfile = optarg; + break; + case 'u': + unsafe = true; + break; + default: + usage(); + return EXIT_FAILURE; + } + } + + if (argc - optind != 2) { + usage(); + return EXIT_FAILURE; + } + + config.domain = argv[optind]; + config.srs = srs_read_secrets(argv[optind + 1]); + if (!config.srs + || common_setup(pidfile, unsafe, RUNAS_USER, RUNAS_GROUP, + daemonize) != EXIT_SUCCESS + || start_listener(port_enc, false) < 0 + || start_listener(port_dec, true) < 0) { + return EXIT_FAILURE; + } + return server_loop(srsd_starter, NULL, process_srs, &config); +}