/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ #include "test-lib.h" #include "net.h" #include "fdpass.h" #include "istream.h" #include "istream-unix.h" #include #include #include static int send_fd, send_fd2; static void write_one(int fd) { if (write(fd, "1", 1) < 0) i_fatal("write() failed: %m"); } static void read_one(int fd) { char buf; if (read(fd, &buf, 1) < 0) i_fatal("read() failed: m"); } static void test_server_read_nofd(struct istream *input, unsigned int idx) { const unsigned char *data; size_t size; test_assert_idx(i_stream_read_more(input, &data, &size) == 1, idx); i_stream_skip(input, 1); test_assert_idx(i_stream_unix_get_read_fd(input) == -1, idx); } static void test_server_read_fd(struct istream *input, int wanted_fd, unsigned int idx) { struct stat st1, st2; const unsigned char *data; size_t size; int recv_fd; test_assert_idx(i_stream_read_more(input, &data, &size) == 1, idx); i_stream_skip(input, 1); test_assert_idx((recv_fd = i_stream_unix_get_read_fd(input)) != -1, idx); if (recv_fd != -1) { if (fstat(recv_fd, &st1) < 0 || fstat(wanted_fd, &st2) < 0) i_fatal("fstat() failed: %m"); test_assert_idx(st1.st_ino == st2.st_ino, idx); i_close_fd(&recv_fd); } } static void test_istream_unix_server(int fd) { struct istream *input; const unsigned char *data; size_t size; input = i_stream_create_unix(fd, 1024); /* 1) simple read */ test_server_read_nofd(input, 1); write_one(fd); /* 2) fd was sent but we won't get it */ test_server_read_nofd(input, 2); /* we still shouldn't have the fd */ i_stream_set_blocking(input, FALSE); i_stream_unix_set_read_fd(input); test_assert(i_stream_read_more(input, &data, &size) == 0); test_assert(i_stream_unix_get_read_fd(input) == -1); i_stream_set_blocking(input, TRUE); write_one(fd); /* 3) the previous fd should be lost now */ test_server_read_nofd(input, 3); write_one(fd); /* 4) we should get the fd now */ test_server_read_fd(input, send_fd2, 4); write_one(fd); /* 5) the previous fd shouldn't be returned anymore */ i_stream_unix_set_read_fd(input); test_server_read_nofd(input, 5); write_one(fd); /* 6) with i_stream_unix_unset_read_fd() we shouldn't get fd anymore */ i_stream_unix_unset_read_fd(input); test_server_read_nofd(input, 6); write_one(fd); /* 7-8) two fds were sent, but we'll get only the first one */ i_stream_unix_set_read_fd(input); test_server_read_fd(input, send_fd, 7); test_server_read_nofd(input, 8); write_one(fd); /* 9-10) two fds were sent, and we'll get them both */ i_stream_unix_set_read_fd(input); test_server_read_fd(input, send_fd, 9); i_stream_unix_set_read_fd(input); test_server_read_fd(input, send_fd2, 10); write_one(fd); i_stream_destroy(&input); i_close_fd(&fd); } static void test_istream_unix_client(int fd) { /* 1) */ write_one(fd); read_one(fd); /* 2) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 3) */ write_one(fd); read_one(fd); /* 4) */ if (fd_send(fd, send_fd2, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 5) */ write_one(fd); read_one(fd); /* 6) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 7-8) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); if (fd_send(fd, send_fd2, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); /* 9-10) */ if (fd_send(fd, send_fd, "1", 1) < 0) i_fatal("fd_send() failed: %m"); if (fd_send(fd, send_fd2, "1", 1) < 0) i_fatal("fd_send() failed: %m"); read_one(fd); i_close_fd(&fd); } void test_istream_unix(void) { int fd[2]; test_begin("istream unix"); if ((send_fd = open("/dev/null", O_RDONLY)) == -1) i_fatal("open(/dev/null) failed: %m"); if ((send_fd2 = open("/dev/zero", O_RDONLY)) == -1) i_fatal("open(/dev/zero) failed: %m"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) i_fatal("socketpair() failed: %m"); switch (fork()) { case -1: i_fatal("fork() failed: %m"); case 0: i_close_fd(&fd[0]); test_istream_unix_client(fd[1]); test_exit(0); default: i_close_fd(&fd[1]); test_istream_unix_server(fd[0]); break; } i_close_fd(&send_fd); i_close_fd(&send_fd2); test_end(); }