2989a2f17f068fbe80927284a5279b7d3cb13fd8
[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 static const char* decoder_ptr = "decoder";
60 static const char* encoder_ptr = "encoder";
61
62 static void *srsd_new_decoder(void)
63 {
64     return (void*)decoder_ptr;
65 }
66
67 static void *srsd_new_encoder(void)
68 {
69     return (void*)encoder_ptr;
70 }
71
72 static void *srsd_stater(server_t *server)
73 {
74     return server->data;
75 }
76
77 void urldecode(char *s, char *end)
78 {
79     char *p = s;
80
81     while (*p) {
82         if (*p == '%' && end - p >= 3) {
83             int h = (hexval(p[1]) << 4) | hexval(p[2]);
84
85             if (h >= 0) {
86                 *s++ = h;
87                 p += 3;
88                 continue;
89             }
90         }
91
92         *s++ = *p++;
93     }
94     *s++ = '\0';
95 }
96
97 int process_srs(server_t *srsd, void* vconfig)
98 {
99     srs_config_t* config = vconfig;
100     int res = buffer_read(&srsd->ibuf, srsd->fd, -1);
101
102     if ((res < 0 && errno != EINTR && errno != EAGAIN) || res == 0)
103         return -1;
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             if (srsd->obuf.len) {
116               epoll_modify(srsd->fd, EPOLLIN | EPOLLOUT, srsd);
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->data == (void*)decoder_ptr) {
138             err = srs_reverse(config->srs, buf, ssizeof(buf), p);
139         } else {
140             err = srs_forward(config->srs, buf, ssizeof(buf), p, config->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     if (srsd->obuf.len) {
164       epoll_modify(srsd->fd, EPOLLIN | EPOLLOUT, srsd);
165     }
166     return 0;
167 }
168
169 int start_listener(int port, bool decoder)
170 {
171     return start_server(port, decoder ? srsd_new_decoder : srsd_new_encoder, NULL);
172 }
173
174 /* }}} */
175 /* administrivia {{{ */
176
177
178 void usage(void)
179 {
180     fputs("usage: "DAEMON_NAME" [options] domain secrets\n"
181           "\n"
182           "Options:\n"
183           "    -e <port>    port to listen to for encoding requests\n"
184           "                 (default: "STR(DEFAULT_ENCODER_PORT)")\n"
185           "    -d <port>    port to listen to for decoding requests\n"
186           "                 (default: "STR(DEFAULT_DECODER_PORT)")\n"
187           "    -p <pidfile> file to write our pid to\n"
188           "    -u           unsafe mode: don't drop privilegies\n"
189           "    -f           stay in foreground\n"
190          , stderr);
191 }
192
193 /* }}} */
194
195 static srs_t *srs_read_secrets(const char *sfile)
196 {
197     srs_t *srs;
198     char buf[BUFSIZ];
199     FILE *f;
200     int lineno = 0;
201
202     f = fopen(sfile, "r");
203     if (!f) {
204         UNIXERR("fopen");
205         return NULL;
206     }
207
208     srs = srs_new();
209
210     while (fgets(buf, sizeof(buf), f)) {
211         int n = strlen(buf);
212
213         ++lineno;
214         if (n == sizeof(buf) - 1 && buf[n - 1] != '\n') {
215             syslog(LOG_CRIT, "%s:%d: line too long", sfile, lineno);
216             goto error;
217         }
218         m_strrtrim(buf);
219         srs_add_secret(srs, skipspaces(buf));
220     }
221
222     if (!lineno) {
223         syslog(LOG_CRIT, "%s: empty file, no secrets", sfile);
224         goto error;
225     }
226
227     fclose(f);
228     return srs;
229
230   error:
231     fclose(f);
232     srs_free(srs);
233     return NULL;
234 }
235
236 int main(int argc, char *argv[])
237 {
238     bool unsafe  = false;
239     bool daemonize = true;
240     int port_enc = DEFAULT_ENCODER_PORT;
241     int port_dec = DEFAULT_DECODER_PORT;
242     const char *pidfile = NULL;
243     srs_config_t config;
244
245     for (int c = 0; (c = getopt(argc, argv, "hfu" "e:d:p:")) >= 0; ) {
246         switch (c) {
247           case 'e':
248             port_enc = atoi(optarg);
249             break;
250           case 'f':
251             daemonize = false;
252             break;
253           case 'd':
254             port_dec = atoi(optarg);
255             break;
256           case 'p':
257             pidfile = optarg;
258             break;
259           case 'u':
260             unsafe = true;
261             break;
262           default:
263             usage();
264             return EXIT_FAILURE;
265         }
266     }
267
268     if (argc - optind != 2) {
269         usage();
270         return EXIT_FAILURE;
271     }
272
273     config.domain = argv[optind];
274     config.srs = srs_read_secrets(argv[optind + 1]);
275     if (!config.srs
276         || common_setup(pidfile, unsafe, RUNAS_USER, RUNAS_GROUP,
277                         daemonize) != EXIT_SUCCESS
278         || start_listener(port_enc, false) < 0
279         || start_listener(port_dec, true) < 0) {
280         return EXIT_FAILURE;
281     }
282     return server_loop(srsd_stater, NULL, process_srs, &config);
283 }