/******************************************************************************/
-/* postlicyd: a postfix policy daemon with a lot of features */
+/* pfixtools: a collection of postfix related tools */
/* ~~~~~~~~~ */
/* ________________________________________________________________________ */
/* */
#include "mem.h"
#include "buffer.h"
+#define DAEMON_NAME "pfix-srsd"
#define DEFAULT_ENCODER_PORT 10000
#define DEFAULT_DECODER_PORT 10001
+#define RUNAS_USER "nobody"
+#define RUNAS_GROUP "nogroup"
+
#define __tostr(x) #x
#define STR(x) __tostr(x)
}
}
+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(srs_t *srs, const char *domain, srsd_t *srsd)
{
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;
+ }
+ return 0;
+ }
+
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) {
+ buffer_addstr(&srsd->obuf, "400 empty request ???\n");
syslog(LOG_WARNING, "empty request");
goto skip;
}
+ urldecode(p, q);
+
if (srsd->decoder) {
err = srs_reverse(srs, buf, ssizeof(buf), p);
} else {
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:
break;
}
buffer_addstr(&srsd->obuf, srs_strerror(err));
- buffer_addstr(&srsd->obuf, "\r\n");
}
+ buffer_addch(&srsd->obuf, '\n');
skip:
buffer_consume(&srsd->ibuf, nl - srsd->ibuf.data);
int sock;
addr.sin_port = htons(port);
- sock = tcp_listen((const struct sockaddr *)&addr, sizeof(addr));
+ sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
if (sock < 0) {
return -1;
}
static int main_initialize(void)
{
- openlog("srsd", LOG_PID, LOG_MAIL);
+ openlog(DAEMON_NAME, LOG_PID, LOG_MAIL);
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, &common_sighandler);
signal(SIGTERM, &common_sighandler);
signal(SIGHUP, &common_sighandler);
+ signal(SIGSEGV, &common_sighandler);
syslog(LOG_INFO, "Starting...");
return 0;
}
static void main_shutdown(void)
{
- syslog(LOG_INFO, cleanexit ? "Stopping..." : "Unclean exit...");
closelog();
}
void usage(void)
{
- fputs("usage: srsd [ -e <port> ] [ -d <port> ] domain secrets\n"
+ fputs("usage: "DAEMON_NAME" [options] domain secrets\n"
"\n"
+ "Options:\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"
+ " -p <pidfile> file to write our pid to\n"
+ " -u unsafe mode: don't drop privilegies\n"
, stderr);
}
srsd_t *tmp;
int sock;
- sock = accept(srsd->fd, NULL, NULL);
+ sock = accept_nonblock(srsd->fd);
if (sock < 0) {
UNIXERR("accept");
continue;
close(epollfd);
error:
- cleanexit = true;
return exitcode;
}
int n = strlen(buf);
++lineno;
- if (buf[n - 1] != '\n') {
+ if (n == sizeof(buf) - 1 && buf[n - 1] != '\n') {
syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno);
goto error;
}
-
- srs_add_secret(srs, buf);
+ m_strrtrim(buf);
+ srs_add_secret(srs, skipspaces(buf));
}
if (!lineno) {
int main(int argc, char *argv[])
{
+ bool unsafe = false;
int port_enc = DEFAULT_ENCODER_PORT;
int port_dec = DEFAULT_DECODER_PORT;
+ const char *pidfile = NULL;
+ FILE *f = NULL;
+ int res;
srs_t *srs;
if (atexit(common_shutdown)) {
}
common_initialize();
- for (int c = 0; (c = getopt(argc, argv, "he:d:")) >= 0; ) {
+ for (int c = 0; (c = getopt(argc, argv, "he:d:p:u")) >= 0; ) {
switch (c) {
case 'e':
port_enc = atoi(optarg);
case 'd':
port_dec = atoi(optarg);
break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ case 'u':
+ unsafe = true;
+ break;
default:
usage();
return EXIT_FAILURE;
return EXIT_FAILURE;
}
- return main_loop(srs, argv[optind], port_enc, port_dec);
+ if (pidfile) {
+ f = fopen(pidfile, "w");
+ if (!f) {
+ syslog(LOG_CRIT, "unable to write pidfile %s", pidfile);
+ }
+ fprintf(f, "%d\n", getpid());
+ fflush(f);
+ }
+
+ if (!unsafe && drop_privileges(RUNAS_USER, RUNAS_GROUP) < 0) {
+ syslog(LOG_CRIT, "unable to drop privileges");
+ return EXIT_FAILURE;
+ }
+
+ if (daemon_detach() < 0) {
+ syslog(LOG_CRIT, "unable to fork");
+ return EXIT_FAILURE;
+ }
+
+ if (f) {
+ rewind(f);
+ ftruncate(fileno(f), 0);
+ fprintf(f, "%d\n", getpid());
+ fflush(f);
+ }
+ res = main_loop(srs, argv[optind], port_enc, port_dec);
+ if (f) {
+ rewind(f);
+ ftruncate(fileno(f), 0);
+ fclose(f);
+ f = NULL;
+ }
+ syslog(LOG_INFO, "Stopping...");
+ return res;
}