ping: Simplify protocol selection.

* Interrupt the option loop as soon as we have an indication of which
  protocol is intended.
* If we end up having to perform a DNS lookup, loop over the entire
  result looking for either IPv4 or IPv6 addresses.

Sponsored by:	NetApp, Inc.
Sponsored by:	Klara, Inc.
Reviewed by:	rscheff, kevans, allanjude
Differential Revision:	https://reviews.freebsd.org/D42137
This commit is contained in:
Dag-Erling Smørgrav 2023-10-11 00:47:46 +02:00
parent 26103ccba8
commit 7fd2c91a29
2 changed files with 78 additions and 77 deletions

View file

@ -62,112 +62,99 @@
int
main(int argc, char *argv[])
{
#if defined(INET) && defined(INET6)
#if defined(INET)
struct in_addr a;
#endif
#if defined(INET6)
struct in6_addr a6;
#endif
#if defined(INET) || defined(INET6)
struct addrinfo hints;
#endif
int ch;
#ifdef INET
bool ipv4 = false;
#if defined(INET) && defined(INET6)
struct addrinfo hints, *res, *ai;
int error;
#endif
int opt;
#ifdef INET6
bool ipv6 = false;
if (strcmp(getprogname(), "ping6") == 0)
ipv6 = true;
return ping6(argc, argv);
#endif
while ((ch = getopt(argc, argv, ":" OPTSTR)) != -1) {
switch(ch) {
while ((opt = getopt(argc, argv, ":" OPTSTR)) != -1) {
switch (opt) {
#ifdef INET
case '4':
ipv4 = true;
break;
goto ping4;
#endif
#ifdef INET6
case '6':
ipv6 = true;
break;
goto ping6;
#endif
#if defined(INET) && defined(INET6)
case 'S':
/*
* If -S is given with a numeric parameter,
* force use of the corresponding version.
*/
#ifdef INET
if (inet_pton(AF_INET, optarg, &a) == 1)
ipv4 = true;
else if (inet_pton(AF_INET6, optarg, &a6) == 1)
ipv6 = true;
break;
goto ping4;
#endif
#ifdef INET6
if (inet_pton(AF_INET6, optarg, &a6) == 1)
goto ping6;
#endif
break;
default:
break;
}
}
/*
* For IPv4, only one positional argument, the target, is allowed.
* For IPv6, multiple positional argument are allowed; the last
* one is the target, and preceding ones are intermediate hops.
* This nuance is lost here, but the only case where it matters is
* an error.
*/
if (optind >= argc)
usage();
optreset = 1;
optind = 1;
#if defined(INET) && defined(INET6)
if (ipv4 && ipv6)
errx(1, "-4 and -6 cannot be used simultaneously");
#endif
#if defined(INET) && defined(INET6)
if (inet_pton(AF_INET, argv[argc - 1], &a) == 1) {
if (ipv6)
errx(1, "IPv6 requested but IPv4 target address "
"provided");
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_RAW;
if (feature_present("inet") && !feature_present("inet6"))
hints.ai_family = AF_INET;
}
else if (inet_pton(AF_INET6, argv[argc - 1], &a6) == 1) {
if (ipv4)
errx(1, "IPv4 requested but IPv6 target address "
"provided");
if (feature_present("inet6") && !feature_present("inet"))
hints.ai_family = AF_INET6;
} else if (ipv6)
hints.ai_family = AF_INET6;
else if (ipv4)
hints.ai_family = AF_INET;
else {
if (!feature_present("inet6"))
hints.ai_family = AF_INET;
else if (!feature_present("inet"))
hints.ai_family = AF_INET6;
else {
struct addrinfo *res;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_RAW;
hints.ai_family = AF_UNSPEC;
getaddrinfo(argv[argc - 1], NULL, &hints, &res);
if (res != NULL) {
hints.ai_family = res[0].ai_family;
freeaddrinfo(res);
}
else
hints.ai_family = AF_UNSPEC;
error = getaddrinfo(argv[argc - 1], NULL, &hints, &res);
if (res == NULL)
errx(1, "%s", gai_strerror(error));
for (ai = res; ai != NULL; ai = ai->ai_next) {
if (ai->ai_family == AF_INET) {
freeaddrinfo(res);
goto ping4;
}
if (ai->ai_family == AF_INET6) {
freeaddrinfo(res);
goto ping6;
}
}
#elif defined(INET)
hints.ai_family = AF_INET;
#elif defined(INET6)
hints.ai_family = AF_INET6;
#endif
#ifdef INET
if (hints.ai_family == AF_INET)
return ping(argc, argv);
#endif /* INET */
#ifdef INET6
if (hints.ai_family == AF_INET6)
return ping6(argc, argv);
#endif /* INET6 */
freeaddrinfo(res);
errx(1, "Unknown host");
#endif
#ifdef INET
ping4:
optreset = 1;
optind = 1;
return ping(argc, argv);
#endif
#ifdef INET6
ping6:
optreset = 1;
optind = 1;
return ping6(argc, argv);
#endif
}
void

View file

@ -135,21 +135,34 @@ ping_46_body()
require_ipv4
require_ipv6
atf_check -s exit:1 \
-e match:"-4 and -6 cannot be used simultaneously" \
-e match:"illegal option -- 6" \
ping -4 -6 localhost
}
ping6_46_head()
ping_64_head()
{
atf_set "descr" "-4 and -6 cannot be used simultaneously"
}
ping6_46_body()
ping_64_body()
{
require_ipv4
require_ipv6
atf_check -s exit:1 \
-e match:"-4 and -6 cannot be used simultaneously" \
ping6 -4 -6 localhost
-e match:"illegal option -- 4" \
ping -6 -4 localhost
}
ping6_4_head()
{
atf_set "descr" "ping6 does not accept -4"
}
ping6_4_body()
{
require_ipv4
require_ipv6
atf_check -s exit:1 \
-e match:"illegal option -- 4" \
ping6 -4 localhost
}
atf_test_case "inject_opts" "cleanup"
@ -212,7 +225,8 @@ atf_init_test_cases()
atf_add_test_case ping_c1t6
atf_add_test_case ping6_c1t4
atf_add_test_case ping_46
atf_add_test_case ping6_46
atf_add_test_case ping_64
atf_add_test_case ping6_4
atf_add_test_case inject_opts
atf_add_test_case inject_pip
atf_add_test_case inject_reply