accept/epoll problem - linux

I have this code that uses epoll and it has a problem. When I run it, it gives output:
Server-socket() is OK...
Server-bind() is OK...
3
4
accept: Invalid argument
I'm running it on ubuntu linux, system updated, both as limited user and root
what is wrong with the input to accept? What should I change?
struct epoll_event ev, events[MAX_EVENTS];
struct sockaddr_in serveraddr;
int listen_sock, conn_sock, nfds, epollfd;
int yes = 1;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
if((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("Server-socket() error lol!");
//just exit lol!
exit(1);
}
printf("Server-socket() is OK...\n");
//"address already in use" error message
/*if(setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("Server-setsockopt() error lol!");
exit(1);
}
printf("Server-setsockopt() is OK...\n");*/
// bind
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = INADDR_ANY;
serveraddr.sin_port = htons(PORT);
memset(&(serveraddr.sin_zero), '\0', 8);
if(bind(listen_sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
{
perror("Server-bind() error lol!");
exit(1);
}
printf("Server-bind() is OK...\n");
epollfd = epoll_create(MAX_EVENTS);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (int n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
struct sockaddr_in clientaddr;
socklen_t addrlen = sizeof(clientaddr);
cout <<listen_sock <<'\n' <<epollfd <<'\n';
conn_sock = accept(listen_sock, (struct sockaddr *) &clientaddr, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
fcntl(conn_sock, F_SETFL, fcntl(conn_sock, F_GETFD, 0)|O_NONBLOCK);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
printf("%d\n", events[n].data.fd);
}
}
}

You forgot to call listen() after bind():
listen(listen_sock, 5);

I built your source and had the same results. The epoll_wait returns immediately even though there is nothing to accept.
It appears to be the case that using epoll_wait instead of listen is not supported behavior.

Related

Socket bind() failed with Invalid argument error for program running on MacOS

I'm trying to run a simple C program which uses the bind() function to bind a IPv4/IPv6 address to a socket.
Below is the code:
int main(int argc, char *argv[])
{
int socket_fd = -1, addrlen = 0, af;
struct sockaddr_storage addr = {0};
unsigned connections = 0;
pthread_t workers[WORKER_NUM] = { 0 };
int client_sock_fds[WORKER_NUM] = { 0 };
char ip_string[64];
if (argc > 1 && strcmp(argv[1], "inet6") == 0) {
af = AF_INET6;
init_sockaddr_inet6((struct sockaddr_in6 *)&addr);
}
else {
af = AF_INET;
init_sockaddr_inet((struct sockaddr_in *)&addr);
}
printf("[Server] Create socket\n");
socket_fd = socket(af, SOCK_STREAM, 0);
if (socket_fd < 0) {
perror("Create socket failed");
goto fail;
}
printf("[Server] Bind socket\n");
addrlen = sizeof(addr);
if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
perror("Bind failed");
goto fail;
}
printf("[Server] Listening on socket\n");
if (listen(socket_fd, 3) < 0) {
perror("Listen failed");
goto fail;
}
printf("[Server] Wait for clients to connect ..\n");
while (connections < WORKER_NUM) {
client_sock_fds[connections] =
accept(socket_fd, (struct sockaddr *)&addr, (socklen_t *)&addrlen);
if (client_sock_fds[connections] < 0) {
perror("Accept failed");
break;
}
if (sockaddr_to_string((struct sockaddr *)&addr, ip_string,
sizeof(ip_string) / sizeof(ip_string[0]))
!= 0) {
printf("[Server] failed to parse client address\n");
goto fail;
}
printf("[Server] Client connected (%s)\n", ip_string);
if (pthread_create(&workers[connections], NULL, run,
&client_sock_fds[connections])) {
perror("Create a worker thread failed");
shutdown(client_sock_fds[connections], SHUT_RDWR);
break;
}
connections++;
}
if (connections == WORKER_NUM) {
printf("[Server] Achieve maximum amount of connections\n");
}
for (int i = 0; i < WORKER_NUM; i++) {
pthread_join(workers[i], NULL);
}
printf("[Server] Shuting down ..\n");
shutdown(socket_fd, SHUT_RDWR);
sleep(3);
printf("[Server] BYE \n");
return EXIT_SUCCESS;
fail:
printf("[Server] Shuting down ..\n");
if (socket_fd >= 0)
close(socket_fd);
sleep(3);
return EXIT_FAILURE;
}
static void
init_sockaddr_inet(struct sockaddr_in *addr)
{
/* 0.0.0.0:1234 */
addr->sin_family = AF_INET;
addr->sin_port = htons(1234);
addr->sin_addr.s_addr = htonl(INADDR_ANY);
}
static void
init_sockaddr_inet6(struct sockaddr_in6 *addr)
{
/* [::]:1234 */
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(1234);
addr->sin6_addr = in6addr_any;
}
When this program is compiled and run on Linux it works without any errors. But when the same program is compiled and run on MacOS, the bind() function returns an "Invalid Argument" error.
I checked the man page for the bind() function to see what the possible reasons for this error could be. The three possible reasons were:
The socket was already bound to another address
The addrlen value passed to the function is incorrect
The addr is not a valid address for this socket's domain
I was able to verify that it wasn't due to the first and third reason.
My question would be as to why does the addrlen value passed to the bind() function throw an "Incorrect argument" error when ran on a Unix system but works perfectly fine when ran on a Linux system?
The addrlen value you pass to bind() must exactly match the address family you specify in socket(). That means addrlen must be set to sizeof(sockaddr_in) for AF_INET, and sizeof(sockaddr_in6) for AF_INET6. Using sizeof(sockaddr_storage) is the wrong value, as sockaddr_storage is designed to be large enough to hold all possible sockaddr_... types, so its size may be larger than sockaddr_in6.
On the other hand, when calling accept(), you need to set addrlen to the full size of addr beforehand, so it knows how much memory it has to work with when writing the client's address to addr. addrlen will be adjusted to the actual size written. However, you can't simply type-cast an int* into a socklen_t*, so addrlen needs to be an actual socklen_t type. Besides, bind() is expecting a socklen_t anyway, not an int.
Try something more like this instead:
int main(int argc, char *argv[])
{
int socket_fd = -1, af;
socklen_t addrlen; // <-- add this!
struct sockaddr_storage addr = {0};
unsigned connections = 0;
pthread_t workers[WORKER_NUM] = { 0 };
int client_sock_fds[WORKER_NUM] = { 0 };
char ip_string[64];
if (argc > 1 && strcmp(argv[1], "inet6") == 0) {
af = AF_INET6;
addrlen = sizeof(struct sockaddr_in6); // <-- add this!
init_sockaddr_inet6((struct sockaddr_in6 *)&addr);
}
else {
af = AF_INET;
addrlen = sizeof(struct sockaddr_in); // <-- add this!
init_sockaddr_inet((struct sockaddr_in *)&addr);
}
printf("[Server] Create socket\n");
socket_fd = socket(af, SOCK_STREAM, 0);
if (socket_fd < 0) {
perror("Create socket failed");
goto fail;
}
printf("[Server] Bind socket\n");
if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) {
perror("Bind failed");
goto fail;
}
printf("[Server] Listening on socket\n");
if (listen(socket_fd, 3) < 0) {
perror("Listen failed");
goto fail;
}
printf("[Server] Wait for clients to connect ..\n");
while (connections < WORKER_NUM) {
addrlen = sizeof(addr); // <-- add this!
client_sock_fds[connections] =
accept(socket_fd, (struct sockaddr *)&addr, &addrlen);
if (client_sock_fds[connections] < 0) {
perror("Accept failed");
break;
}
if (sockaddr_to_string((struct sockaddr *)&addr, ip_string,
sizeof(ip_string) / sizeof(ip_string[0])) != 0) {
printf("[Server] failed to parse client address\n");
goto fail;
}
printf("[Server] Client connected (%s)\n", ip_string);
if (pthread_create(&workers[connections], NULL, run,
&client_sock_fds[connections]) != 0) {
perror("Create a worker thread failed");
shutdown(client_sock_fds[connections], SHUT_RDWR);
break;
}
connections++;
}
if (connections == WORKER_NUM) {
printf("[Server] Achieve maximum amount of connections\n");
}
for (int i = 0; i < connections; i++) { // <-- needs to be the actual thread count, not WORKER_NUM!
pthread_join(workers[i], NULL);
}
printf("[Server] Shuting down ..\n");
shutdown(socket_fd, SHUT_RDWR);
sleep(3);
printf("[Server] BYE \n");
return EXIT_SUCCESS;
fail:
printf("[Server] Shuting down ..\n");
if (socket_fd >= 0)
close(socket_fd);
sleep(3);
return EXIT_FAILURE;
}
That being said, you should use getaddrinfo() instead to initialize the sockaddr_... that you pass to bind(). You should not be initializing it manually at all.
...
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char *argv[])
{
int socket_fd = -1, res;
...
struct addrinfo hints = { 0 };
struct addrinfo *addrs = NULL;
if (argc > 1 && strcmp(argv[1], "inet6") == 0)
hints.ai_family = AF_INET6;
else
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
printf("[Server] Initializing socket address\n");
res = getaddrinfo(NULL, "1234", &hints, &addrs);
if (res != 0) {
fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(res));
goto fail;
}
printf("[Server] Create socket\n");
socket_fd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (socket_fd < 0) {
perror("Create socket failed");
freeaddrinfo(addrs);
goto fail;
}
printf("[Server] Bind socket\n");
if (bind(socket_fd, addrs->ai_addr, addrs->ai_addrlen) < 0) {
perror("Bind failed");
freeaddrinfo(addrs);
goto fail;
}
freeaddrinfo(addrs);
...
}

