1 /*
2 * main.c
3 * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
4 * All rights reserved.
5 *
6 * $Id: main.c,v 1.30 2015/03/14 06:11:29 marc Exp $
7 *
8 */
9
10 #ifndef __GNUC__
11 #define __attribute__(A)
12 #endif /* __GNUC__ */
13
14 static const char rcsid[] __attribute__ ((used)) = "$Id: main.c,v 1.30 2015/03/14 06:11:29 marc Exp $";
15
16 #define __MAIN__
17
18 #include "mavisd/headers.h"
19 #include "misc/version.h"
20 #include "misc/memops.h"
21 #include "mavis/av_send.h"
22 #include "misc/crc32.h"
23 #include "misc/sig_segv.h"
24 #include <sysexits.h>
25
26 #include "misc/base64.h"
27 #include "misc/rb.h"
28
usage(void)29 static void usage(void)
30 {
31 fprintf(stderr,
32 "Usage: %s <config> [<id>]\n"
33 "Version: " VERSION " (compiled: " __DATE__ " " __TIME__ ")\n"
34 "Copyright (C) 1998-2001 by Marc Huber <Marc.Huber@web.de>\n", common_data.progname);
35 exit(EX_USAGE);
36 }
37
38 struct query {
39 char *serial;
40 sockaddr_union sa;
41 int fd;
42 u_int serial_crc;
43 };
44
compare_crc_serial(const void * v1,const void * v2)45 static int compare_crc_serial(const void *v1, const void *v2)
46 {
47 if (((struct query *) v1)->serial_crc < ((struct query *) v2)->serial_crc)
48 return -1;
49 if (((struct query *) v1)->serial_crc > ((struct query *) v2)->serial_crc)
50 return +1;
51 return strcmp(((struct query *) v1)->serial, ((struct query *) v2)->serial);
52 }
53
54 static int backlog = 0;
55 static int backlog_max = 0;
56 static int backlog_max_p = 0;
57 static rb_tree_t *deferred_by_serial;
58 static unsigned long long counter_query = 0, counter_p_query = 0;
59 static unsigned long long counter_retry = 0, counter_p_retry = 0;
60 static unsigned long long counter_answered = 0, counter_p_answered = 0;
61 static unsigned long long counter_expired = 0, counter_p_expired = 0;
62 static unsigned long long counter_err = 0, counter_p_err = 0;
63
mavis_io(struct context * ctx)64 static void mavis_io(struct context *ctx)
65 {
66 av_ctx *avc = NULL;
67 struct query *q;
68 rb_node_t *r;
69 char *serial;
70
71 Debug((DEBUG_PROC, "mavis_io %p\n", ctx));
72 switch (mavis_recv(mcx, &avc, ctx)) {
73 case MAVIS_FINAL:
74 break;
75 case MAVIS_TIMEOUT:
76 counter_expired++, counter_p_expired++;
77 default:
78 return;
79 }
80
81 if (!(serial = av_get(avc, AV_A_SERIAL)))
82 return;
83
84 q = alloca(sizeof(struct query));
85 q->serial = serial;
86 q->serial_crc = crc32_update(INITCRC32, (u_char *) serial, strlen(serial));
87
88 if (!(r = RB_search(deferred_by_serial, q)))
89 return;
90
91 q = RB_payload(r, struct query *);
92
93 /* XXX -- move the unset functionality to a separate module? */
94 if (!transmit_password) {
95 av_unset(avc, AV_A_PASSWORD);
96 av_unset(avc, AV_A_DBPASSWORD);
97 }
98
99 ctx = io_get_ctx(io, q->fd);
100 /* Send answer to client */
101 av_send(avc, q->fd, &q->sa, ctx->blowfish);
102
103 /* Remove query from deferred queue */
104 RB_delete(deferred_by_serial, r);
105 backlog--;
106 setproctitle("%s: backlog: %d", common_data.progname, backlog);
107
108 counter_answered++, counter_p_answered++;
109 return;
110 } /* if */
111
client_io(struct context * ctx,int cur)112 void client_io(struct context *ctx, int cur)
113 {
114 /* We have incoming data. */
115 char *serial;
116 int res;
117 ssize_t buflen;
118 sockaddr_union sa;
119 socklen_t sinlen = (socklen_t) sizeof(sockaddr_union);
120 char *avt;
121 char buf[BUFSIZE_MAVIS];
122 av_ctx *avc;
123 static struct query *q = NULL;
124
125 if (!q)
126 q = Xcalloc(1, sizeof(struct query));
127
128 Debug((DEBUG_PROC, "client_io\n"));
129
130 /* Receive request from client */
131 buflen = recvfrom(cur, buf, sizeof(buf) - 1, 0, &sa.sa, &sinlen);
132 if (buflen <= 0)
133 return;
134
135 buf[buflen] = 0;
136
137 /* Decode data, if neccessary */
138 if (ctx->blowfish)
139 blowfish_dec(ctx->blowfish, (a_char *) buf, buflen);
140
141 /* Check client IP address */
142 res = acl_check(&sa);
143 if (!res) {
144 char ibuf[INET6_ADDRSTRLEN];
145 logmsg("Ignoring query from %s", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf)));
146 return;
147 }
148
149 counter_query++, counter_p_query++;
150
151 avc = av_new(NULL, NULL);
152 av_char_to_array(avc, buf, NULL);
153 serial = av_get(avc, AV_A_SERIAL);
154 if (!serial) {
155 char ibuf[INET6_ADDRSTRLEN];
156 logmsg("query from %s lacks serial", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf)));
157 counter_err++, counter_p_err++;
158 av_free(avc);
159 return;
160 }
161
162 q->serial = serial;
163 q->serial_crc = crc32_update(INITCRC32, (u_char *) serial, strlen(serial));
164
165 if (RB_search(deferred_by_serial, q)) {
166 char ibuf[INET6_ADDRSTRLEN];
167 Debug((DEBUG_PROC, "Duplicate detected\n"));
168 logmsg("Ignoring duplicate query from %s (backlog: %d)", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf)), backlog);
169 counter_retry++, counter_p_retry++;
170 av_free(avc);
171 return;
172 }
173
174 if (av_get(avc, AV_A_RESULT)) {
175 char ibuf[INET6_ADDRSTRLEN];
176 Debug((DEBUG_PROC, "AV_A_RESULT already set. Spoofing?\n"));
177 logmsg("Ignoring query with pre-set result code " "from %s (backlog: %d)", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf)), backlog);
178 counter_err++, counter_p_err++;
179 av_free(avc);
180 return;
181 }
182
183 avt = av_get(avc, AV_A_TYPE);
184
185 if (!avt || !strncmp(avt, AV_V_TYPE_PRIVATE_PREFIX, AV_V_TYPE_PRIVATE_PREFIX_LEN)) {
186 counter_err++, counter_p_err++;
187 av_free(avc);
188 return;
189 }
190
191 av_setcb(avc, (void *) mavis_io, (void *) q);
192
193 switch (mavis_send(mcx, &avc)) {
194 case MAVIS_DEFERRED:
195 Debug((DEBUG_PROC, "mavis_send yields DEFERRED\n"));
196 q->sa = sa;
197 q->fd = cur;
198 q->serial = Xstrdup(serial);
199 RB_insert(deferred_by_serial, q);
200 q = NULL;
201 backlog++;
202 if (backlog > backlog_max)
203 backlog_max = backlog;
204 if (backlog > backlog_max_p)
205 backlog_max_p = backlog;
206 setproctitle("%s: backlog: %d", common_data.progname, backlog);
207 return;
208 case MAVIS_TIMEOUT:
209 counter_expired++, counter_p_expired++;
210 break;
211
212 case MAVIS_FINAL:
213 if (!transmit_password) {
214 av_unset(avc, AV_A_PASSWORD);
215 av_unset(avc, AV_A_DBPASSWORD);
216 }
217 av_send(avc, cur, &sa, ctx->blowfish);
218 counter_answered++, counter_p_answered++;
219 }
220
221 av_free(avc);
222 }
223
udp_error(struct context * ctx,int cur)224 void udp_error(struct context *ctx __attribute__ ((unused)), int cur)
225 {
226 /*
227 * Linux sets the error flag if an UDP packet is sent to a local address
228 * not bound to a socket. We need to resolve this by clearing the socket
229 * error status.
230 *
231 */
232 int sockerr;
233 socklen_t sockerrlen = (socklen_t) sizeof(sockerr);
234 getsockopt(cur, SOL_SOCKET, SO_ERROR, (char *) &sockerr, &sockerrlen);
235 }
236
237 struct statistics {
238 struct io_context *io;
239 time_t startup_time;
240 time_t start_of_period;
241 };
242
243 /* Do statistics logging */
log_statistics(struct statistics * s,int i)244 static void log_statistics(struct statistics *s, int i __attribute__ ((unused)))
245 {
246 av_ctx *avc = av_new(NULL, NULL);
247 io_sched_renew(s->io, s);
248 logmsg("STAT: Q=%llu A=%llu R=%llu X=%llu E=%llu B=%d T=%lld "
249 "q=%llu a=%llu r=%llu x=%llu e=%llu b=%d t=%lld",
250 counter_query, counter_answered, counter_retry, counter_expired,
251 counter_err, backlog_max,
252 (long long) (io_now.tv_sec - s->startup_time),
253 counter_p_query, counter_p_answered, counter_p_retry,
254 counter_p_expired, counter_p_err, backlog_max_p, (long long) (io_now.tv_sec - s->start_of_period));
255
256 s->start_of_period = io_now.tv_sec;
257 counter_p_query = counter_p_retry = 0;
258 counter_p_answered = counter_p_err = 0;
259 backlog_max_p = backlog;
260
261 av_clear(avc);
262 av_set(avc, AV_A_TYPE, AV_V_TYPE_LOGSTATS);
263 mavis_send(mcx, &avc);
264 av_free(avc);
265 }
266
free_payload(void * q)267 static void free_payload(void *q)
268 {
269 Xfree(&((struct query *) q)->serial);
270 free(q);
271 }
272
main(int argc,char ** argv,char ** envp)273 int main(int argc, char **argv, char **envp)
274 {
275 extern char *optarg;
276 extern int optind;
277 pid_t pid;
278 int c;
279
280 init_common_data();
281 common_data.progname = Xstrdup(basename(argv[0]));
282 logopen();
283
284 while ((c = getopt(argc, argv, "vPd:")) != EOF)
285 switch (c) {
286 case 'v':
287 fprintf(stderr, "%s version " VERSION "\n", common_data.progname);
288 exit(EX_OK);
289 case 'P':
290 common_data.parse_only = 1;
291 break;
292 case 'd':
293 common_data.debug = atoi(optarg);
294 break;
295 default:
296 usage();
297 }
298
299 if (argc != optind + 1 && argc != optind + 2)
300
301 usage();
302
303 io = io_init();
304
305 logmsg("startup (version " VERSION ")");
306
307
308 cfg_read_config(argv[optind], parse_decls, argv[optind + 1] ? argv[optind + 1] : common_data.progname);
309 if (common_data.parse_only)
310 exit(EX_OK);
311
312 setproctitle_init(argv, envp);
313
314 if (background)
315 switch (pid = fork()) {
316 case 0:
317 mavis_detach();
318 break;
319 case -1:
320 logerr("fork (%s:%d)", __FILE__, __LINE__);
321 exit(EX_OSERR);
322 default:
323 logmsg("fork() succeeded. New PID is %u.", (u_int) pid);
324 exit(EX_OK);
325 }
326 #ifdef DEBUG
327 debug_setpid();
328 #endif /* DEBUG */
329
330 if (pidfile && !(common_data.pidfile = pid_write(pidfile)))
331 logerr("pid_write(%s) (%s:%d)", pidfile, __FILE__, __LINE__);
332
333 if (stat_period) {
334 struct statistics *s = Xcalloc(1, sizeof(struct statistics));
335 s->io = io;
336 s->startup_time = (time_t) io_now.tv_sec;
337 s->start_of_period = (time_t) io_now.tv_sec;
338
339 io_sched_add(io, s, (void *) log_statistics, stat_period, 0);
340 }
341
342 mavis_init(mcx, VERSION);
343
344 setproctitle("%s: backlog: %d", common_data.progname, backlog);
345
346 setup_signals();
347 setup_sig_segv(common_data.coredumpdir, common_data.gcorepath, common_data.debug_cmd);
348
349 deferred_by_serial = RB_tree_new(compare_crc_serial, free_payload);
350
351 io_main(io);
352 }
353