git: 9ce201f2ee3c - main - ping: fix parsing of options including '4' and '6'

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Thu, 21 Oct 2021 00:05:56 UTC
The branch main has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=9ce201f2ee3ca340032d9cc71d91a36b3b45a4c3

commit 9ce201f2ee3ca340032d9cc71d91a36b3b45a4c3
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2021-10-06 22:54:59 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2021-10-21 00:05:43 +0000

    ping: fix parsing of options including '4' and '6'
    
    ping uses a two-pass option parser.  The first pass determines whether
    ipv4 or ipv6 is desired, and the second parses the rest of the options.
    But the first pass wrongly detects a '4' or '6' in an option's value as
    a request to use ipv6 or ipv6 respectively, for example in an invocation
    like "ping -c6 1.2.3.4".
    
    Fix this confusion by including all options in the first round of
    parsing, but ignoring those unrelated to ipv4/ipv6 selection.
    
    PR:             258048
    Reported by:    ghuckriede@blackberry.com
    Submitted by:   ghuckriede@blackberry.com
    MFC after:      2 weeks
    Reviewed by:    emaste
    Differential Revision: https://reviews.freebsd.org/D32344
---
 sbin/ping/main.c             | 14 +++++------
 sbin/ping/main.h             | 20 +++++++++++++++
 sbin/ping/ping.c             | 10 +-------
 sbin/ping/ping6.c            | 20 ++++++---------
 sbin/ping/tests/ping_test.sh | 58 +++++++++++++++++++++++++++++++++++++-------
 5 files changed, 84 insertions(+), 38 deletions(-)

diff --git a/sbin/ping/main.c b/sbin/ping/main.c
index 01442679efff..c021e2c26b14 100644
--- a/sbin/ping/main.c
+++ b/sbin/ping/main.c
@@ -52,13 +52,11 @@ __FBSDID("$FreeBSD$");
 #endif
 
 #if defined(INET) && defined(INET6)
-#define	OPTSTR ":46"
+#define	OPTSTR PING6OPTS PING4OPTS
 #elif defined(INET)
-#define OPTSTR ":4"
+#define	OPTSTR PING4OPTS
 #elif defined(INET6)
-#define	OPTSTR ":6"
-#else
-#define OPTSTR ""
+#define	OPTSTR PING6OPTS
 #endif
 
 int
@@ -82,7 +80,7 @@ main(int argc, char *argv[])
 		ipv6 = true;
 #endif
 
