about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2023-08-09 17:21:20 +0200
committerLeah Neukirchen <leah@vuxu.org>2023-08-09 17:22:01 +0200
commitdf9bc5bec20ac4d63f64cc5bee2d1b67716fa9ed (patch)
treed5124373489ff812808d403556b66413e44b5e32
parent454f553834e39d35d311714681d3cebe6922a5e3 (diff)
downloadlistening-master.tar.gz
listening-master.tar.xz
listening-master.zip
use DNS to lookup addresses, add -4/-6 HEAD master
Put host and port into two arguments, as separation by : complicates
IPv6 usage.
-rw-r--r--README8
-rw-r--r--listening.111
-rw-r--r--listening.c57
3 files changed, 45 insertions, 31 deletions
diff --git a/README b/README
index c13b2a9..b24f3c8 100644
--- a/README
+++ b/README
@@ -4,7 +4,7 @@ NAME
      listening – check if a TCP server is listening
 
 SYNOPSIS
-     listening [-t connect-timeout] [-w wait-timeout] [host:]port
+     listening [-46] [-t connect-timeout] [-w wait-timeout] [host] port
 
 DESCRIPTION
      The listening utility performs a TCP scan against the given host
@@ -15,6 +15,10 @@ DESCRIPTION
 
      The options are as follows:
 
+     -4      Force use of IPv4.
+
+     -6      Force use of IPv6.
+
      -t connect-timeout
              Wait at most connect-timeout seconds per connection attempt
              (default: 0.2s, decimal fractions are allowed).
@@ -22,7 +26,7 @@ DESCRIPTION
      -w wait-timeout
              Wait at most wait-timeout seconds total (decimal fractions are
              allowed), and keep trying to connecting when connection has been
-             refused.
+             refused (default: only try once).
 
 DETAILS
      listening implements a TCP SYN scan (half-open scan), which has several
diff --git a/listening.1 b/listening.1
index 54c9f5f..7989a6d 100644
--- a/listening.1
+++ b/listening.1
@@ -6,9 +6,11 @@
 .Nd check if a TCP server is listening
 .Sh SYNOPSIS
 .Nm
+.Op Fl 46
 .Oo Fl t Ar connect-timeout Oc
 .Oo Fl w Ar wait-timeout Oc
-.Oo Ar host Ns \&: Oc Ns Ar port
+.Op Ar host
+.Ar port
 .Sh DESCRIPTION
 The
 .Nm
@@ -23,6 +25,10 @@ accept connections.
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
+.It Fl 4
+Force use of IPv4.
+.It Fl 6
+Force use of IPv6.
 .It Fl t Ar connect-timeout
 Wait at most
 .Ar connect-timeout
@@ -33,7 +39,8 @@ Wait at most
 .Ar wait-timeout
 seconds total
 .Po decimal fractions are allowed Pc ,
-and keep trying to connecting when connection has been refused.
+and keep trying to connecting when connection has been refused
+.Po default: only try once Pc .
 .El
 .Sh DETAILS
 .Nm
diff --git a/listening.c b/listening.c
index bb4d241..ceaa1b1 100644
--- a/listening.c
+++ b/listening.c
@@ -6,10 +6,12 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+#include <sys/types.h>
 #include <sys/socket.h>
 
 #include <arpa/inet.h>
 #include <errno.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <poll.h>
@@ -20,6 +22,8 @@
 #include <time.h>
 #include <unistd.h>
 
+int protocol;
+
 uint64_t wait_timeout = 0;       // nanoseconds
 uint64_t connect_timeout = 200;  // milliseconds
 
@@ -74,25 +78,18 @@ scanfix(char *s, uint64_t *result, int scale)
 }
 
 int
-syn_scan(const char *host, int port)
+syn_scan(struct addrinfo *addr)
 {
-	printf("test %s:%d\n", host, port);
-
-	struct sockaddr_in addr;
-	memset(&addr, 0, sizeof (struct sockaddr_in));
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(port);
-	// XXX use GAI
-	inet_aton(host, &addr.sin_addr);
-
-	int sock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+	int sock = socket(addr->ai_family,
+	    addr->ai_socktype | SOCK_NONBLOCK,
+	    addr->ai_protocol);
 	struct linger l = {1, 0};
 	setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof (struct linger));
 	int zero = 0;
 	setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, (void *)&zero, sizeof zero);
 	int r;
 	errno = 0;
-	r = connect(sock, (struct sockaddr *)&addr, sizeof (struct sockaddr_in));
+	r = connect(sock, addr->ai_addr, addr->ai_addrlen);
 	if (r > 0 || errno != EINPROGRESS) {
 		fprintf(stderr, "connect failed: %m\n");
 		close(sock);
@@ -132,8 +129,10 @@ main(int argc, char *argv[])
 {
 	int c, err;
 
-	while ((c = getopt(argc, argv, "+t:w:")) != -1) {
+	while ((c = getopt(argc, argv, "+46t:w:")) != -1) {
 		switch (c) {
+		case '4': protocol = 4; break;
+		case '6': protocol = 6; break;
                 case 't':
 			if ((err = scanfix(optarg, &connect_timeout, 3)) < 0) {
 				fprintf(stderr, "failed to parse number '%s': %s\n",
@@ -151,28 +150,32 @@ main(int argc, char *argv[])
                 default:
 		usage:
                         fprintf(stderr,
-                            "Usage: %s [-w WAIT_TIMEOUT] [-t CONNECT_TIMEOUT] [HOST:]PORT\n",
+                            "Usage: %s [-w WAIT_TIMEOUT] [-t CONNECT_TIMEOUT] [HOST] PORT\n",
                             argv[0]);
-                        exit(99);
+                        return 99;
                 }
 	}
 
-	if (optind != argc - 1)
+	struct addrinfo hints = {
+		.ai_socktype = SOCK_STREAM,
+	}, *res;
+	hints.ai_family = (protocol == 4) ? AF_INET : AF_INET6;
+	hints.ai_flags = (protocol == 0) ? (AI_PASSIVE | AI_V4MAPPED) : AI_PASSIVE;
+
+	if (optind == argc - 1)
+		err = getaddrinfo("localhost", argv[argc-1], &hints, &res);
+	else if (optind == argc - 2)
+		err = getaddrinfo(argv[argc-2], argv[argc-1], &hints, &res);
+	else
 		goto usage;
 
-	int port;
-	const char *host = argv[argc-1];
-	char *colon = strchr(host, ':');
-	if (colon) {
-		*colon = 0;
-		port = atoi(colon+1);
-	} else {
-		port = atoi(host);
-		host = "127.0.0.1";
+	if (err) {
+		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
+		return 99;
 	}
 
 	if (wait_timeout == 0)
-		return syn_scan(host, port);
+		return syn_scan(res);
 
 	/* else we are waiting for the port to come up: */
 
@@ -192,7 +195,7 @@ main(int argc, char *argv[])
 
 	while (now.tv_sec < deadline.tv_sec ||
 	    (now.tv_sec == deadline.tv_sec && now.tv_nsec <= deadline.tv_nsec)) {
-		switch (syn_scan(host, port)) {
+		switch (syn_scan(res)) {
 		case 0:
 			return 0;
 		case 99: