/*
 * client.c
 * Main code for the client 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 = "netstrain";

/* internal functions */
static void usage(void);
static void setup_socket(const char *hostspec, const char *portspec);
static void send_handshake(int hs);

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


int main(int argc, char **argv)
{
  int ch;
  const char *hostspec = NULL;
  const char *portspec = NULL;
  int my_handshake, his_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 != 3)
    usage();
  hostspec = argv[0];
  portspec = argv[1];
  if (strcmp(argv[2], "send") == 0) {
    my_handshake = 1;
    his_handshake = 0;
  } else if (strcmp(argv[2], "recv") == 0) {
    my_handshake = 0;
    his_handshake = 1;
  } else if (strcmp(argv[2], "both") == 0) {
    my_handshake = 1;
    his_handshake = 1;
  } else {
    fprintf(stderr, "Unknown direction (%s)\n", argv[2]);
    usage();
    exit(1);
  }

  transfer_init();
  setup_socket(hostspec, portspec);
  send_handshake(his_handshake);
  stats_init();
  transfer_run(sock, my_handshake);

  return 0;
}

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

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

  printf("Looking up hostname %s...\n", hostspec);

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = family;
  hints.ai_socktype = SOCK_STREAM;
  gai_error = getaddrinfo(hostspec, 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("Connecting to %s port %s using %s...\n",
	   hostaddr, portaddr,
	   (ai->ai_family == AF_INET) ? "IPv4" :
	   ((ai->ai_family == AF_INET6) ? "IPv6" : "unknown proto")
	  );

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

    if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
      error("connect");
      close(sock);
      continue;
    }

    freeaddrinfo(aitop);

    printf("Connected\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 void send_handshake(int hs)
{
  int got;
  unsigned char c = (unsigned char)hs;

  for(;;) {
    got = write(sock, &c, 1);
    if (got == 0) {
      /* nothing */
    } else if (got < 0) {
      if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
        bailout("write");
      }
    } else {
      break;
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1