26280de513e98d2e36abb4f19f04de50c4c115e3
[apps/pfixtools.git] / srsd.c
1 /******************************************************************************/
2 /*          postlicyd: a postfix policy daemon with a lot of features         */
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 "daemon.h"
45 #include "mem.h"
46 #include "buffer.h"
47
48 #define DAEMON_NAME             "srsd"
49 #define DEFAULT_ENCODER_PORT    10000
50 #define DEFAULT_DECODER_PORT    10001
51 #define __tostr(x)  #x
52 #define STR(x)      __tostr(x)
53
54 /* srs encoder/decoder/listener worker {{{ */
55
56 typedef struct srsd_t {
57     unsigned listener : 1;
58     unsigned decoder  : 1;
59     unsigned watchwr  : 1;
60     int fd;
61     buffer_t ibuf;
62     buffer_t obuf;
63 } srsd_t;
64
65 static srsd_t *srsd_new(void)
66 {
67     srsd_t *srsd = p_new(srsd_t, 1);
68     srsd->fd = -1;
69     return srsd;
70 }
71
72 static void srsd_delete(srsd_t **srsd)
73 {
74     if (*srsd) {
75         if ((*srsd)->fd >= 0)
76             close((*srsd)->fd);
77         buffer_wipe(&(*srsd)->ibuf);
78         buffer_wipe(&(*srsd)->obuf);
79         p_delete(srsd);
80     }
81 }
82
83 void urldecode(char *s, char *end)
84 {
85     char *p = s;
86
87     while (*p) {
88         if (*p == '%' && end - p >= 3) {
89             int h = (hexval(p[1]) << 4) | hexval(p[2]);
90
91             if (h >= 0) {
92                 *s++ = h;
93                 p += 3;
94                 continue;
95             }
96         }
97
98         *s++ = *p++;
99     }
100     *s++ = '\0';
101 }
102
103 int process_srs(srs_t *srs, const char *domain, srsd_t *srsd)
104 {
105     while (srsd->ibuf.len > 4) {
106         char buf[BUFSIZ], *p, *q, *nl;
107         int err;
108
109         nl = strchr(srsd->ibuf.data + 4, '\n');
110         if (!nl) {
111             if (srsd->ibuf.len > BUFSIZ) {
112                 syslog(LOG_ERR, "unreasonnable amount of data without a \\n");
113                 return -1;
114             }
115             return 0;
116         }
117
118         if (strncmp("get ", srsd->ibuf.data, 4)) {
119             syslog(LOG_ERR, "bad request, not starting with \"get \"");
120             return -1;
121         }
122
123         for (p = srsd->ibuf.data + 4; p < nl && isspace(*p); p++);
124         for (q = nl++; q >= p && isspace(*q); *q-- = '\0');
125
126         if (p == q) {
127             buffer_addstr(&srsd->obuf, "400 empty request ???\n");
128             syslog(LOG_WARNING, "empty request");
129             goto skip;
130         }
131
132         urldecode(p, q);
133
134         if (srsd->decoder) {
135             err = srs_reverse(srs, buf, ssizeof(buf), p);
136         } else {
137             err = srs_forward(srs, buf, ssizeof(buf), p, domain);
138         }
139
140         if (err == 0) {
141             buffer_addstr(&srsd->obuf, "200 ");
142             buffer_addstr(&srsd->obuf, buf);
143         } else {
144             switch (SRS_ERROR_TYPE(err)) {
145               case SRS_ERRTYPE_SRS:
146               case SRS_ERRTYPE_SYNTAX:
147                 buffer_addstr(&srsd->obuf, "500 ");
148                 break;
149               default:
150                 buffer_addstr(&srsd->obuf, "400 ");
151                 break;
152             }
153             buffer_addstr(&srsd->obuf, srs_strerror(err));
154         }
155         buffer_addch(&srsd->obuf, '\n');
156
157       skip:
158         buffer_consume(&srsd->ibuf, nl - srsd->ibuf.data);
159     }
160
161     return 0;
162 }
163
164 int start_listener(int epollfd, int port, bool decoder)
165 {
166     struct sockaddr_in addr = {
167         .sin_family = AF_INET,
168         .sin_addr   = { htonl(INADDR_LOOPBACK) },
169     };
170     struct epoll_event evt = { .events = EPOLLIN };
171     srsd_t *tmp;
172     int sock;
173
174     addr.sin_port = htons(port);
175     sock = tcp_listen((const struct sockaddr *)&addr, sizeof(addr));
176     if (sock < 0) {
177         return -1;
178     }
179
180     evt.data.ptr  = tmp = srsd_new();
181     tmp->fd       = sock;
182     tmp->decoder  = decoder;
183     tmp->listener = true;
184     if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &evt) < 0) {
185         UNIXERR("epoll_ctl");
186         return -1;
187     }
188     return 0;
189 }
190
191 /* }}} */
192 /* administrivia {{{ */
193
194 static int main_initialize(void)
195 {
196     openlog(DAEMON_NAME, LOG_PID, LOG_MAIL);
197     signal(SIGPIPE, SIG_IGN);
198     signal(SIGINT,  &common_sighandler);
199     signal(SIGTERM, &common_sighandler);
200     signal(SIGHUP,  &common_sighandler);
201     syslog(LOG_INFO, "Starting...");
202     return 0;
203 }
204
205 static void main_shutdown(void)
206 {
207     syslog(LOG_INFO, cleanexit ? "Stopping..." : "Unclean exit...");
208     closelog();
209 }
210
211 module_init(main_initialize);
212 module_exit(main_shutdown);
213
214 void usage(void)
215 {
216     fputs("usage: "DAEMON_NAME" [ -e <port> ] [ -d <port> ] domain secrets\n"
217           "\n"
218           "    -e <port>    port to listen to for encoding requests\n"
219           "                 (default: "STR(DEFAULT_ENCODER_PORT)")\n"
220           "    -d <port>    port to listen to for decoding requests\n"
221           "                 (default: "STR(DEFAULT_DECODER_PORT)")\n"
222          , stderr);
223 }
224
225 /* }}} */
226
227 int main_loop(srs_t *srs, const char *domain, int port_enc, int port_dec)
228 {
229     int exitcode = EXIT_SUCCESS;
230     int epollfd = epoll_create(128);
231
232     if (epollfd < 0) {
233         UNIXERR("epoll_create");
234         exitcode = EXIT_FAILURE;
235         goto error;
236     }
237
238     if (start_listener(epollfd, port_enc, false) < 0)
239         return EXIT_FAILURE;
240     if (start_listener(epollfd, port_dec, true) < 0)
241         return EXIT_FAILURE;
242
243     while (!sigint) {
244         struct epoll_event evts[1024];
245         int n;
246
247         n = epoll_wait(epollfd, evts, countof(evts), -1);
248         if (n < 0) {
249             if (errno != EAGAIN && errno != EINTR) {
250                 UNIXERR("epoll_wait");
251                 exitcode = EXIT_FAILURE;
252                 break;
253             }
254             continue;
255         }
256
257         while (--n >= 0) {
258             srsd_t *srsd = evts[n].data.ptr;
259
260             if (srsd->listener) {
261                 struct epoll_event evt = { .events = EPOLLIN };
262                 srsd_t *tmp;
263                 int sock;
264
265                 sock = accept_nonblock(srsd->fd);
266                 if (sock < 0) {
267                     UNIXERR("accept");
268                     continue;
269                 }
270
271                 evt.data.ptr = tmp = srsd_new();
272                 tmp->decoder = srsd->decoder;
273                 tmp->fd      = sock;
274                 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &evt) < 0) {
275                     UNIXERR("epoll_ctl");
276                     srsd_delete(&tmp);
277                     close(sock);
278                 }
279                 continue;
280             }
281
282             if (evts[n].events & EPOLLIN) {
283                 int res = buffer_read(&srsd->ibuf, srsd->fd, -1);
284
285                 if ((res < 0 && errno != EINTR && errno != EAGAIN)
286                 ||  res == 0)
287                 {
288                     srsd_delete(&srsd);
289                     continue;
290                 }
291
292                 if (process_srs(srs, domain, srsd) < 0) {
293                     srsd_delete(&srsd);
294                     continue;
295                 }
296             }
297
298             if ((evts[n].events & EPOLLOUT) && srsd->obuf.len) {
299                 int res = write(srsd->fd, srsd->obuf.data, srsd->obuf.len);
300
301                 if (res < 0 && errno != EINTR && errno != EAGAIN) {
302                     srsd_delete(&srsd);
303                     continue;
304                 }
305
306                 if (res > 0) {
307                     buffer_consume(&srsd->obuf, res);
308                 }
309             }
310
311             if (srsd->watchwr == !srsd->obuf.len) {
312                 struct epoll_event evt = {
313                     .events   = EPOLLIN | (srsd->obuf.len ? EPOLLOUT : 0),
314                     .data.ptr = srsd,
315                 };
316                 if (epoll_ctl(epollfd, EPOLL_CTL_MOD, srsd->fd, &evt) < 0) {
317                     UNIXERR("epoll_ctl");
318                     srsd_delete(&srsd);
319                     continue;
320                 }
321                 srsd->watchwr = srsd->obuf.len != 0;
322             }
323         }
324     }
325
326     close(epollfd);
327
328   error:
329     cleanexit = true;
330     return exitcode;
331 }
332
333 static srs_t *srs_read_secrets(const char *sfile)
334 {
335     srs_t *srs;
336     char buf[BUFSIZ];
337     FILE *f;
338     int lineno = 0;
339
340     f = fopen(sfile, "r");
341     if (!f) {
342         UNIXERR("fopen");
343         return NULL;
344     }
345
346     srs = srs_new();
347
348     while (fgets(buf, sizeof(buf), f)) {
349         int n = strlen(buf);
350
351         ++lineno;
352         if (buf[n - 1] != '\n') {
353             syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno);
354             goto error;
355         }
356
357         srs_add_secret(srs, buf);
358     }
359
360     if (!lineno) {
361         syslog(LOG_CRIT, "%s: empty file, no secrets", sfile);
362         goto error;
363     }
364
365     fclose(f);
366     return srs;
367
368   error:
369     fclose(f);
370     srs_free(srs);
371     return NULL;
372 }
373
374 int main(int argc, char *argv[])
375 {
376     int port_enc = DEFAULT_ENCODER_PORT;
377     int port_dec = DEFAULT_DECODER_PORT;
378
379     srs_t *srs;
380
381     if (atexit(common_shutdown)) {
382         fputs("Cannot hook my atexit function, quitting !\n", stderr);
383         return EXIT_FAILURE;
384     }
385     common_initialize();
386
387     for (int c = 0; (c = getopt(argc, argv, "he:d:")) >= 0; ) {
388         switch (c) {
389           case 'e':
390             port_enc = atoi(optarg);
391             break;
392           case 'd':
393             port_dec = atoi(optarg);
394             break;
395           default:
396             usage();
397             return EXIT_FAILURE;
398         }
399     }
400
401     if (argc - optind != 2) {
402         usage();
403         return EXIT_FAILURE;
404     }
405
406     srs = srs_read_secrets(argv[optind + 1]);
407     if (!srs) {
408         return EXIT_FAILURE;
409     }
410
411     return main_loop(srs, argv[optind], port_enc, port_dec);
412 }