NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Non-blocking socket bug



I've come across an issue when testing networking code, which looks
like a bug. This is on NetBSD-8.1

When a listening socket is set non-blocking and we call accept() on
that socket, the new socket returned by accept() will also be
non-blocking.

I think this is a bug. I thought all new sockets/file descriptors would
be non-blocking by default. I've attached a test program to demonstrate
the issue.

On Linux:
$ ./a.out
Create server thread
Create client thread
Accept connection, fd=5, addr=127.0.0.1, port=47746
Connected socket is blocking

On NetBSD:
$ ./a.out
Create server thread
Create client thread
Accept connection, fd=5, addr=127.0.0.1, port=65534
Connected socket is non-blocking

Any ideas or comments?

Thanks. 
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>
#include <poll.h>

#define ADDR "127.0.0.1"
#define PORT 9999


/* Set fd blocking or non-blocking */
void set_fd_nonblock(int fd, bool set_flag)
{
	int ret_int, fcntl_flags;

	/* Get current flags */
	fcntl_flags = fcntl(fd, F_GETFL, 0);
	if (fcntl_flags == -1)
	{
		fprintf(stderr, "Error %s:%d: fcntl() with F_GETFL failed, %s\n",
			__FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}

	if (set_flag)
	{
		/* Set non-blocking */
		fcntl_flags |= O_NONBLOCK;
	}
	else
	{
		/* Set blocking */
		fcntl_flags &= ~O_NONBLOCK;
	}

	ret_int = fcntl(fd, F_SETFL, fcntl_flags);
	if (ret_int == -1)
	{
		fprintf(stderr, "Error %s:%d: fcntl() with F_SETFL failed, %s\n",
			__FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}
}

/* Check fd is non-blocking */
bool is_fd_nonblock(int fd)
{
	int fcntl_flags;

	/* Get current flags */
	fcntl_flags = fcntl(fd, F_GETFL, 0);
	if (fcntl_flags == -1)
	{
		fprintf(stderr, "Error %s:%d: fcntl() with F_GETFL failed, %s\n",
			__FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}

	return (fcntl_flags & O_NONBLOCK);
}


/* Client thread */
static void *cli(void *arg)
{
	struct sockaddr_in addr;
	int conn_fd;

	memset(&addr, 0, sizeof(addr));
	if (inet_pton(AF_INET, ADDR, &(addr.sin_addr)) != 1)
	{
		fprintf(stderr, "Error %s:%d: inet_pton() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);

	conn_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (conn_fd < 0)
	{
		fprintf(stderr, "Error %s:%d: socket() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}

	if (connect(conn_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
	{
		fprintf(stderr, "Error %s:%d: connect() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}

	return NULL;
}

/* Server thread */
static void *srv(void *arg)
{
	struct sockaddr_in addr, cli_addr;
	socklen_t cli_addr_len;
	char addr_str[INET6_ADDRSTRLEN];
	int ret_int, listen_fd, conn_fd;
	ssize_t ret_ssize;
	struct pollfd pollfd_array[1];

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(PORT);

	/* The usual - socket, bind, listen */
	listen_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_fd < 0)
	{
		fprintf(stderr, "Error %s:%d: socket() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}
	if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		fprintf(stderr, "Error %s:%d: bind() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}
	if (listen(listen_fd, 128) < 0)
	{
		fprintf(stderr, "Error %s:%d: listen() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}

	/* Set listening socket non-blocking */
	set_fd_nonblock(listen_fd, true);

	/* Set poll events */
	pollfd_array[0].fd = listen_fd;
	pollfd_array[0].events = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI);

	/* Block in poll until socket is ready for I/O */
	ret_int = poll(pollfd_array, 1, -1);
	if (ret_int < 0)
	{
		fprintf(stderr, "Error %s:%d: poll() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}

	/* Accept new connection */
	cli_addr_len = sizeof(cli_addr);
	conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
	if (conn_fd < 0)
	{
			fprintf(stderr, "Error %s:%d: accept() failed, %s\n", __FILE__, __LINE__, strerror(errno));
			exit(EXIT_FAILURE);
	}

	if (inet_ntop(AF_INET, &(cli_addr.sin_addr), addr_str, sizeof(addr_str)) == NULL)
	{
		fprintf(stderr, "Error %s:%d: inet_ntop() failed, %s\n", __FILE__, __LINE__, strerror(errno));
		exit(EXIT_FAILURE);
	}
	fprintf(stdout, "Accept connection, fd=%d, addr=%s, port=%hu\n",
			conn_fd, addr_str, ntohs(cli_addr.sin_port));

	/* Check if connected socket is non-blocking or blocking */
	if (is_fd_nonblock(conn_fd))
	{
		fprintf(stdout, "Connected socket is non-blocking\n");
	}
	else
	{
		fprintf(stdout, "Connected socket is blocking\n");
	}

	return NULL;
}


int main(void)
{
	int ret_int;
	pthread_t cli_tid, srv_tid;

	fprintf(stdout, "Create server thread\n");
	ret_int = pthread_create(&srv_tid, NULL, &srv, NULL);
	if (ret_int != 0)
	{
		fprintf(stderr, "Error %s:%d: pthread_create() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
		exit(EXIT_FAILURE);
	}

	sleep(1); /* Give server thread time to setup listening socket */

	fprintf(stdout, "Create client thread\n");
	ret_int = pthread_create(&cli_tid, NULL, &cli, NULL);
	if (ret_int != 0)
	{
		fprintf(stderr, "Error %s:%d: pthread_create() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
		exit(EXIT_FAILURE);
	}


	/* Wait for client to exit */
	ret_int = pthread_join(cli_tid, NULL);
	if (ret_int != 0)
	{
		fprintf(stderr, "Error %s:%d: pthread_join() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
		exit(EXIT_FAILURE);
	}

	/* Wait for server to exit */
	ret_int = pthread_join(srv_tid, NULL);
	if (ret_int != 0)
	{
		fprintf(stderr, "Error %s:%d: pthread_join() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
		exit(EXIT_FAILURE);
	}

}


Home | Main Index | Thread Index | Old Index