summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortedu <>2014-10-30 16:06:07 +0000
committertedu <>2014-10-30 16:06:07 +0000
commit56e63b7a9edf56693944d3b05ae76e1d2be27991 (patch)
treea772cfa5cc001f902fc25e12569c3c562b6ad18d /src
parenta0b4b08aa4811ef730e76a9ffc401a5293770f92 (diff)
downloadopenbsd-56e63b7a9edf56693944d3b05ae76e1d2be27991.tar.gz
openbsd-56e63b7a9edf56693944d3b05ae76e1d2be27991.tar.bz2
openbsd-56e63b7a9edf56693944d3b05ae76e1d2be27991.zip
rework the poll loop to poll in both directions so it doesn't get stuck
if one pipe stalls out. from a diff by Arne Becker. (buffer size left alone for now)
Diffstat (limited to 'src')
-rw-r--r--src/usr.bin/nc/netcat.c258
1 files changed, 211 insertions, 47 deletions
diff --git a/src/usr.bin/nc/netcat.c b/src/usr.bin/nc/netcat.c
index e6ec97ed0f..a8e90186e9 100644
--- a/src/usr.bin/nc/netcat.c
+++ b/src/usr.bin/nc/netcat.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: netcat.c,v 1.124 2014/10/26 13:59:30 millert Exp $ */ 1/* $OpenBSD: netcat.c,v 1.125 2014/10/30 16:06:07 tedu Exp $ */
2/* 2/*
3 * Copyright (c) 2001 Eric Jackson <ericj@monkey.org> 3 * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
4 * 4 *
@@ -64,6 +64,12 @@
64#define PORT_MAX_LEN 6 64#define PORT_MAX_LEN 6
65#define UNIX_DG_TMP_SOCKET_SIZE 19 65#define UNIX_DG_TMP_SOCKET_SIZE 19
66 66
67#define POLL_STDIN 0
68#define POLL_NETOUT 1
69#define POLL_NETIN 2
70#define POLL_STDOUT 3
71#define BUFSIZE 2048
72
67/* Command Line Options */ 73/* Command Line Options */
68int dflag; /* detached, no stdin */ 74int dflag; /* detached, no stdin */
69int Fflag; /* fdpass sock to stdout */ 75int Fflag; /* fdpass sock to stdout */
@@ -111,6 +117,8 @@ void set_common_sockopts(int);
111int map_tos(char *, int *); 117int map_tos(char *, int *);
112void report_connect(const struct sockaddr *, socklen_t); 118void report_connect(const struct sockaddr *, socklen_t);
113void usage(int); 119void usage(int);
120ssize_t drainbuf(int, unsigned char *, size_t *);
121ssize_t fillbuf(int, unsigned char *, size_t *);
114 122
115int 123int
116main(int argc, char *argv[]) 124main(int argc, char *argv[])
@@ -390,7 +398,7 @@ main(int argc, char *argv[])
390 &len); 398 &len);
391 if (connfd == -1) { 399 if (connfd == -1) {
392 /* For now, all errnos are fatal */ 400 /* For now, all errnos are fatal */
393 err(1, "accept"); 401 err(1, "accept");
394 } 402 }
395 if (vflag) 403 if (vflag)
396 report_connect((struct sockaddr *)&cliaddr, len); 404 report_connect((struct sockaddr *)&cliaddr, len);
@@ -729,68 +737,224 @@ local_listen(char *host, char *port, struct addrinfo hints)
729 * Loop that polls on the network file descriptor and stdin. 737 * Loop that polls on the network file descriptor and stdin.
730 */ 738 */
731void 739void
732readwrite(int nfd) 740readwrite(int net_fd)
733{ 741{
734 struct pollfd pfd[2]; 742 struct pollfd pfd[4];
735 unsigned char buf[16 * 1024]; 743 int stdin_fd = STDIN_FILENO;
736 int n, wfd = fileno(stdin); 744 int stdout_fd = STDOUT_FILENO;
737 int lfd = fileno(stdout); 745 unsigned char netinbuf[BUFSIZE];
738 int plen; 746 size_t netinbufpos = 0;
739 747 unsigned char stdinbuf[BUFSIZE];
740 plen = sizeof(buf); 748 size_t stdinbufpos = 0;
741 749 int n, num_fds, flags;
742 /* Setup Network FD */ 750 ssize_t ret;
743 pfd[0].fd = nfd; 751
744 pfd[0].events = POLLIN; 752 /* don't read from stdin if requested */
745 753 if (dflag)
746 /* Set up STDIN FD. */ 754 stdin_fd = -1;
747 pfd[1].fd = wfd; 755
748 pfd[1].events = POLLIN; 756 /* stdin */
757 pfd[POLL_STDIN].fd = stdin_fd;
758 pfd[POLL_STDIN].events = POLLIN;
759
760 /* network out */
761 pfd[POLL_NETOUT].fd = net_fd;
762 pfd[POLL_NETOUT].events = 0;
763
764 /* network in */
765 pfd[POLL_NETIN].fd = net_fd;
766 pfd[POLL_NETIN].events = POLLIN;
767
768 /* stdout */
769 pfd[POLL_STDOUT].fd = stdout_fd;
770 pfd[POLL_STDOUT].events = 0;
771
772 while (1) {
773 /* both inputs are gone, buffers are empty, we are done */
774 if (pfd[POLL_STDIN].fd == -1 && pfd[POLL_NETIN].fd == -1
775 && stdinbufpos == 0 && netinbufpos == 0) {
776 close(net_fd);
777 return;
778 }
779 /* both outputs are gone, we can't continue */
780 if (pfd[POLL_NETOUT].fd == -1 && pfd[POLL_STDOUT].fd == -1) {
781 close(net_fd);
782 return;
783 }
784 /* listen and net in gone, queues empty, done */
785 if (lflag && pfd[POLL_NETIN].fd == -1
786 && stdinbufpos == 0 && netinbufpos == 0) {
787 close(net_fd);
788 return;
789 }
749 790
750 while (pfd[0].fd != -1) { 791 /* help says -i is for "wait between lines sent". We read and
792 * write arbitrary amounts of data, and we don't want to start
793 * scanning for newlines, so this is as good as it gets */
751 if (iflag) 794 if (iflag)
752 sleep(iflag); 795 sleep(iflag);
753 796
754 if ((n = poll(pfd, 2 - dflag, timeout)) < 0) { 797 /* poll */
755 int saved_errno = errno; 798 num_fds = poll(pfd, 4, timeout);
756 close(nfd); 799
757 errc(1, saved_errno, "Polling Error"); 800 /* treat poll errors */
801 if (num_fds == -1) {
802 close(net_fd);
803 err(1, "polling error");
758 } 804 }
759 805
760 if (n == 0) 806 /* timeout happened */
807 if (num_fds == 0)
761 return; 808 return;
762 809
763 if (pfd[0].revents & (POLLIN|POLLHUP)) { 810 /* treat socket error conditions */
764 if ((n = read(nfd, buf, plen)) < 0) 811 for (n = 0; n < 4; n++) {
765 return; 812 if (pfd[n].revents & (POLLERR|POLLNVAL)) {
766 else if (n == 0) { 813 pfd[n].fd = -1;
767 shutdown(nfd, SHUT_RD);
768 pfd[0].fd = -1;
769 pfd[0].events = 0;
770 } else {
771 if (tflag)
772 atelnet(nfd, buf, n);
773 if (atomicio(vwrite, lfd, buf, n) != n)
774 return;
775 } 814 }
776 } 815 }
816 /* reading is possible after HUP */
817 if (pfd[POLL_STDIN].events & POLLIN &&
818 pfd[POLL_STDIN].revents & POLLHUP &&
819 ! (pfd[POLL_STDIN].revents & POLLIN))
820 pfd[POLL_STDIN].fd = -1;
821
822 if (pfd[POLL_NETIN].events & POLLIN &&
823 pfd[POLL_NETIN].revents & POLLHUP &&
824 ! (pfd[POLL_NETIN].revents & POLLIN))
825 pfd[POLL_NETIN].fd = -1;
826
827 if (pfd[POLL_NETOUT].revents & POLLHUP) {
828 if (Nflag)
829 shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
830 pfd[POLL_NETOUT].fd = -1;
831 }
832 /* if HUP, stop watching stdout */
833 if (pfd[POLL_STDOUT].revents & POLLHUP)
834 pfd[POLL_STDOUT].fd = -1;
835 /* if no net out, stop watching stdin */
836 if (pfd[POLL_NETOUT].fd == -1)
837 pfd[POLL_STDIN].fd = -1;
838 /* if no stdout, stop watching net in */
839 if (pfd[POLL_STDOUT].fd == -1) {
840 if (pfd[POLL_NETIN].fd != -1)
841 shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
842 pfd[POLL_NETIN].fd = -1;
843 }
777 844
778 if (!dflag && pfd[1].revents & (POLLIN|POLLHUP)) { 845 /* try to read from stdin */
779 if ((n = read(wfd, buf, plen)) < 0) 846 if (pfd[POLL_STDIN].revents & POLLIN && stdinbufpos < BUFSIZE) {
780 return; 847 ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf,
781 else if (n == 0) { 848 &stdinbufpos);
782 if (Nflag) 849 /* error or eof on stdin - remove from pfd */
783 shutdown(nfd, SHUT_WR); 850 if (ret == 0 || ret == -1)
784 pfd[1].fd = -1; 851 pfd[POLL_STDIN].fd = -1;
785 pfd[1].events = 0; 852 /* read something - poll net out */
786 } else { 853 if (stdinbufpos > 0)
787 if (atomicio(vwrite, nfd, buf, n) != n) 854 pfd[POLL_NETOUT].events = POLLOUT;
788 return; 855 /* filled buffer - remove self from polling */
856 if (stdinbufpos == BUFSIZE)
857 pfd[POLL_STDIN].events = 0;
858 }
859 /* try to write to network */
860 if (pfd[POLL_NETOUT].revents & POLLOUT && stdinbufpos > 0) {
861 ret = drainbuf(pfd[POLL_NETOUT].fd, stdinbuf,
862 &stdinbufpos);
863 if (ret == -1)
864 pfd[POLL_NETOUT].fd = -1;
865 /* buffer empty - remove self from polling */
866 if (stdinbufpos == 0)
867 pfd[POLL_NETOUT].events = 0;
868 /* buffer no longer full - poll stdin again */
869 if (stdinbufpos < BUFSIZE)
870 pfd[POLL_STDIN].events = POLLIN;
871 }
872 /* try to read from network */
873 if (pfd[POLL_NETIN].revents & POLLIN && netinbufpos < BUFSIZE) {
874 ret = fillbuf(pfd[POLL_NETIN].fd, netinbuf,
875 &netinbufpos);
876 if (ret == -1)
877 pfd[POLL_NETIN].fd = -1;
878 /* eof on net in - remove from pfd */
879 if (ret == 0) {
880 shutdown(pfd[POLL_NETIN].fd, SHUT_RD);
881 pfd[POLL_NETIN].fd = -1;
789 } 882 }
883 /* read something - poll stdout */
884 if (netinbufpos > 0)
885 pfd[POLL_STDOUT].events = POLLOUT;
886 /* filled buffer - remove self from polling */
887 if (netinbufpos == BUFSIZE)
888 pfd[POLL_NETIN].events = 0;
889 /* handle telnet */
890 if (tflag)
891 atelnet(pfd[POLL_NETIN].fd, netinbuf,
892 netinbufpos);
893 }
894 /* try to write to stdout */
895 if (pfd[POLL_STDOUT].revents & POLLOUT && netinbufpos > 0) {
896 ret = drainbuf(pfd[POLL_STDOUT].fd, netinbuf,
897 &netinbufpos);
898 if (ret == -1)
899 pfd[POLL_STDOUT].fd = -1;
900 /* buffer empty - remove self from polling */
901 if (netinbufpos == 0)
902 pfd[POLL_STDOUT].events = 0;
903 /* buffer no longer full - poll net in again */
904 if (netinbufpos < BUFSIZE)
905 pfd[POLL_NETIN].events = POLLIN;
906 }
907
908 /* stdin gone and queue empty? */
909 if (pfd[POLL_STDIN].fd == -1 && stdinbufpos == 0) {
910 if (pfd[POLL_NETOUT].fd != -1 && Nflag)
911 shutdown(pfd[POLL_NETOUT].fd, SHUT_WR);
912 pfd[POLL_NETOUT].fd = -1;
913 }
914 /* net in gone and queue empty? */
915 if (pfd[POLL_NETIN].fd == -1 && netinbufpos == 0) {
916 pfd[POLL_STDOUT].fd = -1;
790 } 917 }
791 } 918 }
792} 919}
793 920
921ssize_t
922drainbuf(int fd, unsigned char *buf, size_t *bufpos)
923{
924 ssize_t n;
925 ssize_t adjust;
926
927 n = write(fd, buf, *bufpos);
928 /* don't treat EAGAIN, EINTR as error */
929 if (n == -1 && (errno == EAGAIN || errno == EINTR))
930 n = -2;
931 if (n <= 0)
932 return n;
933 /* adjust buffer */
934 adjust = *bufpos - n;
935 if (adjust > 0)
936 memmove(buf, buf + n, adjust);
937 *bufpos -= n;
938 return n;
939}
940
941
942ssize_t
943fillbuf(int fd, unsigned char *buf, size_t *bufpos)
944{
945 size_t num = BUFSIZE - *bufpos;
946 ssize_t n;
947
948 n = read(fd, buf + *bufpos, num);
949 /* don't treat EAGAIN, EINTR as error */
950 if (n == -1 && (errno == EAGAIN || errno == EINTR))
951 n = -2;
952 if (n <= 0)
953 return n;
954 *bufpos += n;
955 return n;
956}
957
794/* 958/*
795 * fdpass() 959 * fdpass()
796 * Pass the connected file descriptor to stdout and exit. 960 * Pass the connected file descriptor to stdout and exit.