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

/* This program allows the server in test-case-unix-socket-exit to exit
 * before its client, a condition which exposed an error in SimpleFlow.
 * Using nc did not trigger this condition.
 *
 * We used to rely on i_security for Unix-socket taint tacking. However, if the
 * peer process exits before a process reads from a Unix socket, then the
 * struct sock associated with the peer process no longer exists. Without
 * this, the kernel cannot trace back to the peer process's inode structure.
 * Thus we explicitly track whether a socket it tainted directly in its
 * sk_security structure.
 */

int main(int argc, char *argv[]) {
  char *socket_path = NULL;
  struct sockaddr_un addr;
  char buf[BUFSIZ];
  int fd,cl,rc;

  if (argc != 2) {
    fprintf(stderr, "usage: %s sockpath\n", argv[1]);
  }

  socket_path = argv[1];

  if (-1 == (fd = socket(AF_UNIX, SOCK_STREAM, 0))) {
    perror("socket error");
    exit(EXIT_FAILURE);
  }

  memset(&addr, 0, sizeof(addr));
  addr.sun_family = AF_UNIX;
  strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);

  unlink(socket_path);

  if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
    perror("bind error");
    exit(EXIT_FAILURE);
  }

  if (listen(fd, 5) == -1) {
    perror("listen error");
    exit(EXIT_FAILURE);
  }

  if ( (cl = accept(fd, NULL, NULL)) == -1) {
    perror("accept error");
    exit(EXIT_FAILURE);
  }

  while ((rc = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
    write(cl, buf, rc);
  }
  if (rc == -1) {
    perror("read");
    exit(EXIT_FAILURE);
  } else if (rc == 0) {
    close(cl);
  } 

  exit(EXIT_SUCCESS);
}
