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.
This commit is contained in:
Дмитрий Левин 2016-01-30 23:50:54 +00:00
parent cc09ba1a84
commit 3c86e0ea5d
3 changed files with 75 additions and 48 deletions

@ -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 @@ inet_send_query(const int fd, const int family, const int proto)
.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 @@ inet_send_query(const int fd, const int family, const int proto)
}
}
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 @@ inet_parse_response(const char *proto_name, const void *data, int data_len,
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 @@ inet_parse_response(const char *proto_name, const void *data, int data_len,
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 @@ inet_parse_response(const char *proto_name, const void *data, int data_len,
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 @@ receive_responses(const int fd, const unsigned long inode,
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 @@ receive_responses(const int fd, const unsigned long inode,
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 @@ unix_send_query(const int fd, const unsigned long inode)
.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 @@ unix_send_query(const int fd, const unsigned long inode)
}
}
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 @@ unix_parse_response(const char *proto_name, const void *data, int data_len,
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 @@ unix_parse_response(const char *proto_name, const void *data, int data_len,
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 @@ unix_parse_response(const char *proto_name, const void *data, int data_len,
}
}
tprints("]");
return true;
return 1;
}
else
return false;
return -1;
}
static bool

@ -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 @@ send_query(const int fd, const int family, const int proto)
.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 @@ check_responses(const int fd)
.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 @@ check_responses(const int fd)
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)

@ -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 @@ send_query(const int fd, const int family, const int proto)
.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 @@ check_responses(const int fd)
.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 @@ check_responses(const int fd)
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"