return 0;
}
-static void test_tcp_sendmsg_recvmsg_fd(void **state)
+struct test_fd {
+ int fd;
+ struct torture_address sock_addr;
+ struct torture_address peer_addr;
+};
+
+static int test_fill_test_fd(struct test_fd *tfd, int fd)
{
- struct torture_address addr = {
- .sa_socklen = sizeof(struct sockaddr_in),
+ struct torture_address saddr = {
+ .sa_socklen = sizeof(struct sockaddr_storage),
};
- int pass_sock_fd;
- int sv[2];
- int child_fd, parent_fd;
- pid_t pid;
- int rc;
+ struct torture_address paddr = {
+ .sa_socklen = sizeof(struct sockaddr_storage),
+ };
+ int ret;
- (void) state; /* unused */
+ *tfd = (struct test_fd) { .fd = fd, };
- /* create socket file descriptor to be passed */
- pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- assert_int_not_equal(pass_sock_fd, -1);
+ ret = getsockname(fd, &saddr.sa.s, &saddr.sa_socklen);
+ if (ret == -1 && errno == ENOTSOCK) {
+ return 0;
+ }
+ if (ret == -1) {
+ return ret;
+ }
- addr.sa.in.sin_family = AF_INET;
- addr.sa.in.sin_port = htons(torture_server_port());
+ ret = getpeername(fd, &paddr.sa.s, &paddr.sa_socklen);
+ if (ret == -1) {
+ return ret;
+ }
- rc = inet_pton(addr.sa.in.sin_family,
- torture_server_address(AF_INET),
- &addr.sa.in.sin_addr);
- assert_int_equal(rc, 1);
+ tfd->sock_addr = saddr;
+ tfd->peer_addr = paddr;
+ return 0;
+}
- rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
- assert_int_equal(rc, 0);
+static void _assert_torture_address_equal(const struct torture_address *ga,
+ const struct torture_address *ea,
+ const char * const file,
+ const int line)
+{
+ _assert_int_equal(ga->sa_socklen, ea->sa_socklen, file, line);
+ if (ga->sa_socklen == 0) {
+ return;
+ }
+ _assert_memory_equal(&ga->sa, &ea->sa, ga->sa_socklen, file, line);
+}
+
+#define assert_test_fd_equal(gfd, efd) \
+ _assert_test_fd_equal(gfd, efd, __FILE__, __LINE__)
+
+static void _assert_test_fd_equal(const struct test_fd *gfd,
+ const struct test_fd *efd,
+ const char * const file,
+ const int line)
+{
+ if (efd->fd == -1) {
+ _assert_int_equal(gfd->fd, -1, file, line);
+ return;
+ }
+
+ _assert_int_not_equal(gfd->fd, -1, file, line);
+
+ _assert_torture_address_equal(&gfd->sock_addr, &efd->sock_addr, file, line);
+ _assert_torture_address_equal(&gfd->peer_addr, &efd->peer_addr, file, line);
+}
+
+static void test_tcp_sendmsg_recvmsg_fd_array(const int *fds, size_t num_fds)
+{
+ struct test_fd tfds[num_fds];
+ size_t idx;
+ int sv[2];
+ int child_fd, parent_fd;
+ pid_t pid;
+ int rc;
+
+ for (idx = 0; idx < num_fds; idx++) {
+ rc = test_fill_test_fd(&tfds[idx], fds[idx]);
+ assert_int_equal(rc, 0);
+ }
/* create unix domain socket stream */
rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
if (pid == 0) {
/* Child */
- struct torture_address peer_addr = {
- .sa_socklen = sizeof(struct sockaddr_in),
- };
struct msghdr child_msg;
- char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ int recv_fd_array[num_fds];
+ char cmsgbuf[CMSG_SPACE(sizeof(int)*num_fds)];
struct cmsghdr *cmsg;
- int rcv_sock_fd, port;
ssize_t ret;
- char send_buf[64] = {0};
- char recv_buf[64] = {0};
- char ipstr[INET_ADDRSTRLEN];
char byte = { 0, };
struct iovec iov;
assert_non_null(cmsg);
assert_int_equal(cmsg->cmsg_type, SCM_RIGHTS);
- memcpy(&rcv_sock_fd, CMSG_DATA(cmsg), sizeof(rcv_sock_fd));
- assert_int_not_equal(rcv_sock_fd, -1);
+ memcpy(recv_fd_array, CMSG_DATA(cmsg), sizeof(int)*num_fds);
+ for (idx = 0; idx < num_fds; idx++) {
+ assert_int_not_equal(recv_fd_array[idx], -1);
+ }
- /* extract peer info from received socket fd */
- ret = getpeername(rcv_sock_fd, &peer_addr.sa.s, &peer_addr.sa_socklen);
- assert_int_not_equal(ret, -1);
+ for (idx = 0; idx < num_fds; idx++) {
+ struct test_fd recv_tfd = { .fd = -1, };
- port = ntohs(peer_addr.sa.in.sin_port);
- inet_ntop(AF_INET, &peer_addr.sa.in.sin_addr, ipstr, sizeof(ipstr));
+ ret = test_fill_test_fd(&recv_tfd, recv_fd_array[idx]);
+ assert_int_equal(ret, 0);
- /* check whether it is the same socket previously connected */
- assert_string_equal(ipstr, torture_server_address(AF_INET));
- assert_int_equal(port, torture_server_port());
+ assert_test_fd_equal(&recv_tfd, &tfds[idx]);
+ }
- snprintf(send_buf, sizeof(send_buf), "packet");
+ for (idx = 0; idx < num_fds; idx++) {
+ int recv_fd = recv_fd_array[idx];
+ char send_buf[64] = {0,};
+ char recv_buf[64] = {0,};
- ret = write(rcv_sock_fd,
- send_buf,
- sizeof(send_buf));
- assert_int_not_equal(ret, -1);
+ if (tfds[idx].sock_addr.sa_socklen == 0) {
+ /*
+ * skip fds not belonging to
+ * a socket.
+ */
+ continue;
+ }
- ret = read(rcv_sock_fd,
- recv_buf,
- sizeof(recv_buf));
- assert_int_not_equal(ret, -1);
+ snprintf(send_buf, sizeof(send_buf), "packet");
- assert_memory_equal(send_buf, recv_buf, sizeof(send_buf));
+ ret = write(recv_fd,
+ send_buf,
+ sizeof(send_buf));
+ assert_int_not_equal(ret, -1);
+
+ ret = read(recv_fd,
+ recv_buf,
+ sizeof(recv_buf));
+ assert_int_not_equal(ret, -1);
+
+ assert_memory_equal(send_buf, recv_buf, sizeof(send_buf));
+ }
exit(0);
} else {
/* Parent */
struct msghdr parent_msg;
struct cmsghdr *cmsg;
- char cmsgbuf[CMSG_SPACE(sizeof(pass_sock_fd))];
+ char cmsgbuf[CMSG_SPACE(sizeof(int)*num_fds)];
+ int pass_fd_array[num_fds];
char byte = '!';
struct iovec iov;
int cs;
- (void) state; /* unused */
+ for (idx = 0; idx < num_fds; idx++) {
+ pass_fd_array[idx] = tfds[idx].fd;
+ }
iov.iov_base = &byte;
iov.iov_len = 1;
cmsg = CMSG_FIRSTHDR(&parent_msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(pass_sock_fd));
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int)*num_fds);
/* place previously connected socket fd as ancillary data */
- memcpy(CMSG_DATA(cmsg), &pass_sock_fd, sizeof(pass_sock_fd));
+ memcpy(CMSG_DATA(cmsg), pass_fd_array, sizeof(int)*num_fds);
parent_msg.msg_controllen = cmsg->cmsg_len;
rc = sendmsg(parent_fd, &parent_msg, 0);
}
}
+static void test_tcp_sendmsg_recvmsg_fd_1(void **state)
+{
+ struct torture_address addr = {
+ .sa_socklen = sizeof(struct sockaddr_in),
+ };
+ int pass_sock_fd;
+ int rc;
+
+ (void) state; /* unused */
+
+ /* create socket file descriptor to be passed */
+ pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert_int_not_equal(pass_sock_fd, -1);
+
+ addr.sa.in.sin_family = AF_INET;
+ addr.sa.in.sin_port = htons(torture_server_port());
+
+ rc = inet_pton(addr.sa.in.sin_family,
+ torture_server_address(AF_INET),
+ &addr.sa.in.sin_addr);
+ assert_int_equal(rc, 1);
+
+ rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
+ assert_int_equal(rc, 0);
+
+ test_tcp_sendmsg_recvmsg_fd_array(&pass_sock_fd, 1);
+}
+
int main(void) {
int rc;
const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd,
+ cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_1,
setup_echo_srv_tcp_ipv4,
teardown)
};