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