Hello,

Syslog can be configured to send log to remote hosts using both
IP-addresses and resolvable hostnames. DNS resolution of these hostnames
is done in the "cfline()" function, which is invoked only during parsing
of the configuration file.

If DNS is not available when syslog starts, no attempts are made at a
later stage to perform this resolution, so a temporary outage during
startup causes the log stream to be permanently disabled.

I took a stab at implementing a fix to this, but would very much
appreciate if someone experienced could review it as I am not at all
confident in my C coding abilities.

The patch adds a new variable - "f_resolved" - which is set to zero on
startup. If syslog is about to attempt a TCP connection and "f_resolved"
is zero, a new function "resolve_host" is called to try a DNS
resolution. With UDP, this resolution is attempted before every sendmsg
if address resolution failed earlier - I'm worried this might cause
overhead, but I also didn't want to overcomplicate things.

Brgds,
R

Index: syslogd.c
===================================================================
RCS file: /cvs/src/usr.sbin/syslogd/syslogd.c,v
retrieving revision 1.277
diff -u -p -u -r1.277 syslogd.c
--- syslogd.c 16 Mar 2023 18:22:08 -0000 1.277
+++ syslogd.c 22 Jun 2023 12:41:09 -0000
@@ -159,6 +159,7 @@ struct filed {
  struct tls *f_ctx;
  char *f_host;
  int f_reconnectwait;
+ char f_resolved;
  } f_forw; /* forwarding address */
  char f_fname[PATH_MAX];
  struct {
@@ -1446,6 +1447,75 @@ tcp_errorcb(struct bufferevent *bufev, s
  log_info(LOG_WARNING, "%s", ebuf);
 }

+int
+resolve_host(struct filed *f)
+{
+ int retval = -1;
+ char *loghostcpy = NULL, *ipproto, *proto, *host, *port;
+
+ if (f->f_un.f_forw.f_loghost[0] != '@') {
+ log_warnx("invalid loghost \"%s\"",
+ f->f_un.f_forw.f_loghost);
+ goto cleanup;
+ }
+
+ // loghost_parse breaks the loghost line in a strtok:y way,
+ // so we work on a copy. Note that loghost_parse uses this
+ // buffer to store proto, host and port, so don't free this
+ // until we are completely done.
+ loghostcpy = strdup(f->f_un.f_forw.f_loghost+1);
+ if(loghostcpy == NULL) {
+ log_warnx("strdup error: %d", errno);
+ goto cleanup;
+ }
+
+ if(loghost_parse(loghostcpy, &proto, &host, &port) == -1) {
+ log_warnx("bad loghost \"%s\"",
+    f->f_un.f_forw.f_loghost);
+ goto cleanup;
+ }
+
+ // This code somewhat repeats tests in cfline(), but maybe
+ // not enought to warrant breaking it out into a separate
+ // function?
+ ipproto = proto;
+ if (strcmp(proto, "tls") == 0) {
+ ipproto = "tcp";
+ } else if (strcmp(proto, "tls4") == 0) {
+ ipproto = "tcp4";
+ } else if (strcmp(proto, "tls6") == 0) {
+ ipproto = "tcp6";
+ } else if (
+ strcmp(proto, "tcp") == 0 || \
+ strcmp(proto, "tcp4") == 0 || \
+ strcmp(proto, "tcp6") == 0 || \
+ strcmp(proto, "udp") == 0 || \
+ strcmp(proto, "udp4") == 0 || \
+ strcmp(proto, "udp6") == 0) {
+ ;
+ } else {
+ log_warnx("bad protocol \"%s\"",
+    f->f_un.f_forw.f_loghost);
+ goto cleanup;
+ }
+
+ if (priv_getaddrinfo(ipproto, host, port,
+    (struct sockaddr*)&f->f_un.f_forw.f_addr,
+    sizeof(f->f_un.f_forw.f_addr)) != 0) {
+ log_warnx("could not resolve \"%s\"",
+    host);
+ goto cleanup;
+ }
+
+ f->f_un.f_forw.f_resolved = 1;
+ retval = 0;
+cleanup:
+ if(loghostcpy != NULL)
+ free(loghostcpy);
+ log_debug("resolve host: %s for \"%s\"", retval ? "failed" :
"successful", f->f_un.f_forw.f_loghost);
+ return retval;
+}
+
 void
 tcp_connectcb(int fd, short event, void *arg)
 {
@@ -1453,6 +1523,12 @@ tcp_connectcb(int fd, short event, void
  struct bufferevent *bufev = f->f_un.f_forw.f_bufev;
  int s;

+ if(!f->f_un.f_forw.f_resolved) {
+ if(resolve_host(f) == -1) {
+ goto error;
+ }
+ }
+
  if ((s = tcp_socket(f)) == -1) {
  tcp_connect_retry(bufev, f);
  return;
@@ -1943,6 +2019,22 @@ fprintlog(struct filed *f, int flags, ch
  else
  iov[5].iov_len = 0;
  }
+
+ if(!f->f_un.f_forw.f_resolved) {
+ if(resolve_host(f) == -1) {
+ break;
+ }
+ switch (f->f_un.f_forw.f_addr.ss_family) {
+ case AF_INET:
+ f->f_file = fd_udp;
+ break;
+ case AF_INET6:
+ f->f_file = fd_udp6;
+ break;
+ }
+ }
+
+
  memset(&msghdr, 0, sizeof(msghdr));
  msghdr.msg_name = &f->f_un.f_forw.f_addr;
  msghdr.msg_namelen = f->f_un.f_forw.f_addr.ss_len;
@@ -2704,25 +2796,12 @@ cfline(char *line, char *progblock, char
     f->f_un.f_forw.f_loghost);
  break;
  }
- if (priv_getaddrinfo(ipproto, host, port,
-    (struct sockaddr*)&f->f_un.f_forw.f_addr,
-    sizeof(f->f_un.f_forw.f_addr)) != 0) {
- log_warnx("bad hostname \"%s\"",
-    f->f_un.f_forw.f_loghost);
- break;
- }
  f->f_file = -1;
  if (strncmp(proto, "udp", 3) == 0) {
- switch (f->f_un.f_forw.f_addr.ss_family) {
- case AF_INET:
- f->f_file = fd_udp;
- break;
- case AF_INET6:
- f->f_file = fd_udp6;
- break;
- }
+ f->f_un.f_forw.f_resolved = 0;
  f->f_type = F_FORWUDP;
  } else if (strncmp(ipproto, "tcp", 3) == 0) {
+ f->f_un.f_forw.f_resolved = 0;
  if ((f->f_un.f_forw.f_bufev = bufferevent_new(-1,
     tcp_dropcb, tcp_writecb, tcp_errorcb, f)) == NULL) {
  log_warn("bufferevent \"%s\"",

Reply via email to