-	while ((ch = getopt(argc, argv, OPTSTR)) != -1) {
+	while ((ch = getopt(argc, argv, ":" OPTSTR)) != -1) {
 		switch(ch) {
 #ifdef INET
 		case '4':
@@ -170,7 +168,7 @@ usage(void)
 	    "[-G sweepmaxsize]\n"
 	    "	    [-g sweepminsize] [-h sweepincrsize] [-i wait] "
 	    "[-l preload]\n"
-	    "	    [-M mask | time] [-m ttl]" 
+	    "	    [-M mask | time] [-m ttl] "
 #ifdef IPSEC
 	    "[-P policy] "
 #endif
@@ -188,7 +186,7 @@ usage(void)
 	    "            [-z tos] IPv4-mcast-group\n"
 #endif /* INET */
 #ifdef INET6
-            "\tping [-6aADd"
+            "\tping [-6AaDd"
 #if defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
             "E"
 #endif
diff --git a/sbin/ping/main.h b/sbin/ping/main.h
index f9707ccfb5ff..0f987e9a20ae 100644
--- a/sbin/ping/main.h
+++ b/sbin/ping/main.h
@@ -31,6 +31,26 @@
 #ifndef MAIN_H
 #define MAIN_H 1
 
+#ifdef IPSEC
+#include <netipsec/ipsec.h>
+#endif /*IPSEC*/
+
+#if defined(INET) && defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+ #define PING4ADDOPTS "P:"
+#else
+ #define PING4ADDOPTS
+#endif
+#define PING4OPTS "4AaC:c:DdfG:g:Hh:I:i:Ll:M:m:nop:QqRrS:s:T:t:vW:z:" PING4ADDOPTS
+
+#if defined(INET6) && defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
+ #define PING6ADDOPTS "P:"
+#elif defined(INET6) && defined(IPSEC) && !defined(IPSEC_POLICY_IPSEC)
+ #define PING6ADDOPTS "ZE"
+#else
+ #define PING6ADDOPTS
+#endif
+#define PING6OPTS "6Aab:C:c:Dde:fHI:i:k:l:m:nNoOp:qS:s:t:uvyYW:z:" PING6ADDOPTS
+
 void usage(void) __dead2;
 
 #endif
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
index fe197928085e..be535f72146a 100644
--- a/sbin/ping/ping.c
+++ b/sbin/ping/ping.c
@@ -301,15 +301,7 @@ ping(int argc, char *const *argv)
 	alarmtimeout = df = preload = tos = pcp = 0;
 
 	outpack = outpackhdr + sizeof(struct ip);
-	while ((ch = getopt(argc, argv,
-		"4AaC:c:DdfG:g:Hh:I:i:Ll:M:m:nop:QqRrS:s:T:t:vW:z:"
-#ifdef IPSEC
-#ifdef IPSEC_POLICY_IPSEC
-		"P:"
-#endif /*IPSEC_POLICY_IPSEC*/
-#endif /*IPSEC*/
-		)) != -1)
-	{
+	while ((ch = getopt(argc, argv, PING4OPTS)) != -1) {
 		switch(ch) {
 		case '4':
 			/* This option is processed in main(). */
diff --git a/sbin/ping/ping6.c b/sbin/ping/ping6.c
index 4cbeae770372..76a96f0631ff 100644
--- a/sbin/ping/ping6.c
+++ b/sbin/ping/ping6.c
@@ -293,7 +293,11 @@ static void	 pr_rthdr(void *, size_t);
 static int	 pr_bitrange(u_int32_t, int, int);
 static void	 pr_retip(struct ip6_hdr *, u_char *);
 static void	 summary(void);
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
 static int	 setpolicy(int, char *);
+#endif
+#endif
 static char	*nigroup(char *, int);
 
 int
@@ -345,18 +349,8 @@ ping6(int argc, char *argv[])
 	alarmtimeout = preload = 0;
 	datap = &outpack[ICMP6ECHOLEN + ICMP6ECHOTMLEN];
 	capdns = capdns_setup();
-#ifndef IPSEC
-#define ADDOPTS
-#else
-#ifdef IPSEC_POLICY_IPSEC
-#define ADDOPTS	"P:"
-#else
-#define ADDOPTS	"ZE"
-#endif /*IPSEC_POLICY_IPSEC*/
-#endif
-	while ((ch = getopt(argc, argv,
-	    "6k:b:C:c:DdfHe:m:I:i:l:unNop:qaAS:s:OvyYW:t:z:" ADDOPTS)) != -1) {
-#undef ADDOPTS
+
+	while ((ch = getopt(argc, argv, PING6OPTS)) != -1) {
 		switch (ch) {
 		case '6':
 			/* This option is processed in main(). */
@@ -2667,7 +2661,9 @@ pr_retip(struct ip6_hdr *ip6, u_char *end)
 	nh = ip6->ip6_nxt;
 	cp += hlen;
 	while (end - cp >= 8) {
+#ifdef IPSEC
 		struct ah ah;
+#endif
 
 		switch (nh) {
 		case IPPROTO_HOPOPTS:
diff --git a/sbin/ping/tests/ping_test.sh b/sbin/ping/tests/ping_test.sh
index ed95594abbd2..54af89f4a22b 100644
--- a/sbin/ping/tests/ping_test.sh
+++ b/sbin/ping/tests/ping_test.sh
@@ -27,14 +27,23 @@
 #
 # $FreeBSD$
 
+require_ipv4() {
+    if ! getaddrinfo -f inet localhost 1>/dev/null 2>&1; then
+	atf_skip "IPv4 is not configured"
+    fi
+}
+require_ipv6() {
+    if ! getaddrinfo -f inet6 localhost 1>/dev/null 2>&1; then
+	atf_skip "IPv6 is not configured"
+    fi
+}
+
 atf_test_case ping_c1_s56_t1
 ping_c1_s56_t1_head() {
     atf_set "descr" "Stop after receiving 1 ECHO_RESPONSE packet"
 }
 ping_c1_s56_t1_body() {
-    if ! getaddrinfo -f inet localhost 1>/dev/null 2>&1; then
-	atf_skip "IPv4 is not configured"
-    fi
+    require_ipv4
     atf_check -s exit:0 -o save:std.out -e empty \
 	      ping -4 -c 1 -s 56 -t 1 localhost
     check_ping_statistics std.out $(atf_get_srcdir)/ping_c1_s56_t1.out
@@ -45,9 +54,7 @@ ping_6_c1_s8_t1_head() {
     atf_set "descr" "Stop after receiving 1 ECHO_RESPONSE packet"
 }
 ping_6_c1_s8_t1_body() {
-    if ! getaddrinfo -f inet6 localhost 1>/dev/null 2>&1; then
-	atf_skip "IPv6 is not configured"
-    fi
+    require_ipv6
     atf_check -s exit:0 -o save:std.out -e empty \
 	      ping -6 -c 1 -s 8 -t 1 localhost
     check_ping_statistics std.out $(atf_get_srcdir)/ping_6_c1_s8_t1.out
@@ -58,18 +65,51 @@ ping6_c1_s8_t1_head() {
     atf_set "descr" "Use IPv6 when invoked as ping6"
 }
 ping6_c1_s8_t1_body() {
-    if ! getaddrinfo -f inet6 localhost 1>/dev/null 2>&1; then
-	atf_skip "IPv6 is not configured"
-    fi
+    require_ipv6
     atf_check -s exit:0 -o save:std.out -e empty \
 	      ping6 -c 1 -s 8 -t 1 localhost
     check_ping_statistics std.out $(atf_get_srcdir)/ping_6_c1_s8_t1.out
 }
 
+ping_c1t6_head() {
+    atf_set "descr" "-t6 is not interpreted as -t -6 by ping"
+}
+ping_c1t6_body() {
+    require_ipv4
+    atf_check -s exit:0 -o ignore -e empty ping -c1 -t6 127.0.0.1
+}
+
+ping6_c1t4_head() {
+    atf_set "descr" "-t4 is not interpreted as -t -4 by ping6"
+}
+ping6_c1t4_body() {
+    require_ipv6
+    atf_check -s exit:0 -o ignore -e empty ping6 -c1 -t4 ::1
+}
+
+ping_46_head() {
+    atf_set "descr" "-4 and -6 may not be used together"
+}
+ping_46_body() {
+    atf_check -s exit:1 -e ignore ping -4 -6
+}
+
+ping6_46_head() {
+    atf_set "descr" "-4 and -6 may not be used together"
+}
+ping6_46_body() {
+    atf_check -s exit:1 -e ignore ping6 -4 -6
+}
+
+
 atf_init_test_cases() {
     atf_add_test_case ping_c1_s56_t1
     atf_add_test_case ping_6_c1_s8_t1
     atf_add_test_case ping6_c1_s8_t1
+    atf_add_test_case ping_c1t6
+    atf_add_test_case ping6_c1t4
+    atf_add_test_case ping_46
+    atf_add_test_case ping6_46
 }
 
 check_ping_statistics() {