File: connect-timeout.patch

package info (click to toggle)
netcat-openbsd 1.229-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,560 kB
  • sloc: ansic: 31,646; sh: 678; makefile: 68
file content (136 lines) | stat: -rw-r--r-- 3,915 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
From: Aron Xu <aron@debian.org>
Date: Mon, 13 Feb 2012 14:43:56 +0800
Subject: Fix connect() timeout

---
 netcat.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 77 insertions(+), 5 deletions(-)

diff --git a/netcat.c b/netcat.c
index bec6e46..461e2bd 100644
--- a/netcat.c
+++ b/netcat.c
@@ -90,6 +90,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
 #include <netdb.h>
 #include <poll.h>
@@ -124,6 +125,10 @@
 # define TLS_MUSTSTAPLE	(1 << 4)
 #endif
 
+#define CONNECTION_SUCCESS 0
+#define CONNECTION_FAILED 1
+#define CONNECTION_TIMEOUT 2
+
 /* Command Line Options */
 int	dflag;					/* detached, no stdin */
 int	Fflag;					/* fdpass sock to stdout */
@@ -215,6 +220,8 @@ ssize_t drainbuf(int, unsigned char *, size_t *);
 ssize_t fillbuf(int, unsigned char *, size_t *);
 #endif
 
+static int connect_with_timeout(int, const struct sockaddr *, socklen_t, int);
+
 int
 main(int argc, char *argv[])
 {
@@ -1088,18 +1095,21 @@ remote_connect(const char *host, const char *port, struct addrinfo hints,
 			}
 		}
 
-		if (timeout_connect(s, res->ai_addr, res->ai_addrlen) == 0)
+		if ((error = connect_with_timeout(s, res->ai_addr, res->ai_addrlen,
+		    timeout)) == CONNECTION_SUCCESS)
 			break;
 
 		if (vflag) {
 			/* only print IP if there is something to report */
 			if (nflag || ipaddr == NULL ||
 			    (strncmp(host, ipaddr, NI_MAXHOST) == 0))
-				warn("connect to %s port %s (%s) failed", host,
-				    port, uflag ? "udp" : "tcp");
+				warn("connect to %s port %s (%s) %s", host,
+				    port, uflag ? "udp" : "tcp",
+				    error == CONNECTION_TIMEOUT ? "timed out" : "failed");
 			else
-				warn("connect to %s (%s) port %s (%s) failed",
-				    host, ipaddr, port, uflag ? "udp" : "tcp");
+				warn("connect to %s (%s) port %s (%s) %s",
+				    host, ipaddr, port, uflag ? "udp" : "tcp",
+				    error == CONNECTION_TIMEOUT ? "timed out" : "failed");
 		}
 
 		save_errno = errno;
@@ -1141,6 +1151,68 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen)
 	return ret;
 }
 
+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 available");
+		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