#849192 netcat-openbsd: `nc $host $port </dev/null` hangs forever

Package:
netcat-openbsd
Source:
netcat-openbsd
Description:
TCP/IP swiss army knife
Submitter:
Cristian Ionescu-Idbohrn
Date:
2017-11-23 15:15:10 UTC
Severity:
important
#849192#5
Date:
2016-12-23 11:47:10 UTC
From:
To:
This is netcat-openbsd 1.105-7
------------------------------

Command line is:

	$ echo 'ehlo itsme' | nc.openbsd host.example.com 25

The output is:

220 host.example.com ESMTP Postfix
250-host.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

The simple trace is:

  1  0.000000000  1.1.1.11 ? 1.1.1.1   TCP 74 47242?smtp(25) [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1188714834 TSecr=0 WS=128
  2  0.000130857   1.1.1.1 ? 1.1.1.11  TCP 74 smtp(25)?47242 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=1189398612 TSecr=1188714834 WS=128
  3  0.000171694  1.1.1.11 ? 1.1.1.1   TCP 66 47242?smtp(25) [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=1188714834 TSecr=1189398612
  4  0.000539830  1.1.1.11 ? 1.1.1.1   SMTP 77 C: ehlo itsme
  5  0.000555726  1.1.1.11 ? 1.1.1.1   TCP 66 47242?smtp(25) [FIN, ACK] Seq=12 Ack=1 Win=29312 Len=0 TSval=1188714834 TSecr=1189398612
  6  0.000682959   1.1.1.1 ? 1.1.1.11  TCP 66 smtp(25)?47242 [ACK] Seq=1 Ack=12 Win=29056 Len=0 TSval=1189398612 TSecr=1188714834
  7  0.009776682   1.1.1.1 ? 1.1.1.11  SMTP 106 S: 220 host.example.com ESMTP Postfix
  8  0.009800662  1.1.1.11 ? 1.1.1.1   TCP 66 47242?smtp(25) [ACK] Seq=13 Ack=41 Win=29312 Len=0 TSval=1188714836 TSecr=1189398615
  9  0.009807945   1.1.1.1 ? 1.1.1.11  SMTP 195 S: 250 host.example.com | 250 PIPELINING | 250 SIZE 10240000 | 250 VRFY | 250 ETRN | 250 ENHANCEDSTATUSCODES | 250 8BITMIME | 250 DSN
 10  0.009822556  1.1.1.11 ? 1.1.1.1   TCP 66 47242?smtp(25) [ACK] Seq=13 Ack=171 Win=30336 Len=0 TSval=1188714836 TSecr=1189398615



This is netcat-openbsd 1.130-1
------------------------------

Same command line:

	$ echo 'ehlo itsme' | nc.openbsd host.example.com 25

There is _no_ output.

It seems nc.openbsd is closing the socket before reading/showing
whatever host.example.com returns.  Note the two resets '[RST]'
bellow.

 11 25.973506450  1.1.1.11 ? 1.1.1.1   TCP 74 47246?smtp(25) [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1188721327 TSecr=0 WS=128
 12 25.973662894   1.1.1.1 ? 1.1.1.11  TCP 74 smtp(25)?47246 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=1189405106 TSecr=1188721327 WS=128
 13 25.973690712  1.1.1.11 ? 1.1.1.1   TCP 66 47246?smtp(25) [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=1188721327 TSecr=1189405106
 14 25.973728427  1.1.1.11 ? 1.1.1.1   SMTP 77 C: ehlo itsme
 15 25.973747259  1.1.1.11 ? 1.1.1.1   TCP 66 47246?smtp(25) [FIN, ACK] Seq=12 Ack=1 Win=29312 Len=0 TSval=1188721327 TSecr=1189405106
 16 25.973845701   1.1.1.1 ? 1.1.1.11  TCP 66 smtp(25)?47246 [ACK] Seq=1 Ack=12 Win=29056 Len=0 TSval=1189405106 TSecr=1188721327
 17 25.974788937   1.1.1.1 ? 1.1.1.11  SMTP 106 S: 220 host.example.com ESMTP Postfix
 18 25.974817575  1.1.1.11 ? 1.1.1.1   TCP 54 47246?smtp(25) [RST] Seq=13 Win=0 Len=0
                                                              ^^^
 19 25.974872212   1.1.1.1 ? 1.1.1.11  SMTP 195 S: 250 host.example.com | 250 PIPELINING | 250 SIZE 10240000 | 250 VRFY | 250 ETRN | 250 ENHANCEDSTATUSCODES | 250 8BITMIME | 250 DSN
 20 25.974896332  1.1.1.11 ? 1.1.1.1   TCP 54 47246?smtp(25) [RST] Seq=13 Win=0 Len=0
                                                              ^^^

If I add the '-q1' option (wait one second after EOF on stdin) to the
command line:

	$ echo 'ehlo itsme' | nc.openbsd -q1 host.example.com 25

then I get the expected output:

220 host.example.com ESMTP Postfix
250-host.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

Note, no '[RST]' in the packet trace.

  1  0.000000000  1.1.1.11 ? 1.1.1.1   TCP 74 47742?smtp(25) [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1189159653 TSecr=0 WS=128
  2  0.000144981   1.1.1.1 ? 1.1.1.11  TCP 74 smtp(25)?47742 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=1189843431 TSecr=1189159653 WS=128
  3  0.000173801  1.1.1.11 ? 1.1.1.1   TCP 66 47742?smtp(25) [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=1189159653 TSecr=1189843431
  4  0.000208860  1.1.1.11 ? 1.1.1.1   SMTP 77 C: ehlo itsme
  5  0.000338115   1.1.1.1 ? 1.1.1.11  TCP 66 smtp(25)?47742 [ACK] Seq=1 Ack=12 Win=29056 Len=0 TSval=1189843432 TSecr=1189159653
  6  0.001715233   1.1.1.1 ? 1.1.1.11  SMTP 106 S: 220 host.example.com ESMTP Postfix
  7  0.001733564  1.1.1.11 ? 1.1.1.1   TCP 66 47742?smtp(25) [ACK] Seq=12 Ack=41 Win=29312 Len=0 TSval=1189159653 TSecr=1189843432
  8  0.001875842   1.1.1.1 ? 1.1.1.11  SMTP 195 S: 250 host.example.com | 250 PIPELINING | 250 SIZE 10240000 | 250 VRFY | 250 ETRN | 250 ENHANCEDSTATUSCODES | 250 8BITMIME | 250 DSN
  9  0.001895568  1.1.1.11 ? 1.1.1.1   TCP 66 47742?smtp(25) [ACK] Seq=12 Ack=170 Win=30336 Len=0 TSval=1189159653 TSecr=1189843432
 10  1.002139332  1.1.1.11 ? 1.1.1.1   TCP 66 47742?smtp(25) [FIN, ACK] Seq=12 Ack=170 Win=30336 Len=0 TSval=1189159903 TSecr=1189843432
 11  1.002387350   1.1.1.1 ? 1.1.1.11  TCP 66 smtp(25)?47742 [FIN, ACK] Seq=170 Ack=13 Win=29056 Len=0 TSval=1189843682 TSecr=1189159903
 12  1.002412151  1.1.1.11 ? 1.1.1.1   TCP 66 47742?smtp(25) [ACK] Seq=13 Ack=171 Win=30336 Len=0 TSval=1189159903 TSecr=1189843682

#849192#10
Date:
2017-01-25 08:40:41 UTC
From:
To:
This particular problem (along with the same no-output when using "nc -d -l")
seems to be caused by a Debian change which introduces the -q option[1]. This
causes the readwrite loop to exit before handling possible return data or the
received data in the listen case.
One draw back with the proposed fix: while this now allows to receive the data
sent back, it will hang there until stopped. That problem comes from upstream
changes. They added a -N flag to send shutdown messages to the remote socket
when the input stream was closed (which was the default in the old version of
nc). So:

    echo 'ehlo itsme' | nc.openbsd -N host.example.com 25

would then behave as expected. Not sure how to handle this. IMO it would have
been better if upstream kept the old behaviour as default and added an option to
*not* do the shutdown. But now that its done that way it might be confusing to
change it.
--- one possible fix ---

Description: Fix handling of delayed exit option
 The option for delayed exit (-q#) existed before but since upstream
 rewrote the readwrite call the patch adding it had to be adapted.
 However the current implementation causes the loop to exit too early.
 Move the exit handling to those places which do exit the loop in the
 original code. While on it also add some code to close the net_fd on
 delayed exit.
Author: Stefan Bader <stefan.bader@canonical.com>

Index: netcat-openbsd-1.130/netcat.c
===================================================================
--- netcat-openbsd-1.130.orig/netcat.c
+++ netcat-openbsd-1.130/netcat.c
@@ -178,6 +178,8 @@ char    *proto_name(int uflag, int dccpf

 static int connect_with_timeout(int fd, const struct sockaddr *sa,
         socklen_t salen, int ctimeout);
+
+int	quit_fd = -1;
 static void quit();

 int
@@ -1028,19 +1030,31 @@ readwrite(int net_fd)
 		/* both inputs are gone, buffers are empty, we are done */
 		if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
 		    && stdinbufpos == 0 && netinbufpos == 0) {
-			close(net_fd);
-			return;
+			if (qflag == 0) {
+				close(net_fd);
+				return;
+			}
+			goto delay_exit;
 		}
 		/* both outputs are gone, we can't continue */
 		if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) {
-			close(net_fd);
-			return;
+			if (qflag == 0) {
+				close(net_fd);
+				return;
+			}
+			goto delay_exit;
 		}
 		/* listen and net in gone, queues empty, done */
 		if (lflag && pfd[POLL_NETIN].fd == -1
 		    && stdinbufpos == 0 && netinbufpos == 0) {
-			close(net_fd);
-			return;
+			if (qflag == 0) {
+				close(net_fd);
+				return;
+			}
+delay_exit:
+			quit_fd = net_fd;
+			signal(SIGALRM, quit);
+			alarm(qflag);
 		}

 		/* poll */
@@ -1159,20 +1173,10 @@ readwrite(int net_fd)
 			if (pfd[POLL_NETOUT].fd != -1 && Nflag)
 				shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
 			pfd[POLL_NETOUT].fd = -1;
-			goto exit_on_eof;
 		}
 		/* net in gone and queue empty? */
 		if (pfd[POLL_NETIN].fd == -1 && netinbufpos == 0) {
 			pfd[POLL_STDOUT].fd = -1;
-			exit_on_eof:
-				/* if the user asked to exit on EOF, do it */
-				if (qflag == 0)
-					return;
-				/* if user asked to die after a while, arrange for it */
-				else if (qflag > 0) {
-					signal(SIGALRM, quit);
-					alarm(qflag);
-				}
 		}
 	}
 }
@@ -1615,6 +1619,8 @@ usage(int ret)
  */
 static void quit()
 {
-        /* XXX: should explicitly close fds here */
+        if (quit_fd >= 0)
+		close(quit_fd);
+
         exit(0);
 }

#849192#15
Date:
2017-01-26 10:04:37 UTC
From:
To:
We believe that the bug you reported is fixed in the latest version of
netcat-openbsd, which is due to be installed in the Debian FTP archive.

A summary of the changes between this version and the previous one is
attached.

Thank you for reporting the bug, which will now be closed.  If you
have further comments please address them to 849192@bugs.debian.org,
and the maintainer will reopen the bug report if appropriate.

Debian distribution maintenance software
pp.
Aron Xu <aron@debian.org> (supplier of updated netcat-openbsd package)

(This message was generated automatically at their request; if you
believe that there is a problem with it please contact the archive
administrators by mailing ftpmaster@ftp-master.debian.org)
Format: 1.8
Date: Thu, 26 Jan 2017 16:51:13 +0800
Source: netcat-openbsd
Binary: netcat-openbsd
Architecture: source amd64
Version: 1.130-2
Distribution: unstable
Urgency: medium
Maintainer: Aron Xu <aron@debian.org>
Changed-By: Aron Xu <aron@debian.org>
Description:
 netcat-openbsd - TCP/IP swiss army knife
Closes: 849192
Changes:
 netcat-openbsd (1.130-2) unstable; urgency=medium
 .
   * Fix handling of delayed exit option (Closes: #849192, LP: #1656785)
     Thanks to Stefan Bader!
Checksums-Sha1:
 9689c1b4dc540b4b9968d2cdbbeb6a4fa212edc4 1610 netcat-openbsd_1.130-2.dsc
 189dab80d6ec76f7d72be1c44f3288d4e79bb02d 28532 netcat-openbsd_1.130-2.debian.tar.xz
 5a6ed60d496d8856978312c9621896bc4c1bf57e 27260 netcat-openbsd-dbgsym_1.130-2_amd64.deb
 5216ca87d255b2869c0ec5ff55f83e6cb485f6fa 4788 netcat-openbsd_1.130-2_amd64.buildinfo
 7c5958fc1f08b8e01454beb74f9e33c59f450c4b 38024 netcat-openbsd_1.130-2_amd64.deb
Checksums-Sha256:
 4a66048d12a7c08d2ff9d84a44ef0215551d8710f01f4cf8617627797aecd151 1610 netcat-openbsd_1.130-2.dsc
 54322c9385d3ef787cd6fb3b5bf4c50b5b591e51b82e9610e6615254f8a19113 28532 netcat-openbsd_1.130-2.debian.tar.xz
 3204ba643976689742962bba67077775c58ca2208f9ac30205bb6e71913e583c 27260 netcat-openbsd-dbgsym_1.130-2_amd64.deb
 292c567bbd0c3eeb0d46a7717519fe28d789cf7d058f1bc4b03a70312da58276 4788 netcat-openbsd_1.130-2_amd64.buildinfo
 cb3bcba1b2a54550bcccbd4ea37ee82999435367e25f94c3638d2ee0cc94c2d3 38024 netcat-openbsd_1.130-2_amd64.deb
Files:
 66e27be50b46d15d94db7940b75da43e 1610 net important netcat-openbsd_1.130-2.dsc
 9528387da2ee6e4c6b2d1ca33948e196 28532 net important netcat-openbsd_1.130-2.debian.tar.xz
 978f66b24df28682d6ae60f2297a3fbb 27260 debug extra netcat-openbsd-dbgsym_1.130-2_amd64.deb
 054d8296660189dbbb7c125dfed0df18 4788 net important netcat-openbsd_1.130-2_amd64.buildinfo
 806e6f2cfad41d8f7c6fc448da6c7fbe 38024 net important netcat-openbsd_1.130-2_amd64.deb
-----BEGIN PGP SIGNATURE-----

iQFDBAEBCAAtFiEET3MbhxKET+7/a6zdW0gHVdEZ6o4FAliJxY8PHHN5cUBkZWJp
YW4ub3JnAAoJEFtIB1XRGeqOHEIH/1ijNhqLb0rWIUqLhWtoO49Wuo5wvQFu5jk3
SFx7UKR24evo886QXgdI/fnZKb46bNytb/xPUjHldQybki+7tguU5AcmoRXFvjlz
E7V+nI8fo6I4ITepRpCuHjU/9jfoINKZ3lDD6EoMjB+l2NlSC/ek/vQBS9Y/Ieqs
W04Wl7r3a0qM+sGioaRGhcIJPfMUmCkTynIVYMrqsuwxTHSmY2z6HikAaZ7lLoBk
09wqv/TnsbiGRnQPx7cWP2yj0YxbqCLXPR6pKasgxus0buteH4rxRN/Hm/RbENdI
3fYDET111UhvNyHHEqMplBnbmkOH7SUB0SGm41FCaZUfnYENAPE=
=dGtm
-----END PGP SIGNATURE-----

#849192#20
Date:
2017-01-26 16:03:27 UTC
From:
To:
Thanks.

Good but not prefect :(  Try:

	$ echo ehlo itsme | nc.openbsd localhost 25

It will just hang in a 'poll' until it later exits with:

	Error: timeout exceeded

And "later" is 5 minutes.  postfix mail server here.

#849192#29
Date:
2017-01-27 13:45:34 UTC
From:
To:
On Thu, 26 Jan 2017 17:03:27 +0100 (CET) Cristian Ionescu-Idbohrn
This is what I was trying to say about -N. It is a change that comes from
openbsd. From a practical point of view, using -N is what was done by default
before. If the option is not used nc will not send a shutdown request and that
causes it to hang around.

#849192#32
Date:
2017-03-03 19:30:46 UTC
From:
To:
Control: tag -1 wontfix

Hi Cristian,

As Stefan hinted at, this is a deliberate upstream decision.  Since
netcat-openbsd 1.110 one needs to pass ‘-N’ in order to shutdown(2) the
socket once EOF has been reach.

In upstream's own words [0]:

    Don't shutdown nc(1)'s network socket when stdin closes. Matches
    *Hobbit*'s original netcat and GNU netcat; revert to old behaviour
    with the new -N flag if needed. After much discussion with otto
    deraadt tedu and Martin Pelikan.

(Passing ‘-q’ with a non-negative value should imply ‘-N’ though, cf.
#854292.)

Cheers,