3634647812a1c0718308970aeb3e56d76f160ec4
[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  * Copyright © 2008 Florent Bruneau
35  */
36
37 #include "common.h"
38
39 #include <srs2.h>
40
41 #include "epoll.h"
42 #include "mem.h"
43 #include "buffer.h"
44 #include "server.h"
45
46 #define DAEMON_NAME             "pfix-srsd"
47 #define DEFAULT_ENCODER_PORT    10001
48 #define DEFAULT_DECODER_PORT    10002
49 #define RUNAS_USER              "nobody"
50 #define RUNAS_GROUP             "nogroup"
51
52 #define __tostr(x)  #x
53 #define STR(x)      __tostr(x)
54
55 typedef struct srs_config_t {
56     srs_t* srs;
57     const char* domain;
58 } srs_config_t;
59
60 static const char* decoder_ptr = "decoder";
61 static const char* encoder_ptr = "encoder";
62
63 static void *srsd_new_decoder(void)
64 {
65     return (void*)decoder_ptr;
66 }
67
68 static void *srsd_new_encoder(void)
69 {
70     return (void*)encoder_ptr;
71 }
72
73 static void *srsd_stater(server_t *server)
74 {
75     return server->data;
76 }
77
78 void urldecode(char *s, char *end)
79 {
80     char *p = s;
81
82     while (*p) {
83         if (*p == '%' && end - p >= 3) {
84             int h = (hexval(p[1]) << 4) | hexval(p[2]);
85
86             if (h >= 0) {
87                 *s++ = h;
88                 p += 3;
89                 continue;
90             }
91         }
92
93         *s++ = *p++;
94     }
95     *s++ = '\0';
96 }
97
98 int process_srs(server_t *srsd, void* vconfig)
99 {
100     srs_config_t* config = vconfig;
101     int res = buffer_read(&srsd->ibuf, srsd->fd, -1);
102
103     if ((res < 0 && errno != EINTR && errno != EAGAIN) || res == 0)
104         return -1;
105
106     while (srsd->ibuf.len > 4) {
107         char buf[BUFSIZ], *p, *q, *nl;
108         int err;
109
110         nl = strchr(srsd->ibuf.data + 4, '\n');
111         if (!nl) {
112             if (srsd->ibuf.len > BUFSIZ) {
113                 syslog(LOG_ERR, "unreasonnable amount of data without a \\n");
114                 return -1;
115             }
116             if (srsd->obuf.len) {
117               epoll_modify(srsd->fd, EPOLLIN | EPOLLOUT, srsd);
118             }
119             return 0;
120         }
121
122         if (strncmp("get ", srsd->ibuf.data, 4)) {
123             syslog(LOG_ERR, "bad request, not starting with \"get \"");
124             return -1;
125         }
126
127         for (p = srsd->ibuf.data + 4; p < nl && isspace(*p); p++);
128         for (q = nl++; q >= p && isspace(*q); *q-- = '\0');
129
130         if (p == q) {
131             buffer_addstr(&srsd->obuf, "400 empty request ???\n");
132             syslog(LOG_WARNING, "empty request");
133             goto skip;
134         }
135
136         urldecode(p, q);
137
138         if (srsd->data == (void*)decoder_ptr) {
139             err = srs_reverse(config->srs, buf, ssizeof(buf), p);
140         } else {
141             err = srs_forward(config->srs, buf, ssizeof(buf), p, config->domain);
142         }
143
144         if (err == 0) {
145             buffer_addstr(&srsd->obuf, "200 ");
146             buffer_addstr(&srsd->obuf, buf);
147         } else {
148             switch (SRS_ERROR_TYPE(err)) {
149               case SRS_ERRTYPE_SRS:
150               case SRS_ERRTYPE_SYNTAX:
151                 buffer_addstr(&srsd->obuf, "500 ");
152                 break;
153               default:
154                 buffer_addstr(&srsd->obuf, "400 ");
155                 break;
156             }
157             buffer_addstr(&srsd->obuf, srs_strerror(err));
158         }
159         buffer_addch(&srsd->obuf, '\n');
160
161       skip:
162         buffer_consume(&srsd->ibuf, nl - srsd->ibuf.data);
163     }
164     if (srsd->obuf.len) {
165       epoll_modify(srsd->fd, EPOLLIN | EPOLLOUT, srsd);
166     }
167     return 0;
168 }
169
170 int start_listener(int port, bool decoder)
171 {
172     return start_server(port, decoder ? srsd_new_decoder : srsd_new_encoder, NULL);
173 }
174
175 /* }}} */
176 /* administrivia {{{ */
177
178 static int main_initialize(void)
179 {
180     openlog(DAEMON_NAME, LOG_PID, LOG_MAIL);
181     signal(SIGPIPE, SIG_IGN);
182     signal(SIGINT,  &common_sighandler);
183     signal(SIGTERM, &common_sighandler);
184     signal(SIGHUP,  &common_sighandler);
185     signal(SIGSEGV, &common_sighandler);
186     syslog(LOG_INFO, "Starting...");
187     return 0;
188 }
189
190 static void main_shutdown(void)
191 {
192     closelog();
193 }
194
195 module_init(main_initialize);
196 module_exit(main_shutdown);
197
198 void usage(void)
199 {
200     fputs("usage: "DAEMON_NAME" [options] domain secrets\n"
201           "\n"
202           "Options:\n"
203           "    -e <port>    port to listen to for encoding requests\n"
204           "                 (default: "STR(DEFAULT_ENCODER_PORT)")\n"
205           "    -d <port>    port to listen to for decoding requests\n"
206           "                 (default: "STR(DEFAULT_DECODER_PORT)")\n"
207           "    -p <pidfile> file to write our pid to\n"
208           "    -u           unsafe mode: don't drop privilegies\n"
209           "    -f           stay in foreground\n"
210          , stderr);
211 }
212
213 /* }}} */
214
215 static srs_t *srs_read_secrets(const char *sfile)
216 {
217     srs_t *srs;
218     char buf[BUFSIZ];
219     FILE *f;
220     int lineno = 0;
221
222     f = fopen(sfile, "r");
223     if (!f) {
224         UNIXERR("fopen");
225         return NULL;
226     }
227
228     srs = srs_new();
229
230     while (fgets(buf, sizeof(buf), f)) {
231         int n = strlen(buf);
232
233         ++lineno;
234         if (n == sizeof(buf) - 1 && buf[n - 1] != '\n') {
235             syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno);
236             goto error;
237         }
238         m_strrtrim(buf);
239         srs_add_secret(srs, skipspaces(buf));
240     }
241
242     if (!lineno) {
243         syslog(LOG_CRIT, "%s: empty file, no secrets", sfile);
244         goto error;
245     }
246
247     fclose(f);
248     return srs;
249
250   error:
251     fclose(f);
252     srs_free(srs);
253     return NULL;
254 }
255
256 int main(int argc, char *argv[])
257 {
258     bool unsafe  = false;
259     bool daemonize = true;
260     int port_enc = DEFAULT_ENCODER_PORT;
261     int port_dec = DEFAULT_DECODER_PORT;
262     const char *pidfile = NULL;
263
264     int res;
265     srs_t *srs;
266
267     for (int c = 0; (c = getopt(argc, argv, "hfu" "e:d:p:")) >= 0; ) {
268         switch (c) {
269           case 'e':
270             port_enc = atoi(optarg);
271             break;
272           case 'f':
273             daemonize = false;
274             break;
275           case 'd':
276             port_dec = atoi(optarg);
277             break;
278           case 'p':
279             pidfile = optarg;
280             break;
281           case 'u':
282             unsafe = true;
283             break;
284           default:
285             usage();
286             return EXIT_FAILURE;
287         }
288     }
289
290     if (argc - optind != 2) {
291         usage();
292         return EXIT_FAILURE;
293     }
294
295     srs = srs_read_secrets(argv[optind + 1]);
296     if (!srs) {
297         return EXIT_FAILURE;
298     }
299
300     if (pidfile_open(pidfile) < 0) {
301         syslog(LOG_CRIT, "unable to write pidfile %s", pidfile);
302         return EXIT_FAILURE;
303     }
304
305     if (!unsafe && drop_privileges(RUNAS_USER, RUNAS_GROUP) < 0) {
306         syslog(LOG_CRIT, "unable to drop privileges");
307         return EXIT_FAILURE;
308     }
309
310     if (daemonize && daemon_detach() < 0) {
311         syslog(LOG_CRIT, "unable to fork");
312         return EXIT_FAILURE;
313     }
314
315     pidfile_refresh();
316     {
317       srs_config_t config = {
318         .srs    = srs,
319         .domain = argv[optind]
320       };
321
322       if (start_listener(port_enc, false) < 0)
323           return EXIT_FAILURE;
324       if (start_listener(port_dec, true) < 0)
325           return EXIT_FAILURE;
326
327       res = server_loop(srsd_stater, NULL, process_srs, &config);
328     }
329     syslog(LOG_INFO, "Stopping...");
330     return res;
331 }