renames
[apps/pfixtools.git] / main-srsd.c
1 /******************************************************************************/
2 /*          pfixtools: a collection of postfix related tools                  */
3 /*          ~~~~~~~~~                                                         */
4 /*  ________________________________________________________________________  */
5 /*                                                                            */
6 /*  Redistribution and use in source and binary forms, with or without        */
7 /*  modification, are permitted provided that the following conditions        */
8 /*  are met:                                                                  */
9 /*                                                                            */
10 /*  1. Redistributions of source code must retain the above copyright         */
11 /*     notice, this list of conditions and the following disclaimer.          */
12 /*  2. Redistributions in binary form must reproduce the above copyright      */
13 /*     notice, this list of conditions and the following disclaimer in the    */
14 /*     documentation and/or other materials provided with the distribution.   */
15 /*  3. The names of its contributors may not be used to endorse or promote    */
16 /*     products derived from this software without specific prior written     */
17 /*     permission.                                                            */
18 /*                                                                            */
19 /*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
20 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
21 /*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
22 /*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
23 /*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
24 /*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
25 /*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
26 /*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
27 /*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
28 /*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
29 /*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
30 /******************************************************************************/
31
32 /*
33  * Copyright © 2005-2007 Pierre Habouzit
34  */
35
36 #include <fcntl.h>
37 #include <netinet/in.h>
38 #include <sys/epoll.h>
39 #include <sys/stat.h>
40
41 #include <srs2.h>
42
43 #include "common.h"
44 #include "mem.h"
45 #include "buffer.h"
46
47 #define DAEMON_NAME             "pfix-srsd"
48 #define DEFAULT_ENCODER_PORT    10000
49 #define DEFAULT_DECODER_PORT    10001
50 #define RUNAS_USER              "nobody"
51 #define RUNAS_GROUP             "nogroup"
52
53 #define __tostr(x)  #x
54 #define STR(x)      __tostr(x)
55
56 /* srs encoder/decoder/listener worker {{{ */
57
58 typedef struct srsd_t {
59     unsigned listener : 1;
60     unsigned decoder  : 1;
61     unsigned watchwr  : 1;
62     int fd;
63     buffer_t ibuf;
64     buffer_t obuf;
65 } srsd_t;
66
67 static srsd_t *srsd_new(void)
68 {
69     srsd_t *srsd = p_new(srsd_t, 1);
70     srsd->fd = -1;
71     return srsd;
72 }
73
74 static void srsd_delete(srsd_t **srsd)
75 {
76     if (*srsd) {
77         if ((*srsd)->fd >= 0)
78             close((*srsd)->fd);
79         buffer_wipe(&(*srsd)->ibuf);
80         buffer_wipe(&(*srsd)->obuf);
81         p_delete(srsd);
82     }
83 }
84
85 void urldecode(char *s, char *end)
86 {
87     char *p = s;
88
89     while (*p) {
90         if (*p == '%' && end - p >= 3) {
91             int h = (hexval(p[1]) << 4) | hexval(p[2]);
92
93             if (h >= 0) {
94                 *s++ = h;
95                 p += 3;
96                 continue;
97             }
98         }
99
100         *s++ = *p++;
101     }
102     *s++ = '\0';
103 }
104
105 int process_srs(srs_t *srs, const char *domain, srsd_t *srsd)
106 {
107     while (srsd->ibuf.len > 4) {
108         char buf[BUFSIZ], *p, *q, *nl;
109         int err;
110
111         nl = strchr(srsd->ibuf.data + 4, '\n');
112         if (!nl) {
113             if (srsd->ibuf.len > BUFSIZ) {
114                 syslog(LOG_ERR, "unreasonnable amount of data without a \\n");
115                 return -1;
116             }
117             return 0;
118         }
119
120         if (strncmp("get ", srsd->ibuf.data, 4)) {
121             syslog(LOG_ERR, "bad request, not starting with \"get \"");
122             return -1;
123         }
124
125         for (p = srsd->ibuf.data + 4; p < nl && isspace(*p); p++);
126         for (q = nl++; q >= p && isspace(*q); *q-- = '\0');
127
128         if (p == q) {
129             buffer_addstr(&srsd->obuf, "400 empty request ???\n");
130             syslog(LOG_WARNING, "empty request");
131             goto skip;
132         }
133
134         urldecode(p, q);
135
136         if (srsd->decoder) {
137             err = srs_reverse(srs, buf, ssizeof(buf), p);
138         } else {
139             err = srs_forward(srs, buf, ssizeof(buf), p, domain);
140         }
141
142         if (err == 0) {
143             buffer_addstr(&srsd->obuf, "200 ");
144             buffer_addstr(&srsd->obuf, buf);
145         } else {
146             switch (SRS_ERROR_TYPE(err)) {
147               case SRS_ERRTYPE_SRS:
148               case SRS_ERRTYPE_SYNTAX:
149                 buffer_addstr(&srsd->obuf, "500 ");
150                 break;
151               default:
152                 buffer_addstr(&srsd->obuf, "400 ");
153                 break;
154             }
155             buffer_addstr(&srsd->obuf, srs_strerror(err));
156         }
157         buffer_addch(&srsd->obuf, '\n');
158
159       skip:
160         buffer_consume(&srsd->ibuf, nl - srsd->ibuf.data);
161     }
162
163     return 0;
164 }
165
166 int start_listener(int epollfd, int port, bool decoder)
167 {
168     struct sockaddr_in addr = {
169         .sin_family = AF_INET,
170         .sin_addr   = { htonl(INADDR_LOOPBACK) },
171     };
172     struct epoll_event evt = { .events = EPOLLIN };
173     srsd_t *tmp;
174     int sock;
175
176     addr.sin_port = htons(port);
177     sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
178     if (sock < 0) {
179         return -1;
180     }
181
182     evt.data.ptr  = tmp = srsd_new();
183     tmp->fd       = sock;
184     tmp->decoder  = decoder;
185     tmp->listener = true;
186     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &evt) < 0) {
187         UNIXERR("epoll_ctl");
188         return -1;
189     }
190     return 0;
191 }
192
193 /* }}} */
194 /* administrivia {{{ */
195
196 static int main_initialize(void)
197 {
198     openlog(DAEMON_NAME, LOG_PID, LOG_MAIL);
199     signal(SIGPIPE, SIG_IGN);
200     signal(SIGINT,  &common_sighandler);
201     signal(SIGTERM, &common_sighandler);
202     signal(SIGHUP,  &common_sighandler);
203     signal(SIGSEGV, &common_sighandler);
204     syslog(LOG_INFO, "Starting...");
205     return 0;
206 }
207
208 static void main_shutdown(void)
209 {
210     closelog();
211 }
212
213 module_init(main_initialize);
214 module_exit(main_shutdown);
215
216 void usage(void)
217 {
218     fputs("usage: "DAEMON_NAME" [options] domain secrets\n"
219           "\n"
220           "Options:\n"
221           "    -e <port>    port to listen to for encoding requests\n"
222           "                 (default: "STR(DEFAULT_ENCODER_PORT)")\n"
223           "    -d <port>    port to listen to for decoding requests\n"
224           "                 (default: "STR(DEFAULT_DECODER_PORT)")\n"
225           "    -p <pidfile> file to write our pid to\n"
226           "    -u           unsafe mode: don't drop privilegies\n"
227          , stderr);
228 }
229
230 /* }}} */
231
232 int main_loop(srs_t *srs, const char *domain, int port_enc, int port_dec)
233 {
234     int exitcode = EXIT_SUCCESS;
235     int epollfd = epoll_create(128);
236
237     if (epollfd < 0) {
238         UNIXERR("epoll_create");
239         exitcode = EXIT_FAILURE;
240         goto error;
241     }
242
243     if (start_listener(epollfd, port_enc, false) < 0)
244         return EXIT_FAILURE;
245     if (start_listener(epollfd, port_dec, true) < 0)
246         return EXIT_FAILURE;
247
248     while (!sigint) {
249         struct epoll_event evts[1024];
250         int n;
251
252         n = epoll_wait(epollfd, evts, countof(evts), -1);
253         if (n < 0) {
254             if (errno != EAGAIN && errno != EINTR) {
255                 UNIXERR("epoll_wait");
256                 exitcode = EXIT_FAILURE;
257                 break;
258             }
259             continue;
260         }
261
262         while (--n >= 0) {
263             srsd_t *srsd = evts[n].data.ptr;
264
265             if (srsd->listener) {
266                 struct epoll_event evt = { .events = EPOLLIN };
267                 srsd_t *tmp;
268                 int sock;
269
270                 sock = accept_nonblock(srsd->fd);
271                 if (sock < 0) {
272                     UNIXERR("accept");
273                     continue;
274                 }
275
276                 evt.data.ptr = tmp = srsd_new();
277                 tmp->decoder = srsd->decoder;
278                 tmp->fd      = sock;
279                 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &evt) < 0) {
280                     UNIXERR("epoll_ctl");
281                     srsd_delete(&tmp);
282                     close(sock);
283                 }
284                 continue;
285             }
286
287             if (evts[n].events & EPOLLIN) {
288                 int res = buffer_read(&srsd->ibuf, srsd->fd, -1);
289
290                 if ((res < 0 && errno != EINTR && errno != EAGAIN)
291                 ||  res == 0)
292                 {
293                     srsd_delete(&srsd);
294                     continue;
295                 }
296
297                 if (process_srs(srs, domain, srsd) < 0) {
298                     srsd_delete(&srsd);
299                     continue;
300                 }
301             }
302
303             if ((evts[n].events & EPOLLOUT) && srsd->obuf.len) {
304                 int res = write(srsd->fd, srsd->obuf.data, srsd->obuf.len);
305
306                 if (res < 0 && errno != EINTR && errno != EAGAIN) {
307                     srsd_delete(&srsd);
308                     continue;
309                 }
310
311                 if (res > 0) {
312                     buffer_consume(&srsd->obuf, res);
313                 }
314             }
315
316             if (srsd->watchwr == !srsd->obuf.len) {
317                 struct epoll_event evt = {
318                     .events   = EPOLLIN | (srsd->obuf.len ? EPOLLOUT : 0),
319                     .data.ptr = srsd,
320                 };
321                 if (epoll_ctl(epollfd, EPOLL_CTL_MOD, srsd->fd, &evt) < 0) {
322                     UNIXERR("epoll_ctl");
323                     srsd_delete(&srsd);
324                     continue;
325                 }
326                 srsd->watchwr = srsd->obuf.len != 0;
327             }
328         }
329     }
330
331     close(epollfd);
332
333   error:
334     return exitcode;
335 }
336
337 static srs_t *srs_read_secrets(const char *sfile)
338 {
339     srs_t *srs;
340     char buf[BUFSIZ];
341     FILE *f;
342     int lineno = 0;
343
344     f = fopen(sfile, "r");
345     if (!f) {
346         UNIXERR("fopen");
347         return NULL;
348     }
349
350     srs = srs_new();
351
352     while (fgets(buf, sizeof(buf), f)) {
353         int n = strlen(buf);
354
355         ++lineno;
356         if (n == sizeof(buf) - 1 && buf[n - 1] != '\n') {
357             syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno);
358             goto error;
359         }
360         m_strrtrim(buf);
361         srs_add_secret(srs, skipspaces(buf));
362     }
363
364     if (!lineno) {
365         syslog(LOG_CRIT, "%s: empty file, no secrets", sfile);
366         goto error;
367     }
368
369     fclose(f);
370     return srs;
371
372   error:
373     fclose(f);
374     srs_free(srs);
375     return NULL;
376 }
377
378 int main(int argc, char *argv[])
379 {
380     bool unsafe  = false;
381     int port_enc = DEFAULT_ENCODER_PORT;
382     int port_dec = DEFAULT_DECODER_PORT;
383     const char *pidfile = NULL;
384
385     FILE *f = NULL;
386     int res;
387     srs_t *srs;
388
389     if (atexit(common_shutdown)) {
390         fputs("Cannot hook my atexit function, quitting !\n", stderr);
391         return EXIT_FAILURE;
392     }
393     common_initialize();
394
395     for (int c = 0; (c = getopt(argc, argv, "he:d:p:u")) >= 0; ) {
396         switch (c) {
397           case 'e':
398             port_enc = atoi(optarg);
399             break;
400           case 'd':
401             port_dec = atoi(optarg);
402             break;
403           case 'p':
404             pidfile = optarg;
405             break;
406           case 'u':
407             unsafe = true;
408             break;
409           default:
410             usage();
411             return EXIT_FAILURE;
412         }
413     }
414
415     if (argc - optind != 2) {
416         usage();
417         return EXIT_FAILURE;
418     }
419
420     srs = srs_read_secrets(argv[optind + 1]);
421     if (!srs) {
422         return EXIT_FAILURE;
423     }
424
425     if (pidfile) {
426         f = fopen(pidfile, "w");
427         if (!f) {
428             syslog(LOG_CRIT, "unable to write pidfile %s", pidfile);
429         }
430         fprintf(f, "%d\n", getpid());
431         fflush(f);
432     }
433
434     if (!unsafe && drop_privileges(RUNAS_USER, RUNAS_GROUP) < 0) {
435         syslog(LOG_CRIT, "unable to drop privileges");
436         return EXIT_FAILURE;
437     }
438
439     if (daemon_detach() < 0) {
440         syslog(LOG_CRIT, "unable to fork");
441         return EXIT_FAILURE;
442     }
443
444     if (f) {
445         rewind(f);
446         ftruncate(fileno(f), 0);
447         fprintf(f, "%d\n", getpid());
448         fflush(f);
449     }
450     res = main_loop(srs, argv[optind], port_enc, port_dec);
451     if (f) {
452         rewind(f);
453         ftruncate(fileno(f), 0);
454         fclose(f);
455         f = NULL;
456     }
457     syslog(LOG_INFO, "Stopping...");
458     return res;
459 }