1 /*-
2 * Copyright (c) 2008-2010 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Frédéric Lécaille <flecaille@haproxy.com>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include "config.h"
32
33 #include <sys/types.h>
34 #include <sys/socket.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "vtc.h"
42
43 #include "vsa.h"
44 #include "vss.h"
45 #include "vtcp.h"
46 #include "vre.h"
47
48 struct syslog_srv {
49 unsigned magic;
50 #define SYSLOG_SRV_MAGIC 0xbf28a692
51 char *name;
52 struct vtclog *vl;
53 VTAILQ_ENTRY(syslog_srv) list;
54 char run;
55
56 int repeat;
57 char *spec;
58
59 int sock;
60 char bind[256];
61 int lvl;
62
63 pthread_t tp;
64 ssize_t rxbuf_left;
65 size_t rxbuf_sz;
66 char *rxbuf;
67 vtim_dur timeout;
68 };
69
70 static pthread_mutex_t syslog_mtx;
71
72 static VTAILQ_HEAD(, syslog_srv) syslogs =
73 VTAILQ_HEAD_INITIALIZER(syslogs);
74
75 #define SYSLOGCMDS \
76 CMD_SYSLOG(expect) \
77 CMD_SYSLOG(recv)
78
79 #define CMD_SYSLOG(nm) static cmd_f cmd_syslog_##nm;
80 SYSLOGCMDS
81 #undef CMD_SYSLOG
82
83 static const struct cmds syslog_cmds[] = {
84 #define CMD_SYSLOG(n) { #n, cmd_syslog_##n },
85 SYSLOGCMDS
86 #undef CMD_SYSLOG
87 { NULL, NULL }
88 };
89
90 static const char * const syslog_levels[] = {
91 "emerg",
92 "alert",
93 "crit",
94 "err",
95 "warning",
96 "notice",
97 "info",
98 "debug",
99 NULL,
100 };
101
102 static int
get_syslog_level(struct vtclog * vl,const char * lvl)103 get_syslog_level(struct vtclog *vl, const char *lvl)
104 {
105 int i;
106
107 for (i = 0; syslog_levels[i]; i++)
108 if (!strcmp(lvl, syslog_levels[i]))
109 return (i);
110 vtc_fatal(vl, "wrong syslog level '%s'\n", lvl);
111 }
112
113 /*--------------------------------------------------------------------
114 * Check if a UDP syscall return value is fatal
115 * XXX: Largely copied from VTCP, not sure if really applicable
116 */
117
118 static int
VUDP_Check(int a)119 VUDP_Check(int a)
120 {
121 if (a == 0)
122 return (1);
123 if (errno == ECONNRESET)
124 return (1);
125 #if (defined (__SVR4) && defined (__sun)) || defined (__NetBSD__)
126 /*
127 * Solaris returns EINVAL if the other end unexpectedly reset the
128 * connection.
129 * This is a bug in Solaris and documented behaviour on NetBSD.
130 */
131 if (errno == EINVAL || errno == ETIMEDOUT || errno == EPIPE)
132 return (1);
133 #elif defined (__APPLE__)
134 /*
135 * MacOS returns EINVAL if the other end unexpectedly reset
136 * the connection.
137 */
138 if (errno == EINVAL)
139 return (1);
140 #endif
141 return (0);
142 }
143
144 /*--------------------------------------------------------------------
145 * When closing a UDP connection, a couple of errno's are legit, we
146 * can't be held responsible for the other end wanting to talk to us.
147 */
148
149 static void
VUDP_close(int * s)150 VUDP_close(int *s)
151 {
152 int i;
153
154 i = close(*s);
155
156 assert(VUDP_Check(i));
157 *s = -1;
158 }
159
160 /*--------------------------------------------------------------------
161 * Given a struct suckaddr, open a socket of the appropriate type, and bind
162 * it to the requested address.
163 *
164 * If the address is an IPv6 address, the IPV6_V6ONLY option is set to
165 * avoid conflicts between INADDR_ANY and IN6ADDR_ANY.
166 */
167
168 static int
VUDP_bind(const struct suckaddr * sa,const char ** errp)169 VUDP_bind(const struct suckaddr *sa, const char **errp)
170 {
171 #ifdef IPV6_V6ONLY
172 int val;
173 #endif
174 int sd, e;
175 socklen_t sl;
176 const struct sockaddr *so;
177 int proto;
178
179 if (errp != NULL)
180 *errp = NULL;
181
182 proto = VSA_Get_Proto(sa);
183 sd = socket(proto, SOCK_DGRAM, 0);
184 if (sd < 0) {
185 if (errp != NULL)
186 *errp = "socket(2)";
187 return (-1);
188 }
189
190 #ifdef IPV6_V6ONLY
191 /* forcibly use separate sockets for IPv4 and IPv6 */
192 val = 1;
193 if (proto == AF_INET6 &&
194 setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof val) != 0) {
195 if (errp != NULL)
196 *errp = "setsockopt(IPV6_V6ONLY, 1)";
197 e = errno;
198 closefd(&sd);
199 errno = e;
200 return (-1);
201 }
202 #endif
203 so = VSA_Get_Sockaddr(sa, &sl);
204 if (bind(sd, so, sl) != 0) {
205 if (errp != NULL)
206 *errp = "bind(2)";
207 e = errno;
208 closefd(&sd);
209 errno = e;
210 return (-1);
211 }
212 return (sd);
213 }
214
215 /*--------------------------------------------------------------------*/
216
217 struct udp_helper {
218 const char **errp;
219 };
220
v_matchproto_(vss_resolved_f)221 static int v_matchproto_(vss_resolved_f)
222 vudp_lo_cb(void *priv, const struct suckaddr *sa)
223 {
224 int sock;
225 struct udp_helper *hp = priv;
226
227 sock = VUDP_bind(sa, hp->errp);
228 if (sock > 0) {
229 *hp->errp = NULL;
230 return (sock);
231 }
232 AN(*hp->errp);
233 return (0);
234 }
235
236 static int
VUDP_bind_on(const char * addr,const char * def_port,const char ** errp)237 VUDP_bind_on(const char *addr, const char *def_port, const char **errp)
238 {
239 struct udp_helper h;
240 int sock;
241
242 h.errp = errp;
243
244 sock = VSS_resolver_socktype(
245 addr, def_port, vudp_lo_cb, &h, errp, SOCK_DGRAM);
246 if (*errp != NULL)
247 return (-1);
248 return (sock);
249 }
250
251 /**********************************************************************
252 * Allocate and initialize a syslog
253 */
254
255 static struct syslog_srv *
syslog_new(const char * name,struct vtclog * vl)256 syslog_new(const char *name, struct vtclog *vl)
257 {
258 struct syslog_srv *s;
259
260 VTC_CHECK_NAME(vl, name, "Syslog", 'S');
261 ALLOC_OBJ(s, SYSLOG_SRV_MAGIC);
262 AN(s);
263 REPLACE(s->name, name);
264 s->vl = vtc_logopen("%s", s->name);
265 AN(s->vl);
266 vtc_log_set_cmd(s->vl, syslog_cmds);
267
268 bprintf(s->bind, "%s", default_listen_addr);
269 s->repeat = 1;
270 s->sock = -1;
271 s->lvl = -1;
272 s->timeout = vtc_maxdur * .5; // XXX
273
274 vl = vtc_logopen("%s", s->name);
275 AN(vl);
276
277 s->rxbuf_sz = s->rxbuf_left = 2048*1024;
278 s->rxbuf = malloc(s->rxbuf_sz); /* XXX */
279 AN(s->rxbuf);
280
281 AZ(pthread_mutex_lock(&syslog_mtx));
282 VTAILQ_INSERT_TAIL(&syslogs, s, list);
283 AZ(pthread_mutex_unlock(&syslog_mtx));
284 return (s);
285 }
286
287 /**********************************************************************
288 * Clean up a syslog
289 */
290
291 static void
syslog_delete(struct syslog_srv * s)292 syslog_delete(struct syslog_srv *s)
293 {
294
295 CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
296 macro_undef(s->vl, s->name, "addr");
297 macro_undef(s->vl, s->name, "port");
298 macro_undef(s->vl, s->name, "sock");
299 vtc_logclose(s->vl);
300 free(s->name);
301 free(s->rxbuf);
302 /* XXX: MEMLEAK (?) (VSS ??) */
303 FREE_OBJ(s);
304 }
305
306 static void
syslog_rx(const struct syslog_srv * s,int lvl)307 syslog_rx(const struct syslog_srv *s, int lvl)
308 {
309 ssize_t ret;
310
311 while (!vtc_error) {
312 /* Pointers to syslog priority value (see <PRIVAL>, rfc5424). */
313 char *prib, *prie, *end;
314 unsigned int prival;
315
316 VTCP_set_read_timeout(s->sock, s->timeout);
317
318 ret = recv(s->sock, s->rxbuf, s->rxbuf_sz - 1, 0);
319 if (ret < 0) {
320 if (errno == EINTR || errno == EAGAIN)
321 continue;
322
323 vtc_fatal(s->vl,
324 "%s: recv failed (fd: %d read: %s", __func__,
325 s->sock, strerror(errno));
326 }
327 if (ret == 0)
328 vtc_fatal(s->vl,
329 "syslog rx timeout (fd: %d %.3fs ret: %zd)",
330 s->sock, s->timeout, ret);
331
332 s->rxbuf[ret] = '\0';
333 vtc_dump(s->vl, 4, "syslog", s->rxbuf, ret);
334
335 prib = s->rxbuf;
336 if (*prib++ != '<')
337 vtc_fatal(s->vl, "syslog PRI, no '<'");
338 prie = strchr(prib, '>');
339 if (prie == NULL)
340 vtc_fatal(s->vl, "syslog PRI, no '>'");
341
342 prival = strtoul(prib, &end, 10);
343 if (end != prie)
344 vtc_fatal(s->vl, "syslog PRI, bad number");
345
346 if (lvl >= 0 && lvl == (prival & 0x7))
347 return;
348 }
349 }
350
351 /**********************************************************************
352 * Syslog server bind
353 */
354
355 static void
syslog_bind(struct syslog_srv * s)356 syslog_bind(struct syslog_srv *s)
357 {
358 const char *err;
359 char aaddr[VTCP_ADDRBUFSIZE];
360 char aport[VTCP_PORTBUFSIZE];
361 char buf[vsa_suckaddr_len];
362 struct suckaddr *sua;
363
364 CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
365
366 if (s->sock >= 0)
367 VUDP_close(&s->sock);
368 s->sock = VUDP_bind_on(s->bind, "0", &err);
369 if (err != NULL)
370 vtc_fatal(s->vl,
371 "Syslog server bind address (%s) cannot be resolved: %s",
372 s->bind, err);
373 assert(s->sock > 0);
374 sua = VSA_getsockname(s->sock, buf, sizeof buf);
375 AN(sua);
376 VTCP_name(sua, aaddr, sizeof aaddr, aport, sizeof aport);
377 macro_def(s->vl, s->name, "addr", "%s", aaddr);
378 macro_def(s->vl, s->name, "port", "%s", aport);
379 if (VSA_Get_Proto(sua) == AF_INET)
380 macro_def(s->vl, s->name, "sock", "%s:%s", aaddr, aport);
381 else
382 macro_def(s->vl, s->name, "sock", "[%s]:%s", aaddr, aport);
383 /* Record the actual port, and reuse it on subsequent starts */
384 bprintf(s->bind, "%s %s", aaddr, aport);
385 }
386
v_matchproto_(cmd_f)387 static void v_matchproto_(cmd_f)
388 cmd_syslog_expect(CMD_ARGS)
389 {
390 struct syslog_srv *s;
391 vre_t *vre;
392 const char *error;
393 int erroroffset, i, ret;
394 char *cmp, *spec;
395
396 (void)vl;
397 CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC);
398 AZ(strcmp(av[0], "expect"));
399 av++;
400
401 cmp = av[0];
402 spec = av[1];
403 AN(cmp);
404 AN(spec);
405 AZ(av[2]);
406
407 assert(!strcmp(cmp, "~") || !strcmp(cmp, "!~"));
408
409 vre = VRE_compile(spec, 0, &error, &erroroffset);
410 if (!vre)
411 vtc_fatal(s->vl, "REGEXP error: '%s' (@%d) (%s)",
412 error, erroroffset, spec);
413
414 i = VRE_exec(vre, s->rxbuf, strlen(s->rxbuf), 0, 0, NULL, 0, 0);
415
416 VRE_free(&vre);
417
418 ret = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!');
419 if (!ret)
420 vtc_fatal(s->vl, "EXPECT FAILED %s \"%s\"", cmp, spec);
421 else
422 vtc_log(s->vl, 4, "EXPECT MATCH %s \"%s\"", cmp, spec);
423 }
424
v_matchproto_(cmd_f)425 static void v_matchproto_(cmd_f)
426 cmd_syslog_recv(CMD_ARGS)
427 {
428 int lvl;
429 struct syslog_srv *s;
430
431 CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC);
432 (void)vl;
433 AZ(strcmp(av[0], "recv"));
434 av++;
435 if (av[0] == NULL)
436 lvl = s->lvl;
437 else
438 lvl = get_syslog_level(vl, av[0]);
439
440 syslog_rx(s, lvl);
441 }
442
443 /**********************************************************************
444 * Syslog server thread
445 */
446
447 static void *
syslog_thread(void * priv)448 syslog_thread(void *priv)
449 {
450 struct syslog_srv *s;
451 int i;
452
453 CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC);
454 assert(s->sock >= 0);
455
456 vtc_log(s->vl, 2, "Started on %s (level: %d)", s->bind, s->lvl);
457 for (i = 0; i < s->repeat; i++) {
458 if (s->repeat > 1)
459 vtc_log(s->vl, 3, "Iteration %d", i);
460 parse_string(s->vl, s, s->spec);
461 vtc_log(s->vl, 3, "shutting fd %d", s->sock);
462 }
463 VUDP_close(&s->sock);
464 vtc_log(s->vl, 2, "Ending");
465 return (NULL);
466 }
467
468 /**********************************************************************
469 * Start the syslog thread
470 */
471
472 static void
syslog_start(struct syslog_srv * s)473 syslog_start(struct syslog_srv *s)
474 {
475 CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
476 vtc_log(s->vl, 2, "Starting syslog server");
477 if (s->sock == -1)
478 syslog_bind(s);
479 vtc_log(s->vl, 1, "Bound on %s", s->bind);
480 s->run = 1;
481 AZ(pthread_create(&s->tp, NULL, syslog_thread, s));
482 }
483
484 /**********************************************************************
485 * Force stop the syslog thread
486 */
487
488 static void
syslog_stop(struct syslog_srv * s)489 syslog_stop(struct syslog_srv *s)
490 {
491 void *res;
492
493 CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
494 vtc_log(s->vl, 2, "Stopping for syslog server");
495 (void)pthread_cancel(s->tp);
496 AZ(pthread_join(s->tp, &res));
497 s->tp = 0;
498 s->run = 0;
499 }
500
501 /**********************************************************************
502 * Wait for syslog thread to stop
503 */
504
505 static void
syslog_wait(struct syslog_srv * s)506 syslog_wait(struct syslog_srv *s)
507 {
508 void *res;
509
510 CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
511 vtc_log(s->vl, 2, "Waiting for syslog server (%d)", s->sock);
512 AZ(pthread_join(s->tp, &res));
513 if (res != NULL && !vtc_stop)
514 vtc_fatal(s->vl, "Syslog server returned \"%p\"",
515 (char *)res);
516 s->tp = 0;
517 s->run = 0;
518 }
519
520 /* SECTION: syslog syslog
521 *
522 * Define and interact with syslog instances (for use with haproxy)
523 *
524 * To define a syslog server, you'll use this syntax::
525 *
526 * syslog SNAME
527 *
528 * Arguments:
529 *
530 * SNAME
531 * Identify the syslog server with a string which must start with 'S'.
532 *
533 * \-level STRING
534 * Set the default syslog priority level used by any subsequent "recv"
535 * command.
536 * Any syslog dgram with a different level will be skipped by
537 * "recv" command. This default level value may be superseded
538 * by "recv" command if supplied as first argument: "recv <level>".
539 *
540 * \-start
541 * Start the syslog server thread in the background.
542 *
543 * \-repeat
544 * Instead of processing the specification only once, do it
545 * NUMBER times.
546 *
547 * \-bind
548 * Bind the syslog socket to a local address.
549 *
550 * \-wait
551 * Wait for that thread to terminate.
552 *
553 * \-stop
554 * Stop the syslog server thread.
555 */
556
v_matchproto_(cmd_f)557 void v_matchproto_(cmd_f)
558 cmd_syslog(CMD_ARGS)
559 {
560 struct syslog_srv *s;
561
562 (void)priv;
563
564 if (av == NULL) {
565 /* Reset and free */
566 do {
567 AZ(pthread_mutex_lock(&syslog_mtx));
568 s = VTAILQ_FIRST(&syslogs);
569 CHECK_OBJ_ORNULL(s, SYSLOG_SRV_MAGIC);
570 if (s != NULL)
571 VTAILQ_REMOVE(&syslogs, s, list);
572 AZ(pthread_mutex_unlock(&syslog_mtx));
573 if (s != NULL) {
574 if (s->run) {
575 (void)pthread_cancel(s->tp);
576 syslog_wait(s);
577 }
578 if (s->sock >= 0)
579 VUDP_close(&s->sock);
580 syslog_delete(s);
581 }
582 } while (s != NULL);
583 return;
584 }
585
586 AZ(strcmp(av[0], "syslog"));
587 av++;
588
589 AZ(pthread_mutex_lock(&syslog_mtx));
590 VTAILQ_FOREACH(s, &syslogs, list)
591 if (!strcmp(s->name, av[0]))
592 break;
593 AZ(pthread_mutex_unlock(&syslog_mtx));
594 if (s == NULL)
595 s = syslog_new(av[0], vl);
596 CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
597 av++;
598
599 for (; *av != NULL; av++) {
600 if (vtc_error)
601 break;
602 if (!strcmp(*av, "-wait")) {
603 if (!s->run)
604 vtc_fatal(s->vl, "Syslog server not -started");
605 syslog_wait(s);
606 continue;
607 }
608
609 if (!strcmp(*av, "-stop")) {
610 syslog_stop(s);
611 continue;
612 }
613
614 /*
615 * We do an implict -wait if people muck about with a
616 * running syslog.
617 * This only works if the previous ->spec has completed
618 */
619 if (s->run)
620 syslog_wait(s);
621
622 AZ(s->run);
623 if (!strcmp(*av, "-repeat")) {
624 AN(av[1]);
625 s->repeat = atoi(av[1]);
626 av++;
627 continue;
628 }
629 if (!strcmp(*av, "-bind")) {
630 AN(av[1]);
631 bprintf(s->bind, "%s", av[1]);
632 av++;
633 syslog_bind(s);
634 continue;
635 }
636 if (!strcmp(*av, "-level")) {
637 AN(av[1]);
638 s->lvl = get_syslog_level(vl, av[1]);
639 av++;
640 continue;
641 }
642 if (!strcmp(*av, "-start")) {
643 syslog_start(s);
644 continue;
645 }
646 if (**av == '-')
647 vtc_fatal(s->vl, "Unknown syslog argument: %s", *av);
648 s->spec = *av;
649 }
650 }
651
652 void
init_syslog(void)653 init_syslog(void)
654 {
655 AZ(pthread_mutex_init(&syslog_mtx, NULL));
656 }
657