Fix typo, and use the privileges drop.
[apps/pfixtools.git] / srsd.c
diff --git a/srsd.c b/srsd.c
index 4004c13..0958bfd 100644 (file)
--- a/srsd.c
+++ b/srsd.c
@@ -1,5 +1,5 @@
 /******************************************************************************/
-/*          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             "srsd"
+#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)
 
@@ -80,29 +83,57 @@ static void srsd_delete(srsd_t **srsd)
     }
 }
 
+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 {
@@ -112,7 +143,6 @@ int process_srs(srs_t *srs, const char *domain, srsd_t *srsd)
         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:
@@ -124,8 +154,8 @@ int process_srs(srs_t *srs, const char *domain, srsd_t *srsd)
                 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);
@@ -145,7 +175,7 @@ int start_listener(int epollfd, int port, bool decoder)
     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;
     }
@@ -186,12 +216,15 @@ module_exit(main_shutdown);
 
 void usage(void)
 {
-    fputs("usage: "DAEMON_NAME" [ -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);
 }
 
@@ -235,7 +268,7 @@ int main_loop(srs_t *srs, const char *domain, int port_enc, int port_dec)
                 srsd_t *tmp;
                 int sock;
 
-                sock = accept(srsd->fd, NULL, NULL);
+                sock = accept_nonblock(srsd->fd);
                 if (sock < 0) {
                     UNIXERR("accept");
                     continue;
@@ -322,12 +355,12 @@ static srs_t *srs_read_secrets(const char *sfile)
         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) {
@@ -346,9 +379,13 @@ static srs_t *srs_read_secrets(const char *sfile)
 
 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)) {
@@ -357,7 +394,7 @@ int main(int argc, char *argv[])
     }
     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);
@@ -365,6 +402,12 @@ int main(int argc, char *argv[])
           case 'd':
             port_dec = atoi(optarg);
             break;
+          case 'p':
+            pidfile = optarg;
+            break;
+          case 'u':
+            unsafe = true;
+            break;
           default:
             usage();
             return EXIT_FAILURE;
@@ -381,5 +424,37 @@ int main(int argc, char *argv[])
         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;
+    }
+    return res;
 }