7c48de6cc4
Change-Id: Ibc1b7f7cd45ad7295285e59c6ce55888266fece8
1469 lines
33 KiB
C
1469 lines
33 KiB
C
/*
|
|
* Test Program for Unix Domain Sockets
|
|
*
|
|
* Overview: This program tests Unix Domain Sockets. It attempts
|
|
* to exercise the functions associated with Unix Domain Sockets.
|
|
* It also attempts to make sure all of the functions which handle
|
|
* file/socket descriptors work correctly when given a socket
|
|
* descriptor for a Unix domain socket. It also implicitly checks
|
|
* for the existance of constants like AF_UNIX and structures like
|
|
* sockaddr_un (it won't compile if they aren't defined). Besides
|
|
* checking that the sockets work properly, this test program also
|
|
* checks that the errors returned conform to the POSIX 2008
|
|
* standards. Some tests are omitted as they could adversely affect
|
|
* the operation of the host system. For example, implementing a test
|
|
* for socket() failing with errno = ENFILE would require using up all
|
|
* of the file descriptors supported by the OS (defined in
|
|
* /proc/sys/fs/file-max on Linux); this could cause problems for
|
|
* daemons and other processes running on the system. Some tests are
|
|
* omitted because they would require changes to libc or the kernel.
|
|
* For example, getting EINTR would require delaying the system call
|
|
* execution time long enough to raise a signal to interupt it. Some
|
|
* tests were omitted because the particular errors cannot occur when
|
|
* using Unix domain sockets. For example, write() will never fail with
|
|
* ENETDOWN because Unix domain sockets don't use network interfaces.
|
|
*
|
|
* Structure: Some functions can be tested or partially tested without
|
|
* making a connection, socket() for example. These have test
|
|
* functions like test_NAME(). The functionality that needs two way
|
|
* communication is contained within test_xfer().
|
|
*
|
|
* Functions Tested: accept(), bind(), close(), connect(), dup(),
|
|
* dup2(), fstat(), getpeername(), getsockname(), getsockopt(),
|
|
* listen(), read(), readv(), recv(), recvfrom(), recvmsg(), select(),
|
|
* send(), sendmsg(), sendto(), setsockopt(), shutdown(), socket(),
|
|
* socketpair(), write(), writev()
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ucred.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
/* Maximum number of errors that we'll allow to occur before this test
|
|
* program gives us and quits.
|
|
*/
|
|
int max_error = 4;
|
|
#include "common.h"
|
|
#include "common-socket.h"
|
|
|
|
|
|
/* Use the common testing code instead of reinventing the wheel. */
|
|
|
|
/* path of the unix domain socket */
|
|
#define TEST_SUN_PATH "test.sock"
|
|
#define TEST_SUN_PATHB "testb.sock"
|
|
|
|
/* filenames for symlinks -- we link these to each other to test ELOOP .*/
|
|
#define TEST_SYM_A "test.a"
|
|
#define TEST_SYM_B "test.b"
|
|
|
|
/* text file and test phrase for testing file descriptor passing */
|
|
#define TEST_TXT_FILE "test.txt"
|
|
#define MSG "This raccoon loves to eat bugs.\n"
|
|
|
|
/* socket types supported */
|
|
static int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM};
|
|
static char sock_fullpath[PATH_MAX + 1];
|
|
|
|
/* Convert name to the full path of the socket. Assumes name is in cwd. */
|
|
static char *fullpath(const char *name)
|
|
{
|
|
char cwd[PATH_MAX + 1];
|
|
|
|
if (realpath(".", cwd) == NULL)
|
|
test_fail("Couldn't retrieve current working dir");
|
|
|
|
snprintf(sock_fullpath, PATH_MAX, "%s/%s", cwd, name);
|
|
|
|
return(sock_fullpath);
|
|
}
|
|
|
|
static void test_header(void)
|
|
{
|
|
struct sockaddr_un sun;
|
|
debug("entering test_header()");
|
|
|
|
sun.sun_family = AF_UNIX;
|
|
sun.sun_path[0] = 'x';
|
|
sun.sun_path[1] = 'x';
|
|
sun.sun_path[2] = 'x';
|
|
sun.sun_path[3] = '\0';
|
|
|
|
if (SUN_LEN(&sun) != 5) {
|
|
test_fail("SUN_LEN(&sun) should be 5");
|
|
}
|
|
|
|
if (PF_UNIX != PF_LOCAL || PF_UNIX != AF_UNIX) {
|
|
test_fail("PF_UNIX, PF_LOCAL and AF_UNIX");
|
|
}
|
|
}
|
|
|
|
static void test_socketpair(void)
|
|
{
|
|
char buf[128];
|
|
struct sockaddr_un addr;
|
|
int socket_vector[2];
|
|
int rc;
|
|
int i;
|
|
|
|
debug("entering test_socketpair()");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
|
|
debug("Testing socketpair() success");
|
|
|
|
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector);
|
|
if (rc == -1) {
|
|
test_fail("socketpair() should have worked");
|
|
}
|
|
|
|
debug("Testing a simple read/write using sockets from socketpair()");
|
|
memset(buf, '\0', sizeof(buf));
|
|
|
|
strncpy(buf, "Howdy Partner", sizeof(buf) - 1);
|
|
|
|
rc = write(socket_vector[0], buf, sizeof(buf));
|
|
if (rc == -1) {
|
|
test_fail("write(sd, buf, sizeof(buf)) failed unexpectedly");
|
|
}
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
|
|
rc = read(socket_vector[1], buf, sizeof(buf));
|
|
if (rc == -1) {
|
|
test_fail("read() failed unexpectedly");
|
|
}
|
|
|
|
if (strncmp(buf, "Howdy Partner", strlen("Howdy Partner")) != 0) {
|
|
test_fail("We did not read what we wrote");
|
|
}
|
|
|
|
CLOSE(socket_vector[0]);
|
|
CLOSE(socket_vector[1]);
|
|
|
|
debug("Test socketpair() with all FDs open by this process");
|
|
|
|
for (i = 3; i < getdtablesize(); i++) {
|
|
rc = open("/dev/null", O_RDONLY);
|
|
if (rc == -1) {
|
|
test_fail("we couldn't open /dev/null for read");
|
|
}
|
|
}
|
|
|
|
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector);
|
|
if (!(rc == -1 && errno == EMFILE)) {
|
|
test_fail("socketpair() should have failed with EMFILE");
|
|
}
|
|
|
|
for (i = 3; i < getdtablesize(); i++) {
|
|
CLOSE(i);
|
|
}
|
|
|
|
rc = socketpair(PF_UNIX, SOCK_STREAM, 4, socket_vector);
|
|
if (!(rc == -1 && errno == EPROTONOSUPPORT)) {
|
|
test_fail("socketpair() should have failed");
|
|
}
|
|
|
|
debug("leaving test_socketpair()");
|
|
}
|
|
|
|
static void test_ucred(void)
|
|
{
|
|
struct uucred credentials;
|
|
socklen_t ucred_length;
|
|
uid_t euid = geteuid();
|
|
gid_t egid = getegid();
|
|
int sv[2];
|
|
int rc;
|
|
|
|
debug("Test credentials passing");
|
|
|
|
ucred_length = sizeof(struct uucred);
|
|
|
|
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sv);
|
|
if (rc == -1) {
|
|
test_fail("socketpair(PF_UNIX, SOCK_STREAM, 0, sv) failed");
|
|
}
|
|
|
|
memset(&credentials, '\0', ucred_length);
|
|
rc = getsockopt(sv[0], SOL_SOCKET, SO_PEERCRED, &credentials,
|
|
&ucred_length);
|
|
if (rc == -1) {
|
|
test_fail("getsockopt(SO_PEERCRED) failed");
|
|
} else if (credentials.cr_ngroups != 0 ||
|
|
credentials.cr_uid != geteuid() ||
|
|
credentials.cr_gid != getegid()) {
|
|
/* printf("%d=%d %d=%d %d=%d",credentials.cr_ngroups, 0,
|
|
credentials.cr_uid, geteuid(), credentials.cr_gid, getegid()); */
|
|
test_fail("Credential passing gave us the wrong cred");
|
|
}
|
|
|
|
rc = getpeereid(sv[0], &euid, &egid);
|
|
if (rc == -1) {
|
|
test_fail("getpeereid(sv[0], &euid, &egid) failed");
|
|
} else if (credentials.cr_uid != euid || credentials.cr_gid != egid) {
|
|
test_fail("getpeereid() didn't give the correct euid/egid");
|
|
}
|
|
|
|
CLOSE(sv[0]);
|
|
CLOSE(sv[1]);
|
|
}
|
|
|
|
static void callback_check_sockaddr(const struct sockaddr *sockaddr,
|
|
socklen_t sockaddrlen, const char *callname, int addridx) {
|
|
char buf[256];
|
|
const char *path;
|
|
const struct sockaddr_un *sockaddr_un =
|
|
(const struct sockaddr_un *) sockaddr;
|
|
|
|
switch (addridx) {
|
|
case 1: path = TEST_SUN_PATH; break;
|
|
case 2: path = TEST_SUN_PATHB; break;
|
|
default:
|
|
fprintf(stderr, "error: invalid addridx %d in "
|
|
"callback_check_sockaddr\n", addridx);
|
|
abort();
|
|
}
|
|
|
|
if (!(sockaddr_un->sun_family == AF_UNIX &&
|
|
strncmp(sockaddr_un->sun_path,
|
|
fullpath(path),
|
|
sizeof(sockaddr_un->sun_path) - 1) == 0)) {
|
|
|
|
snprintf(buf, sizeof(buf), "%s() didn't return the right addr",
|
|
callname);
|
|
test_fail(buf);
|
|
fprintf(stderr, "exp: '%s' | got: '%s'\n", path,
|
|
sockaddr_un->sun_path);
|
|
}
|
|
}
|
|
|
|
static void callback_cleanup(void) {
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SUN_PATHB);
|
|
UNLINK(TEST_SYM_A);
|
|
UNLINK(TEST_SYM_B);
|
|
}
|
|
|
|
static void test_bind_unix(void)
|
|
{
|
|
struct sockaddr_un addr;
|
|
int sd;
|
|
int rc;
|
|
|
|
debug("entering test_bind_unix()");
|
|
UNLINK(TEST_SUN_PATH);
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
|
|
debug("Test bind() with an empty sun_path");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
memset(addr.sun_path, '\0', sizeof(addr.sun_path));
|
|
errno = 0;
|
|
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (!(rc == -1 && errno == ENOENT)) {
|
|
test_fail("bind() should have failed with ENOENT");
|
|
}
|
|
CLOSE(sd);
|
|
|
|
debug("Test bind() using a symlink loop");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SYM_A);
|
|
UNLINK(TEST_SYM_B);
|
|
|
|
SYMLINK(TEST_SYM_A, TEST_SYM_B);
|
|
SYMLINK(TEST_SYM_B, TEST_SYM_A);
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1);
|
|
errno = 0;
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (!((rc == -1) && (errno == ELOOP))) {
|
|
test_fail("bind() should have failed with ELOOP");
|
|
}
|
|
CLOSE(sd);
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SYM_A);
|
|
UNLINK(TEST_SYM_B);
|
|
|
|
/* Test bind with garbage in sockaddr_un */
|
|
memset(&addr, '?', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
addr.sun_path[0] = 'f';
|
|
addr.sun_path[1] = 'o';
|
|
addr.sun_path[2] = 'o';
|
|
addr.sun_path[3] = '\0';
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
rc = bind(sd, (struct sockaddr *) &addr, strlen(addr.sun_path) + 1);
|
|
if (rc == -1) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
CLOSE(sd);
|
|
UNLINK("foo");
|
|
|
|
debug("leaving test_bind_unix()");
|
|
}
|
|
|
|
static void callback_xfer_prepclient(void) {
|
|
debug("Creating symlink to TEST_SUN_PATH");
|
|
|
|
SYMLINK(TEST_SUN_PATH, TEST_SYM_A);
|
|
}
|
|
|
|
static void callback_xfer_peercred(int sd) {
|
|
struct uucred credentials;
|
|
int rc;
|
|
socklen_t ucred_length;
|
|
|
|
ucred_length = sizeof(struct uucred);
|
|
|
|
debug("Test passing the client credentials to the server");
|
|
|
|
memset(&credentials, '\0', ucred_length);
|
|
rc = getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &credentials,
|
|
&ucred_length);
|
|
|
|
if (rc == -1) {
|
|
test_fail("[client] getsockopt() failed");
|
|
} else if (credentials.cr_uid != geteuid() ||
|
|
credentials.cr_gid != getegid()) {
|
|
printf("%d=%d=%d %d=%d=%d\n", credentials.cr_uid, getuid(),
|
|
geteuid(), credentials.cr_gid, getgid(), getegid());
|
|
test_fail("[client] Credential passing gave us a bad UID/GID");
|
|
}
|
|
}
|
|
|
|
static void test_vectorio(int type)
|
|
{
|
|
int sv[2];
|
|
int rc;
|
|
struct iovec iov[3];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE*3];
|
|
const struct iovec *iovp = iov;
|
|
|
|
debug("begin vectorio tests");
|
|
|
|
memset(buf1, '\0', BUFSIZE);
|
|
strncpy(buf1, "HELLO ", BUFSIZE - 1);
|
|
|
|
memset(buf2, '\0', BUFSIZE);
|
|
strncpy(buf2, "WORLD", BUFSIZE - 1);
|
|
|
|
memset(buf3, '\0', BUFSIZE);
|
|
|
|
rc = socketpair(PF_UNIX, type, 0, sv);
|
|
if (rc == -1) {
|
|
test_fail("socketpair");
|
|
}
|
|
|
|
iov[0].iov_base = buf1;
|
|
iov[0].iov_len = strlen(buf1);
|
|
iov[1].iov_base = buf2;
|
|
iov[1].iov_len = strlen(buf2);
|
|
iov[2].iov_base = buf3;
|
|
iov[2].iov_len = 1;
|
|
|
|
rc = writev(sv[0], iovp, 3);
|
|
if (rc == -1) {
|
|
test_fail("writev");
|
|
}
|
|
|
|
memset(buf4, '\0', BUFSIZE*3);
|
|
|
|
rc = read(sv[1], buf4, BUFSIZE*3);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
}
|
|
|
|
if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) {
|
|
test_fail("the string we read was not 'HELLO WORLD'");
|
|
}
|
|
|
|
memset(buf1, '\0', BUFSIZE);
|
|
strncpy(buf1, "Unit Test Time", BUFSIZE - 1);
|
|
|
|
rc = write(sv[1], buf1, strlen(buf1) + 1);
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
}
|
|
|
|
memset(buf2, '\0', BUFSIZE);
|
|
memset(buf3, '\0', BUFSIZE);
|
|
memset(buf4, '\0', BUFSIZE*3);
|
|
|
|
iov[0].iov_base = buf2;
|
|
iov[0].iov_len = 5;
|
|
iov[1].iov_base = buf3;
|
|
iov[1].iov_len = 5;
|
|
iov[2].iov_base = buf4;
|
|
iov[2].iov_len = 32;
|
|
|
|
rc = readv(sv[0], iovp, 3);
|
|
if (rc == -1) {
|
|
test_fail("readv");
|
|
}
|
|
|
|
if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) ||
|
|
strncmp(buf4, "Time", 4)) {
|
|
test_fail("readv");
|
|
}
|
|
|
|
rc = close(sv[0]);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
rc = close(sv[1]);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
debug("done vector io tests");
|
|
}
|
|
|
|
static void test_msg(int type)
|
|
{
|
|
int sv[2];
|
|
int rc;
|
|
struct msghdr msg1;
|
|
struct msghdr msg2;
|
|
struct iovec iov[3];
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char buf4[BUFSIZE*3];
|
|
|
|
debug("begin sendmsg/recvmsg tests");
|
|
|
|
memset(buf1, '\0', BUFSIZE);
|
|
strncpy(buf1, "HELLO ", BUFSIZE - 1);
|
|
|
|
memset(buf2, '\0', BUFSIZE);
|
|
strncpy(buf2, "WORLD", BUFSIZE - 1);
|
|
|
|
memset(buf3, '\0', BUFSIZE);
|
|
|
|
rc = socketpair(PF_UNIX, type, 0, sv);
|
|
if (rc == -1) {
|
|
test_fail("socketpair");
|
|
}
|
|
|
|
iov[0].iov_base = buf1;
|
|
iov[0].iov_len = strlen(buf1);
|
|
iov[1].iov_base = buf2;
|
|
iov[1].iov_len = strlen(buf2);
|
|
iov[2].iov_base = buf3;
|
|
iov[2].iov_len = 1;
|
|
|
|
memset(&msg1, '\0', sizeof(struct msghdr));
|
|
msg1.msg_name = NULL;
|
|
msg1.msg_namelen = 0;
|
|
msg1.msg_iov = iov;
|
|
msg1.msg_iovlen = 3;
|
|
msg1.msg_control = NULL;
|
|
msg1.msg_controllen = 0;
|
|
msg1.msg_flags = 0;
|
|
|
|
rc = sendmsg(sv[0], &msg1, 0);
|
|
if (rc == -1) {
|
|
test_fail("writev");
|
|
}
|
|
|
|
memset(buf4, '\0', BUFSIZE*3);
|
|
|
|
rc = read(sv[1], buf4, BUFSIZE*3);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
}
|
|
|
|
if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) {
|
|
test_fail("the string we read was not 'HELLO WORLD'");
|
|
}
|
|
|
|
memset(buf1, '\0', BUFSIZE);
|
|
strncpy(buf1, "Unit Test Time", BUFSIZE - 1);
|
|
|
|
rc = write(sv[1], buf1, strlen(buf1) + 1);
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
}
|
|
|
|
memset(buf2, '\0', BUFSIZE);
|
|
memset(buf3, '\0', BUFSIZE);
|
|
memset(buf4, '\0', BUFSIZE*3);
|
|
|
|
iov[0].iov_base = buf2;
|
|
iov[0].iov_len = 5;
|
|
iov[1].iov_base = buf3;
|
|
iov[1].iov_len = 5;
|
|
iov[2].iov_base = buf4;
|
|
iov[2].iov_len = 32;
|
|
|
|
memset(&msg2, '\0', sizeof(struct msghdr));
|
|
msg2.msg_name = NULL;
|
|
msg2.msg_namelen = 0;
|
|
msg2.msg_iov = iov;
|
|
msg2.msg_iovlen = 3;
|
|
msg2.msg_control = NULL;
|
|
msg2.msg_controllen = 0;
|
|
msg2.msg_flags = 0;
|
|
|
|
rc = recvmsg(sv[0], &msg2, 0);
|
|
if (rc == -1) {
|
|
test_fail("readv");
|
|
}
|
|
|
|
if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) ||
|
|
strncmp(buf4, "Time", 4)) {
|
|
test_fail("readv");
|
|
}
|
|
|
|
rc = close(sv[0]);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
rc = close(sv[1]);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
}
|
|
|
|
static void test_scm_credentials(void)
|
|
{
|
|
int rc;
|
|
int src;
|
|
int dst;
|
|
struct uucred cred;
|
|
struct cmsghdr *cmsg = NULL;
|
|
struct sockaddr_un addr;
|
|
struct iovec iov[3];
|
|
struct msghdr msg1;
|
|
struct msghdr msg2;
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
char ctrl[BUFSIZE];
|
|
socklen_t addrlen = sizeof(struct sockaddr_un);
|
|
|
|
debug("test_scm_credentials");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SUN_PATHB);
|
|
|
|
debug("creating src socket");
|
|
|
|
src = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
if (src == -1) {
|
|
test_fail("socket");
|
|
}
|
|
|
|
debug("creating dst socket");
|
|
|
|
dst = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
if (dst == -1) {
|
|
test_fail("socket");
|
|
}
|
|
|
|
debug("binding src socket");
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, TEST_SUN_PATHB, sizeof(addr.sun_path) - 1);
|
|
rc = bind(src, (struct sockaddr *) &addr, addrlen);
|
|
if (rc == -1) {
|
|
test_fail("bind");
|
|
}
|
|
|
|
debug("binding dst socket");
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
|
|
rc = bind(dst, (struct sockaddr *) &addr, addrlen);
|
|
if (rc == -1) {
|
|
test_fail("bind");
|
|
}
|
|
|
|
memset(&buf1, '\0', BUFSIZE);
|
|
memset(&buf2, '\0', BUFSIZE);
|
|
memset(&buf3, '\0', BUFSIZE);
|
|
memset(&ctrl, '\0', BUFSIZE);
|
|
|
|
strncpy(buf1, "Minix ", BUFSIZE-1);
|
|
strncpy(buf2, "is ", BUFSIZE-1);
|
|
strncpy(buf3, "great!", BUFSIZE-1);
|
|
|
|
iov[0].iov_base = buf1;
|
|
iov[0].iov_len = 6;
|
|
iov[1].iov_base = buf2;
|
|
iov[1].iov_len = 3;
|
|
iov[2].iov_base = buf3;
|
|
iov[2].iov_len = 32;
|
|
|
|
memset(&msg1, '\0', sizeof(struct msghdr));
|
|
msg1.msg_name = &addr;
|
|
msg1.msg_namelen = addrlen;
|
|
msg1.msg_iov = iov;
|
|
msg1.msg_iovlen = 3;
|
|
msg1.msg_control = NULL;
|
|
msg1.msg_controllen = 0;
|
|
msg1.msg_flags = 0;
|
|
|
|
debug("sending msg1");
|
|
|
|
rc = sendmsg(src, &msg1, 0);
|
|
if (rc == -1) {
|
|
test_fail("sendmsg");
|
|
}
|
|
|
|
memset(&buf1, '\0', BUFSIZE);
|
|
memset(&buf2, '\0', BUFSIZE);
|
|
memset(&buf3, '\0', BUFSIZE);
|
|
memset(&ctrl, '\0', BUFSIZE);
|
|
|
|
iov[0].iov_base = buf1;
|
|
iov[0].iov_len = 9;
|
|
iov[1].iov_base = buf2;
|
|
iov[1].iov_len = 32;
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
memset(&msg2, '\0', sizeof(struct msghdr));
|
|
msg2.msg_name = &addr;
|
|
msg2.msg_namelen = sizeof(struct sockaddr_un);
|
|
msg2.msg_iov = iov;
|
|
msg2.msg_iovlen = 2;
|
|
msg2.msg_control = ctrl;
|
|
msg2.msg_controllen = BUFSIZE;
|
|
msg2.msg_flags = 0;
|
|
|
|
debug("recv msg2");
|
|
|
|
rc = recvmsg(dst, &msg2, 0);
|
|
if (rc == -1) {
|
|
test_fail("recvmsg");
|
|
}
|
|
|
|
debug("checking results");
|
|
|
|
if (strncmp(buf1, "Minix is ", 9) || strncmp(buf2, "great!", 6)) {
|
|
test_fail("recvmsg");
|
|
}
|
|
|
|
/* we need to use the full path "/usr/src/test/DIR_56/testb.sock"
|
|
* because that is what is returned by recvmsg().
|
|
*/
|
|
if (addr.sun_family != AF_UNIX || strcmp(addr.sun_path,
|
|
fullpath(TEST_SUN_PATHB))) {
|
|
test_fail("recvmsg");
|
|
}
|
|
|
|
debug("looking for credentials");
|
|
|
|
memset(&cred, '\0', sizeof(struct uucred));
|
|
for (cmsg = CMSG_FIRSTHDR(&msg2); cmsg != NULL;
|
|
cmsg = CMSG_NXTHDR(&msg2, cmsg)) {
|
|
|
|
if (cmsg->cmsg_level == SOL_SOCKET &&
|
|
cmsg->cmsg_type == SCM_CREDS) {
|
|
|
|
memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct uucred));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cred.cr_ngroups != 0 || cred.cr_uid != geteuid() ||
|
|
cred.cr_gid != getegid()) {
|
|
|
|
test_fail("did no receive the proper credentials");
|
|
}
|
|
|
|
rc = close(dst);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
rc = close(src);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SUN_PATHB);
|
|
}
|
|
|
|
static void test_connect(const struct socket_test_info *info)
|
|
{
|
|
int i, sd, sds[2], rc;
|
|
|
|
/* connect() is already tested throughout test56, but
|
|
* in most cases the client and server end up on /dev/uds
|
|
* minor 0 and minor 1. This test opens some sockets first and
|
|
* then calls test_simple_client_server(). This forces the
|
|
* client and server minor numbers higher in the descriptor table.
|
|
*/
|
|
|
|
debug("starting test_connect()");
|
|
|
|
sd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
|
if (sd == -1) {
|
|
test_fail("couldn't create a socket");
|
|
}
|
|
|
|
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sds);
|
|
if (rc == -1) {
|
|
test_fail("couldn't create a socketpair");
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
test_simple_client_server(info, types[i]);
|
|
}
|
|
|
|
rc = close(sds[1]);
|
|
if (rc == -1) {
|
|
test_fail("close() failed");
|
|
}
|
|
|
|
rc = close(sds[0]);
|
|
if (rc == -1) {
|
|
test_fail("close() failed");
|
|
}
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("close() failed");
|
|
}
|
|
|
|
debug("exiting test_connect()");
|
|
}
|
|
|
|
static int test_multiproc_read(void)
|
|
{
|
|
/* test that when we fork() a process with an open socket descriptor,
|
|
* the descriptor in each process points to the same thing.
|
|
*/
|
|
|
|
pid_t pid;
|
|
int sds[2];
|
|
int rc, status;
|
|
char buf[3];
|
|
|
|
debug("entering test_multiproc_read()");
|
|
|
|
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sds);
|
|
if (rc == -1) {
|
|
test_fail("socketpair");
|
|
return 1;
|
|
}
|
|
|
|
memset(buf, '\0', 3);
|
|
|
|
|
|
/* the signal handler is only used by the client, but we have to
|
|
* install it now. if we don't the server may signal the client
|
|
* before the handler is installed.
|
|
*/
|
|
debug("installing signal handler");
|
|
if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
|
|
test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
|
|
return 1;
|
|
}
|
|
|
|
debug("signal handler installed");
|
|
|
|
server_ready = 0;
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1) {
|
|
|
|
test_fail("fork");
|
|
return 1;
|
|
|
|
} else if (pid == 0) {
|
|
|
|
while (server_ready == 0) {
|
|
debug("waiting for SIGUSR1 from parent");
|
|
sleep(1);
|
|
}
|
|
|
|
rc = read(sds[1], buf, 2);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
exit(1);
|
|
}
|
|
|
|
if (!(buf[0] == 'X' && buf[1] == '3')) {
|
|
test_fail("Didn't read X3");
|
|
exit(1);
|
|
}
|
|
|
|
exit(0);
|
|
} else {
|
|
|
|
rc = write(sds[0], "MNX3", 4);
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
}
|
|
|
|
rc = read(sds[1], buf, 2);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
}
|
|
|
|
if (!(buf[0] == 'M' && buf[1] == 'N')) {
|
|
test_fail("Didn't read MN");
|
|
}
|
|
|
|
/* time to tell the client to start the test */
|
|
kill(pid, SIGUSR1);
|
|
|
|
do {
|
|
rc = waitpid(pid, &status, 0);
|
|
} while (rc == -1 && errno == EINTR);
|
|
|
|
/* we use the exit status to get its error count */
|
|
errct += WEXITSTATUS(status);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int test_multiproc_write(void)
|
|
{
|
|
/* test that when we fork() a process with an open socket descriptor,
|
|
* the descriptor in each process points to the same thing.
|
|
*/
|
|
|
|
pid_t pid;
|
|
int sds[2];
|
|
int rc, status;
|
|
char buf[7];
|
|
|
|
debug("entering test_multiproc_write()");
|
|
|
|
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sds);
|
|
if (rc == -1) {
|
|
test_fail("socketpair");
|
|
return 1;
|
|
}
|
|
|
|
memset(buf, '\0', 7);
|
|
|
|
|
|
/* the signal handler is only used by the client, but we have to
|
|
* install it now. if we don't the server may signal the client
|
|
* before the handler is installed.
|
|
*/
|
|
debug("installing signal handler");
|
|
if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
|
|
test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
|
|
return 1;
|
|
}
|
|
|
|
debug("signal handler installed");
|
|
|
|
server_ready = 0;
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1) {
|
|
|
|
test_fail("fork");
|
|
return 1;
|
|
|
|
} else if (pid == 0) {
|
|
|
|
while (server_ready == 0) {
|
|
debug("waiting for SIGUSR1 from parent");
|
|
sleep(1);
|
|
}
|
|
|
|
rc = write(sds[1], "IX3", 3);
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
exit(1);
|
|
}
|
|
|
|
rc = read(sds[0], buf, 6);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
exit(1);
|
|
}
|
|
|
|
if (strcmp(buf, "MINIX3") != 0) {
|
|
test_fail("didn't read MINIX3");
|
|
exit(1);
|
|
}
|
|
|
|
exit(0);
|
|
} else {
|
|
|
|
rc = write(sds[1], "MIN", 3);
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
}
|
|
|
|
/* time to tell the client to start the test */
|
|
kill(pid, SIGUSR1);
|
|
|
|
do {
|
|
rc = waitpid(pid, &status, 0);
|
|
} while (rc == -1 && errno == EINTR);
|
|
|
|
/* we use the exit status to get its error count */
|
|
errct += WEXITSTATUS(status);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void test_fd_passing_child(int sd)
|
|
{
|
|
int fd, rc;
|
|
char x = 'x';
|
|
struct msghdr msghdr;
|
|
struct cmsghdr *cmsg;
|
|
struct iovec iov;
|
|
char buf[BUFSIZE];
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
|
|
fd = open(TEST_TXT_FILE, O_CREAT|O_TRUNC|O_RDWR);
|
|
if (fd == -1) {
|
|
test_fail("could not open test.txt");
|
|
}
|
|
|
|
msghdr.msg_name = NULL;
|
|
msghdr.msg_namelen = 0;
|
|
|
|
iov.iov_base = &x;
|
|
iov.iov_len = 1;
|
|
msghdr.msg_iov = &iov;
|
|
msghdr.msg_iovlen = 1;
|
|
|
|
msghdr.msg_control = buf;
|
|
msghdr.msg_controllen = CMSG_SPACE(sizeof(int));
|
|
|
|
msghdr.msg_flags = 0;
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msghdr);
|
|
cmsg->cmsg_len = CMSG_SPACE(sizeof(int));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
((int *) CMSG_DATA(cmsg))[0] = fd;
|
|
|
|
rc = sendmsg(sd, &msghdr, 0);
|
|
if (rc == -1) {
|
|
test_fail("could not send message");
|
|
}
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
rc = read(sd, buf, BUFSIZE);
|
|
if (rc == -1) {
|
|
test_fail("could not read from socket");
|
|
}
|
|
|
|
if (strcmp(buf, "done") != 0) {
|
|
test_fail("we didn't read the right message");
|
|
}
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
rc = lseek(fd, 0, SEEK_SET);
|
|
if (rc == -1) {
|
|
test_fail("could not seek to start of test.txt");
|
|
}
|
|
|
|
rc = read(fd, buf, BUFSIZE);
|
|
if (rc == -1) {
|
|
test_fail("could not read from test.txt");
|
|
}
|
|
|
|
if (strcmp(buf, MSG) != 0) {
|
|
test_fail("other process didn't write MSG to test.txt");
|
|
}
|
|
|
|
rc = close(fd);
|
|
if (rc == -1) {
|
|
test_fail("could not close test.txt");
|
|
}
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("could not close socket");
|
|
}
|
|
|
|
rc = unlink(TEST_TXT_FILE);
|
|
if (rc == -1) {
|
|
test_fail("could not unlink test.txt");
|
|
}
|
|
|
|
exit(errct);
|
|
}
|
|
|
|
static void test_fd_passing_parent(int sd)
|
|
{
|
|
int rc, fd;
|
|
char x;
|
|
struct msghdr msghdr;
|
|
struct cmsghdr *cmsg;
|
|
struct iovec iov;
|
|
char buf[BUFSIZE];
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
|
|
msghdr.msg_name = NULL;
|
|
msghdr.msg_namelen = 0;
|
|
|
|
iov.iov_base = &x;
|
|
iov.iov_len = 1;
|
|
msghdr.msg_iov = &iov;
|
|
msghdr.msg_iovlen = 1;
|
|
|
|
msghdr.msg_iov = &iov;
|
|
msghdr.msg_iovlen = 1;
|
|
|
|
msghdr.msg_control = buf;
|
|
msghdr.msg_controllen = BUFSIZE;
|
|
|
|
msghdr.msg_flags = 0;
|
|
|
|
rc = recvmsg(sd, &msghdr, 0);
|
|
if (rc == -1) {
|
|
test_fail("could not recv message.");
|
|
}
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msghdr);
|
|
fd = ((int *) CMSG_DATA(cmsg))[0];
|
|
|
|
rc = write(fd, MSG, strlen(MSG));
|
|
if (rc != strlen(MSG)) {
|
|
test_fail("could not write the full message to test.txt");
|
|
}
|
|
|
|
rc = close(fd);
|
|
if (rc == -1) {
|
|
test_fail("could not close test.txt");
|
|
}
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
strcpy(buf, "done");
|
|
rc = write(sd, buf, BUFSIZE);
|
|
if (rc == -1) {
|
|
test_fail("could not write to socket");
|
|
}
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("could not close socket");
|
|
}
|
|
}
|
|
|
|
static void test_permissions(void) {
|
|
/* Test bind and connect for permission verification
|
|
*
|
|
* After creating a UDS socket we change user credentials. At that
|
|
* point we should not be allowed to bind or connect to the UDS socket
|
|
*/
|
|
|
|
pid_t pid;
|
|
int sd, rc, status;
|
|
struct sockaddr_un addr;
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
pid = fork();
|
|
if (pid < 0) test_fail("unable to fork");
|
|
else if (pid == 0) {
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
if (setuid(999) != 0) test_fail("unable to chance uid");
|
|
rc = bind(sd, (struct sockaddr *) &addr,
|
|
sizeof(struct sockaddr_un));
|
|
if (rc != -1) {
|
|
test_fail("bind() should not have worked");
|
|
}
|
|
exit(errct);
|
|
} else {
|
|
rc = waitpid(pid, &status, 0);
|
|
errct += WEXITSTATUS(status);
|
|
}
|
|
|
|
/* the signal handler is only used by the client, but we have to
|
|
* install it now. if we don't the server may signal the client
|
|
* before the handler is installed.
|
|
*/
|
|
debug("installing signal handler");
|
|
if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) {
|
|
test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed");
|
|
}
|
|
|
|
debug("signal handler installed");
|
|
|
|
server_ready = 0;
|
|
|
|
pid = fork();
|
|
if (pid < 0) test_fail("unable to fork");
|
|
else if (pid == 0) {
|
|
while (server_ready == 0) {
|
|
debug("[client] waiting for the server to signal");
|
|
sleep(1);
|
|
}
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
if (setuid(999) != 0) test_fail("unable to chance uid");
|
|
rc = connect(sd, (struct sockaddr *) &addr,
|
|
sizeof(struct sockaddr_un));
|
|
if (rc != -1)
|
|
test_fail("connect should not have worked");
|
|
exit(errct);
|
|
} else {
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
rc = bind(sd, (struct sockaddr *) &addr,
|
|
sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
|
|
rc = listen(sd, 8);
|
|
if (rc == -1) {
|
|
test_fail("listen(sd, 8) should have worked");
|
|
}
|
|
kill(pid, SIGUSR1);
|
|
sleep(1);
|
|
CLOSE(sd);
|
|
|
|
rc = waitpid(pid, &status, 0);
|
|
errct += WEXITSTATUS(status);
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
}
|
|
|
|
static void test_fd_passing(void) {
|
|
int status;
|
|
int sv[2];
|
|
pid_t pid;
|
|
int rc;
|
|
|
|
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
|
if (rc == -1) {
|
|
test_fail("socketpair failed");
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid == -1) {
|
|
test_fail("fork() failed");
|
|
|
|
rc = close(sv[0]);
|
|
if (rc == -1) {
|
|
test_fail("could not close sv[0]");
|
|
}
|
|
|
|
rc = close(sv[1]);
|
|
if (rc == -1) {
|
|
test_fail("could not close sv[1]");
|
|
}
|
|
|
|
exit(0);
|
|
} else if (pid == 0) {
|
|
rc = close(sv[0]);
|
|
if (rc == -1) {
|
|
test_fail("could not close sv[0]");
|
|
}
|
|
|
|
test_fd_passing_child(sv[1]);
|
|
test_fail("should never get here");
|
|
exit(1);
|
|
} else {
|
|
rc = close(sv[1]);
|
|
if (rc == -1) {
|
|
test_fail("could not close sv[1]");
|
|
}
|
|
|
|
test_fd_passing_parent(sv[0]);
|
|
|
|
/* wait for client to exit */
|
|
do {
|
|
errno = 0;
|
|
rc = waitpid(pid, &status, 0);
|
|
} while (rc == -1 && errno == EINTR);
|
|
|
|
/* we use the exit status to get its error count */
|
|
errct += WEXITSTATUS(status);
|
|
}
|
|
}
|
|
|
|
static void test_select(void)
|
|
{
|
|
int nfds = -1;
|
|
int socks[2];
|
|
fd_set readfds, writefds;
|
|
struct timeval tv;
|
|
int res = 0;
|
|
char buf[1];
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_ZERO(&writefds);
|
|
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0; /* 2 sec time out */
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) {
|
|
test_fail("Can't open socket pair.");
|
|
}
|
|
FD_SET(socks[0], &readfds);
|
|
nfds = socks[0] + 1;
|
|
|
|
/* Close the write end of the socket to generate EOF on read end */
|
|
if ((res = shutdown(socks[1], SHUT_WR)) != 0) {
|
|
test_fail("shutdown failed\n");
|
|
}
|
|
|
|
res = select(nfds, &readfds, NULL, NULL, &tv);
|
|
if (res != 1) {
|
|
test_fail("select should've returned 1 ready fd\n");
|
|
}
|
|
if (!(FD_ISSET(socks[0], &readfds))) {
|
|
test_fail("The server didn't respond within 2 seconds");
|
|
}
|
|
/* Now try to read from empty, closed pipe */
|
|
if (read(socks[0], buf, sizeof(buf)) != 0) {
|
|
test_fail("reading from empty, closed pipe should return EOF");
|
|
}
|
|
|
|
close(socks[0]);
|
|
|
|
/* Try again the other way around: create a socketpair, close the
|
|
* read end, and try to write. This should cause an EPIPE */
|
|
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0; /* 2 sec time out */
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) {
|
|
test_fail("Can't open socket pair.");
|
|
}
|
|
FD_SET(socks[1], &writefds);
|
|
nfds = socks[1] + 1;
|
|
|
|
/* kill the read end of the socket to generate EPIPE on write end */
|
|
if ((res = shutdown(socks[0], SHUT_RD)) != 0) {
|
|
test_fail("shutdown failed\n");
|
|
}
|
|
|
|
res = select(nfds, NULL, &writefds, NULL, &tv);
|
|
if (res != 1) {
|
|
test_fail("select should've returned 1 ready fd\n");
|
|
}
|
|
if (!(FD_ISSET(socks[1], &writefds))) {
|
|
test_fail("The server didn't respond within 2 seconds");
|
|
}
|
|
|
|
/* Now try to write to closed pipe */
|
|
errno = 0;
|
|
if ((res = write(socks[1], buf, sizeof(buf))) != -1) {
|
|
printf("write res = %d\n", res);
|
|
test_fail("writing to empty, closed pipe should fail");
|
|
}
|
|
if (errno != EPIPE) {
|
|
printf("errno = %d\n", errno);
|
|
test_fail("writing to closed pipe should return EPIPE\n");
|
|
}
|
|
|
|
close(socks[1]);
|
|
}
|
|
|
|
static void test_select_close(void)
|
|
{
|
|
int res, socks[2];
|
|
fd_set readfds;
|
|
struct timeval tv;
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) {
|
|
test_fail("Can't open socket pair.");
|
|
}
|
|
|
|
switch (fork()) {
|
|
case 0:
|
|
sleep(1);
|
|
|
|
exit(0);
|
|
case -1:
|
|
test_fail("Can't fork.");
|
|
default:
|
|
break;
|
|
}
|
|
|
|
close(socks[1]);
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(socks[0], &readfds);
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0; /* 2 sec time out */
|
|
|
|
res = select(socks[0] + 1, &readfds, NULL, NULL, &tv);
|
|
if (res != 1) {
|
|
test_fail("select should've returned 1 ready fd\n");
|
|
}
|
|
if (!(FD_ISSET(socks[0], &readfds))) {
|
|
test_fail("The server didn't respond within 2 seconds");
|
|
}
|
|
|
|
wait(NULL);
|
|
|
|
close(socks[0]);
|
|
}
|
|
|
|
static void test_fchmod(void)
|
|
{
|
|
int socks[2];
|
|
struct stat st1, st2;
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) {
|
|
test_fail("Can't open socket pair.");
|
|
}
|
|
|
|
if (fstat(socks[0], &st1) < 0 || fstat(socks[1], &st2) < 0) {
|
|
test_fail("fstat failed.");
|
|
}
|
|
|
|
if ((st1.st_mode & (S_IRUSR|S_IWUSR)) == S_IRUSR &&
|
|
(st2.st_mode & (S_IRUSR|S_IWUSR)) == S_IWUSR) {
|
|
test_fail("fstat failed.");
|
|
}
|
|
|
|
if (fchmod(socks[0], S_IRUSR) < 0 ||
|
|
fstat(socks[0], &st1) < 0 ||
|
|
(st1.st_mode & (S_IRUSR|S_IWUSR)) != S_IRUSR) {
|
|
test_fail("fchmod/fstat mode set/check failed (1).");
|
|
}
|
|
|
|
if (fchmod(socks[1], S_IWUSR) < 0 || fstat(socks[1], &st2) < 0 ||
|
|
(st2.st_mode & (S_IRUSR|S_IWUSR)) != S_IWUSR) {
|
|
test_fail("fchmod/fstat mode set/check failed (2).");
|
|
}
|
|
|
|
close(socks[0]);
|
|
close(socks[1]);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
struct sockaddr_un clientaddr = {
|
|
.sun_family = AF_UNIX,
|
|
.sun_path = TEST_SUN_PATH,
|
|
};
|
|
struct sockaddr_un clientaddr2 = {
|
|
.sun_family = AF_UNIX,
|
|
.sun_path = TEST_SUN_PATHB,
|
|
};
|
|
struct sockaddr_un clientaddrsym = {
|
|
.sun_family = AF_UNIX,
|
|
.sun_path = TEST_SYM_A,
|
|
};
|
|
const struct socket_test_info info = {
|
|
.clientaddr = (struct sockaddr *) &clientaddr,
|
|
.clientaddrlen = sizeof(clientaddr),
|
|
.clientaddr2 = (struct sockaddr *) &clientaddr2,
|
|
.clientaddr2len = sizeof(clientaddr2),
|
|
.clientaddrsym = (struct sockaddr *) &clientaddrsym,
|
|
.clientaddrsymlen = sizeof(clientaddrsym),
|
|
.domain = PF_UNIX,
|
|
.expected_rcvbuf = PIPE_BUF,
|
|
.expected_sndbuf = PIPE_BUF,
|
|
.serveraddr = (struct sockaddr *) &clientaddr,
|
|
.serveraddrlen = sizeof(clientaddr),
|
|
.serveraddr2 = (struct sockaddr *) &clientaddr2,
|
|
.serveraddr2len = sizeof(clientaddr2),
|
|
.type = SOCK_STREAM,
|
|
.types = types,
|
|
.typecount = 3,
|
|
.callback_check_sockaddr = callback_check_sockaddr,
|
|
.callback_cleanup = callback_cleanup,
|
|
.callback_xfer_prepclient = callback_xfer_prepclient,
|
|
.callback_xfer_peercred = callback_xfer_peercred,
|
|
};
|
|
|
|
debug("entering main()");
|
|
|
|
start(56);
|
|
|
|
test_socket(&info);
|
|
test_bind(&info);
|
|
test_bind_unix();
|
|
test_listen(&info);
|
|
test_getsockname(&info);
|
|
test_header();
|
|
test_shutdown(&info);
|
|
test_close(&info);
|
|
test_permissions();
|
|
test_dup(&info);
|
|
test_dup2(&info);
|
|
test_socketpair();
|
|
test_shutdown(&info);
|
|
test_read(&info);
|
|
test_write(&info);
|
|
test_sockopts(&info);
|
|
test_ucred();
|
|
test_xfer(&info);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
test_simple_client_server(&info, types[i]);
|
|
if (types[i] != SOCK_DGRAM) test_vectorio(types[i]);
|
|
if (types[i] != SOCK_DGRAM) test_msg(types[i]);
|
|
}
|
|
|
|
test_abort_client_server(&info, 1);
|
|
test_abort_client_server(&info, 2);
|
|
test_msg_dgram(&info);
|
|
test_connect(&info);
|
|
test_multiproc_read();
|
|
test_multiproc_write();
|
|
test_scm_credentials();
|
|
test_fd_passing();
|
|
test_select();
|
|
test_select_close();
|
|
test_fchmod();
|
|
test_nonblock(&info);
|
|
test_connect_nb(&info);
|
|
test_intr(&info);
|
|
test_connect_close(&info);
|
|
test_listen_close(&info);
|
|
test_listen_close_nb(&info);
|
|
|
|
quit();
|
|
|
|
return -1; /* we should never get here */
|
|
}
|