| // 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); |
| } |