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