getsockopt doesn't return error when trying to connect to a closed port

I try to use connect() & O_NONBLOCK to connect to a port in localhost asynchronously. In order to get the connect result, I use getsockopt to check the socket error status. I can sure no one listen to this port, so ECONNREFUSED should be returned. But in my test, this error may not be returned in some cases.
My test program:
int main() {
uint64_t p = 0;
while (1) {
int fd = ::socket(PF_INET, SOCK_STREAM, 0);
assert(fd > 0);
int flags = ::fcntl(fd, F_GETFL, 0);
flags |= O_NONBLOCK;
int ret = ::fcntl(fd, F_SETFL, flags);
assert(ret != -1);
sockaddr_in addr;
bzero(&addr, sizeof(sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_port = htons(25400);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = ::connect(fd, (sockaddr *)&addr, sizeof(sockaddr));
assert(ret != 0);
switch (errno) {
case EINPROGRESS:
case EINTR:
case EISCONN:
break;
default:
assert(false);
}
int epfd = epoll_create1(0);
assert(epfd > 0);
epoll_event ev;
ev.events = EPOLLOUT;
ev.data.fd = fd;
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
assert(ret == 0);
epoll_event events;
auto nfds = epoll_wait(epfd, &events, 1, -1);
assert(nfds == 1);
assert(events.data.fd == fd);
int err;
socklen_t optlen = static_cast<socklen_t>(sizeof err);
ret = ::getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen);
assert(ret == 0);
assert(err != 0);
::close(fd);
::close(epfd);
if (p++ % 10000 == 0) {
std::cout << p << std::endl;
}
}
}
assert(err != 0); will be triggered.
In this test code, I use connect() without bind(), in this case, the source port is choosen by kernel. So the source port may be the same as destination port 25400. Due to the destination ip address is localhost, this TCP connection can be established in this case.

Why is the Write system call in not writing anything to the sockets file?

I am writing a simple Server Client program that exchanges the data. After I write to the socket file, the write doesn't fail or it is not even partial write. but when I check the details of the socket file using ls -l , I still see it's size as zero and server doesn't recieve anything. Can anyone help me out with what I am doing wrong in here??
This is server.c
int main()
{
int socket_fd = 0;
struct sockaddr_un addr;
int result = -1;
char buffer[MAX_BUFFER_SIZE];
printf("Creating a socket\n");
socket_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(socket_fd == -1)
{
perror("SOCKET");
return 0;
}
printf("Socket has been created %d\n",socket_fd);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path,_SOCKET_PATH,sizeof(addr.sun_path)-1);
printf("PATH : %s\n",addr.sun_path);
if(remove(_SOCKET_PATH) == -1)
{
perror("REMOVE");
return 0;
}
printf("Binding the socket\n");
result = bind(socket_fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_un));
if(result == -1)
{
perror("BIND");
return 0;
}
printf("Binding the socket is done\n");
printf("Listening to the socket\n");
if(listen(socket_fd,1) == -1)
{
perror("Listen");
return 0;
}
if((result = accept(socket_fd,NULL,NULL)) == -1)
{
perror("ACCEPT");
return 0;
}
printf("Connection Accepted\n");
while (1)
{
while (result = read(socket_fd,buffer,sizeof(buffer)-1) > 0)
{
printf("Client said : %s\n",buffer);
}
}
}
This is client.c
int main()
{
int socket_fd = 0;
struct sockaddr_un addr;
int result = -1;
char buffer[MAX_BUFFER_SIZE];
printf("Creating a socket\n");
socket_fd = socket(AF_UNIX,SOCK_STREAM,0);
if(socket_fd == -1)
{
perror("SOCKET");
return 0;
}
printf("Socket has been found %d\n",socket_fd);
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path,_SOCKET_PATH,sizeof(addr.sun_path)-1);
printf("Connecting to the socket\n");
result = connect(socket_fd,(struct sockaddr *)&addr,sizeof(struct sockaddr_un));
if(result == -1)
{
perror("CONNECT");
return 0;
}
printf("The client is connected to the server.\n");
while (1)
{
memset(buffer,0,sizeof(buffer));
scanf("%s",buffer);
printf("DATA WRITTEN %s,%d\n",buffer,strlen(buffer)+1);
result = write(socket_fd,buffer,strlen(buffer)+1);
printf("result = %d\n",result);
sleep(5);
}
}
Thanks for any help!
if((result = accept(socket_fd,NULL,NULL)) == -1)
...
while (result = read(socket_fd,buffer,sizeof(buffer)-1) > 0)
You are trying to read from the server socket (socket_fd). Instead you need to read from the new socket returned by accept, i.e. what you call result in result = accept.... To cite from man accept:
On success, these system calls return a nonnegative integer that is a
file descriptor for the accepted socket. On error, -1 is returned,
and errno is set appropriately.

