/*
 * server.c
 * Main code for the server side of things.
 *
 * Copyright (c) 2002 Christoph Pfisterer
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

#include "netstrain.h"

/* error reporting settings */
const char *progname = "netstraind";

/* internal functions */
static void usage(void);
static void setup_socket(const char *portspec);
static int wait_for_connection();

/* internal data */
static int family = PF_UNSPEC;
static int listen_sock;
static int sock;


int main(int argc, char **argv)
{
  int ch;
  const char *portspec;
  int handshake;

  printf(PRODUCT_NAME " " PRODUCT_VERSION "  " PRODUCT_COPYRIGHT "\n");

  while ((ch = getopt(argc, argv, "46hv")) != -1)
    switch (ch) {
    case '4':
      family = PF_INET;
      break;
    case '6':
      family = PF_INET6;
      break;
    case 'v':
      /* we have already printed version info */
      exit(0);
    case 'h':
    case '?':
    default:
      usage();
    }

  argc -= optind;
  argv += optind;

  if (argc != 1)
    usage();
  portspec = argv[0];

  transfer_init();
  setup_socket(portspec);
  handshake = wait_for_connection();
  transfer_run(sock, handshake);

  return 0;
}

static void usage(void)
{
  fprintf(stderr, "Usage: %s [-46] <port>\n", progname);
  exit(1);
}

static void setup_socket(const char *portspec)
{
  struct addrinfo hints, *ai, *aitop;
  int gai_error, tries;
  char hostaddr[NI_MAXHOST], portaddr[NI_MAXSERV];

  memset(&hints, 0, sizeof(hints));
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = family;
  hints.ai_socktype = SOCK_STREAM;
  gai_error = getaddrinfo(NULL, portspec, &hints, &aitop);
  if (gai_error)
    bailout_n("getaddrinfo: %s", gai_strerror(gai_error));

  for (tries = 0, ai = aitop; ai != NULL; ai = ai->ai_next) {
    if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
      continue;
    tries++;

    if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
                    hostaddr, sizeof(hostaddr), portaddr, sizeof(portaddr),
                    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
      error("getnameinfo");
      continue;
    }

    printf("Listening on %s port %s using %s...\n",
           hostaddr, portaddr,
           (ai->ai_family == AF_INET) ? "IPv4" :
           ((ai->ai_family == AF_INET6) ? "IPv6" : "unknown proto")
	   );

    listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (listen_sock < 0) {
      error("socket");
      continue;
    }

    if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
      error("bind");
      close(sock);
      continue;
    }
    if (listen(listen_sock, 1) < 0) {
      error("listen");
      close(sock);
      continue;
    }

    freeaddrinfo(aitop);

    printf("One-shot server waiting for connection\n");
    return;
  }

  freeaddrinfo(aitop);

  if (tries == 0) {
    bailout_n("Didn't get any usable addresses");
  } else if (tries > 1) {
    bailout_n("All addresses failed");
  }
  exit(1);
}

static int wait_for_connection()
{
  struct sockaddr_storage hisaddr;
  int al;
  char hostaddr[NI_MAXHOST], portaddr[NI_MAXSERV];
  int got;
  unsigned char c;

  al = sizeof(hisaddr);
  sock = accept(listen_sock, (struct sockaddr *)&hisaddr, &al);
  if (sock < 0)
    bailout("accept");

  close(listen_sock);

  if (getnameinfo((struct sockaddr *)&hisaddr, al,
		  hostaddr, sizeof(hostaddr), portaddr, sizeof(portaddr),
		  NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
    bailout("getnameinfo");
  }

  printf("Incoming connection from %s port %s\n",
	 hostaddr, portaddr);

  for(;;) {
    got = read(sock, &c, 1);
    if (got == 0) {
      printf("Got EOF from socket while waiting for handshake\n");
      close(sock);
      exit(0);
    } else if (got < 0) {
      if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
	bailout("read");
      }
    } else {
      return (int)c;
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1