Linux local communication sockets: Why is bind() not failing as expected?

819 views Asked by At

I have this server application that sets up a local communication stream socket for clients to connect to. If a second instance of the server is launched and it tries to bind to the same address (file name), bind() should fail with EADDRINUSE. But it does not. Why?

Here is pared-down code that showcases the problem:

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>

int make_socket(const char *filename, int style) {
   struct sockaddr_un name;
   int sock;
   size_t size;

   sock = socket(PF_LOCAL, style, 0);
   if (sock < 0) {
      perror("socket");
      exit(EXIT_FAILURE);
   }

   if ((!filename) || (filename[0] == '\0')) {
      return sock;
   }

   name.sun_family = AF_LOCAL;
   strncpy(name.sun_path, filename, sizeof(name.sun_path));
   name.sun_path[sizeof(name.sun_path) - 1] = '\0';
   size = SUN_LEN(&name);

   if (bind(sock, (struct sockaddr *) &name, size) < 0) {
      perror("bind");
      exit(EXIT_FAILURE);
   }

   return sock;
}

int make_stream_socket(const char *filename) {
   return make_socket(filename, SOCK_STREAM);
}

#define TS_SERVER  "/tmp/socket-server"

int main(int argc, char *argv[]) {
   int sock_server, sock_client;
   struct sockaddr_un name_client;
   size_t size_name_client;
   int i;
   fd_set client_set, selected_set;
   int quitting = 0;

   fprintf(stderr, "Server: starting up\n");
   unlink(TS_SERVER);
   sock_server = make_stream_socket(TS_SERVER);
   fprintf(stderr, "Server: accepting connections on %d\n", sock_server);
   if (0 > listen(sock_server, 1)) {
      perror("listen(connection requests)");
      exit(EXIT_FAILURE);
   }
   FD_ZERO(&client_set);
   FD_SET(sock_server, &client_set);
   while (!quitting) {
      fprintf(stderr, "Server: waiting for connections\n");
      selected_set = client_set; //shallow-clone client_set
      if (0 > select(FD_SETSIZE, &selected_set, NULL, NULL, NULL)) {
         perror("select(for readability)");
         exit(EXIT_FAILURE);
      }

      fprintf(stderr, "Server: connection(s) received\n");
      for (i = 0; i < FD_SETSIZE; ++i) {
         if (FD_ISSET(i, &selected_set)) {
            if (i == sock_server) {
               fprintf(stderr, "Server: accepting connection\n");
               size_name_client = sizeof(name_client);
               sock_client = accept(sock_server,
                                    (struct sockaddr *) &name_client, 
                                    (socklen_t *) &size_name_client);
               //Ubuntu Launchpad bug 1463553: accept() returns
               // an off-by-one size_name_client
               size_name_client = SUN_LEN(&name_client);
               if (sock_client < 0) {
                  perror("accept(connection request)");
                  exit(EXIT_FAILURE);
               }
               fprintf(stderr, "Server: accepted connection request from '%s' on %d\n",
                               name_client.sun_path, sock_client);
               quitting = 1;
               close(sock_client);
               break; //out of the for
            } //if (i == sock_server)
         } //if (FD_ISSET(i, &selected_set))
      } //for
   } //while

   fprintf(stderr, "Server: shutting down\n");
   close(sock_server);
   unlink(TS_SERVER);

   exit(EXIT_SUCCESS);
}

After compilation, run a first instance from the command line, then run a second instance from another command line. The second instance should fail on bind(), but does not.

1

There are 1 answers

3
davmac On BEST ANSWER

Because:

  unlink(TS_SERVER);

You remove the existing socket file, which allows a new one to be created in its place.