blob: 3dece8b292536e242fcac5c901f353c6417b87e1 [file] [log] [blame]
// 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(struct sockaddr)) != 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 flags;
int atmark;
char *tmp_file;
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 and 63 bytes are
* read and oob is '@'
*/
wait_for_data(pfd, POLLIN | 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);
}