net: decode setsockopt() multicast arguments

* configure.ac (AC_CHECK_FUNCS): Add inet_pton.
* net.c (print_mreq, print_mreq6): New functions.
(print_setsockopt): Use them to decode IP_ADD_MEMBERSHIP,
IP_DROP_MEMBERSHIP, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP,
IPV6_JOIN_ANYCAST, and IPV6_LEAVE_ANYCAST.
* tests/ip_mreq.c: New file.
* tests/ip_mreq.expected: Likewise.
* tests/ip_mreq.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add ip_mreq.
(TESTS): Add ip_mreq.test.
(EXTRA_DIST): ip_mreq.expected.
* tests/.gitignore: Add ip_mreq.

Based on patch by Ben Noordhuis <info@bnoordhuis.nl>.
diff --git a/tests/.gitignore b/tests/.gitignore
index 6903a5c..c3eada9 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -3,6 +3,7 @@
 getrandom
 inet-accept-connect-send-recv
 ioctl
+ip_mreq
 ipc_msg
 ipc_sem
 ipc_shm
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7ace705..f1e8820 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -14,6 +14,7 @@
 	getrandom \
 	inet-accept-connect-send-recv \
 	ioctl \
+	ip_mreq \
 	ipc_msg \
 	ipc_sem \
 	ipc_shm \
@@ -61,6 +62,7 @@
 	getdents.test \
 	getrandom.test \
 	ioctl.test \
+	ip_mreq.test \
 	ipc_msg.test \
 	ipc_shm.test \
 	ipc_sem.test \
@@ -109,6 +111,7 @@
 	     getdents.out \
 	     getrandom.awk \
 	     ioctl.expected \
+	     ip_mreq.expected \
 	     ipc.sh \
 	     mmsg.expected \
 	     net.expected \
diff --git a/tests/ip_mreq.c b/tests/ip_mreq.c
new file mode 100644
index 0000000..db208b8
--- /dev/null
+++ b/tests/ip_mreq.c
@@ -0,0 +1,45 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <assert.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+int
+main(void)
+{
+#if defined IP_ADD_MEMBERSHIP && defined IPV6_ADD_MEMBERSHIP \
+ && defined IPV6_JOIN_ANYCAST && defined HAVE_INET_PTON
+	struct ip_mreq m4;
+	struct ipv6_mreq m6;
+
+	inet_pton(AF_INET, "224.0.0.3", &m4.imr_multiaddr);
+	inet_pton(AF_INET, "127.0.0.1", &m4.imr_interface);
+	inet_pton(AF_INET6, "ff01::c", &m6.ipv6mr_multiaddr);
+	m6.ipv6mr_interface = 1;
+
+	(void) close(0);
+	assert(socket(AF_INET, SOCK_DGRAM, 0) == 0);
+
+	assert(setsockopt(0, SOL_IP, IP_ADD_MEMBERSHIP, &m4, 1) == -1);
+	assert(setsockopt(0, SOL_IP, IP_DROP_MEMBERSHIP, &m4, 1) == -1);
+	assert(setsockopt(0, SOL_IP, IP_ADD_MEMBERSHIP, &m4, sizeof(m4)) == 0);
+	assert(setsockopt(0, SOL_IP, IP_DROP_MEMBERSHIP, &m4, sizeof(m4)) == 0);
+
+	assert(setsockopt(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, 1) == -1);
+	assert(setsockopt(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, 1) == -1);
+	assert(setsockopt(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, sizeof(m6)) == -1);
+	assert(setsockopt(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, sizeof(m6)) == -1);
+
+	assert(setsockopt(0, SOL_IPV6, IPV6_JOIN_ANYCAST, &m6, 1) == -1);
+	assert(setsockopt(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, &m6, 1) == -1);
+	assert(setsockopt(0, SOL_IPV6, IPV6_JOIN_ANYCAST, &m6, sizeof(m6)) == -1);
+	assert(setsockopt(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, &m6, sizeof(m6)) == -1);
+
+	return 0;
+#else
+	return 77;
+#endif
+}
diff --git a/tests/ip_mreq.expected b/tests/ip_mreq.expected
new file mode 100644
index 0000000..e694c73
--- /dev/null
+++ b/tests/ip_mreq.expected
@@ -0,0 +1,12 @@
+setsockopt\(0, SOL_IP, IP_ADD_MEMBERSHIP, "\\340", 1\) = -1 EINVAL .*
+setsockopt\(0, SOL_IP, IP_DROP_MEMBERSHIP, "\\340", 1\) = -1 EINVAL .*
+setsockopt\(0, SOL_IP, IP_ADD_MEMBERSHIP, \{imr_multiaddr=inet_addr\("224\.0\.0\.3"\), imr_interface=inet_addr\("127\.0\.0\.1"\)\}, 8\) = 0
+setsockopt\(0, SOL_IP, IP_DROP_MEMBERSHIP, \{imr_multiaddr=inet_addr\("224\.0\.0\.3"\), imr_interface=inet_addr\("127\.0\.0\.1"\)\}, 8\) = 0
+setsockopt\(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_ADD_MEMBERSHIP, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_DROP_MEMBERSHIP, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_JOIN_ANYCAST, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, "\\377", 1\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_JOIN_ANYCAST, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
+setsockopt\(0, SOL_IPV6, IPV6_LEAVE_ANYCAST, \{ipv6mr_multiaddr=inet_pton\("ff01::c"\), ipv6mr_interface=if_nametoindex\("lo"\)\}, 20\) = -1 ENOPROTOOPT .*
diff --git a/tests/ip_mreq.test b/tests/ip_mreq.test
new file mode 100755
index 0000000..d423b1b
--- /dev/null
+++ b/tests/ip_mreq.test
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Check {IP,IPV6}_{ADD,DROP}_MEMBERSHIP setsockopt decoding.
+
+. "${srcdir=.}/init.sh"
+
+run_prog
+run_strace -e setsockopt $args
+match_grep
+
+exit 0