From fdfa72276a16d0bd42b795aef3ac6c8969210469 Mon Sep 17 00:00:00 2001
From: "Dmitry V. Levin" <ldv@altlinux.org>
Date: Tue, 23 Sep 2014 00:14:04 +0000
Subject: [PATCH] tests: add a test for -yy option

* tests/net-yy.test: New test.
* tests/inet-accept-connect-send-recv.c: New file.
* tests/netlink_inet_diag.c: Likewise.
* tests/net-yy-accept.awk: Likewise.
* tests/net-yy-connect.awk: Likewise.
* tests/.gitignore: Add inet-accept-connect-send-recv,
netlink_inet_diag, *.tmp-*, and *.tmp.*.
* tests/Makefile.am (check_PROGRAMS): Add inet-accept-connect-send-recv
and netlink_inet_diag.
(TESTS): Add net-yy.test.
(EXTRA_DIST): Add net-yy-accept.awk and net-yy-connect.awk.
---
 tests/.gitignore                      |  6 +-
 tests/Makefile.am                     | 10 ++-
 tests/inet-accept-connect-send-recv.c | 53 +++++++++++++++
 tests/net-yy-accept.awk               | 76 +++++++++++++++++++++
 tests/net-yy-connect.awk              | 55 +++++++++++++++
 tests/net-yy.test                     | 59 ++++++++++++++++
 tests/netlink_inet_diag.c             | 96 +++++++++++++++++++++++++++
 7 files changed, 353 insertions(+), 2 deletions(-)
 create mode 100644 tests/inet-accept-connect-send-recv.c
 create mode 100644 tests/net-yy-accept.awk
 create mode 100644 tests/net-yy-connect.awk
 create mode 100755 tests/net-yy.test
 create mode 100644 tests/netlink_inet_diag.c

diff --git a/tests/.gitignore b/tests/.gitignore
index cf7aaf0d..dc408c9a 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,4 +1,6 @@
+inet-accept-connect-send-recv
 net-accept-connect
+netlink_inet_diag
 scm_rights
 set_ptracer_any
 sigaction
@@ -8,5 +10,7 @@ uio
 *.log
 *.log.*
 *.o
-*.trs
 *.tmp
+*.tmp-*
+*.tmp.*
+*.trs
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3b97b2cd..541a0c46 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -3,7 +3,9 @@
 AM_CFLAGS = $(WARN_CFLAGS)
 
 check_PROGRAMS = \
+	inet-accept-connect-send-recv \
 	net-accept-connect \
+	netlink_inet_diag \
 	scm_rights \
 	set_ptracer_any \
 	sigaction \
@@ -27,6 +29,7 @@ TESTS = \
 	statfs.test \
 	net.test \
 	net-fd.test \
+	net-yy.test \
 	uio.test \
 	count.test \
 	detach-sleeping.test \
@@ -38,6 +41,11 @@ net-fd.log: net.log
 
 TEST_LOG_COMPILER = $(srcdir)/run.sh
 
-EXTRA_DIST = init.sh run.sh getdents.awk sigaction.awk $(TESTS)
+EXTRA_DIST = init.sh run.sh \
+	     getdents.awk \
+	     net-yy-accept.awk \
+	     net-yy-connect.awk \
+	     sigaction.awk \
+	     $(TESTS)
 
 CLEANFILES = $(TESTS:=.tmp)
