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