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