summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/syslogd/syslogd.c142
1 files changed, 114 insertions, 28 deletions
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
index 89e14064205..a393ad03559 100644
--- a/usr.sbin/syslogd/syslogd.c
+++ b/usr.sbin/syslogd/syslogd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: syslogd.c,v 1.174 2015/07/18 22:33:46 bluhm Exp $ */
+/* $OpenBSD: syslogd.c,v 1.175 2015/07/19 20:10:46 bluhm Exp $ */
/*
* Copyright (c) 1983, 1988, 1993, 1994
@@ -294,6 +294,8 @@ void klog_readcb(int, short, void *);
void udp_readcb(int, short, void *);
void unix_readcb(int, short, void *);
void tcp_acceptcb(int, short, void *);
+int octet_counting(struct evbuffer *, char **, int);
+int non_transparent_framing(struct evbuffer *, char **);
void tcp_readcb(struct bufferevent *, void *);
void tcp_closecb(struct bufferevent *, short, void *);
int tcp_socket(struct filed *);
@@ -914,31 +916,120 @@ tcp_acceptcb(int fd, short event, void *arg)
logmsg(LOG_SYSLOG|LOG_INFO, ebuf, LocalHostName, ADDDATE);
}
-void
-tcp_readcb(struct bufferevent *bufev, void *arg)
+/*
+ * Syslog over TCP RFC 6587 3.4.1. Octet Counting
+ */
+int
+octet_counting(struct evbuffer *evbuf, char **msg, int drain)
{
- struct peer *p = arg;
- char *line;
+ char *p, *buf, *end;
+ int len;
+ buf = EVBUFFER_DATA(evbuf);
+ end = buf + EVBUFFER_LENGTH(evbuf);
/*
- * Syslog over TCP RFC 6587 3.4.2. Non-Transparent-Framing
- * XXX Incompatible to ourself, should do: 3.4.1. Octet Counting
+ * It can be assumed that octet-counting framing is used if a syslog
+ * frame starts with a digit.
*/
- while ((line = evbuffer_readline(bufev->input))) {
- dprintf("tcp logger \"%s\" complete line\n", p->p_peername);
- printline(p->p_hostname, line);
- free(line);
- }
- if (EVBUFFER_LENGTH(bufev->input) >= MAXLINE) {
- dprintf("tcp logger \"%s\" incomplete line, use %zu bytes\n",
- p->p_peername, EVBUFFER_LENGTH(bufev->input));
- printline(p->p_hostname, EVBUFFER_DATA(bufev->input));
- evbuffer_drain(bufev->input, -1);
+ if (buf >= end || !isdigit(*buf))
+ return (-1);
+ /*
+ * SYSLOG-FRAME = MSG-LEN SP SYSLOG-MSG
+ * MSG-LEN is the octet count of the SYSLOG-MSG in the SYSLOG-FRAME.
+ * We support up to 5 digits in MSG-LEN, so the maximum is 99999.
+ */
+ for (p = buf; p < end && p < buf + 5; p++) {
+ if (!isdigit(*p))
+ break;
}
- if (EVBUFFER_LENGTH(bufev->input) > 0) {
- dprintf("tcp logger \"%s\" buffer %zu bytes\n",
- p->p_peername, EVBUFFER_LENGTH(bufev->input));
+ if (buf >= p || p >= end || *p != ' ')
+ return (-1);
+ p++;
+ /* Using atoi() is safe as buf starts with 1 to 5 digits and a space. */
+ len = atoi(buf);
+ if (drain)
+ dprintf(" octet counting %d", len);
+ if (p + len > end)
+ return (0);
+ if (drain)
+ evbuffer_drain(evbuf, p - buf);
+ if (msg)
+ *msg = p;
+ return (len);
+}
+
+/*
+ * Syslog over TCP RFC 6587 3.4.2. Non-Transparent-Framing
+ */
+int
+non_transparent_framing(struct evbuffer *evbuf, char **msg)
+{
+ char *p, *buf, *end;
+
+ buf = EVBUFFER_DATA(evbuf);
+ end = buf + EVBUFFER_LENGTH(evbuf);
+ /*
+ * The TRAILER has usually been a single character and most often
+ * is ASCII LF (%d10). However, other characters have also been
+ * seen, with ASCII NUL (%d00) being a prominent example.
+ */
+ for (p = buf; p < end; p++) {
+ if (*p == '\0' || *p == '\n')
+ break;
}
+ if (p + 1 - buf >= INT_MAX)
+ return (-1);
+ dprintf(" non transparent framing");
+ if (p >= end)
+ return (0);
+ /*
+ * Some devices have also been seen to emit a two-character
+ * TRAILER, which is usually CR and LF.
+ */
+ if (buf < p && p[0] == '\n' && p[-1] == '\r')
+ p[-1] = '\0';
+ if (msg)
+ *msg = buf;
+ return (p + 1 - buf);
+}
+
+void
+tcp_readcb(struct bufferevent *bufev, void *arg)
+{
+ struct peer *p = arg;
+ char *msg, line[MAXLINE + 1];
+ int len;
+
+ while (EVBUFFER_LENGTH(bufev->input) > 0) {
+ dprintf("tcp logger \"%s\"", p->p_peername);
+ msg = NULL;
+ len = octet_counting(bufev->input, &msg, 1);
+ if (len < 0)
+ len = non_transparent_framing(bufev->input, &msg);
+ if (len < 0)
+ dprintf("unknown method");
+ if (msg == NULL) {
+ dprintf(", incomplete frame");
+ break;
+ }
+ dprintf(", use %d bytes\n", len);
+ if (len > 0 && msg[len-1] == '\n')
+ msg[len-1] = '\0';
+ if (len == 0 || msg[len-1] != '\0') {
+ strlcpy(line, msg,
+ MINIMUM((size_t)len+1, sizeof(line)));
+ msg = line;
+ }
+ printline(p->p_hostname, msg);
+ evbuffer_drain(bufev->input, len);
+ }
+ /* Maximum frame has 5 digits, 1 space, MAXLINE chars, 1 new line. */
+ if (EVBUFFER_LENGTH(bufev->input) >= 5 + 1 + MAXLINE + 1) {
+ dprintf(", use %zu bytes\n", EVBUFFER_LENGTH(bufev->input));
+ printline(p->p_hostname, EVBUFFER_DATA(bufev->input));
+ evbuffer_drain(bufev->input, -1);
+ } else if (EVBUFFER_LENGTH(bufev->input) > 0)
+ dprintf(", buffer %zu bytes\n", EVBUFFER_LENGTH(bufev->input));
}
void
@@ -1062,13 +1153,8 @@ tcp_errorcb(struct bufferevent *bufev, short event, void *arg)
*/
buf = EVBUFFER_DATA(bufev->output);
end = buf + EVBUFFER_LENGTH(bufev->output);
- for (p = buf; p < end && p < buf + 4; p++) {
- if (!isdigit(*p))
- break;
- }
- /* Using atoi() is safe as buf starts with 1 to 4 digits and a space. */
- if (buf < end && !(buf + 1 <= p && p < end && *p == ' ' &&
- (l = atoi(buf)) > 0 && buf + l < end && buf[l] == '\n')) {
+ if (buf < end && !((l = octet_counting(bufev->output, &p, 0)) > 0 &&
+ p[l-1] == '\n')) {
for (p = buf; p < end; p++) {
if (*p == '\n') {
evbuffer_drain(bufev->output, p - buf + 1);
@@ -1077,7 +1163,7 @@ tcp_errorcb(struct bufferevent *bufev, short event, void *arg)
}
/* Without '\n' discard everything. */
if (p == end)
- evbuffer_drain(bufev->output, p - buf);
+ evbuffer_drain(bufev->output, -1);
dprintf("loghost \"%s\" dropped partial message\n",
f->f_un.f_forw.f_loghost);
f->f_un.f_forw.f_dropped++;