Hi Omar,

Thank you for the pointers - apologies for missing the style guide. I
also didn't know I could attach diffs rather than fight with GMail
about whitespace preservation - I've attached a diff that should look
a lot better.

1. I've fixed the styling issues you mentioned.
2. I implemented a time limit so that I don't attempt DNS resolution
on every UDP packet if resolution consistently fails - now it tries at
most every 10 seconds. To do this I've added f_resolvetime to f_forw -
I chose this approach rather than the TCP evtimer because I don't
fully understand the concurrency or blocking aspects of evtimer, and
because any future additions (e. g. resolving the hostname again after
a certain period of time) benefits from having an approximate time of
first resolve saved.

Regards,
R


Den ons 28 juni 2023 kl 16:16 skrev Omar Polo <o...@omarpolo.com>:
>
> Hello,
>
> On 2023/06/22 13:22:56 +0000, Robert Larsson <larssonrobert...@gmail.com> 
> wrote:
> > 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.
>
> I don't have any opinion on this, I just stumbled on the thread and
> noticed that the diff was mangled due to whitespaces.  Please consider
> fixing your MUA or attaching a diff.  To test, you could send the diff
> to yourself and see if it applies.
>
> While here however, point out some obvious style(9) violation
>
> > 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.
>
> please don't use C++ style comments, but /* C style comments */
>
> > + loghostcpy = strdup(f->f_un.f_forw.f_loghost+1);
> > + if(loghostcpy == NULL) {
>
> use spaces between if and the condition
>
>   + if (loghostcpy == NULL) {
>
> > + log_warnx("strdup error: %d", errno);
>
> I fear the raw `errno' value is useless.  consider using
>
>         log_warn("strdup");
>
> instead, which appends the error description to the message.  (note
> the difference, it's log_warn not log_warnx.)
>
> > + goto cleanup;
> > + }
> > +
> > + if(loghost_parse(loghostcpy, &proto, &host, &port) == -1) {
>
> same spacing as noted before.
>
> > + 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) {
>
> this is not shell scripting, no need for \ to break lines.
>
> also, similar constructs are often spelled as
>
>         else if (strcmp(...) == 0 ||
>             strcmp(...) == 0 ||
>             strcmp(...) == 0 ||
>             ...) {
>                 /* ... */
>         }
>
> > + ;
>
> instead of an empty body I'd reverse the previous logic and error
> there.
>
> > + } 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\"",
>
>
> Thanks,
>
> Omar Polo
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   29 Jun 2023 13:23:02 -0000
@@ -159,6 +159,8 @@ struct filed {
                        struct tls              *f_ctx;
                        char                    *f_host;
                        int                      f_reconnectwait;
+                       char                     f_resolved;    /* indicate if 
address resolution successful */
+                       long                     f_resolvetime; /* save time of 
resolution for retry attempts */
                } f_forw;               /* forwarding address */
                char    f_fname[PATH_MAX];
                struct {
@@ -1446,6 +1448,74 @@ 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_warn("strdup");
+               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;
@@ -1933,6 +2009,31 @@ fprintlog(struct filed *f, int flags, ch
 
        case F_FORWUDP:
                log_debug(" %s", f->f_un.f_forw.f_loghost);
+
+               if (!f->f_un.f_forw.f_resolved) {
+                       (void)gettimeofday(&now, NULL);
+                       if ((now.tv_sec - f->f_un.f_forw.f_resolvetime) > 10) {
+                               /* attempt resolution only if more than 10 
seconds have
+                                  passed since last failed attempt. */
+                               f->f_un.f_forw.f_resolvetime = now.tv_sec;
+                               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;
+                               }
+                       } else {
+                               /* the host is not resolved, but it's also too 
soon for us to
+                                  try to resolve it again, so just abort. */
+                               break;
+                       }
+               }
+
                l = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len +
                    iov[3].iov_len + iov[4].iov_len + iov[5].iov_len +
                    iov[6].iov_len;
@@ -1943,6 +2044,7 @@ fprintlog(struct filed *f, int flags, ch
                        else
                                iov[5].iov_len = 0;
                }
+
                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 +2806,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