"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
bufferize smtp parsing in got-notify-email
To:
gameoftrees@openbsd.org
Date:
Wed, 20 Mar 2024 13:13:28 +0100

Download raw body.

Thread
rea(2)ing one byte at a time is a big ugly.  This adds a buffer for the
server replies and reworks read_smtp_code() to consume the data from
said buffer first.  it also makes got_poll_read_full_timeout()
redundant.

ok?

diff /home/op/w/got
commit - 39910b637a9a53cc48b0c63766da691dec0af593
path + /home/op/w/got
blob - ef9b199c10bfcb34951de0d9a6e4c0915a91e274
file + gotd/libexec/got-notify-email/got-notify-email.c
--- gotd/libexec/got-notify-email/got-notify-email.c
+++ gotd/libexec/got-notify-email/got-notify-email.c
@@ -18,6 +18,7 @@
 #include <sys/socket.h>
 
 #include <errno.h>
+#include <poll.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -33,7 +34,11 @@
 
 #include "got_lib_poll.h"
 
+#define SMTP_LINE_MAX	65535
+
 static int smtp_timeout = 60; /* in seconds */
+static char smtp_buf[SMTP_LINE_MAX];
+static size_t smtp_buflen;
 
 __dead static void
 usage(void)
@@ -111,43 +116,54 @@ static int
 read_smtp_code(int s, const char *code)
 {
 	const struct got_error *error;
-	char buf[4];
-	size_t n;
+	char	*endl;
+	size_t	 linelen;
+	ssize_t	 r;
 
-	error = got_poll_read_full_timeout(s, &n, buf, 3, 3, smtp_timeout);
-	if (error)
-		errx(1, "read: %s", error->msg);
-	if (strncmp(buf, code, 3) != 0) {
-		buf[3] = '\0';
-		warnx("unexpected SMTP message code: %s", buf);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int
-skip_to_crlf(int s)
-{
-	const struct got_error *error;
-	char buf[1];
-	size_t len;
-
 	for (;;) {
-		error = got_poll_read_full_timeout(s, &len, buf, 1, 1,
-		    smtp_timeout);
+		endl = memmem(smtp_buf, smtp_buflen, "\r\n", 2);
+		if (endl != NULL)
+			break;
+
+		if (smtp_buflen == sizeof(smtp_buf))
+			errx(1, "line too long");
+
+		error = got_poll_fd(s, POLLIN, smtp_timeout);
 		if (error)
-			errx(1, "read: %s", error->msg);
-		if (buf[0] == '\r') {
-			error = got_poll_read_full(s, &len, buf, 1, 1);
-			if (error)
-				errx(1, "read: %s", error->msg);
-			if (buf[0] == '\n')
-				return 0;
-		}
+			errx(1, "poll: %s", error->msg);
+
+		r = read(s, smtp_buf + smtp_buflen,
+		    sizeof(smtp_buf) - smtp_buflen);
+		if (r == -1 && errno != EAGAIN)
+			err(1, "read");
+		if (r == 0)
+			errx(1, "unexpected EOF");
+		smtp_buflen += r;
 	}
 
-	return -1;
+	linelen = endl - smtp_buf;
+	if (linelen < 3)
+		errx(1, "invalid SMTP response");
+
+	if (strncmp(code, smtp_buf, 3) != 0) {
+		smtp_buf[3] = '\0';
+		warnx("unexpected SMTP message code: %s", smtp_buf);
+		return -1;
+	}
+
+	/*
+	 * Normally we would get just one reply, but the regress doesn't
+	 * use a real SMTP server and queues all the replies upfront.
+	 */
+	linelen += 2;
+	if (smtp_buflen == linelen)
+		smtp_buflen = 0;
+	else {
+		memmove(smtp_buf, smtp_buf + linelen, smtp_buflen - linelen);
+		smtp_buflen -= linelen;
+	}
+
+	return 0;
 }
 
 static int
@@ -216,36 +232,26 @@ send_email(int s, const char *myfromaddr, const char *
 
 	if (read_smtp_code(s, "220"))
 		errx(1, "unexpected SMTP greeting received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	if (send_smtp_msg(s, "HELO localhost\r\n"))
 		errx(1, "could not send HELO");
 	if (read_smtp_code(s, "250"))
 		errx(1, "unexpected SMTP response received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	if (send_smtp_msg(s, "MAIL FROM:<%s>\r\n", myfromaddr))
 		errx(1, "could not send MAIL FROM");
 	if (read_smtp_code(s, "250"))
 		errx(1, "unexpected SMTP response received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	if (send_smtp_msg(s, "RCPT TO:<%s>\r\n", recipient))
 		errx(1, "could not send MAIL FROM");
 	if (read_smtp_code(s, "250"))
 		errx(1, "unexpected SMTP response received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	if (send_smtp_msg(s, "DATA\r\n"))
 		errx(1, "could not send MAIL FROM");
 	if (read_smtp_code(s, "354"))
 		errx(1, "unexpected SMTP response received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	if (send_smtp_msg(s, "From: %s\r\n", fromaddr))
 		errx(1, "could not send From header");
@@ -279,16 +285,12 @@ send_email(int s, const char *myfromaddr, const char *
 		errx(1, "could not send data terminator");
 	if (read_smtp_code(s, "250"))
 		errx(1, "unexpected SMTP response received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	if (send_smtp_msg(s, "QUIT\r\n"))
 		errx(1, "could not send QUIT");
 
 	if (read_smtp_code(s, "221"))
 		errx(1, "unexpected SMTP response received");
-	if (skip_to_crlf(s))
-		errx(1, "invalid SMTP message received");
 
 	close(s);
 	free(line);