/*
 * Copyright (c) 2003-2012
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

/*
 * sslclient - copy stdin to a server over an SSL connection
 *
 * Usage: sslclient [options] [-] server[:port]
 * See usage() for details.
 *
 * Uses the OpenSSL library.
 * Exit status is 1 if an error occurred, 0 otherwise.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2012\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: sslclient.c 2546 2012-01-11 19:47:24Z brachman $";
#endif

#include "dacs_ssl.h"

static MAYBE_UNUSED char *log_module_name = "sslclient";

Ssl_global_conf global_conf;

#ifndef PROG

static void
usage()
{

  fprintf(stderr, "Usage: sslclient %s [options] [-] server[:port]\n",
		  standard_command_line_usage);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -caf|--ca_cert_file filename:    CA certificate file\n");
  fprintf(stderr, "  -cad|--ca_cert_dir dirname:      CA certificate directory\n");
  fprintf(stderr, "  -ccf|--cert_chain_file filename: certificate chain file\n");
  fprintf(stderr, "  -C|--ciphers cipherstring:       cipher list\n");
  fprintf(stderr, "  -dvp|--default_verify_paths:     use default verification paths\n");
  fprintf(stderr, "  -h|--help:                       print this usage blurb\n");
  fprintf(stderr, "  -kf|--key_file filename:         private key file\n");
  fprintf(stderr, "  -kft|--key_file_type {pem,asn1}: private key file type\n");
  fprintf(stderr, "  -p|-sp|--server_port portnum:    override default server port (default: %s)\n", DEFAULT_SERVER_PORT);
  fprintf(stderr, "  -r|--random filename:            PRNG seed file\n");
  fprintf(stderr, "  -sm|--server_match regex:        regex must match server's cert's identity\n");
  fprintf(stderr, "  -vd|--verify_depth depth:        certificate verification depth\n");
  fprintf(stderr, "  -vt|--verify_type {none,peer}:   certificate verification type\n");
  fprintf(stderr, "  --:                              end of option flags\n");
  fprintf(stderr, "\nThe server argument is the hostname or IP address of");
  fprintf(stderr, " the server to connect to.\n");
  fprintf(stderr, "If a port number follows the hostname, it overrides any");
  fprintf(stderr, " default.\n");

  exit(1);
}

int
sslclient_main(int argc, char **argv, int do_init, void *main_out)
{
  int i;
  char *errmsg, *p, *remote_addr, *server, *server_port;
  DACS_app_type app_type;
  Ssl_conf conf;

  if ((remote_addr = getenv("REMOTE_ADDR")) == NULL)
	app_type = DACS_UTILITY_OPT;
  else
	app_type = DACS_UTILITY_PROC;

  server = NULL;
  server_port = DEFAULT_SERVER_PORT;
  ssl_init_defaults(&conf);

  if (dacs_init(app_type, &argc, &argv, NULL, &errmsg) == -1) {
    fprintf(stderr, "sslclient: %s\n", errmsg);
    usage();
	/*NOTREACHED*/
  }

  for (i = 1; i < argc; i++) {
	if (streq(argv[i], "-caf") || streq(argv[i], "--ca_cert_file")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  conf.ca_cert_file = argv[i];
	}
	else if (streq(argv[i], "-cad") || streq(argv[i], "--ca_cert_dir")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  conf.ca_cert_dir = argv[i];
	}
	else if (streq(argv[i], "-ccf") || streq(argv[i], "--cert_chain_file")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  conf.cert_chain_file = argv[i];
	}
	else if (streq(argv[i], "-C") || streq(argv[i], "--ciphers")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  conf.cipher_list = argv[i];
	}
	else if (streq(argv[i], "-dvp")
			 || streq(argv[i], "--default_verify_paths")) {
	  conf.use_default_verify_paths = 1;
	}
	else if (streq(argv[i], "-kf") || streq(argv[i], "--key_file")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  conf.key_file = argv[i];
	}
	else if (streq(argv[i], "-kft") || streq(argv[i], "--key_file_type")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  if (strcaseeq(argv[i], "pem"))
		conf.key_file_type = SSL_FILETYPE_PEM;
	  else if (strcaseeq(argv[i], "asn1"))
		conf.key_file_type = SSL_FILETYPE_ASN1;
	  else {
		usage();
		/*NOTREACHED*/
	  }
	}
	else if (streq(argv[i], "-p") || streq(argv[i], "-sp")
			 || streq(argv[i], "--server_port")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  server_port = argv[i];
	}
	else if (streq(argv[i], "-r") || streq(argv[i], "--random")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  conf.rand_seed_file = argv[i];
	}
	else if (streq(argv[i], "-sm") || streq(argv[i], "--server_match")) {
	  int flags, st;
	  char *expr;
	  regex_t *regex;
	  Ssl_peer_match *sm;

	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  expr = argv[i];
	  flags = REG_EXTENDED | REG_ICASE;
	  regex = ALLOC(regex_t);
	  if ((st = regcomp(regex, expr, flags)) != 0) {
		char errbuf[128];

		regerror(st, regex, errbuf, sizeof(errbuf));
		fprintf(stderr, "sslclient: regex compilation error: %s\n", errbuf);
		usage();
		/*NOTREACHED*/
	  }
	  if (conf.peer_match_vec == NULL)
		conf.peer_match_vec = dsvec_init(NULL, sizeof(Ssl_peer_match *));
	  sm = ALLOC(Ssl_peer_match);
	  sm->regex_str = strdup(expr);
	  sm->regex = regex;
	  dsvec_add_ptr(conf.peer_match_vec, sm);
	}
	else if (streq(argv[i], "-vd") || streq(argv[i], "--verify_depth")) {
	  if (++i == argc || argv[i][0] == '\0')
		usage();
	  conf.verify_depth = strtol(argv[i], &p, 10);
	  if (conf.verify_depth < 0 || *p != '\0') {
		usage();
		/*NOTREACHED*/
	  }
	}
	else if (streq(argv[i], "-vt") || streq(argv[i], "--verify_type")) {
	  if (++i == argc || argv[i][0] == '\0') {
		usage();
		/*NOTREACHED*/
	  }
	  if (strcaseeq(argv[i], "none")) {
		conf.verify_type = SSL_VERIFY_NONE;
		conf.verify_allow_self_signed = 1;
	  }
	  else if (strcaseeq(argv[i], "peer"))
		conf.verify_type = SSL_VERIFY_PEER;
	  else {
		usage();
		/*NOTREACHED*/
	  }
	}
	else if (streq(argv[i], "--")) {
	  i++;
	  break;
	}
	else if (streq(argv[i], "-h") || streq(argv[i], "--help")) {
	  usage();
	  /*NOTREACHED*/
	}
	else if (argv[i][0] != '-')
	  break;
	else {
	  usage();
	  /*NOTREACHED*/
	}
  }

  conf.verbose_flag = verbose_level;

  if (conf.peer_match_vec != NULL)
	dsvec_add_ptr(conf.peer_match_vec, NULL);

  if (argv[i] == NULL || argv[i][0] == '-' || argv[i + 1] != NULL) {
	usage();
	/*NOTREACHED*/
  }

  server = strdup(argv[i]);
  if ((p = strchr(server, ':')) != NULL) {
	*p++ = '\0';
	server_port = p;
  }

  log_msg((LOG_DEBUG_LEVEL, "SSL transfer to server \"%s:%s\"",
		   server, server_port));

  /* Use the library's default stdin/stdout data functions. */
  if (ssl_transfer(server, server_port, &conf, NULL, NULL, NULL, NULL) == -1) {
	log_msg((LOG_ERROR_LEVEL, "SSL transfer to server \"%s:%s\" failed",
			 server, server_port));
	return(-1);
  }

  return(0);
}

#else

int
main(int argc, char **argv)
{
  int rc;
  extern int sslclient_main(int, char **, int, void *);

  if ((rc = sslclient_main(argc, argv, 1, NULL)) == 0)
	exit(0);

  exit(1);
}
#endif
