Hi, Looking into the socklog package test errors a bug was found in the pflocal socket implementation, specifically in connect(): 1) PID1: create a local named socket using a server to receive data PID2: connect to that socket with a client to send data everything is fine :)
Terminal1: ./test_socket_server.DGRAM test_socket_server:main(): Domain = AF_UNIX, Type = SOCK_DGRAM test_socket_server:main(): Removing old socket: 'test_socket' test_socket_server:socket_unix(): socket() socket_fd = 3 test_socket_server:read_socket(): Waiting for incoming connections... Terminal2: ./test_socket_client.DGRAM test_socket_client:main(): Domain = AF_UNIX, Type = SOCK_DGRAM test_socket_client:main(): socket_name = 'test_socket' test_socket_client:write_socket(): connect(): err = '(os/kern) successful' test_socket_client:write_socket(): send() err = '(os/kern) successful' Terminal1: test_socket_server:read_socket(): read() Message = 'Testing, testing, ...' 2) kill $PID1 to leave the socket behind PID2: connect to that socket with a client to send data On hurd this is not detected: /test_socket_client.DGRAM test_socket_client:main(): Domain = AF_UNIX, Type = SOCK_DGRAM test_socket_client:main(): socket_name = 'test_socket' test_socket_client:write_socket(): connect(): err = '(os/kern) successful' <-- BUG IS HERE test_socket_client:write_socket(): send() err = '(os/kern) successful' Expected output e.g. on Linux (or with AF_INET): ./test_socket_client.DGRAM test_socket_client:main(): Domain = AF_UNIX, Type = SOCK_DGRAM test_socket_client:main(): socket_name = 'test_socket' test_socket_client:write_socket(): connect(): err = 'Connection refused' <-- correct ECONNREFUSED is returned test_socket_client:write_socket(): connect(): Connection refused test_socket_client:main(): write_socket() SOCK_DGRAM: Connection refused Programs are attached to cover the following cases selected in defs.inc: AF_UNIX+SOCK_DGRAM, AF_UNIX+SOCK_STREAM (pflocal is buggy) AF_INET+SOCK_DGRAM, AF_INET+SOCK_STREAM (pfinet is fine) I've tried to find out how to detect that the socket server is gone but not found a solution yet. Functions tried in pflocal/sock.c: pipe_acquire_reader(pipe): modifies the reference count and locking problems pipe_acquire_writer(pipe): modifies the reference count and locking problems pipe_is_readable(pipe, 0/1): always returns false pipe_wait_readable(pipe, 1, 0/1): always returns EAGAIN, setting noblock to 0 makes the box freeze :( pipe_wait_writable(pipe, 0/1): Always returns 0 since pipe->readable(pipe, 1)=0 and pipe->write_limit=16384 (another bug?) The test calls were placed in the function: void connect() planned to return an error code instead. Hopefully the problem is described detailed enough.
#include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <err.h> #include <error.h> #include <errno.h> #include "defs.inc" #ifdef USE_SOCK_STREAM #define type SOCK_STREAM #else #define type SOCK_DGRAM #endif /* FIXME: Support AF_INET */ #ifdef USE_AF_UNIX #define domain AF_UNIX #else #define domain AF_INET #endif #define SOCKET_NAME "test_socket" #define DATA "Testing, testing, ..." int write_socket(const char* socket_name) { int err = 0, socket_fd, write_size; #ifdef USE_AF_UNIX struct sockaddr_un server; int c = sizeof(struct sockaddr_un); #else struct sockaddr_in server; int c = sizeof(struct sockaddr_in); #endif /* Create socket */ socket_fd = socket (domain, type, 0); if (socket_fd == -1) { perror("test_socket_client:write_socket(): socket()"); return -1; } #ifdef USE_AF_UNIX server.sun_family = domain; strncpy (server.sun_path, socket_name, sizeof(server.sun_path)); #else server.sin_family = domain; server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_port = htons( 8888 ); #endif /* Connect to socket_fd */ err = connect (socket_fd, (struct sockaddr *)&server, c); printf("test_socket_client:write_socket(): connect(): err = '%s'\n", strerror(errno)); if (err) { close(socket_fd); #ifdef EDESTADDRREQ if (errno == EDESTADDRREQ) errno = ECONNREFUSED; #endif perror("test_socket_client:write_socket(): connect()"); return err; } #if 0 write_size = write (socket_fd, DATA, sizeof(DATA)); printf("test_socket_client:write_socket(): write(): err = '%s'\n", strerror(errno)); #else write_size = send (socket_fd, DATA, sizeof(DATA), 0); printf("test_socket_client:write_socket(): send() err = '%s'\n", strerror(errno)); #endif if (write_size == -1) { #ifdef USE_SOCK_STREAM perror("test_socket_client:write_socket(): write(): Sending on stream socket"); #else perror("test_socket_client:write_socket(): write(): Sending on datagram socket"); #endif close(socket_fd); return err; } close(socket_fd); return err; } int main(void) { const char* socket_name = SOCKET_NAME; int err = 0; printf ("test_socket_client:main(): Domain = %s, Type = %s\n", (domain == AF_UNIX) ? "AF_UNIX": "AF_INET", (type == SOCK_STREAM) ? "SOCK_STREAM": "SOCK_DGRAM"); printf("test_socket_client:main(): socket_name = '%s'\n", socket_name); err = write_socket(socket_name); if (err) { /* Redundant */ //printf("test_socket_client:main(): write_socket() err = '%s'\n", strerror(errno)); #ifdef USE_SOCK_STREAM perror("test_socket_client:main(): write_socket() SOCK_STREAM"); #else perror("test_socket_client:main(): write_socket() SOCK_DGRAM"); #endif } return errno; }
/* File: defs.inc: Case definitions for test_socket_server and test_socket_client */ #define USE_SOCK_STREAM #undef USE_SOCK_STREAM #define USE_AF_UNIX //#undef USE_AF_UNIX
/* File: test_socket_server.c */ #include <sys/types.h> #include <sys/stat.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <err.h> #include <error.h> #include <errno.h> #include "defs.inc" #ifdef USE_SOCK_STREAM #define type SOCK_STREAM #else #define type SOCK_DGRAM #endif /* FIXME: Support AF_INET */ #ifdef USE_AF_UNIX #define domain AF_UNIX #else #define domain AF_INET #endif #define SOCKET_NAME "test_socket" int socket_unix (const char* socket_name) { int err = 0, socket_fd; #ifdef USE_AF_UNIX struct sockaddr_un server; #else struct sockaddr_in server; #endif /* Create socket */ socket_fd = socket (domain, type, 0); if (socket_fd == -1) { perror("test_socket_server:socket_unix(): socket()"); return -1; } printf ("test_socket_server:socket_unix(): socket() socket_fd = %d\n", socket_fd); #ifdef USE_AF_UNIX server.sun_family = domain; strncpy (server.sun_path, socket_name, sizeof(server.sun_path)); #else server.sin_family = domain; server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_port = htons( 8888 ); #endif /* Bind a name to socket_fd */ err = bind (socket_fd, (struct sockaddr*) &server, sizeof(server)); if (err == -1) { perror ("test_socket_server:socket_unix(): bind()"); return -1; } #ifdef USE_SOCK_STREAM /* Listen on socket_fd */ err = listen (socket_fd, 5); if (err == -1) { perror ("test_socket_server:socket_unix(): listen()"); return -1; } printf ("test_socket_server:socket_unix(): listen() Listening on socket_fd = '%d'\n", socket_fd); #endif return socket_fd; } #define BUF_LEN 1024 int read_socket (int socket_fd) { char buf[BUF_LEN]; int read_size, err = 0;; printf( "test_socket_server:read_socket(): Waiting for incoming connections...\n"); #ifdef USE_SOCK_STREAM struct sockaddr_in client; int c = sizeof (struct sockaddr_in); int client_sock; /* Accept connection from an incoming client */ client_sock = accept (socket_fd, (struct sockaddr *)&client, (socklen_t*)&c); if (client_sock == -1) { perror ("test_socket_server:read_socket(): accept()"); return -1; } #endif /* Receive a message from client */ #if 1 do { bzero(buf, sizeof(buf)); #ifdef USE_SOCK_STREAM if ((read_size = read (client_sock, buf, BUF_LEN)) < 0) #else if ((read_size = read (socket_fd, buf, BUF_LEN)) < 0) #endif perror("test_socket_server:read_socket(): read() reading stream message"); else if (read_size == 0) printf("test_socket_server:read_socket(): read() Ending connection\n"); else printf("test_socket_server:read_socket(): read() Message = '%s'\n", buf); } while (read_size > 0); #else #ifdef USE_SOCK_STREAM while ((read_size = recv (client_sock, buf, BUF_LEN, 0)) > 0) #else while ((read_size = recv (socket_fd, buf, BUF_LEN, 0)) > 0) #endif { //Send the message back to client //write (client_sock , buf , strlen(buf)); printf ("test_socket_server:read_socket(): recv() client_message = '%s'\n", buf); } if (read_size == 0) { printf ("test_socket_server:read_socket(): recv() Client disconnected\n"); fflush (stdout); } else if(read_size == -1) perror ("test_socket_server:read_socket(): recv()"); #endif return err; } int file_exist (const char *filename) { struct stat buffer; return (stat (filename, &buffer) == 0); } int main(void) { const char* socket_name = SOCKET_NAME; int socket_fd; int err = 0; printf ("test_socket_server:main(): Domain = %s, Type = %s\n", (domain == AF_UNIX) ? "AF_UNIX": "AF_INET", (type == SOCK_STREAM) ? "SOCK_STREAM": "SOCK_DGRAM"); if (file_exist (socket_name)) { printf ("test_socket_server:main(): Removing old socket: '%s'\n", socket_name); unlink (socket_name); } socket_fd = socket_unix(socket_name); if (socket_fd == -1) { perror ("test_socket_server: unable to create socket"); return -1; } err = read_socket(socket_fd); if (err) { /* Redundant */ //printf ("test_socket_client:main(): read_socket() err = '%s'\n", strerror(errno)); #ifdef USE_SOCK_STREAM perror ("test_socket_client:main(): read_socket() SOCK_STREAM"); #else perror ("test_socket_client:main(): read_socket() SOCK_DGRAM"); #endif } //close(socket_fd); if (file_exist (socket_name)) unlink (socket_name); return err; }