Robustify netlink response parsers
* socketutils.c (inet_parse_response, unix_parse_response): Change
return type from bool to int, return -1 on all parse errors except
inode mismatch.
(receive_responses): Stop on the first nlmsg_type that is not
SOCK_DIAG_BY_FAMILY, also stop when the parser returns -1.
* tests/netlink_inet_diag.c (check_responses): Stop on short messages,
on first nlmsg_type that is not SOCK_DIAG_BY_FAMILY, print more verbose
diagnostics for NLMSG_ERROR.
* tests/netlink_unix_diag.c (check_responses): Likewise.
diff --git a/socketutils.c b/socketutils.c
index aa82163..6fbb8cf 100644
--- a/socketutils.c
+++ b/socketutils.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2014 Zubin Mithra <zubin.mithra@gmail.com>
- * Copyright (c) 2014-2015 Dmitry V. Levin <ldv@altlinux.org>
+ * Copyright (c) 2014-2016 Dmitry V. Levin <ldv@altlinux.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -71,7 +71,7 @@
.iov_len = sizeof(req)
};
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -87,18 +87,18 @@
}
}
-static bool
-inet_parse_response(const char *proto_name, const void *data, int data_len,
- const unsigned long inode)
+static int
+inet_parse_response(const char *proto_name, const void *data,
+ const int data_len, const unsigned long inode)
{
const struct inet_diag_msg *diag_msg = data;
static const char zero_addr[sizeof(struct in6_addr)];
socklen_t addr_size, text_size;
if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
- return false;
+ return -1;
if (diag_msg->idiag_inode != inode)
- return false;
+ return 0;
switch(diag_msg->idiag_family) {
case AF_INET:
@@ -110,14 +110,14 @@
text_size = INET6_ADDRSTRLEN;
break;
default:
- return false;
+ return -1;
}
char src_buf[text_size];
if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
src_buf, text_size))
- return false;
+ return -1;
if (diag_msg->id.idiag_dport ||
memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
@@ -125,7 +125,7 @@
if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
dst_buf, text_size))
- return false;
+ return -1;
tprintf("%s:[%s:%u->%s:%u]",
proto_name,
@@ -136,13 +136,14 @@
ntohs(diag_msg->id.idiag_sport));
}
- return true;
+ return 1;
}
static bool
receive_responses(const int fd, const unsigned long inode,
const char *proto_name,
- bool (* parser) (const char *, const void *, int, const unsigned long))
+ int (* parser) (const char *, const void *,
+ int, unsigned long))
{
static long buf[8192 / sizeof(long)];
struct sockaddr_nl nladdr = {
@@ -156,9 +157,8 @@
for (;;) {
ssize_t ret;
- struct nlmsghdr *h;
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -170,18 +170,19 @@
continue;
return false;
}
- if (!ret)
+
+ struct nlmsghdr *h = (struct nlmsghdr *) buf;
+ if (!NLMSG_OK(h, ret))
return false;
- for (h = (struct nlmsghdr*)buf;
- NLMSG_OK(h, ret);
- h = NLMSG_NEXT(h, ret)) {
- switch (h->nlmsg_type) {
- case NLMSG_DONE:
- case NLMSG_ERROR:
- return false;
- }
- if (parser(proto_name, NLMSG_DATA(h), h->nlmsg_len, inode))
+ for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
+ if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
+ return false;
+ int rc = parser(proto_name, NLMSG_DATA(h),
+ h->nlmsg_len, inode);
+ if (rc > 0)
return true;
+ if (rc < 0)
+ return false;
}
flags = MSG_DONTWAIT;
}
@@ -222,7 +223,7 @@
.iov_len = sizeof(req)
};
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -238,9 +239,9 @@
}
}
-static bool
-unix_parse_response(const char *proto_name, const void *data, int data_len,
- const unsigned long inode)
+static int
+unix_parse_response(const char *proto_name, const void *data,
+ const int data_len, const unsigned long inode)
{
const struct unix_diag_msg *diag_msg = data;
struct rtattr *attr;
@@ -250,11 +251,11 @@
char path[UNIX_PATH_MAX + 1];
if (rta_len < 0)
- return false;
+ return -1;
if (diag_msg->udiag_ino != inode)
- return false;
+ return 0;
if (diag_msg->udiag_family != AF_UNIX)
- return false;
+ return -1;
for (attr = (struct rtattr *) (diag_msg + 1);
RTA_OK(attr, rta_len);
@@ -271,7 +272,7 @@
break;
case UNIX_DIAG_PEER:
if (RTA_PAYLOAD(attr) >= 4)
- peer = *(uint32_t *)RTA_DATA(attr);
+ peer = *(uint32_t *) RTA_DATA(attr);
break;
}
}
@@ -296,10 +297,10 @@
}
}
tprints("]");
- return true;
+ return 1;
}
else
- return false;
+ return -1;
}
static bool
diff --git a/tests/netlink_inet_diag.c b/tests/netlink_inet_diag.c
index 8dd50db..3754d1b 100644
--- a/tests/netlink_inet_diag.c
+++ b/tests/netlink_inet_diag.c
@@ -1,4 +1,6 @@
/*
+ * This file is part of inet-yy strace test.
+ *
* Copyright (c) 2014-2016 Dmitry V. Levin <ldv@altlinux.org>
* All rights reserved.
*
@@ -27,6 +29,7 @@
#include "tests.h"
#include <assert.h>
+#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
@@ -60,7 +63,7 @@
.iov_len = sizeof(req)
};
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -82,7 +85,7 @@
.iov_len = sizeof(buf)
};
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -92,13 +95,23 @@
if (ret <= 0)
perror_msg_and_skip("recvmsg");
- struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ struct nlmsghdr *h = (struct nlmsghdr *) buf;
if (!NLMSG_OK(h, ret))
error_msg_and_skip("!NLMSG_OK");
- if (h->nlmsg_type == NLMSG_ERROR)
- error_msg_and_skip("NLMSG_ERROR");
- if (h->nlmsg_type == NLMSG_DONE)
- error_msg_and_skip("NLMSG_DONE");
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ const struct nlmsgerr *err = NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ error_msg_and_skip("NLMSG_ERROR");
+ errno = -err->error;
+ perror_msg_and_skip("NLMSG_ERROR");
+ }
+ if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
+ error_msg_and_skip("unexpected nlmsg_type %u",
+ (unsigned) h->nlmsg_type);
+
+ const struct inet_diag_msg *diag = NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*diag)))
+ error_msg_and_skip("short response");
}
int main(void)
diff --git a/tests/netlink_unix_diag.c b/tests/netlink_unix_diag.c
index 6e3b63b..12033e0 100644
--- a/tests/netlink_unix_diag.c
+++ b/tests/netlink_unix_diag.c
@@ -1,4 +1,6 @@
/*
+ * This file is part of net-yy-unix strace test.
+ *
* Copyright (c) 2014-2016 Dmitry V. Levin <ldv@altlinux.org>
* All rights reserved.
*
@@ -27,6 +29,7 @@
#include "tests.h"
#include <assert.h>
+#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@@ -68,7 +71,7 @@
.iov_len = sizeof(req)
};
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -90,7 +93,7 @@
.iov_len = sizeof(buf)
};
struct msghdr msg = {
- .msg_name = (void*)&nladdr,
+ .msg_name = (void *) &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1
@@ -100,13 +103,23 @@
if (ret <= 0)
perror_msg_and_skip("recvmsg");
- struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ struct nlmsghdr *h = (struct nlmsghdr *) buf;
if (!NLMSG_OK(h, ret))
error_msg_and_skip("!NLMSG_OK");
- if (h->nlmsg_type == NLMSG_ERROR)
- error_msg_and_skip("NLMSG_ERROR");
- if (h->nlmsg_type == NLMSG_DONE)
- error_msg_and_skip("NLMSG_DONE");
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ const struct nlmsgerr *err = NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ error_msg_and_skip("NLMSG_ERROR");
+ errno = -err->error;
+ perror_msg_and_skip("NLMSG_ERROR");
+ }
+ if (h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
+ error_msg_and_skip("unexpected nlmsg_type %u",
+ (unsigned) h->nlmsg_type);
+
+ const struct unix_diag_msg *diag = NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*diag)))
+ error_msg_and_skip("short response");
}
#define SUN_PATH "netlink_unix_diag_socket"