mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
Merge branch 'af_unix-fix-bunch-of-msg_oob-bugs-and-add-new-tests'
Kuniyuki Iwashima says: ==================== af_unix: Fix bunch of MSG_OOB bugs and add new tests. This series rewrites the selftest for AF_UNIX MSG_OOB and fixes bunch of bugs that AF_UNIX behaves differently compared to TCP. Note that the test discovered few more bugs in TCP side, which will be fixed in another series. ==================== Link: https://lore.kernel.org/r/20240625013645.45034-1-kuniyu@amazon.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
3f4d9e4f82
@ -2613,10 +2613,24 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
|
||||
{
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
|
||||
if (!unix_skb_len(skb) && !(flags & MSG_PEEK)) {
|
||||
skb_unlink(skb, &sk->sk_receive_queue);
|
||||
consume_skb(skb);
|
||||
skb = NULL;
|
||||
if (!unix_skb_len(skb)) {
|
||||
struct sk_buff *unlinked_skb = NULL;
|
||||
|
||||
spin_lock(&sk->sk_receive_queue.lock);
|
||||
|
||||
if (copied && (!u->oob_skb || skb == u->oob_skb)) {
|
||||
skb = NULL;
|
||||
} else if (flags & MSG_PEEK) {
|
||||
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
||||
} else {
|
||||
unlinked_skb = skb;
|
||||
skb = skb_peek_next(skb, &sk->sk_receive_queue);
|
||||
__skb_unlink(unlinked_skb, &sk->sk_receive_queue);
|
||||
}
|
||||
|
||||
spin_unlock(&sk->sk_receive_queue.lock);
|
||||
|
||||
consume_skb(unlinked_skb);
|
||||
} else {
|
||||
struct sk_buff *unlinked_skb = NULL;
|
||||
|
||||
@ -3093,12 +3107,23 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
|
||||
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
|
||||
case SIOCATMARK:
|
||||
{
|
||||
struct unix_sock *u = unix_sk(sk);
|
||||
struct sk_buff *skb;
|
||||
int answ = 0;
|
||||
|
||||
mutex_lock(&u->iolock);
|
||||
|
||||
skb = skb_peek(&sk->sk_receive_queue);
|
||||
if (skb && skb == READ_ONCE(unix_sk(sk)->oob_skb))
|
||||
answ = 1;
|
||||
if (skb) {
|
||||
struct sk_buff *oob_skb = READ_ONCE(u->oob_skb);
|
||||
|
||||
if (skb == oob_skb ||
|
||||
(!oob_skb && !unix_skb_len(skb)))
|
||||
answ = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&u->iolock);
|
||||
|
||||
err = put_user(answ, (int __user *)arg);
|
||||
}
|
||||
break;
|
||||
|
1
tools/testing/selftests/net/.gitignore
vendored
1
tools/testing/selftests/net/.gitignore
vendored
@ -43,7 +43,6 @@ tap
|
||||
tcp_fastopen_backup_key
|
||||
tcp_inq
|
||||
tcp_mmap
|
||||
test_unix_oob
|
||||
timestamping
|
||||
tls
|
||||
toeplitz
|
||||
|
@ -1,4 +1,4 @@
|
||||
CFLAGS += $(KHDR_INCLUDES)
|
||||
TEST_GEN_PROGS := diag_uid test_unix_oob unix_connect scm_pidfd scm_rights
|
||||
TEST_GEN_PROGS := diag_uid msg_oob scm_pidfd scm_rights unix_connect
|
||||
|
||||
include ../../lib.mk
|
||||
|
734
tools/testing/selftests/net/af_unix/msg_oob.c
Normal file
734
tools/testing/selftests/net/af_unix/msg_oob.c
Normal file
@ -0,0 +1,734 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright Amazon.com Inc. or its affiliates. */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../../kselftest_harness.h"
|
||||
|
||||
#define BUF_SZ 32
|
||||
|
||||
FIXTURE(msg_oob)
|
||||
{
|
||||
int fd[4]; /* 0: AF_UNIX sender
|
||||
* 1: AF_UNIX receiver
|
||||
* 2: TCP sender
|
||||
* 3: TCP receiver
|
||||
*/
|
||||
int signal_fd;
|
||||
int epoll_fd[2]; /* 0: AF_UNIX receiver
|
||||
* 1: TCP receiver
|
||||
*/
|
||||
bool tcp_compliant;
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT(msg_oob)
|
||||
{
|
||||
bool peek;
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(msg_oob, no_peek)
|
||||
{
|
||||
.peek = false,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(msg_oob, peek)
|
||||
{
|
||||
.peek = true
|
||||
};
|
||||
|
||||
static void create_unix_socketpair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, self->fd);
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
|
||||
static void create_tcp_socketpair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen;
|
||||
int listen_fd;
|
||||
int ret;
|
||||
|
||||
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
ASSERT_GE(listen_fd, 0);
|
||||
|
||||
ret = listen(listen_fd, -1);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
ret = getsockname(listen_fd, (struct sockaddr *)&addr, &addrlen);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
self->fd[2] = socket(AF_INET, SOCK_STREAM, 0);
|
||||
ASSERT_GE(self->fd[2], 0);
|
||||
|
||||
ret = connect(self->fd[2], (struct sockaddr *)&addr, addrlen);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
self->fd[3] = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
|
||||
ASSERT_GE(self->fd[3], 0);
|
||||
|
||||
ret = fcntl(self->fd[3], F_SETFL, O_NONBLOCK);
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
|
||||
static void setup_sigurg(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self)
|
||||
{
|
||||
struct signalfd_siginfo siginfo;
|
||||
int pid = getpid();
|
||||
sigset_t mask;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret = ioctl(self->fd[i * 2 + 1], FIOSETOWN, &pid);
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
|
||||
ret = sigemptyset(&mask);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = sigaddset(&mask, SIGURG);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
self->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK);
|
||||
ASSERT_GE(self->signal_fd, 0);
|
||||
|
||||
ret = read(self->signal_fd, &siginfo, sizeof(siginfo));
|
||||
ASSERT_EQ(ret, -1);
|
||||
}
|
||||
|
||||
static void setup_epollpri(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self)
|
||||
{
|
||||
struct epoll_event event = {
|
||||
.events = EPOLLPRI,
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int ret;
|
||||
|
||||
self->epoll_fd[i] = epoll_create1(0);
|
||||
ASSERT_GE(self->epoll_fd[i], 0);
|
||||
|
||||
ret = epoll_ctl(self->epoll_fd[i], EPOLL_CTL_ADD, self->fd[i * 2 + 1], &event);
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_sockets(FIXTURE_DATA(msg_oob) *self)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
close(self->fd[i]);
|
||||
}
|
||||
|
||||
FIXTURE_SETUP(msg_oob)
|
||||
{
|
||||
create_unix_socketpair(_metadata, self);
|
||||
create_tcp_socketpair(_metadata, self);
|
||||
|
||||
setup_sigurg(_metadata, self);
|
||||
setup_epollpri(_metadata, self);
|
||||
|
||||
self->tcp_compliant = true;
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(msg_oob)
|
||||
{
|
||||
close_sockets(self);
|
||||
}
|
||||
|
||||
static void __epollpair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self,
|
||||
bool oob_remaining)
|
||||
{
|
||||
struct epoll_event event[2] = {};
|
||||
int i, ret[2];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
ret[i] = epoll_wait(self->epoll_fd[i], &event[i], 1, 0);
|
||||
|
||||
ASSERT_EQ(ret[0], oob_remaining);
|
||||
|
||||
if (self->tcp_compliant)
|
||||
ASSERT_EQ(ret[0], ret[1]);
|
||||
|
||||
if (oob_remaining) {
|
||||
ASSERT_EQ(event[0].events, EPOLLPRI);
|
||||
|
||||
if (self->tcp_compliant)
|
||||
ASSERT_EQ(event[0].events, event[1].events);
|
||||
}
|
||||
}
|
||||
|
||||
static void __sendpair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self,
|
||||
const void *buf, size_t len, int flags)
|
||||
{
|
||||
int i, ret[2];
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct signalfd_siginfo siginfo = {};
|
||||
int bytes;
|
||||
|
||||
ret[i] = send(self->fd[i * 2], buf, len, flags);
|
||||
|
||||
bytes = read(self->signal_fd, &siginfo, sizeof(siginfo));
|
||||
|
||||
if (flags & MSG_OOB) {
|
||||
ASSERT_EQ(bytes, sizeof(siginfo));
|
||||
ASSERT_EQ(siginfo.ssi_signo, SIGURG);
|
||||
|
||||
bytes = read(self->signal_fd, &siginfo, sizeof(siginfo));
|
||||
}
|
||||
|
||||
ASSERT_EQ(bytes, -1);
|
||||
}
|
||||
|
||||
ASSERT_EQ(ret[0], len);
|
||||
ASSERT_EQ(ret[0], ret[1]);
|
||||
}
|
||||
|
||||
static void __recvpair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self,
|
||||
const void *expected_buf, int expected_len,
|
||||
int buf_len, int flags)
|
||||
{
|
||||
int i, ret[2], recv_errno[2], expected_errno = 0;
|
||||
char recv_buf[2][BUF_SZ] = {};
|
||||
bool printed = false;
|
||||
|
||||
ASSERT_GE(BUF_SZ, buf_len);
|
||||
|
||||
errno = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags);
|
||||
recv_errno[i] = errno;
|
||||
}
|
||||
|
||||
if (expected_len < 0) {
|
||||
expected_errno = -expected_len;
|
||||
expected_len = -1;
|
||||
}
|
||||
|
||||
if (ret[0] != expected_len || recv_errno[0] != expected_errno) {
|
||||
TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
|
||||
TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf);
|
||||
|
||||
ASSERT_EQ(ret[0], expected_len);
|
||||
ASSERT_EQ(recv_errno[0], expected_errno);
|
||||
}
|
||||
|
||||
if (ret[0] != ret[1] || recv_errno[0] != recv_errno[1]) {
|
||||
TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
|
||||
TH_LOG("TCP :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]);
|
||||
|
||||
printed = true;
|
||||
|
||||
if (self->tcp_compliant) {
|
||||
ASSERT_EQ(ret[0], ret[1]);
|
||||
ASSERT_EQ(recv_errno[0], recv_errno[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (expected_len >= 0) {
|
||||
int cmp;
|
||||
|
||||
cmp = strncmp(expected_buf, recv_buf[0], expected_len);
|
||||
if (cmp) {
|
||||
TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
|
||||
TH_LOG("Expected:%s", expected_errno ? strerror(expected_errno) : expected_buf);
|
||||
|
||||
ASSERT_EQ(cmp, 0);
|
||||
}
|
||||
|
||||
cmp = strncmp(recv_buf[0], recv_buf[1], expected_len);
|
||||
if (cmp) {
|
||||
if (!printed) {
|
||||
TH_LOG("AF_UNIX :%s", ret[0] < 0 ? strerror(recv_errno[0]) : recv_buf[0]);
|
||||
TH_LOG("TCP :%s", ret[1] < 0 ? strerror(recv_errno[1]) : recv_buf[1]);
|
||||
}
|
||||
|
||||
if (self->tcp_compliant)
|
||||
ASSERT_EQ(cmp, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __setinlinepair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self)
|
||||
{
|
||||
int i, oob_inline = 1;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int ret;
|
||||
|
||||
ret = setsockopt(self->fd[i * 2 + 1], SOL_SOCKET, SO_OOBINLINE,
|
||||
&oob_inline, sizeof(oob_inline));
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void __siocatmarkpair(struct __test_metadata *_metadata,
|
||||
FIXTURE_DATA(msg_oob) *self,
|
||||
bool oob_head)
|
||||
{
|
||||
int answ[2] = {};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int ret;
|
||||
|
||||
ret = ioctl(self->fd[i * 2 + 1], SIOCATMARK, &answ[i]);
|
||||
ASSERT_EQ(ret, 0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(answ[0], oob_head);
|
||||
|
||||
if (self->tcp_compliant)
|
||||
ASSERT_EQ(answ[0], answ[1]);
|
||||
}
|
||||
|
||||
#define sendpair(buf, len, flags) \
|
||||
__sendpair(_metadata, self, buf, len, flags)
|
||||
|
||||
#define recvpair(expected_buf, expected_len, buf_len, flags) \
|
||||
do { \
|
||||
if (variant->peek) \
|
||||
__recvpair(_metadata, self, \
|
||||
expected_buf, expected_len, \
|
||||
buf_len, (flags) | MSG_PEEK); \
|
||||
__recvpair(_metadata, self, \
|
||||
expected_buf, expected_len, buf_len, flags); \
|
||||
} while (0)
|
||||
|
||||
#define epollpair(oob_remaining) \
|
||||
__epollpair(_metadata, self, oob_remaining)
|
||||
|
||||
#define siocatmarkpair(oob_head) \
|
||||
__siocatmarkpair(_metadata, self, oob_head)
|
||||
|
||||
#define setinlinepair() \
|
||||
__setinlinepair(_metadata, self)
|
||||
|
||||
#define tcp_incompliant \
|
||||
for (self->tcp_compliant = false; \
|
||||
self->tcp_compliant == false; \
|
||||
self->tcp_compliant = true)
|
||||
|
||||
TEST_F(msg_oob, non_oob)
|
||||
{
|
||||
sendpair("x", 1, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("", -EINVAL, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, oob)
|
||||
{
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("x", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, oob_drop)
|
||||
{
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("", -EAGAIN, 1, 0); /* Drop OOB. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("", -EINVAL, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, oob_ahead)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("o", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 4, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, oob_break)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 5, 0); /* Break at OOB even with enough buffer. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("o", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("", -EAGAIN, 1, 0);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, oob_ahead_break)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("world", 5, 0);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("o", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 9, 0); /* Break at OOB even after it's recv()ed. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("world", 5, 5, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, oob_break_drop)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("world", 5, 0);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 10, 0); /* Break at OOB even with enough buffer. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("world", 5, 10, 0); /* Drop OOB and recv() the next skb. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("", -EINVAL, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, ex_oob_break)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("wor", 3, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("ld", 2, 0);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("r", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("ld", 2, 2, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, ex_oob_drop)
|
||||
{
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */
|
||||
epollpair(true);
|
||||
|
||||
tcp_incompliant {
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("x", 1, 1, 0); /* TCP drops "y" by passing through it. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("y", 1, 1, MSG_OOB); /* TCP returns -EINVAL. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, ex_oob_drop_2)
|
||||
{
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */
|
||||
epollpair(true);
|
||||
|
||||
tcp_incompliant {
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
recvpair("y", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
|
||||
tcp_incompliant {
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("x", 1, 1, 0); /* TCP returns -EAGAIN. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, ex_oob_ahead_break)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("wor", 3, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("r", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("ld", 2, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
tcp_incompliant {
|
||||
recvpair("hellowol", 8, 10, 0); /* TCP recv()s "helloworl", why "r" ?? */
|
||||
}
|
||||
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("d", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, ex_oob_siocatmark)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("o", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("world", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_oob)
|
||||
{
|
||||
setinlinepair();
|
||||
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("", -EINVAL, 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("x", 1, 1, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_oob_break)
|
||||
{
|
||||
setinlinepair();
|
||||
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("", -EINVAL, 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 5, 0); /* Break at OOB but not at ex-OOB. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("o", 1, 1, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_oob_ahead_break)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("world", 5, 0);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("o", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
setinlinepair();
|
||||
|
||||
recvpair("hell", 4, 9, 0); /* Break at OOB even with enough buffer. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(true);
|
||||
|
||||
tcp_incompliant {
|
||||
recvpair("world", 5, 6, 0); /* TCP recv()s "oworld", ... "o" ??? */
|
||||
}
|
||||
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_ex_oob_break)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("wor", 3, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
sendpair("ld", 2, 0);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
setinlinepair();
|
||||
|
||||
recvpair("hellowo", 7, 10, 0); /* Break at OOB but not at ex-OOB. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("rld", 3, 3, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_ex_oob_no_drop)
|
||||
{
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
setinlinepair();
|
||||
|
||||
sendpair("y", 1, MSG_OOB); /* TCP does NOT drops "x" at this moment. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("x", 1, 1, 0);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("y", 1, 1, 0);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_ex_oob_drop)
|
||||
{
|
||||
sendpair("x", 1, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
sendpair("y", 1, MSG_OOB); /* TCP drops "x" at this moment. */
|
||||
epollpair(true);
|
||||
|
||||
setinlinepair();
|
||||
|
||||
tcp_incompliant {
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("x", 1, 1, 0); /* TCP recv()s "y". */
|
||||
epollpair(true);
|
||||
siocatmarkpair(true);
|
||||
|
||||
recvpair("y", 1, 1, 0); /* TCP returns -EAGAIN. */
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(msg_oob, inline_ex_oob_siocatmark)
|
||||
{
|
||||
sendpair("hello", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("o", 1, 1, MSG_OOB);
|
||||
epollpair(false);
|
||||
siocatmarkpair(false);
|
||||
|
||||
setinlinepair();
|
||||
|
||||
sendpair("world", 5, MSG_OOB);
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
|
||||
recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */
|
||||
epollpair(true);
|
||||
siocatmarkpair(false);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
@ -1,436 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
static int pipefd[2];
|
||||
static int signal_recvd;
|
||||
static pid_t producer_id;
|
||||
static char sock_name[32];
|
||||
|
||||
static void sig_hand(int sn, siginfo_t *si, void *p)
|
||||
{
|
||||
signal_recvd = sn;
|
||||
}
|
||||
|
||||
static int set_sig_handler(int signal)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_sigaction = sig_hand;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
|
||||
return sigaction(signal, &sa, NULL);
|
||||
}
|
||||
|
||||
static void set_filemode(int fd, int set)
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
|
||||
if (set)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(fd, F_SETFL, flags);
|
||||
}
|
||||
|
||||
static void signal_producer(int fd)
|
||||
{
|
||||
char cmd;
|
||||
|
||||
cmd = 'S';
|
||||
write(fd, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
static void wait_for_signal(int fd)
|
||||
{
|
||||
char buf[5];
|
||||
|
||||
read(fd, buf, 5);
|
||||
}
|
||||
|
||||
static void die(int status)
|
||||
{
|
||||
fflush(NULL);
|
||||
unlink(sock_name);
|
||||
kill(producer_id, SIGTERM);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
int is_sioctatmark(int fd)
|
||||
{
|
||||
int ans = -1;
|
||||
|
||||
if (ioctl(fd, SIOCATMARK, &ans, sizeof(ans)) < 0) {
|
||||
#ifdef DEBUG
|
||||
perror("SIOCATMARK Failed");
|
||||
#endif
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
void read_oob(int fd, char *c)
|
||||
{
|
||||
|
||||
*c = ' ';
|
||||
if (recv(fd, c, sizeof(*c), MSG_OOB) < 0) {
|
||||
#ifdef DEBUG
|
||||
perror("Reading MSG_OOB Failed");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int read_data(int pfd, char *buf, int size)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
memset(buf, size, '0');
|
||||
len = read(pfd, buf, size);
|
||||
#ifdef DEBUG
|
||||
if (len < 0)
|
||||
perror("read failed");
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
static void wait_for_data(int pfd, int event)
|
||||
{
|
||||
struct pollfd pfds[1];
|
||||
|
||||
pfds[0].fd = pfd;
|
||||
pfds[0].events = event;
|
||||
poll(pfds, 1, -1);
|
||||
}
|
||||
|
||||
void producer(struct sockaddr_un *consumer_addr)
|
||||
{
|
||||
int cfd;
|
||||
char buf[64];
|
||||
int i;
|
||||
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
cfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
wait_for_signal(pipefd[0]);
|
||||
if (connect(cfd, (struct sockaddr *)consumer_addr,
|
||||
sizeof(*consumer_addr)) != 0) {
|
||||
perror("Connect failed");
|
||||
kill(0, SIGTERM);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
/* Test 1: Test for SIGURG and OOB */
|
||||
wait_for_signal(pipefd[0]);
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[63] = '@';
|
||||
send(cfd, buf, sizeof(buf), MSG_OOB);
|
||||
|
||||
wait_for_signal(pipefd[0]);
|
||||
|
||||
/* Test 2: Test for OOB being overwitten */
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[63] = '%';
|
||||
send(cfd, buf, sizeof(buf), MSG_OOB);
|
||||
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[63] = '#';
|
||||
send(cfd, buf, sizeof(buf), MSG_OOB);
|
||||
|
||||
wait_for_signal(pipefd[0]);
|
||||
|
||||
/* Test 3: Test for SIOCATMARK */
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[63] = '@';
|
||||
send(cfd, buf, sizeof(buf), MSG_OOB);
|
||||
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[63] = '%';
|
||||
send(cfd, buf, sizeof(buf), MSG_OOB);
|
||||
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
send(cfd, buf, sizeof(buf), 0);
|
||||
|
||||
wait_for_signal(pipefd[0]);
|
||||
|
||||
/* Test 4: Test for 1byte OOB msg */
|
||||
memset(buf, 'x', sizeof(buf));
|
||||
buf[0] = '@';
|
||||
send(cfd, buf, 1, MSG_OOB);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int lfd, pfd;
|
||||
struct sockaddr_un consumer_addr, paddr;
|
||||
socklen_t len = sizeof(consumer_addr);
|
||||
char buf[1024];
|
||||
int on = 0;
|
||||
char oob;
|
||||
int atmark;
|
||||
|
||||
lfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
memset(&consumer_addr, 0, sizeof(consumer_addr));
|
||||
consumer_addr.sun_family = AF_UNIX;
|
||||
sprintf(sock_name, "unix_oob_%d", getpid());
|
||||
unlink(sock_name);
|
||||
strcpy(consumer_addr.sun_path, sock_name);
|
||||
|
||||
if ((bind(lfd, (struct sockaddr *)&consumer_addr,
|
||||
sizeof(consumer_addr))) != 0) {
|
||||
perror("socket bind failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pipe(pipefd);
|
||||
|
||||
listen(lfd, 1);
|
||||
|
||||
producer_id = fork();
|
||||
if (producer_id == 0) {
|
||||
producer(&consumer_addr);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
set_sig_handler(SIGURG);
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
pfd = accept(lfd, (struct sockaddr *) &paddr, &len);
|
||||
fcntl(pfd, F_SETOWN, getpid());
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 1:
|
||||
* veriyf that SIGURG is
|
||||
* delivered, 63 bytes are
|
||||
* read, oob is '@', and POLLPRI works.
|
||||
*/
|
||||
wait_for_data(pfd, POLLPRI);
|
||||
read_oob(pfd, &oob);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
if (!signal_recvd || len != 63 || oob != '@') {
|
||||
fprintf(stderr, "Test 1 failed sigurg %d len %d %c\n",
|
||||
signal_recvd, len, oob);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 2:
|
||||
* Verify that the first OOB is over written by
|
||||
* the 2nd one and the first OOB is returned as
|
||||
* part of the read, and sigurg is received.
|
||||
*/
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
len = 0;
|
||||
while (len < 70)
|
||||
len = recv(pfd, buf, 1024, MSG_PEEK);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
read_oob(pfd, &oob);
|
||||
if (!signal_recvd || len != 127 || oob != '#') {
|
||||
fprintf(stderr, "Test 2 failed, sigurg %d len %d OOB %c\n",
|
||||
signal_recvd, len, oob);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 3:
|
||||
* verify that 2nd oob over writes
|
||||
* the first one and read breaks at
|
||||
* oob boundary returning 127 bytes
|
||||
* and sigurg is received and atmark
|
||||
* is set.
|
||||
* oob is '%' and second read returns
|
||||
* 64 bytes.
|
||||
*/
|
||||
len = 0;
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
while (len < 150)
|
||||
len = recv(pfd, buf, 1024, MSG_PEEK);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
atmark = is_sioctatmark(pfd);
|
||||
read_oob(pfd, &oob);
|
||||
|
||||
if (!signal_recvd || len != 127 || oob != '%' || atmark != 1) {
|
||||
fprintf(stderr,
|
||||
"Test 3 failed, sigurg %d len %d OOB %c atmark %d\n",
|
||||
signal_recvd, len, oob, atmark);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
|
||||
len = read_data(pfd, buf, 1024);
|
||||
if (len != 64) {
|
||||
fprintf(stderr, "Test 3.1 failed, sigurg %d len %d OOB %c\n",
|
||||
signal_recvd, len, oob);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 4:
|
||||
* verify that a single byte
|
||||
* oob message is delivered.
|
||||
* set non blocking mode and
|
||||
* check proper error is
|
||||
* returned and sigurg is
|
||||
* received and correct
|
||||
* oob is read.
|
||||
*/
|
||||
|
||||
set_filemode(pfd, 0);
|
||||
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
if ((len == -1) && (errno == 11))
|
||||
len = 0;
|
||||
|
||||
read_oob(pfd, &oob);
|
||||
|
||||
if (!signal_recvd || len != 0 || oob != '@') {
|
||||
fprintf(stderr, "Test 4 failed, sigurg %d len %d OOB %c\n",
|
||||
signal_recvd, len, oob);
|
||||
die(1);
|
||||
}
|
||||
|
||||
set_filemode(pfd, 1);
|
||||
|
||||
/* Inline Testing */
|
||||
|
||||
on = 1;
|
||||
if (setsockopt(pfd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on))) {
|
||||
perror("SO_OOBINLINE");
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 1 -- Inline:
|
||||
* Check that SIGURG is
|
||||
* delivered and 63 bytes are
|
||||
* read and oob is '@'
|
||||
*/
|
||||
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
|
||||
if (!signal_recvd || len != 63) {
|
||||
fprintf(stderr, "Test 1 Inline failed, sigurg %d len %d\n",
|
||||
signal_recvd, len);
|
||||
die(1);
|
||||
}
|
||||
|
||||
len = read_data(pfd, buf, 1024);
|
||||
|
||||
if (len != 1) {
|
||||
fprintf(stderr,
|
||||
"Test 1.1 Inline failed, sigurg %d len %d oob %c\n",
|
||||
signal_recvd, len, oob);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 2 -- Inline:
|
||||
* Verify that the first OOB is over written by
|
||||
* the 2nd one and read breaks correctly on
|
||||
* 2nd OOB boundary with the first OOB returned as
|
||||
* part of the read, and sigurg is delivered and
|
||||
* siocatmark returns true.
|
||||
* next read returns one byte, the oob byte
|
||||
* and siocatmark returns false.
|
||||
*/
|
||||
len = 0;
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
while (len < 70)
|
||||
len = recv(pfd, buf, 1024, MSG_PEEK);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
atmark = is_sioctatmark(pfd);
|
||||
if (len != 127 || atmark != 1 || !signal_recvd) {
|
||||
fprintf(stderr, "Test 2 Inline failed, len %d atmark %d\n",
|
||||
len, atmark);
|
||||
die(1);
|
||||
}
|
||||
|
||||
len = read_data(pfd, buf, 1024);
|
||||
atmark = is_sioctatmark(pfd);
|
||||
if (len != 1 || buf[0] != '#' || atmark == 1) {
|
||||
fprintf(stderr, "Test 2.1 Inline failed, len %d data %c atmark %d\n",
|
||||
len, buf[0], atmark);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 3 -- Inline:
|
||||
* verify that 2nd oob over writes
|
||||
* the first one and read breaks at
|
||||
* oob boundary returning 127 bytes
|
||||
* and sigurg is received and siocatmark
|
||||
* is true after the read.
|
||||
* subsequent read returns 65 bytes
|
||||
* because of oob which should be '%'.
|
||||
*/
|
||||
len = 0;
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
while (len < 126)
|
||||
len = recv(pfd, buf, 1024, MSG_PEEK);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
atmark = is_sioctatmark(pfd);
|
||||
if (!signal_recvd || len != 127 || !atmark) {
|
||||
fprintf(stderr,
|
||||
"Test 3 Inline failed, sigurg %d len %d data %c\n",
|
||||
signal_recvd, len, buf[0]);
|
||||
die(1);
|
||||
}
|
||||
|
||||
len = read_data(pfd, buf, 1024);
|
||||
atmark = is_sioctatmark(pfd);
|
||||
if (len != 65 || buf[0] != '%' || atmark != 0) {
|
||||
fprintf(stderr,
|
||||
"Test 3.1 Inline failed, len %d oob %c atmark %d\n",
|
||||
len, buf[0], atmark);
|
||||
die(1);
|
||||
}
|
||||
|
||||
signal_recvd = 0;
|
||||
signal_producer(pipefd[1]);
|
||||
|
||||
/* Test 4 -- Inline:
|
||||
* verify that a single
|
||||
* byte oob message is delivered
|
||||
* and read returns one byte, the oob
|
||||
* byte and sigurg is received
|
||||
*/
|
||||
wait_for_data(pfd, POLLIN | POLLPRI);
|
||||
len = read_data(pfd, buf, 1024);
|
||||
if (!signal_recvd || len != 1 || buf[0] != '@') {
|
||||
fprintf(stderr,
|
||||
"Test 4 Inline failed, signal %d len %d data %c\n",
|
||||
signal_recvd, len, buf[0]);
|
||||
die(1);
|
||||
}
|
||||
die(0);
|
||||
}
|
Loading…
Reference in New Issue
Block a user