Does libevent support netlink socket

I use netlink to receive an interrupt number from kernel. The application in user space uses libevent to handle TCP/IP request and netlink message. Does libevent support Linux netlink socket? I will appreciate for a simple example.
Yes, libevent supports netlink socket.
There is https://github.com/libevent/libevent/blob/master/sample/hello-world.c, it is modified below to listen to netlink socket.
The basic example listens to Linux network interface creation/deletion and can be executed with sudo to gain privilege needed. It listens to same events as ip monitor link.
Another example of listening to RAW sockets with libevent is here https://github.com/bodgit/libevent-natpmp/blob/master/natpmp.c.
static void link_recvmsg(int fd, short event, void *arg)
{
char buf[NLMSG_SPACE(BUF_SIZE)] = {0};
socklen_t socklen;
struct iovec iov = {.iov_base = buf, .iov_len = sizeof(buf)};
struct sockaddr addr;
memset(&addr, 0, sizeof(struct sockaddr));
if (!fd || -1 == fd)
return;
int status = getsockname(fd, &addr, &socklen);
if(-1 == status)
return;
struct msghdr mh = {.msg_name = NULL, .msg_namelen = 0, .msg_iov = &iov, .msg_iovlen = 1,
.msg_flags = 0, .msg_name = &addr, .msg_namelen = sizeof(struct sockaddr)};
status = recvmsg(fd, &mh, 0);
if ((-1 == status) && ((EINTR == errno) || (EAGAIN == errno)))
return;
if(-1 == status)
return;
if ((mh.msg_flags & MSG_TRUNC) == MSG_TRUNC)
return;
if ((mh.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
return;
for (const struct nlmsghdr *h = (struct nlmsghdr *)buf; NLMSG_OK(h, status); h = NLMSG_NEXT(h, status)) {
switch (h->nlmsg_type) {
case RTM_NEWLINK:
fprintf(stderr, "got RTM_NEWLINK\n");
break;
case RTM_DELLINK:
fprintf(stderr, "got RTM_DELLINK\n");
break;
default:
fprintf(stderr, "unexpected case in swtch statement\n");
break;
}
}
}
int main(int argc, char **argv)
{
/* some init code here */
/* NETLINK socket */
int status;
int buf_size = BUF_SIZE;
struct sockaddr_nl src_addr;
int socket_nl = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, NETLINK_ROUTE);
if(-1 == socket_nl) return -1;
memset(&src_addr, 0, sizeof(struct sockaddr_nl));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
src_addr.nl_groups |= RTNLGRP_LINK;
status = setsockopt(socket_nl, SOL_SOCKET, SO_RCVBUF,
&buf_size, sizeof(buf_size));
if(-1 == status) return -1;
status = bind(socket_nl, (struct sockaddr *)&src_addr, sizeof(struct sockaddr_nl));
if(status < 0) return -1;
static struct event nl_ev;
event_set(&nl_ev, socket_nl, EV_READ|EV_PERSIST, link_recvmsg,
NULL);
if (base) {
event_base_set(base, &nl_ev);
}
event_add(&nl_ev, NULL);
/* some other code, dispatch event and deinit */
}

How can I print client sun_paths name at server side in PF_UNIX socket?

#define NAME "server"
main()
{
int sock, msgsock, rval;
int pid,len;
struct sockaddr_un server,clientv;
char bufRead[1024];
char bufWrite[1024];
unlink(NAME);
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
{
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, NAME);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)))
{
perror("binding stream socket");
exit(1);
}
printf("Socket has name %s\n", server.sun_path);
listen(sock, 5);
msgsock = accept(sock, (struct sockaddr *)&clientv, &len);
if (msgsock == -1)
perror("accept");
printf("clientv add %s\n",clientv.sun_path);
}
but when I connect client gives the output:
Socket has name server
clientv add LK�ĿX�MK
accept() doesn't fill .sun_path in, so you need to get it manually using getsockname(). Should be something like:
struct sockaddr_storage storage;
socklen_t storage_len = sizeof(struct sockaddr_storage);
struct sockaddr_un *clientv = (struct sockaddr_un *)&storage;
if (0 == getsockname(sock, (struct sockaddr *)&clientv, &storage_len)) {
printf("clientv add %s\n", clientv.sun_path);
}

Resources