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;
}

Reply via email to