blob: 933fa0af09086b466541a6a2b7d407b999a74b50 [file] [log] [blame]
/*
* Copyright (c) 2010 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "native_client/src/public/imc_syscalls.h"
#include "native_client/src/public/imc_types.h"
/* TODO(mseaborn): This should really be in an IMC header file. */
static const int kKnownInvalidDescNumber = -1;
void checked_close(int fd) {
int rc = close(fd);
assert(rc == 0);
}
struct accept_thread_args {
int boundsock_fd;
int result_fd;
};
void *accept_thread(void *arg) {
struct accept_thread_args *args = arg;
args->result_fd = imc_accept(args->boundsock_fd);
return NULL;
}
void connect_and_accept(int boundsock_fd, int sockaddr_fd,
int *result_fd1, int *result_fd2) {
struct accept_thread_args args = { -1, -1 };
pthread_t tid;
/* TODO(mseaborn): Use of a thread here is necessary on Windows, but
not on Linux or Mac OS X. See
http://code.google.com/p/nativeclient/issues/detail?id=692 */
args.boundsock_fd = boundsock_fd;
pthread_create(&tid, NULL, accept_thread, &args);
*result_fd1 = imc_connect(sockaddr_fd);
assert(*result_fd1 >= 0);
pthread_join(tid, NULL);
*result_fd2 = args.result_fd;
assert(*result_fd2 >= 0);
}
/* Making a pair of ConnectedSockets is relatively convoluted, since
we have to create them via a BoundSocket/SocketAddress pair. */
void make_socket_pair(int pair[2]) {
int bound_pair[2];
int rc;
rc = imc_makeboundsock(bound_pair);
assert(rc == 0);
connect_and_accept(bound_pair[0], bound_pair[1], &pair[0], &pair[1]);
checked_close(bound_pair[0]);
checked_close(bound_pair[1]);
}
int send_message(int sock_fd, char *data, size_t data_size,
int *fds, int fds_size) {
struct NaClAbiNaClImcMsgIoVec iov;
struct NaClAbiNaClImcMsgHdr msg;
iov.base = data;
iov.length = data_size;
msg.iov = &iov;
msg.iov_length = 1;
msg.descv = fds;
msg.desc_length = fds_size;
return imc_sendmsg(sock_fd, &msg, 0);
}
int receive_message(int sock_fd, char *data, size_t data_size,
int *fds, int fds_size, int *fds_got) {
struct NaClAbiNaClImcMsgIoVec iov;
struct NaClAbiNaClImcMsgHdr msg;
int received;
iov.base = data;
iov.length = data_size;
msg.iov = &iov;
msg.iov_length = 1;
msg.descv = fds;
msg.desc_length = fds_size;
received = imc_recvmsg(sock_fd, &msg, 0);
if (fds_got != NULL)
*fds_got = msg.desc_length;
return received;
}
char *test_message = "Peter Piper picked a peck of pickled peppers";
/* Check that we can send and receive a data-only message between a
pair of sockets. */
void check_send_and_receive_data(int sock_fd1, int sock_fd2) {
int rc;
char buf[100];
int fds_got;
rc = send_message(sock_fd1, test_message, strlen(test_message), NULL, 0);
assert(rc == strlen(test_message));
rc = receive_message(sock_fd2, buf, sizeof(buf), NULL, 0, &fds_got);
assert(rc == strlen(test_message));
assert(memcmp(buf, test_message, strlen(test_message)) == 0);
assert(fds_got == 0);
}
void test_socket_transfer(void) {
int sock_pair[2];
int bound_pair[2];
int rc;
int fd1;
int fd2;
char buf[100];
int received_fds[8];
int received_fd_count;
int received_bytes;
make_socket_pair(sock_pair);
printf("Test data-only messages...\n");
check_send_and_receive_data(sock_pair[0], sock_pair[1]);
check_send_and_receive_data(sock_pair[1], sock_pair[0]);
rc = imc_makeboundsock(bound_pair);
assert(rc == 0);
printf("Test sending a BoundSocket (this is rejected)...\n");
rc = send_message(sock_pair[0], test_message, strlen(test_message),
&bound_pair[0], 1);
assert(rc == -1);
assert(errno == EIO);
printf("Test sending a ConnectedSocket (this is rejected)...\n");
send_message(sock_pair[0], test_message, strlen(test_message),
&sock_pair[0], 1);
assert(rc == -1);
assert(errno == EIO);
printf("Test sending a SocketAddress...\n");
rc = send_message(sock_pair[0], test_message, strlen(test_message),
&bound_pair[1], 1);
assert(rc == strlen(test_message));
printf("Check that we can receive the SocketAddress we sent...\n");
received_bytes = receive_message(sock_pair[1], buf, sizeof(buf),
received_fds, 8, &received_fd_count);
assert(received_bytes == strlen(test_message));
assert(memcmp(buf, test_message, strlen(test_message)) == 0);
assert(received_fd_count == 1);
printf("Check that the received SocketAddress works...\n");
connect_and_accept(bound_pair[0], received_fds[0], &fd1, &fd2);
checked_close(received_fds[0]);
check_send_and_receive_data(fd1, fd2);
check_send_and_receive_data(fd2, fd1);
checked_close(fd1);
checked_close(fd2);
checked_close(sock_pair[0]);
checked_close(sock_pair[1]);
checked_close(bound_pair[0]);
checked_close(bound_pair[1]);
}
void test_imc_accept_end_of_stream(void) {
int bound_pair[2];
int rc;
rc = imc_makeboundsock(bound_pair);
assert(rc == 0);
printf("Test imc_accept() when SocketAddress has been dropped...\n");
checked_close(bound_pair[1]);
rc = imc_accept(bound_pair[0]);
assert(rc == -1);
assert(errno == EIO);
checked_close(bound_pair[0]);
}
void test_imc_connect_with_no_acceptor(void) {
int bound_pair[2];
int rc;
rc = imc_makeboundsock(bound_pair);
assert(rc == 0);
printf("Test imc_connect() when BoundSocket has been dropped...\n");
checked_close(bound_pair[0]);
rc = imc_connect(bound_pair[1]);
assert(rc == -1);
assert(errno == EIO);
checked_close(bound_pair[1]);
}
void test_special_invalid_fd(void) {
int sock_pair[2];
int sent;
int received;
int fd_to_send;
char data_buf[10];
int fds_buf[NACL_ABI_IMC_DESC_MAX];
int fds_got;
printf("Test sending and receiving the special 'invalid' descriptor...\n");
make_socket_pair(sock_pair);
fd_to_send = kKnownInvalidDescNumber;
sent = send_message(sock_pair[0], "", 0, &fd_to_send, 1);
assert(sent == 0);
received = receive_message(sock_pair[1], data_buf, sizeof(data_buf),
fds_buf, NACL_ABI_IMC_DESC_MAX, &fds_got);
assert(received == 0);
assert(fds_got == 1);
assert(fds_buf[0] == kKnownInvalidDescNumber);
/* Other invalid FD numbers are not accepted. */
fd_to_send = 1234;
sent = send_message(sock_pair[0], "", 0, &fd_to_send, 1);
assert(sent == -1);
assert(errno == EBADF);
/* We don't accept other negative numbers. */
fd_to_send = -2;
sent = send_message(sock_pair[0], "", 0, &fd_to_send, 1);
assert(sent == -1);
assert(errno == EBADF);
checked_close(sock_pair[0]);
checked_close(sock_pair[1]);
}
void test_sending_and_receiving_max_fd_count(void) {
int sock_pair[2];
int sent;
int received;
char data_buf[10];
int fds_send_buf[NACL_ABI_IMC_DESC_MAX + 1];
int fds_receive_buf[NACL_ABI_IMC_DESC_MAX];
int fds_got;
int i;
printf("Test sending and receiving many FDs...\n");
make_socket_pair(sock_pair);
/* Test exactly the maximum. */
for (i = 0; i < NACL_ABI_IMC_DESC_MAX; i++) {
fds_send_buf[i] = kKnownInvalidDescNumber;
}
sent = send_message(sock_pair[0], "", 0, fds_send_buf, NACL_ABI_IMC_DESC_MAX);
assert(sent == 0);
received = receive_message(sock_pair[1], data_buf, sizeof(data_buf),
fds_receive_buf, NACL_ABI_IMC_DESC_MAX, &fds_got);
assert(received == 0);
assert(fds_got == NACL_ABI_IMC_DESC_MAX);
for (i = 0; i < NACL_ABI_IMC_DESC_MAX; i++) {
assert(fds_receive_buf[i] == kKnownInvalidDescNumber);
}
/* Test above the maximum. */
for (i = 0; i < NACL_ABI_IMC_DESC_MAX + 1; i++) {
fds_send_buf[i] = kKnownInvalidDescNumber;
}
sent = send_message(sock_pair[0], "", 0, fds_send_buf,
NACL_ABI_IMC_DESC_MAX + 1);
assert(sent == -1);
assert(errno == EINVAL);
checked_close(sock_pair[0]);
checked_close(sock_pair[1]);
}
int main(int argc, char **argv) {
/* TODO(mseaborn): It would be better to have a way to pass
environment variables through sel_ldr into the NaCl process. */
int i;
for(i = 1; i < argc; i++) {
putenv(argv[i]);
}
/* Turn off stdout buffering to aid debugging in case of a crash. */
setvbuf(stdout, NULL, _IONBF, 0);
test_socket_transfer();
if (getenv("DISABLE_IMC_ACCEPT_EOF_TEST") == NULL) {
test_imc_accept_end_of_stream();
}
test_imc_connect_with_no_acceptor();
test_special_invalid_fd();
test_sending_and_receiving_max_fd_count();
return 0;
}