git: d4b3aefdf94f - main - netcat: add --crlf to convert LF into CRLF

From: Pietro Cerutti <gahr_at_FreeBSD.org>
Date: Mon, 28 Aug 2023 11:25:23 UTC
The branch main has been updated by gahr:

URL: https://cgit.FreeBSD.org/src/commit/?id=d4b3aefdf94fe401f6bfc93a33b69e21dd7a634a

commit d4b3aefdf94fe401f6bfc93a33b69e21dd7a634a
Author:     Pietro Cerutti <gahr@FreeBSD.org>
AuthorDate: 2023-08-17 11:42:23 +0000
Commit:     Pietro Cerutti <gahr@FreeBSD.org>
CommitDate: 2023-08-28 10:58:17 +0000

    netcat: add --crlf to convert LF into CRLF
    
    This adds the --crlf option to netcat, which triggers translation of \n
    characters into \r\n sequences in the input -> network direction.
    The Linux version of nc also supports this functionality with --crlf and
    -C. The OpenBSD version uses -C to specify client certificates. Our
    version is too old and doesn't have it, but I avoided adding -C anyway
    to ease future syncs with upstream.
    
    Attempts to upstream the feature were unsuccessful:
    https://marc.info/?t=169282068500001
    
    Differential Revision: https://reviews.freebsd.org/D41489
---
 contrib/netcat/nc.1     |  3 +++
 contrib/netcat/netcat.c | 50 ++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/contrib/netcat/nc.1 b/contrib/netcat/nc.1
index 9f8696135f35..2e34cdc586c3 100644
--- a/contrib/netcat/nc.1
+++ b/contrib/netcat/nc.1
@@ -42,6 +42,7 @@
 .Op Fl i Ar interval
 .Op Fl -no-tcpopt
 .Op Fl -sctp
+.Op Fl -crlf
 .Op Fl O Ar length
 .Op Fl P Ar proxy_username
 .Op Fl p Ar source_port
@@ -104,6 +105,8 @@ to use IPv4 addresses only.
 Forces
 .Nm
 to use IPv6 addresses only.
+.It Fl -crlf
+Convert LF into CRLF when sending data over the network.
 .It Fl D
 Enable debugging on the socket.
 .It Fl d
diff --git a/contrib/netcat/netcat.c b/contrib/netcat/netcat.c
index 65266b99c28e..279489a45e92 100644
--- a/contrib/netcat/netcat.c
+++ b/contrib/netcat/netcat.c
@@ -94,6 +94,7 @@ int	Nflag;					/* shutdown() network socket */
 int	nflag;					/* Don't do name look up */
 int	FreeBSD_Oflag;				/* Do not use TCP options */
 int	FreeBSD_sctp;				/* Use SCTP */
+int	FreeBSD_crlf;				/* Convert LF to CRLF */
 char   *Pflag;					/* Proxy username */
 char   *pflag;					/* Localport flag */
 int	rflag;					/* Random ports flag */
@@ -136,7 +137,8 @@ void	set_common_sockopts(int, int);
 int	map_tos(char *, int *);
 void	report_connect(const struct sockaddr *, socklen_t);
 void	usage(int);
-ssize_t drainbuf(int, unsigned char *, size_t *);
+ssize_t write_wrapper(int, const void *, size_t);
+ssize_t drainbuf(int, unsigned char *, size_t *, int);
 ssize_t fillbuf(int, unsigned char *, size_t *);
 
 #ifdef IPSEC
@@ -165,6 +167,7 @@ main(int argc, char *argv[])
 	struct addrinfo proxyhints;
 	char unix_dg_tmp_socket_buf[UNIX_DG_TMP_SOCKET_SIZE];
 	struct option longopts[] = {
+		{ "crlf",	no_argument,	&FreeBSD_crlf,	1 },
 		{ "no-tcpopt",	no_argument,	&FreeBSD_Oflag,	1 },
 		{ "sctp",	no_argument,	&FreeBSD_sctp,	1 },
 		{ "tun",	required_argument,	NULL,	FREEBSD_TUN },
@@ -972,7 +975,7 @@ readwrite(int net_fd)
 		/* try to write to network */
 		if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) {
 			ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf,
-			    &stdinbufpos);
+			    &stdinbufpos, FreeBSD_crlf);
 			if (ret == -1)
 				pfd[POLL_NETOUT].fd = -1;
 			/* buffer empty - remove self from polling */
@@ -1007,7 +1010,7 @@ readwrite(int net_fd)
 		/* try to write to stdout */
 		if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) {
 			ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf,
-			    &netinbufpos);
+			    &netinbufpos, 0);
 			if (ret == -1)
 				pfd[POLL_STDOUT].fd = -1;
 			/* buffer empty - remove self from polling */
@@ -1037,17 +1040,41 @@ readwrite(int net_fd)
 }
 
 ssize_t
-drainbuf(int fd, unsigned char *buf, size_t *bufpos)
+write_wrapper(int fd, const void *buf, size_t buflen)
 {
-	ssize_t n;
+	ssize_t n = write(fd, buf, buflen);
+	/* don't treat EAGAIN, EINTR as error */
+	return (n == -1 && (errno == EAGAIN || errno == EINTR)) ? -2 : n;
+}
+
+ssize_t
+drainbuf(int fd, unsigned char *buf, size_t *bufpos, int crlf)
+{
+	ssize_t n = *bufpos, n2 = 0;
 	ssize_t adjust;
+	unsigned char *lf = NULL;
+
+	if (crlf) {
+		lf = memchr(buf, '\n', *bufpos);
+		if (lf && (lf == buf || *(lf - 1) != '\r'))
+			n = lf - buf;
+		else
+			lf = NULL;
+	}
+
+	if (n != 0) {
+		n = write_wrapper(fd, buf, n);
+		if (n <= 0)
+			return n;
+	}
+
+	if (lf) {
+		n2 = write_wrapper(fd, "\r\n", 2);
+		if (n2 <= 0)
+			return n2;
+		n += 1;
+	}
 
-	n = write(fd, buf, *bufpos);
-	/* don't treat EAGAIN, EINTR as error */
-	if (n == -1 && (errno == EAGAIN || errno == EINTR))
-		n = -2;
-	if (n <= 0)
-		return n;
 	/* adjust buffer */
 	adjust = *bufpos - n;
 	if (adjust > 0)
@@ -1440,6 +1467,7 @@ help(void)
 	fprintf(stderr, "\tCommand Summary:\n\
 	\t-4		Use IPv4\n\
 	\t-6		Use IPv6\n\
+	\t--crlf	Convert LF into CRLF when sending data over the network\n\
 	\t-D		Enable the debug socket option\n\
 	\t-d		Detach from stdin\n");
 #ifdef IPSEC