diff --git a/tests/inet-accept-connect-send-recv.c b/tests/inet-accept-connect-send-recv.c
new file mode 100644
index 00000000..38376aed
--- /dev/null
+++ b/tests/inet-accept-connect-send-recv.c
@@ -0,0 +1,53 @@
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+int main(void)
+{
+	static const char data[] = "data";
+	const size_t size = sizeof(data) - 1;
+	struct sockaddr_in addr;
+	socklen_t len = sizeof(addr);
+	pid_t pid;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+	close(0);
+	close(1);
+
+	assert(socket(PF_INET, SOCK_STREAM, 0) == 0);
+	assert(bind(0, (struct sockaddr *) &addr, len) == 0);
+	assert(listen(0, 5) == 0);
+
+	memset(&addr, 0, sizeof(addr));
+	assert(getsockname(0, (struct sockaddr *) &addr, &len) == 0);
+
+	assert((pid = fork()) >= 0);
+
+	if (pid) {
+		char buf[sizeof(data)];
+		int status;
+
+		assert(accept(0, (struct sockaddr *) &addr, &len) == 1);
+		assert(close(0) == 0);
+		assert(recv(1, buf, sizeof(buf), MSG_WAITALL) == (int) size);
+		assert(waitpid(pid, &status, 0) == pid);
+		assert(status == 0);
+		assert(close(1) == 0);
+	} else {
+		assert(close(0) == 0);
+		assert(socket(PF_INET, SOCK_STREAM, 0) == 0);
+		assert(connect(0, (struct sockaddr *) &addr, len) == 0);
+		assert(send(0, data, size, MSG_DONTROUTE) == (int) size);
+		assert(close(0) == 0);
+	}
+
+	return 0;
+}
diff --git a/tests/net-yy-accept.awk b/tests/net-yy-accept.awk
new file mode 100644
index 00000000..3ea4afe1
--- /dev/null
+++ b/tests/net-yy-accept.awk
@@ -0,0 +1,76 @@
+BEGIN {
+  lines = 9
+  fail = 0
+
+  inode = "?"
+  port_l = "?"
+  port_r = "?"
+
+  r_i = "[1-9][0-9]*"
+  r_port = "[1-9][0-9][0-9][0-9]+"
+  r_localhost = "127\\.0\\.0\\.1"
+  r_bind = "^bind\\(0<socket:\\[(" r_i ")\\]>, {sa_family=AF_INET, sin_port=htons\\(0\\), sin_addr=inet_addr\\(\"" r_localhost "\"\\)}, " r_i "\\) += 0$"
+  r_listen = "^/$"
+  r_getsockname = "^getsockname\\(0<" r_localhost ":(" r_port ")>, {sa_family=AF_INET, sin_port=htons\\((" r_port ")\\), sin_addr=inet_addr\\(\"" r_localhost "\"\\)}, \\[" r_i "\\]\\) += 0$"
+  r_accept = "^/$"
+  r_close0 = "^/$"
+  r_recv = "^/$"
+  r_recvfrom = "^/$"
+  r_close1 = "^/$"
+}
+
+NR == 1 && /^socket\(PF_INET, SOCK_STREAM, IPPROTO_IP\) += 0$/ {next}
+
+NR == 2 {
+  if (match($0, r_bind, a)) {
+    inode = a[1]
+    r_listen = "^listen\\(0<socket:\\[" inode "\\]>, 5\\) += 0$"
+    next
+  }
+}
+
+NR == 3 {if (match($0, r_listen)) next}
+
+NR == 4 {
+  if (match($0, r_getsockname, a) && a[1] == a[2]) {
+    port_l = a[1]
+    r_accept = "^accept\\(0<" r_localhost ":" port_l ">, {sa_family=AF_INET, sin_port=htons\\((" r_port ")\\), sin_addr=inet_addr\\(\"" r_localhost "\"\\)}, \\[" r_i "\\]\\) += 1<" r_localhost ":" port_l "->" r_localhost ":(" r_port ")>$"
+    r_close0 = "^close\\(0<" r_localhost ":" port_l ">) += 0$"
+    next
+  }
+}
+
+NR == 5 {
+  if (match($0, r_accept, a) && a[1] == a[2]) {
+    port_r = a[1]
+    r_recv = "^recv\\(1<" r_localhost ":" port_l "->" r_localhost ":" port_r ">, \"data\", 5, MSG_WAITALL\\) += 4$"
+    r_recvfrom = "^recvfrom\\(1<" r_localhost ":" port_l "->" r_localhost ":" port_r ">, \"data\", 5, MSG_WAITALL, NULL, NULL\\) += 4$"
+    r_close1 = "^close\\(1<" r_localhost ":" port_l "->" r_localhost ":" port_r ">) += 0$"
+    next
+  }
+}
+
+NR == 6 {if (match($0, r_close0)) next}
+
+NR == 7 {if (match($0, r_recv) || match($0, r_recvfrom)) next}
+
+NR == 8 {if (match($0, r_close1)) next}
+
+NR == lines && /^\+\+\+ exited with 0 \+\+\+$/ {next}
+
+{
+  print "Line " NR " does not match: " $0
+  fail=1
+}
+
+END {
+  if (NR != lines) {
+    print "Expected " lines " lines, found " NR " line(s)."
+    print ""
+    exit 1
+  }
+  if (fail) {
+    print ""
+    exit 1
+  }
+}
diff --git a/tests/net-yy-connect.awk b/tests/net-yy-connect.awk
new file mode 100644
index 00000000..18c1a28d
--- /dev/null
+++ b/tests/net-yy-connect.awk
@@ -0,0 +1,55 @@
+BEGIN {
+  lines = 5
+  fail = 0
+
+  port_l = "?"
+  port_r = "?"
+
+  r_i = "[1-9][0-9]*"
+  r_port = "[1-9][0-9][0-9][0-9]+"
+  r_localhost = "127\\.0\\.0\\.1"
+  r_connect = "^connect\\(0<socket:\\[" r_i "\\]>, {sa_family=AF_INET, sin_port=htons\\((" r_port ")\\), sin_addr=inet_addr\\(\"" r_localhost "\"\\)}, " r_i ") += 0$"
+  r_send = "^/$"
+  r_sendto = "^/$"
+  r_close = "^/$"
+}
+
+NR == 1 && /^socket\(PF_INET, SOCK_STREAM, IPPROTO_IP\) += 0$/ {next}
+
+NR == 2 {
+  if (match($0, r_connect, a)) {
+    port_r = a[1]
+    r_send = "^send\\(0<" r_localhost ":(" r_port ")->" r_localhost ":" port_r ">, \"data\", 4, MSG_DONTROUTE\\) += 4$"
+    r_sendto = "^sendto\\(0<" r_localhost ":(" r_port ")->" r_localhost ":" port_r ">, \"data\", 4, MSG_DONTROUTE, NULL, 0\\) += 4$"
+    next
+  }
+}
+
+NR == 3 {
+  if (match($0, r_send, a) || match($0, r_sendto, a)) {
+    port_l = a[1]
+    r_close = "^close\\(0<" r_localhost ":" port_l "->" r_localhost ":" port_r ">\\) += 0$"
+    next
+  }
+}
+
+NR == 4 {if (match($0, r_close)) next}
+
+NR == lines && /^\+\+\+ exited with 0 \+\+\+$/ {next}
+
+{
+  print "Line " NR " does not match: " $0
+  fail=1
+}
+
+END {
+  if (NR != lines) {
+    print "Expected " lines " lines, found " NR " line(s)."
+    print ""
+    exit 1
+  }
+  if (fail) {
+    print ""
+    exit 1
+  }
+}
diff --git a/tests/net-yy.test b/tests/net-yy.test
new file mode 100755
index 00000000..8b6c0982
--- /dev/null
+++ b/tests/net-yy.test
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# Check decoding of ip:port pairs associated with socket descriptors
+
+. "${srcdir=.}/init.sh"
+
+# strace -yy is implemented using /proc/self/fd
+[ -d /proc/self/fd/ ] ||
+	framework_skip_ '/proc/self/fd/ is not available'
+
+check_prog sed
+check_prog awk
+
+rm -f $LOG.* $LOG-*
+
+./inet-accept-connect-send-recv ||
+	fail_ 'inet-accept-connect-send-recv failed'
+
+./netlink_inet_diag || {
+	if [ $? -eq 77 ]; then
+		framework_skip_ 'NETLINK_INET_DIAG is not available'
+	else
+		fail_ 'netlink_inet_diag failed'
+	fi
+}
+
+args="-tt -ff -yy -o $LOG -eclose,network ./inet-accept-connect-send-recv"
+$STRACE $args ||
+	fail_ "strace $args failed"
+
+"$srcdir"/../strace-log-merge $LOG > $LOG || {
+	cat $LOG
+	fail_ 'strace-log-merge failed'
+}
+rm -f $LOG.*
+
+child="$(sed -rn '/SIGCHLD/ s/^.*, si_pid=([1-9][0-9]*), .*/\1/p' $LOG)"
+[ -n "$child" ] || {
+	cat $LOG
+	fail_ 'failed to find pid of child process'
+}
+
+sed -rn "/^$child"' /!d; / socket\(/,$ s/^[0-9]+ +[^ ]+ (.+)/\1/p' $LOG > $LOG-connect &&
+sed -rn "/^$child"' /d; /SIGCHLD/d; / socket\(/,$ s/^[0-9]+ +[^ ]+ (.+)/\1/p' $LOG > $LOG-accept || {
+	cat $LOG
+	fail_ 'failed to separate logs'
+}
+
+awk -f "$srcdir"/net-yy-connect.awk $LOG-connect || {
+	cat $LOG-connect
+	fail_ "strace $args failed to decode socket descriptors properly"
+}
+
+awk -f "$srcdir"/net-yy-accept.awk $LOG-accept || {
+	cat $LOG-accept
+	fail_ "strace $args failed to decode socket descriptors properly"
+}
+
+exit 0
diff --git a/tests/netlink_inet_diag.c b/tests/netlink_inet_diag.c
new file mode 100644
index 00000000..ffd3591a
--- /dev/null
+++ b/tests/netlink_inet_diag.c
@@ -0,0 +1,96 @@
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
+#include <linux/inet_diag.h>
+
+static int
+send_query(const int fd, const int family, const int proto)
+{
+	struct sockaddr_nl nladdr;
+	struct {
+		struct nlmsghdr nlh;
+		struct inet_diag_req_v2 idr;
+	} req;
+	struct iovec iov = {
+		.iov_base = &req,
+		.iov_len = sizeof(req)
+	};
+	struct msghdr msg = {
+		.msg_name = (void*)&nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1
+	};
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	memset(&req, 0, sizeof(req));
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+	req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+	req.idr.sdiag_family = family;
+	req.idr.sdiag_protocol = proto;
+	req.idr.idiag_states = -1;
+
+	return sendmsg(fd, &msg, 0) > 0;
+}
+
+static int
+check_responses(const int fd)
+{
+	static char buf[8192];
+	struct sockaddr_nl nladdr;
+	struct iovec iov = {
+		.iov_base = buf,
+		.iov_len = sizeof(buf)
+	};
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	struct msghdr msg = {
+		.msg_name = (void*)&nladdr,
+		.msg_namelen = sizeof(nladdr),
+		.msg_iov = &iov,
+		.msg_iovlen = 1,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_flags = 0
+	};
+
+	ssize_t ret = recvmsg(fd, &msg, 0);
+	if (ret <= 0)
+		return 0;
+
+	struct nlmsghdr *h = (struct nlmsghdr*)buf;
+	return (NLMSG_OK(h, ret) &&
+		h->nlmsg_type != NLMSG_ERROR &&
+		h->nlmsg_type != NLMSG_DONE) ? 1 : 0;
+}
+
+int main(void)
+{
+	struct sockaddr_in addr;
+	socklen_t len = sizeof(addr);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+	close(0);
+	close(1);
+
+	assert(socket(PF_INET, SOCK_STREAM, 0) == 0);
+	assert(bind(0, (struct sockaddr *) &addr, len) == 0);
+	assert(listen(0, 5) == 0);
+
+	if (socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG) != 1)
+		return 77;
+
+	return (send_query(1, AF_INET, IPPROTO_TCP) &&
+		check_responses(1)) ? 0 : 77;
+}
-- 
2.40.0