Hi, I've already sent this message to bugs@ list. Based on its mebers' advice I'm resending message with inline patches to tech@ list.
I'm current maintainer of netcat package in Fedora. We use OpenBSD's implementation and we have some patches included which most likely haven't been offered to you yet. Some of them rather old and I'm not even sure they will be useful to you. Anyway I'm sending all of them in this email with a brief description. I re-wrote them all for current CVS snapshot of netcat. Please consider including them in your project, I think some of them offer good functionality/bugfixes. Patches have been constructed in order they are numbered, so applying them all in that order should work with no problems. Patches are also listen in this email in that specific order. Patch description: 01-pollhup.patch - this patch adds detection of POLLHUP during data sending 02-reuseaddr.patch - activates SO_REUSEADDR socket option 03-connect_with_timeout.patch - implements possibility to set timeout for outgoing connection 04-crlf.patch - implements using CRLF as line ending 05-efficient-reads.patch - extends size of buffers for reading 06-crlf-man-page.patch - update of man page to match previous patch 07-improved-udp-scan.patch - improved algorithm performing UDP scan 08-improved-udp-scan-2.patch - complement to previous patch 09-verbose-listen.patch - verbose output when listening on non-unix socket All patches are available at http://jzeleny.fedorapeople.org/patches/nc/ Thank you and looking forward to further cooperation -- Jan diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-08 17:34:27.000000000 +0100 +++ nc/netcat.c 2010-01-08 18:01:40.000000000 +0100 @@ -660,17 +660,29 @@ readwrite(int nfd) return; } } + else if (pfd[0].revents & POLLHUP) { + shutdown(nfd, SHUT_RD); + pfd[0].fd = -1; + pfd[0].events = 0; + } - if (!dflag && pfd[1].revents & POLLIN) { - if ((n = read(wfd, buf, plen)) < 0) - return; - else if (n == 0) { + if (!dflag) { + if (pfd[1].revents & POLLIN) { + if ((n = read(wfd, buf, plen)) < 0) + return; + else if (n == 0) { + shutdown(nfd, SHUT_WR); + pfd[1].fd = -1; + pfd[1].events = 0; + } else { + if (atomicio(vwrite, nfd, buf, n) != n) + return; + } + } + else if (pfd[1].revents & POLLHUP) { shutdown(nfd, SHUT_WR); pfd[1].fd = -1; pfd[1].events = 0; - } else { - if (atomicio(vwrite, nfd, buf, n) != n) - return; } } } diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-08 18:03:26.000000000 +0100 +++ nc/netcat.c 2010-01-08 18:28:54.000000000 +0100 @@ -587,6 +587,10 @@ local_listen(char *host, char *port, str err(1, "setsockopt SO_RDOMAIN"); } + ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)); + if (ret == -1) + err(1, NULL); + ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof(x)); if (ret == -1) err(1, NULL); diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-11 08:33:51.000000000 +0100 +++ nc/netcat.c 2010-01-11 08:42:34.000000000 +0100 @@ -63,6 +63,10 @@ #define PORT_MAX 65535 #define PORT_MAX_LEN 6 +#define CONNECTION_SUCCESS 0 +#define CONNECTION_FAILED 1 +#define CONNECTION_TIMEOUT 2 + /* Command Line Options */ int dflag; /* detached, no stdin */ unsigned int iflag; /* Interval Flag */ @@ -105,6 +109,9 @@ void set_common_sockopts(int); int parse_iptos(char *); void usage(int); +static int connect_with_timeout(int fd, const struct sockaddr *sa, + socklen_t salen, int ctimeout); + int main(int argc, char *argv[]) { @@ -535,11 +542,14 @@ remote_connect(const char *host, const c set_common_sockopts(s); - if (connect(s, res0->ai_addr, res0->ai_addrlen) == 0) + if ((error = connect_with_timeout(s, res0->ai_addr, res0->ai_addrlen, timeout)) == CONNECTION_SUCCESS) break; - else if (vflag) + else if (vflag && error == CONNECTION_FAILED) warn("connect to %s port %s (%s) failed", host, port, uflag ? "udp" : "tcp"); + else if (vflag && error == CONNECTION_TIMEOUT) + warn("connect to %s port %s (%s) timed out", host, port, + uflag ? "udp" : "tcp"); close(s); s = -1; @@ -550,6 +560,73 @@ remote_connect(const char *host, const c return (s); } +static int connect_with_timeout(int fd, const struct sockaddr *sa, + socklen_t salen, int ctimeout) +{ + int err; + struct timeval tv, *tvp = NULL; + fd_set connect_fdset; + socklen_t len; + int orig_flags; + + orig_flags = fcntl(fd, F_GETFL, 0); + if (fcntl(fd, F_SETFL, orig_flags | O_NONBLOCK) < 0 ) { + warn("can't set O_NONBLOCK - timeout not avaliable"); + if (connect(fd, sa, salen) == 0) + return CONNECTION_SUCCESS; + else + return CONNECTION_FAILED; + } + + /* set connect timeout */ + if (ctimeout > 0) { + tv.tv_sec = (time_t)ctimeout/1000; + tv.tv_usec = 0; + tvp = &tv; + } + + /* attempt the connection */ + err = connect(fd, sa, salen); + + if (err != 0 && errno == EINPROGRESS) { + /* connection is proceeding + * it is complete (or failed) when select returns */ + + /* initialize connect_fdset */ + FD_ZERO(&connect_fdset); + FD_SET(fd, &connect_fdset); + + /* call select */ + do { + err = select(fd + 1, NULL, &connect_fdset, + NULL, tvp); + } while (err < 0 && errno == EINTR); + + /* select error */ + if (err < 0) + errx(1,"select error: %s", strerror(errno)); + + /* we have reached a timeout */ + if (err == 0) + return CONNECTION_TIMEOUT; + + /* select returned successfully, but we must test socket + * error for result */ + len = sizeof(err); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0) + errx(1, "getsockopt error: %s", strerror(errno)); + + /* setup errno according to the result returned by + * getsockopt */ + if (err != 0) + errno = err; + } + + /* return aborted if an error occured, and valid otherwise */ + fcntl(fd, F_SETFL, orig_flags); + return (err != 0)? CONNECTION_FAILED : CONNECTION_SUCCESS; +} + /* * local_listen() * Returns a socket listening on a local port, binds to specified source diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-11 08:43:26.000000000 +0100 +++ nc/netcat.c 2010-01-11 09:57:25.000000000 +0100 @@ -88,6 +88,7 @@ int Iflag; /* TCP receive buffer siz int Oflag; /* TCP send buffer size */ int Sflag; /* TCP MD5 signature option */ int Tflag = -1; /* IP Type of Service */ +int Cflag = 0; /* CRLF line-ending */ u_int rdomain; int timeout = -1; @@ -133,7 +134,7 @@ main(int argc, char *argv[]) sv = NULL; while ((ch = getopt(argc, argv, - "46DdhI:i:jklnO:P:p:rSs:tT:UuV:vw:X:x:z")) != -1) { + "46DdhI:i:jklnO:P:p:rSs:tT:UuV:vw:X:x:zC")) != -1) { switch (ch) { case '4': family = AF_INET; @@ -239,6 +240,9 @@ main(int argc, char *argv[]) case 'T': Tflag = parse_iptos(optarg); break; + case 'C': + Cflag = 1; + break; default: usage(1); } @@ -756,8 +760,16 @@ readwrite(int nfd) pfd[1].fd = -1; pfd[1].events = 0; } else { - if (atomicio(vwrite, nfd, buf, n) != n) - return; + if ((Cflag) && (buf[n-1]=='\n')) { + if (atomicio(vwrite, nfd, buf, n-1) != (n-1)) + return; + if (atomicio(vwrite, nfd, "\r\n", 2) != 2) + return; + } + else { + if (atomicio(vwrite, nfd, buf, n) != n) + return; + } } } else if (pfd[1].revents & POLLHUP) { @@ -947,6 +959,7 @@ help(void) fprintf(stderr, "\tCommand Summary:\n\ \t-4 Use IPv4\n\ \t-6 Use IPv6\n\ + \t-C Send CRLF as line-ending\n\ \t-D Enable the debug socket option\n\ \t-d Detach from stdin\n\ \t-h This help text\n\ @@ -979,7 +992,7 @@ void usage(int ret) { fprintf(stderr, - "usage: nc [-46DdhklnrStUuvz] [-I length] [-i interval] [-O length]\n" + "usage: nc [-46CDdhklnrStUuvz] [-I length] [-i interval] [-O length]\n" "\t [-P proxy_username] [-p source_port] [-s source_ip_address] [-T ToS]\n" "\t [-V rdomain] [-w timeout] [-X proxy_protocol]\n" "\t [-x proxy_address[:port]] [hostname] [port]\n"); diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-11 10:00:39.000000000 +0100 +++ nc/netcat.c 2010-01-11 10:02:44.000000000 +0100 @@ -334,11 +334,11 @@ main(int argc, char *argv[]) */ if (uflag) { int rv, plen; - char buf[8192]; + char buf[16384]; struct sockaddr_storage z; len = sizeof(z); - plen = jflag ? 8192 : 1024; + plen = jflag ? 16384 : 2048; rv = recvfrom(s, buf, plen, MSG_PEEK, (struct sockaddr *)&z, &len); if (rv < 0) @@ -704,12 +704,12 @@ void readwrite(int nfd) { struct pollfd pfd[2]; - unsigned char buf[8192]; + unsigned char buf[16384]; int n, wfd = fileno(stdin); int lfd = fileno(stdout); int plen; - plen = jflag ? 8192 : 1024; + plen = jflag ? 16384 : 2048; /* Setup Network FD */ pfd[0].fd = nfd; diff -Nurp nc-orig/nc.1 nc/nc.1 --- nc-orig/nc.1 2010-01-08 17:34:27.000000000 +0100 +++ nc/nc.1 2010-01-11 10:09:38.000000000 +0100 @@ -25,7 +25,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: October 22 2009 $ +.Dd $Mdocdate: January 11, 2010 $ .Dt NC 1 .Os .Sh NAME @@ -34,7 +34,7 @@ .Sh SYNOPSIS .Nm nc .Bk -words -.Op Fl 46DdhklnrStUuvz +.Op Fl 46CDdhklnrStUuvz .Op Fl I Ar length .Op Fl i Ar interval .Op Fl O Ar length @@ -96,6 +96,8 @@ to use IPv4 addresses only. Forces .Nm to use IPv6 addresses only. +.It Fl C +Send CRLF as line-ending .It Fl D Enable debugging on the socket. .It Fl d @@ -329,7 +331,7 @@ More complicated examples can be built u of requests required by the server. As another example, an email may be submitted to an SMTP server using: .Bd -literal -offset indent -$ nc localhost 25 \*(Lt\*(Lt EOF +$ nc [-C] localhost 25 \*(Lt\*(Lt EOF HELO host.example.com MAIL FROM:\*(ltu...@host.example.com\*(Gt RCPT TO:\*(ltus...@host.example.com\*(Gt diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-11 10:04:12.000000000 +0100 +++ nc/netcat.c 2010-01-11 10:56:38.000000000 +0100 @@ -67,6 +67,8 @@ #define CONNECTION_FAILED 1 #define CONNECTION_TIMEOUT 2 +#define UDP_SCAN_TIMEOUT 3 /* Seconds */ + /* Command Line Options */ int dflag; /* detached, no stdin */ unsigned int iflag; /* Interval Flag */ @@ -396,7 +398,7 @@ main(int argc, char *argv[]) continue; ret = 0; - if (vflag || zflag) { + if ((vflag && !uflag) || zflag) { /* For UDP, make sure we are connected. */ if (uflag) { if (udptest(s) == -1) { @@ -887,15 +889,20 @@ build_ports(char *p) int udptest(int s) { - int i, ret; + int i, t; - for (i = 0; i <= 3; i++) { - if (write(s, "X", 1) == 1) - ret = 1; - else - ret = -1; + if ((write(s, "X", 1) != 1) || + ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED))) + return -1; + + /* Give the remote host some time to reply. */ + for (i = 0, t = (timeout == -1) ? UDP_SCAN_TIMEOUT : (timeout / 1000); + i < t; i++) { + sleep(1); + if ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED)) + return -1; } - return (ret); + return 1; } void diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-11 10:57:43.000000000 +0100 +++ nc/netcat.c 2010-01-11 11:05:54.000000000 +0100 @@ -752,6 +752,9 @@ readwrite(int nfd) pfd[0].fd = -1; pfd[0].events = 0; } + else if (pfd[0].revents & POLLERR) + if (write(nfd, "", 1) == -1) + warn("Write error"); if (!dflag) { if (pfd[1].revents & POLLIN) { diff -Nurp nc-orig/netcat.c nc/netcat.c --- nc-orig/netcat.c 2010-01-11 11:56:07.000000000 +0100 +++ nc/netcat.c 2010-01-11 13:51:34.000000000 +0100 @@ -357,6 +357,21 @@ main(int argc, char *argv[]) &len); } + if(vflag && family != AF_UNIX) { + /* Don't look up port if -n. */ + if (nflag) + sv = NULL; + else + sv = getservbyport(ntohs(atoi(uport)), + uflag ? "udp" : "tcp"); + + fprintf(stderr, "Connection from %s port %s [%s/%s] accepted\n", + inet_ntoa(((struct sockaddr_in *)(&cliaddr))->sin_addr), + uport, + uflag ? "udp" : "tcp", + sv ? sv->s_name : "*"); + } + readwrite(connfd); close(connfd); if (family != AF_UNIX)