minix/test/test56.c
Thomas Veerman a209c3ae12 Fix a ton of compiler warnings
This patch fixes most of current reasons to generate compiler warnings.
The changes consist of:
 - adding missing casts
 - hiding or unhiding function declarations
 - including headers where missing
 - add __UNCONST when assigning a const char * to a char *
 - adding missing return statements
 - changing some types from unsigned to signed, as the code seems to want
   signed ints
 - converting old-style function definitions to current style (i.e.,
   void func(param1, param2) short param1, param2; {...} to
   void func (short param1, short param2) {...})
 - making the compiler silent about signed vs unsigned comparisons. We
   have too many of those in the new libc to fix.

A number of bugs in the test set were fixed. These bugs were never
triggered with our old libc. Consequently, these tests are now forced to
link with the new libc or they will generate errors (in particular tests 43
and 55).

Most changes in NetBSD libc are limited to moving aroudn "#ifndef __minix"
or stuff related to Minix-specific things (code in sys-minix or gen/minix).
2011-11-14 10:07:49 +00:00

2572 lines
54 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()
*/
#define DEBUG 0
#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/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.
*/
#define MAX_ERROR 4
/* Use the common testing code instead of reinventing the wheel. */
#include "common.c"
/* 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"
/* buffer for send/recv */
#define BUFSIZE (128)
#define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S"
/* socket types supported */
int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM};
char sock_fullpath[PATH_MAX + 1];
/* timestamps for debug and error logs */
char *get_timestamp(void)
{
struct tm *tm;
time_t t;
size_t len;
char *s;
len = sizeof(char) * 32;
t = time(NULL);
if (t == -1) {
return NULL;
}
tm = gmtime(&t);
if (tm == NULL) {
return NULL;
}
s = (char *) malloc(len);
if (!s) {
perror("malloc");
return NULL;
}
memset(s, '\0', len);
strftime(s, len - 1, ISO8601_FORMAT, tm);
return s;
}
/* macro to display information about a failed test and increment the errct */
#define test_fail(msg) \
do { \
char *timestamp; \
timestamp = get_timestamp(); \
if (errct == 0) fprintf(stderr, "\n"); \
fprintf(stderr, "[ERROR][%s] (%s Line %d) %s [pid=%d:errno=%d:%s]\n", \
timestamp, __FILE__, __LINE__, msg, getpid(), \
errno, strerror(errno)); \
fflush(stderr); \
if (timestamp != NULL) { \
free(timestamp); \
timestamp = NULL; \
} \
errct++; \
if (errct++ > MAX_ERROR) { \
printf("Too many errors; test aborted\n"); \
quit(); \
exit(1); \
} \
} while (0)
/* Convert name to the full path of the socket. Assumes name is in cwd. */
char *fullpath(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);
}
#if DEBUG == 1
/* macros to display debugging information */
#define debug(msg) \
do { \
char *timestamp; \
timestamp = get_timestamp(); \
fprintf(stdout,"[DEBUG][%s] (%s:%d) %s [pid=%d]\n", \
timestamp, __FILE__, __LINE__, msg, getpid()); \
fflush(stdout); \
if (timestamp != NULL) { \
free(timestamp); \
timestamp = NULL; \
} \
} while (0)
#else
#define debug(msg) \
do { /* nothing */; } while (0)
#endif
#define SOCKET(sd,domain,type,protocol) \
do { \
errno = 0; \
sd = socket(domain, type, protocol); \
if (sd == -1) { \
test_fail("sd = socket(domain, type, protocol) failed");\
} \
} while (0)
#define UNLINK(path) \
do { \
int rc; \
errno = 0; \
rc = unlink(path); \
if (rc == -1 && errno != ENOENT) { \
test_fail("unlink(path) failed"); \
} \
} while(0)
#define SYMLINK(oldpath,newpath) \
do { \
int rc; \
errno = 0; \
rc = symlink(oldpath,newpath); \
if (rc == -1) { \
test_fail("symlink(oldpath,newpath) failed"); \
} \
} while(0)
#define CLOSE(sd) \
do { \
int rc; \
errno = 0; \
rc = close(sd); \
if (rc == -1) { \
test_fail("close(sd) failed"); \
} \
} while (0)
void test_socket(void)
{
struct stat statbuf, statbuf2;
int sd, sd2;
int rc;
int i;
debug("entering test_socket()");
debug("Test socket() with an unsupported address family");
errno = 0;
sd = socket(-1, SOCK_STREAM, 0);
if (!(sd == -1 && errno == EAFNOSUPPORT)) {
test_fail("socket");
if (sd != -1) {
CLOSE(sd);
}
}
debug("Test socket() with all available 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");
}
}
errno = 0;
sd = socket(PF_UNIX, SOCK_STREAM, 0);
if (!(sd == -1 && errno == EMFILE)) {
test_fail("socket() call with all fds open should fail");
if (sd != -1) {
CLOSE(sd);
}
}
for (i = 3; i < getdtablesize(); i++) {
CLOSE(i);
}
debug("Test socket() with an mismatched protocol");
errno = 0;
sd = socket(PF_UNIX, SOCK_STREAM, 4);
if (!(sd == -1 && errno == EPROTONOSUPPORT)) {
test_fail("socket() should fail with errno = EPROTONOSUPPORT");
if (sd != -1) {
CLOSE(sd);
}
}
debug("Test socket() success");
/*
* open 2 sockets at once and *then* close them.
* This will test that /dev/uds is cloning properly.
*/
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0);
rc = fstat(sd, &statbuf);
if (rc == -1) {
test_fail("fstat failed on sd");
}
rc = fstat(sd2, &statbuf2);
if (rc == -1) {
test_fail("fstat failed on sd2");
}
if (statbuf.st_dev == statbuf2.st_dev) {
test_fail("/dev/uds isn't being cloned");
}
CLOSE(sd2);
CLOSE(sd);
debug("leaving test_socket()");
}
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) != 4) {
test_fail("SUN_LEN(&sun) should be 4");
}
if (PF_UNIX != PF_LOCAL || PF_UNIX != PF_FILE || PF_UNIX != AF_UNIX) {
test_fail("PF_UNIX, PF_LOCAL, PF_FILE, and AF_UNIX");
}
}
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()");
}
void test_ucred(void)
{
struct ucred 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 ucred);
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.pid != getpid() ||
credentials.uid != geteuid() ||
credentials.gid != getegid()) {
/* printf("%d=%d %d=%d %d=%d",credentials.pid, getpid(),
credentials.uid, geteuid(), credentials.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.uid != euid || credentials.gid != egid) {
test_fail("getpeereid() didn't give the correct euid/egid");
}
CLOSE(sv[0]);
CLOSE(sv[1]);
}
void test_getsockname(void)
{
int sd;
int rc;
struct sockaddr_un addr, sock_addr;
socklen_t sock_addr_len;
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() success");
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");
}
debug("Test getsockname() success");
memset(&sock_addr, '\0', sizeof(struct sockaddr_un));
sock_addr_len = sizeof(struct sockaddr_un);
rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len);
if (rc == -1) {
test_fail("getsockname() should have worked");
}
if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path,
fullpath(TEST_SUN_PATH),
sizeof(sock_addr.sun_path) - 1) == 0)) {
test_fail("getsockname() did return the right address");
fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path,
sock_addr.sun_path);
}
CLOSE(sd);
}
void test_bind(void)
{
struct sockaddr_un addr;
struct sockaddr_un sock_addr;
socklen_t sock_addr_len;
int sd;
int sd2;
int rc;
int on;
debug("entering test_bind()");
on = 1;
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() success");
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");
}
debug("Test getsockname() success");
memset(&sock_addr, '\0', sizeof(struct sockaddr_un));
sock_addr_len = sizeof(struct sockaddr_un);
rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len);
if (rc == -1) {
test_fail("getsockname() should have worked");
}
if (!(sock_addr.sun_family == AF_UNIX &&
strncmp(sock_addr.sun_path,
fullpath(TEST_SUN_PATH),
sizeof(sock_addr.sun_path) - 1) == 0)) {
test_fail("getsockname() didn't return the right addr");
fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path,
sock_addr.sun_path);
}
debug("Test bind() with a address that has already been bind()'d");
SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0);
errno = 0;
rc = bind(sd2, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (!((rc == -1) && (errno == EADDRINUSE))) {
test_fail("bind() should have failed with EADDRINUSE");
}
CLOSE(sd2);
CLOSE(sd);
UNLINK(TEST_SUN_PATH);
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() with a NULL address");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
errno = 0;
rc = bind(sd, (struct sockaddr *) NULL, sizeof(struct sockaddr_un));
if (!((rc == -1) && (errno == EFAULT))) {
test_fail("bind() should have failed with EFAULT");
}
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);
debug("leaving test_bind()");
}
void test_listen(void)
{
int rc;
debug("entering test_listen()");
debug("Test listen() with a bad file descriptor");
errno = 0;
rc = listen(-1, 0);
if (!(rc == -1 && errno == EBADF)) {
test_fail("listen(-1, 0) should have failed");
}
debug("Test listen() with a non-socket file descriptor");
errno = 0;
rc = listen(0, 0);
if (!(rc == -1 && errno == ENOTTY)) {
test_fail("listen(0, 0) should have failed");
}
debug("leaving test_listen()");
}
void test_shutdown(void)
{
int how[3] = { SHUT_RD, SHUT_WR, SHUT_RDWR };
int sd;
int rc;
int i;
debug("entering test_shutdown()");
/* test for each direction (read, write, read-write) */
for (i = 0; i < 3; i++) {
debug("test shutdown() with an invalid descriptor");
errno = 0;
rc = shutdown(-1, how[i]);
if (!(rc == -1 && errno == EBADF)) {
test_fail("shutdown(-1, how[i]) should have failed");
}
debug("test shutdown() with a non-socket descriptor");
errno = 0;
rc = shutdown(0, how[i]);
if (!(rc == -1 && errno == ENOSYS)) {
test_fail("shutdown() should have failed with ENOSYS");
}
debug("test shutdown() with a socket that is not connected");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
errno = 0;
rc = shutdown(sd, how[i]);
if (!(rc == -1 && errno == ENOTCONN)) {
test_fail("shutdown() should have failed");
}
CLOSE(sd);
}
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
errno = 0;
rc = shutdown(sd, -1);
if (!(rc == -1 && errno == ENOTCONN)) {
test_fail("shutdown(sd, -1) should have failed with ENOTCONN");
}
CLOSE(sd);
debug("leaving test_shutdown()");
}
void test_close(void)
{
struct sockaddr_un addr;
int sd, sd2;
int rc, i;
debug("entering test_close()");
UNLINK(TEST_SUN_PATH);
memset(&addr, '\0', sizeof(struct sockaddr_un));
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
debug("Test close() success");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc != 0) {
test_fail("bind() should have worked");
}
CLOSE(sd);
debug("Close an already closed file descriptor");
errno = 0;
rc = close(sd);
if (!(rc == -1 && errno == EBADF)) {
test_fail("close(sd) should have failed with EBADF");
}
UNLINK(TEST_SUN_PATH);
debug("dup()'ing a file descriptor and closing both should work");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc != 0) {
test_fail("bind() should have worked");
}
errno = 0;
sd2 = dup(sd);
if (sd2 == -1) {
test_fail("dup(sd) should have worked");
} else {
CLOSE(sd2);
CLOSE(sd);
}
UNLINK(TEST_SUN_PATH);
/* Create and close a socket a bunch of times.
* If the implementation doesn't properly free the
* socket during close(), eventually socket() will
* fail when the internal descriptor table is full.
*/
for (i = 0; i < 1024; i++) {
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
CLOSE(sd);
}
debug("leaving test_close()");
}
void test_sockopts(void)
{
int i;
int rc;
int sd;
int option_value;
socklen_t option_len;
debug("entering test_sockopts()");
for (i = 0; i < 3; i++) {
SOCKET(sd, PF_UNIX, types[i], 0);
debug("Test setsockopt() works");
option_value = 0;
option_len = sizeof(option_value);
errno = 0;
rc = getsockopt(sd, SOL_SOCKET, SO_TYPE, &option_value,
&option_len);
if (rc != 0) {
test_fail("setsockopt() should have worked");
}
if (option_value != types[i]) {
test_fail("SO_TYPE didn't seem to work.");
}
CLOSE(sd);
}
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
debug("Test setsockopt() works");
option_value = 0;
option_len = sizeof(option_value);
errno = 0;
rc = getsockopt(sd, SOL_SOCKET, SO_SNDBUF, &option_value, &option_len);
if (rc != 0) {
test_fail("getsockopt() should have worked");
}
if (option_value != PIPE_BUF) {
test_fail("SO_SNDBUF didn't seem to work.");
}
CLOSE(sd);
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
debug("Test setsockopt() works");
option_value = 0;
option_len = sizeof(option_value);
errno = 0;
rc = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &option_value, &option_len);
if (rc != 0) {
test_fail("getsockopt() should have worked");
}
if (option_value != PIPE_BUF) {
test_fail("SO_RCVBUF didn't seem to work.");
}
CLOSE(sd);
debug("leaving test_sockopts()");
}
void test_read(void)
{
int rc;
int fd;
char buf[BUFSIZE];
debug("entering test_read()");
errno = 0;
rc = read(-1, buf, sizeof(buf));
if (!(rc == -1 && errno == EBADF)) {
test_fail("read() should have failed with EBADF");
}
fd = open("/tmp", O_RDONLY);
if (fd == -1) {
test_fail("open(\"/tmp\", O_RDONLY) should have worked");
}
CLOSE(fd);
debug("leaving test_read()");
}
void test_write(void)
{
int rc;
char buf[BUFSIZE];
debug("entering test_write()");
errno = 0;
rc = write(-1, buf, sizeof(buf));
if (!(rc == -1 && errno == EBADF)) {
test_fail("write() should have failed with EBADF");
}
debug("leaving test_write()");
}
void test_dup(void)
{
struct stat info1;
struct stat info2;
struct sockaddr_un addr;
int sd, sd2;
int rc;
int i;
debug("entering test_dup()");
UNLINK(TEST_SUN_PATH);
memset(&addr, '\0', sizeof(struct sockaddr_un));
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
debug("Test dup()");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc != 0) {
test_fail("bind() should have worked");
}
errno = 0;
sd2 = dup(sd);
if (sd2 == -1) {
test_fail("dup(sd) should have worked");
}
rc = fstat(sd, &info1);
if (rc == -1) {
test_fail("fstat(fd, &info1) failed");
}
rc = fstat(sd2, &info2);
if (rc == -1) {
test_fail("fstat(sd, &info2) failed");
}
if (info1.st_ino != info2.st_ino) {
test_fail("dup() failed info1.st_ino != info2.st_ino");
}
CLOSE(sd);
CLOSE(sd2);
debug("Test dup() with a closed socket");
errno = 0;
rc = dup(sd);
if (!(rc == -1 && errno == EBADF)) {
test_fail("dup(sd) on a closed socket shouldn't have worked");
}
debug("Test dup() with socket descriptor of -1");
errno = 0;
rc = dup(-1);
if (!(rc == -1 && errno == EBADF)) {
test_fail("dup(-1) shouldn't have worked");
}
debug("Test dup() when all of the file descriptors are taken");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
for (i = 4; i < getdtablesize(); i++) {
rc = open("/dev/null", O_RDONLY);
if (rc == -1) {
test_fail("we couldn't open /dev/null for read");
}
}
errno = 0;
sd2 = dup(sd);
if (!(sd2 == -1 && errno == EMFILE)) {
test_fail("dup(sd) should have failed with errno = EMFILE");
}
for (i = 3; i < getdtablesize(); i++) {
CLOSE(i);
}
UNLINK(TEST_SUN_PATH);
debug("leaving test_dup()");
}
void test_dup2(void)
{
struct stat info1;
struct stat info2;
struct sockaddr_un addr;
int sd;
int fd;
int rc;
debug("entering test_dup2()");
UNLINK(TEST_SUN_PATH);
memset(&addr, '\0', sizeof(struct sockaddr_un));
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc != 0) {
test_fail("bind() should have worked");
}
fd = open("/dev/null", O_RDONLY);
if (fd == -1) {
test_fail("open(\"/dev/null\", O_RDONLY) failed");
}
fd = dup2(sd, fd);
if (fd == -1) {
test_fail("dup2(sd, fd) failed.");
}
memset(&info1, '\0', sizeof(struct stat));
memset(&info2, '\0', sizeof(struct stat));
rc = fstat(fd, &info1);
if (rc == -1) {
test_fail("fstat(fd, &info1) failed");
}
rc = fstat(sd, &info2);
if (rc == -1) {
test_fail("fstat(sd, &info2) failed");
}
if (!(info1.st_ino == info2.st_ino &&
major(info1.st_dev) == major(info2.st_dev) &&
minor(info1.st_dev) == minor(info2.st_dev))) {
test_fail("dup2() failed");
}
CLOSE(fd);
CLOSE(sd);
UNLINK(TEST_SUN_PATH);
debug("leaving test_dup2()");
}
/*
* A toupper() server. This toy server converts a string to upper case.
*/
void test_xfer_server(pid_t pid)
{
socklen_t ucred_length;
int i;
int on;
struct timeval tv;
fd_set readfds;
int status;
int rc;
int sd;
char buf[BUFSIZE];
socklen_t client_addr_size;
int client_sd;
struct sockaddr_un addr;
struct sockaddr_un client_addr;
on = 1;
status = 0;
rc = 0;
sd = 0;
ucred_length = sizeof(struct ucred);
client_sd = 0;
client_addr_size = sizeof(struct sockaddr_un);
memset(&buf, '\0', sizeof(buf));
memset(&addr, '\0', sizeof(struct sockaddr_un));
memset(&client_addr, '\0', sizeof(struct sockaddr_un));
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
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");
}
/* we're ready for connections, time to tell the client to start
* the test
*/
kill(pid, SIGUSR1);
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(sd, &readfds);
/* use select() in case the client is really broken and never
* attempts to connect (we don't want to block on accept()
* forever).
*/
rc = select(sd + 1, &readfds, NULL, NULL, &tv);
if (rc == -1) {
test_fail("[server] select() should not have failed");
}
if (rc != 1) {
test_fail("[server] select() should have returned 1");
printf("[server] select returned %d\n", rc);
}
if (!(FD_ISSET(sd, &readfds))) {
test_fail("[server] client didn't connect within 10 seconds");
kill(pid, SIGKILL);
return;
}
client_sd = accept(sd, (struct sockaddr *) &client_addr,
&client_addr_size);
if (client_sd == -1) {
test_fail("accept() should have worked");
kill(pid, SIGKILL);
return;
} else {
debug("[server] client accept()'d");
}
debug("[server] Reading message");
rc = read(client_sd, buf, sizeof(buf));
if (rc == -1) {
test_fail("read() failed unexpectedly");
kill(pid, SIGKILL);
return;
}
debug("[server] we got the following message:");
debug(buf);
for (i = 0; i < rc && i < 127; i++) {
buf[i] = toupper(buf[i]);
}
debug("[server] Writing message...");
rc = write(client_sd, buf, sizeof(buf));
if (rc == -1) {
test_fail("write(client_sd, buf, sizeof(buf)) failed");
kill(pid, SIGKILL);
return;
}
if (rc < strlen(buf)) {
test_fail("[server] write didn't write all the bytes");
}
memset(&buf, '\0', sizeof(buf));
debug("[server] Recv message");
rc = recv(client_sd, buf, sizeof(buf), 0);
if (rc == -1) {
test_fail("recv() failed unexpectedly");
kill(pid, SIGKILL);
return;
}
debug("[server] we got the following message:");
debug(buf);
for (i = 0; i < rc && i < 127; i++) {
buf[i] = toupper(buf[i]);
}
debug("[server] Sending message...");
rc = send(client_sd, buf, sizeof(buf), 0);
if (rc == -1) {
test_fail("send(client_sd, buf, sizeof(buf), 0) failed");
kill(pid, SIGKILL);
return;
}
if (rc < strlen(buf)) {
test_fail("[server] write didn't write all the bytes");
}
memset(&buf, '\0', sizeof(buf));
debug("[server] Recvfrom message");
rc = recvfrom(client_sd, buf, sizeof(buf), 0, NULL, 0);
if (rc == -1) {
test_fail("recvfrom() failed unexpectedly");
kill(pid, SIGKILL);
return;
}
debug("[server] we got the following message:");
debug(buf);
for (i = 0; i < rc && i < 127; i++) {
buf[i] = toupper(buf[i]);
}
debug("[server] Sendto message...");
rc = sendto(client_sd, buf, sizeof(buf), 0, NULL, 0);
if (rc == -1) {
test_fail("sendto() failed");
kill(pid, SIGKILL);
return;
}
if (rc < strlen(buf)) {
test_fail("[server] write didn't write all the bytes");
}
shutdown(client_sd, SHUT_RDWR);
CLOSE(client_sd);
shutdown(sd, SHUT_RDWR);
CLOSE(sd);
/* 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);
}
int server_ready = 0;
/* signal handler for the client */
void test_xfer_sighdlr(int sig)
{
debug("entering signal handler");
switch (sig) {
/* the server will send SIGUSR1 when it is time for us
* to start the tests
*/
case SIGUSR1:
server_ready = 1;
debug("got SIGUSR1, the server is ready for the client");
break;
default:
debug("didn't get SIGUSR1");
}
debug("leaving signal handler");
}
/*
* A toupper() client.
*/
void test_xfer_client(void)
{
struct ucred credentials;
socklen_t ucred_length;
struct timeval tv;
fd_set readfds;
struct sockaddr_un addr;
struct sockaddr_un peer_addr;
socklen_t peer_addr_len;
int sd;
int rc;
char buf[BUFSIZE];
debug("[client] entering test_xfer_client()");
errct = 0; /* reset error count */
ucred_length = sizeof(struct ucred);
memset(&buf, '\0', sizeof(buf));
while (server_ready == 0) {
debug("[client] waiting for the server to signal");
sleep(1);
}
peer_addr_len = sizeof(struct sockaddr_un);
debug("Creating symlink to TEST_SUN_PATH");
SYMLINK(TEST_SUN_PATH, TEST_SYM_A);
memset(&addr, '\0', sizeof(struct sockaddr_un));
strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
debug("[client] creating client socket");
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
debug("[client] connecting to server through the symlink");
rc = connect(sd, (struct sockaddr *) &addr,
sizeof(struct sockaddr_un));
if (rc == -1) {
test_fail("[client] connect() should have worked");
} else {
debug("[client] connected");
}
debug("[client] testing getpeername()");
memset(&peer_addr, '\0', sizeof(struct sockaddr_un));
rc = getpeername(sd, (struct sockaddr *) &peer_addr, &peer_addr_len);
if (rc == -1) {
test_fail("[client] getpeername() should have worked");
}
/* we need to use the full path "/usr/src/test/DIR_56/test.sock"
* because that is what is returned by getpeername().
*/
if (!(peer_addr.sun_family == AF_UNIX &&
strncmp(peer_addr.sun_path,
fullpath(TEST_SUN_PATH),
sizeof(peer_addr.sun_path) - 1) == 0)) {
test_fail("getpeername() didn't return the right address");
}
strncpy(buf, "Hello, World!", sizeof(buf) - 1);
debug("[client] send to server");
rc = write(sd, buf, sizeof(buf));
if (rc == -1) {
test_fail("[client] write() failed unexpectedly");
}
memset(buf, '\0', sizeof(buf));
debug("[client] read from server");
rc = read(sd, buf, sizeof(buf));
if (rc == -1) {
test_fail("[client] read() failed unexpectedly");
} else {
debug("[client] we got the following message:");
debug(buf);
}
if (strncmp(buf, "HELLO, WORLD!", sizeof(buf)) != 0) {
test_fail("[client] We didn't get the correct response");
}
memset(&buf, '\0', sizeof(buf));
strncpy(buf, "Bonjour!", sizeof(buf) - 1);
debug("[client] send to server");
rc = send(sd, buf, sizeof(buf), 0);
if (rc == -1) {
test_fail("[client] send() failed unexpectedly");
}
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.uid != geteuid() ||
credentials.gid != getegid()) {
printf("%d=%d=%d %d=%d=%d\n", credentials.uid, getuid(),
geteuid(), credentials.gid, getgid(), getegid());
test_fail("[client] Credential passing gave us a bad UID/GID");
}
debug("Testing select()");
tv.tv_sec = 2;
tv.tv_usec = 500000;
FD_ZERO(&readfds);
FD_SET(sd, &readfds);
rc = select(sd + 1, &readfds, NULL, NULL, &tv);
if (rc == -1) {
test_fail("[client] select() should not have failed");
}
if (rc != 1) {
test_fail("[client] select() should have returned 1");
}
if (!(FD_ISSET(sd, &readfds))) {
test_fail("The server didn't respond within 2.5 seconds");
}
memset(buf, '\0', sizeof(buf));
debug("[client] recv from server");
rc = recv(sd, buf, sizeof(buf), 0);
if (rc == -1) {
test_fail("[client] recv() failed unexpectedly");
} else {
debug("[client] we got the following message:");
debug(buf);
}
if (strncmp(buf, "BONJOUR!", sizeof(buf)) != 0) {
test_fail("[client] We didn't get the right response.");
}
memset(&buf, '\0', sizeof(buf));
strncpy(buf, "Hola!", sizeof(buf) - 1);
debug("[client] sendto to server");
rc = sendto(sd, buf, sizeof(buf), 0, NULL, 0);
if (rc == -1) {
test_fail("[client] sendto() failed");
}
debug("Testing select()");
tv.tv_sec = 2;
tv.tv_usec = 500000;
FD_ZERO(&readfds);
FD_SET(sd, &readfds);
rc = select(sd + 1, &readfds, NULL, NULL, &tv);
if (rc == -1) {
test_fail("[client] select() should not have failed");
}
if (rc != 1) {
test_fail("[client] select() should have returned 1");
}
if (!(FD_ISSET(sd, &readfds))) {
test_fail("[client] The server didn't respond in 2.5 seconds");
}
memset(buf, '\0', sizeof(buf));
debug("[client] recvfrom from server");
rc = recvfrom(sd, buf, sizeof(buf), 0, NULL, 0);
if (rc == -1) {
test_fail("[cleint] recvfrom() failed unexpectedly");
} else {
debug("[client] we got the following message:");
debug(buf);
}
if (strncmp(buf, "HOLA!", sizeof(buf)) != 0) {
test_fail("[client] We didn't get the right response.");
}
debug("[client] closing socket");
CLOSE(sd);
debug("[client] leaving test_xfer_client()");
exit(errct);
}
void test_xfer(void)
{
pid_t pid;
UNLINK(TEST_SYM_A);
UNLINK(TEST_SUN_PATH);
/* 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 == -1) {
test_fail("fork() failed");
return;
} else if (pid == 0) {
debug("child");
test_xfer_client();
test_fail("we should never get here");
exit(1);
} else {
debug("parent");
test_xfer_server(pid);
debug("parent done");
}
UNLINK(TEST_SYM_A);
UNLINK(TEST_SUN_PATH);
}
void test_simple_client(int type)
{
char buf[BUFSIZE];
int sd, rc;
struct sockaddr_un addr;
sd = socket(PF_UNIX, type, 0);
if (sd == -1) {
test_fail("socket");
exit(errct);
}
while (server_ready == 0) {
debug("[client] waiting for the server");
sleep(1);
}
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
bzero(buf, BUFSIZE);
snprintf(buf, BUFSIZE-1, "Hello, My Name is Client.");
if (type == SOCK_DGRAM) {
rc = sendto(sd, buf, strlen(buf) + 1, 0,
(struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc == -1) {
test_fail("sendto");
exit(errct);
}
} else {
rc = connect(sd, (struct sockaddr *) &addr,
sizeof(struct sockaddr_un));
if (rc == -1) {
test_fail("connect");
exit(errct);
}
rc = write(sd, buf, strlen(buf) + 1);
if (rc == -1) {
test_fail("write");
}
memset(buf, '\0', BUFSIZE);
rc = read(sd, buf, BUFSIZE);
if (rc == -1) {
test_fail("read");
}
if (strcmp("Hello, My Name is Server.", buf) != 0) {
test_fail("didn't read the correct string");
}
}
rc = close(sd);
if (rc == -1) {
test_fail("close");
}
exit(errct);
}
void test_simple_server(int type, pid_t pid)
{
char buf[BUFSIZE];
int sd, rc, client_sd, status;
struct sockaddr_un addr;
socklen_t addr_len;
addr_len = sizeof(struct sockaddr_un);
sd = socket(PF_UNIX, type, 0);
if (sd == -1) {
test_fail("socket");
}
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
addr.sun_family = AF_UNIX;
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
if (rc == -1) {
test_fail("bind");
}
if (type == SOCK_DGRAM) {
/* ready for client */
kill(pid, SIGUSR1);
rc = recvfrom(sd, buf, BUFSIZE, 0,
(struct sockaddr *) &addr, &addr_len);
if (rc == -1) {
test_fail("recvfrom");
}
} else {
rc = listen(sd, 5);
if (rc == -1) {
test_fail("listen");
}
/* we're ready for connections, time to tell the client
* to start the test
*/
kill(pid, SIGUSR1);
client_sd = accept(sd, (struct sockaddr *) &addr, &addr_len);
if (client_sd == -1) {
test_fail("accept");
}
memset(buf, '\0', BUFSIZE);
rc = read(client_sd, buf, BUFSIZE);
if (rc == -1) {
test_fail("read");
}
if (strcmp("Hello, My Name is Client.", buf) != 0) {
test_fail("didn't read the correct string");
}
/* added for extra fun to make the client block on read() */
sleep(1);
bzero(buf, BUFSIZE);
snprintf(buf, BUFSIZE-1, "Hello, My Name is Server.");
rc = write(client_sd, buf, strlen(buf) + 1);
if (rc == -1) {
test_fail("write");
}
rc = close(client_sd);
if (rc == -1) {
test_fail("close");
}
}
rc = close(sd);
if (rc == -1) {
test_fail("close");
}
/* 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);
}
void test_simple_client_server(int type)
{
pid_t pid;
debug("test_simple_client_server()");
UNLINK(TEST_SUN_PATH);
/* 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 == -1) {
test_fail("fork() failed");
return;
} else if (pid == 0) {
debug("child");
test_simple_client(type);
test_fail("we should never get here");
exit(1);
} else {
debug("parent");
test_simple_server(type, pid);
debug("parent done");
}
UNLINK(TEST_SUN_PATH);
}
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");
}
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");
}
}
void test_msg_dgram(void)
{
int rc;
int src;
int dst;
struct sockaddr_un addr;
struct iovec iov[3];
struct msghdr msg1;
struct msghdr msg2;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
socklen_t addrlen = sizeof(struct sockaddr_un);
debug("test msg_dgram");
UNLINK(TEST_SUN_PATH);
UNLINK(TEST_SUN_PATHB);
src = socket(PF_UNIX, SOCK_DGRAM, 0);
if (src == -1) {
test_fail("socket");
}
dst = socket(PF_UNIX, SOCK_DGRAM, 0);
if (dst == -1) {
test_fail("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");
}
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);
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;
rc = sendmsg(src, &msg1, 0);
if (rc == -1) {
test_fail("sendmsg");
}
memset(&buf1, '\0', BUFSIZE);
memset(&buf2, '\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 = NULL;
msg2.msg_controllen = 0;
msg2.msg_flags = 0;
rc = recvmsg(dst, &msg2, 0);
if (rc == -1) {
test_fail("recvmsg");
}
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");
}
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);
}
void test_scm_credentials(void)
{
int rc;
int src;
int dst;
struct ucred 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 ucred));
for (cmsg = CMSG_FIRSTHDR(&msg2); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg2, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS) {
memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct ucred));
break;
}
}
if (cred.pid != getpid() || cred.uid != geteuid() ||
cred.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);
}
void test_connect(void)
{
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(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()");
}
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;
}
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;
}
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);
}
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");
}
}
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);
}
}
int main(int argc, char *argv[])
{
int i;
debug("entering main()");
start(56);
test_socket();
test_listen();
test_getsockname();
test_header();
test_shutdown();
test_close();
test_dup();
test_dup2();
test_socketpair();
test_shutdown();
test_read();
test_write();
test_sockopts();
test_ucred();
test_bind();
test_xfer();
for (i = 0; i < 3; i++) {
test_simple_client_server(types[i]);
if (types[i] != SOCK_DGRAM) test_vectorio(types[i]);
if (types[i] != SOCK_DGRAM) test_msg(types[i]);
}
test_msg_dgram();
test_connect();
test_multiproc_read();
test_multiproc_write();
test_scm_credentials();
test_fd_passing();
quit();
return -1; /* we should never get here */
}