| /* Test the accept4 function with differing flags arguments. |
| Copyright (C) 2017-2018 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <arpa/inet.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <support/check.h> |
| #include <support/xsocket.h> |
| #include <support/xunistd.h> |
| #include <sys/socket.h> |
| |
| static bool |
| is_nonblocking (int fd) |
| { |
| int status = fcntl (fd, F_GETFL); |
| if (status < 0) |
| FAIL_EXIT1 ("fcntl (F_GETFL): %m"); |
| return status & O_NONBLOCK; |
| } |
| |
| static bool |
| is_cloexec (int fd) |
| { |
| int status = fcntl (fd, F_GETFD); |
| if (status < 0) |
| FAIL_EXIT1 ("fcntl (F_GETFD): %m"); |
| return status & FD_CLOEXEC; |
| } |
| |
| struct client |
| { |
| int socket; |
| struct sockaddr_in address; |
| }; |
| |
| /* Perform a non-blocking connect to *SERVER_ADDRESS. */ |
| static struct client |
| client_connect (const struct sockaddr_in *server_address) |
| { |
| struct client result; |
| result.socket = xsocket (AF_INET, |
| SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); |
| TEST_VERIFY (is_nonblocking (result.socket)); |
| TEST_VERIFY (is_cloexec (result.socket)); |
| int ret = connect (result.socket, (const struct sockaddr *) server_address, |
| sizeof (*server_address)); |
| if (ret < 0 && errno != EINPROGRESS) |
| FAIL_EXIT1 ("client connect: %m"); |
| socklen_t sa_len = sizeof (result.address); |
| xgetsockname (result.socket, (struct sockaddr *) &result.address, |
| &sa_len); |
| TEST_VERIFY (sa_len == sizeof (result.address)); |
| return result; |
| } |
| |
| static void |
| check_same_address (const struct sockaddr_in *left, |
| const struct sockaddr_in *right) |
| { |
| TEST_VERIFY (left->sin_family == AF_INET); |
| TEST_VERIFY (right->sin_family == AF_INET); |
| TEST_VERIFY (left->sin_addr.s_addr == right->sin_addr.s_addr); |
| TEST_VERIFY (left->sin_port == right->sin_port); |
| } |
| |
| static int |
| do_test (void) |
| { |
| /* Create server socket. */ |
| int server_socket = xsocket (AF_INET, SOCK_STREAM, 0); |
| TEST_VERIFY (!is_nonblocking (server_socket)); |
| TEST_VERIFY (!is_cloexec (server_socket)); |
| struct sockaddr_in server_address = |
| { |
| .sin_family = AF_INET, |
| .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK) }, |
| }; |
| xbind (server_socket, |
| (struct sockaddr *) &server_address, sizeof (server_address)); |
| { |
| socklen_t sa_len = sizeof (server_address); |
| xgetsockname (server_socket, (struct sockaddr *) &server_address, |
| &sa_len); |
| TEST_VERIFY (sa_len == sizeof (server_address)); |
| } |
| xlisten (server_socket, 5); |
| |
| for (int do_nonblock = 0; do_nonblock < 2; ++do_nonblock) |
| for (int do_cloexec = 0; do_cloexec < 2; ++do_cloexec) |
| { |
| int sockflags = 0; |
| if (do_nonblock) |
| sockflags |= SOCK_NONBLOCK; |
| if (do_cloexec) |
| sockflags |= SOCK_CLOEXEC; |
| |
| struct client client = client_connect (&server_address); |
| struct sockaddr_in client_address; |
| socklen_t sa_len = sizeof (client_address); |
| int client_socket = xaccept4 (server_socket, |
| (struct sockaddr *) &client_address, |
| &sa_len, sockflags); |
| TEST_VERIFY (sa_len == sizeof (client_address)); |
| TEST_VERIFY (is_nonblocking (client_socket) == do_nonblock); |
| TEST_VERIFY (is_cloexec (client_socket) == do_cloexec); |
| check_same_address (&client.address, &client_address); |
| xclose (client_socket); |
| xclose (client.socket); |
| } |
| |
| xclose (server_socket); |
| return 0; |
| } |
| |
| #include <support/test-driver.c> |