
/* Gconnect - codename:Blue Sky Effort (C)  2003 l0rdloki@digitalsynapse-ks.com 
 *
 * Original copyright notice for Proxytunnel - (C) 2001-2002 Jos Visser / Mark Janssen
 * Contact:                  josv@osp.nl / maniac@maniac.nl 
*/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "stdheaders.h"


#ifndef HAVE_GETOPT_LONG
	char *optarg;
#else
#include <getopt.h>
#endif

#include "cmdline.h"

static unsigned	addressForName(char *name)
{
  unsigned address = INADDR_NONE;
  struct hostent *hostInfo;
  
  if(name == NULL || *name == 0)
    {
      address = INADDR_ANY;
    }
  else
    {
      address = inet_addr(name);
      if((int)address == -1)
	{		
	  hostInfo = gethostbyname(name);
	  if(hostInfo != NULL && (hostInfo->h_addr_list[0] != NULL))
	    {
	      address = *((unsigned *)hostInfo->h_addr_list[0]);
	    }
	}
    }
  return address;
}


void cmdline_parser_print_version (void)
{
  printf ("%s %s\n%s\n", PACKAGE, VERSION, AUTHORS);
}

void cmdline_parser_print_help (void)
{
  cmdline_parser_print_version ();
  printf("\n Purpose:\n  Builds a SSL tunnel through a True Proxy, with Proxy authentication features\n");
  fprintf(stderr, "Usage: [OPTIONS]...\n");
  fprintf(stderr, "   -C           --sslClientmode         Makes this a client ssl connection\n");
  fprintf(stderr, "   -c           --certfile <cert.pem>   Use this certificate file\n");
  fprintf(stderr, "   -D STRING    --desthost=<host>       Destination host to built the tunnel to\n");
  fprintf(stderr, "   -d INT       --destport=<port>       Destination portnumber to built the tunnel to\n");
  fprintf(stderr, "   -f INT       --standalone=<port>     Forked process for proxytunnel authentication port\n");
  fprintf(stderr, "   -G STRING    --proxyhost=<host>      HTTPS Proxy host to connect to\n");
  fprintf(stderr, "   -g INT       --proxyport=<port>      HTTPS Proxy portnumber to connect to\n");
  fprintf(stderr, "   -h           --help                  Print help and exit\n");
  fprintf(stderr, "   -k STRING    --keyfile <key.pem>     Use the given key in PEM format (may be contained in cert)\n");
  fprintf(stderr, "   -L STRING    --sslLocalIp <IPAddr>   Listening address for ssl proxy\n");
  fprintf(stderr, "   -l INT       --sslLocalport <port>   Listening port for ssl proxy\n");
  fprintf(stderr, "   -n           --dottedquad            Convert destination hostname to dotted quad\n");
  fprintf(stderr, "   -P STRING    --sslProto <protocol>   Protocol to use connect: ssl23 (Default), ssl2, ssl3, tls1\n");
  fprintf(stderr, "   -p STRING    --pass=<password>       Password to send to HTTPS proxy for auth\n");
  fprintf(stderr, "   -R STRING    --redirIP=<IPAddr>      Redirected IP address (Default is 0.0.0.0)\n");
  fprintf(stderr, "   -r INT       --redirPort=<port>      Redir Listening Port for gateway tunnel (Def. port is 999)\n");
  fprintf(stderr, "   -s           --sslServermode         This a server SSL proxy opt and not for Client proxy\n");
  fprintf(stderr, "   -T STRING    --verifydir <dir>       directory containing CA certificates in hashed format\n");
  fprintf(stderr, "   -t STRING    --verifyfile <file>     file containing the CA's certificate\n");
  fprintf(stderr, "   -u STRING    --user=<user>           Username to send to HTTPS proxy for auth\n");
  fprintf(stderr, "   -V           --version               Print version and exit\n");
  fprintf(stderr, "   -v           --verbose               Turn on verbosity (default=off)\n");
  
  fprintf(stderr, "\nFor examples view the Readme.txt file\n");
  
#ifndef HAVE_GETOPT_LONG
  fprintf( stderr, "\nNotice: This version is compiled without support for long options.\n  This means you can only use the short (1 letter) options on the command line.\n" );
#endif
}

static char *gengetopt_strdup (char * s)
{
  char *n, *pn, *ps = s;
  while (*ps) ps++;
  n = (char *) malloc (1 + ps - s);
  if (n != NULL)
    {
      for (ps=s,pn=n; *ps; ps++,pn++)
        *pn = *ps;
      *pn = 0;
    }
  return n;
}

int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *args_info )
{
  int c;	//Character of the parsed option.
  args_info->help_given = 0;
  args_info->version_given = 0;
  args_info->user_given = 0;
  args_info->pass_given = 0;
  args_info->proxyhost_given = 0;
  args_info->proxyport_given = 0;
  args_info->desthost_given = 0;
  args_info->destport_given = 0;
  args_info->dottedquad_given = 0;
  args_info->verbose_given = 0;
  args_info->quiet_given = 0;
  args_info->sslClientmode_given = 0;
  args_info->sslServermode_given = 0;
  args_info->sslLocalIp_given = 0;
  args_info->sslLocalPort_given = 0;
  args_info->sslRedirectIp_given = 0;
  args_info->sslRedirectPort_given = 0;
  args_info->standalone_given = 0;
  args_info->certFile_given = 0;
  args_info->keyFile_given = 0;
  args_info->peerCert_given = 0;
  args_info->verifyFile_given = 0;
  args_info->verifyDir_given = 0;
  args_info->sslProto_given = 0;
  args_info->standalone_given = 0;


#define clear_args() 
  { 
    args_info->user_arg = NULL; 
    args_info->pass_arg = NULL; 
    args_info->proxyhost_arg = NULL; 
    args_info->desthost_arg = NULL; 
    args_info->dottedquad_flag = 0; 
    args_info->verbose_flag = 0;
    args_info->quiet_flag = 0; 
    args_info->standalone_arg = 0;
    args_info->sslClientmode_flag = 0;
    args_info->sslServermode_flag = 0;
    args_info->sslLocalIp_arg = 0;
    args_info->sslLocalPort_arg = 0;
    args_info->sslRedirectIp_arg = 0;
    args_info->sslRedirectPort_arg = 0;
    args_info->peerCert_flag = 0; 
    args_info->certFile_arg = NULL;
    args_info->keyFile_arg = NULL;
    args_info->verifyFile_arg = NULL;
    args_info->verifyDir_arg = NULL;
    args_info->sslProto_arg = NULL;
  }

  clear_args();
  optarg = 0;

#ifdef HAVE_GETOPT_LONG
  optind = 1;
  opterr = 1;
  optopt = '?';
#endif

  while (1)
    {
#ifdef HAVE_GETOPT_LONG
      int option_index = 0;

      /* Struct option: Name, Has_arg, Flag, Value */
      static struct option long_options[] = {
	{ "requirePeerCert", 0, NULL, 'a' },
	{ "sslClientmode",   0, NULL, 'C' },
	{ "certfile",	     1, NULL, 'c' },
        { "desthost",	     1, NULL, 'D' },
        { "destport",	     1, NULL, 'd' },
	{ "standalone",      1, NULL, 'f' },
        { "proxyhost",	     1, NULL, 'G' },
        { "proxyport",	     1, NULL, 'g' },
        { "help",	     0, NULL, 'h' },
	{ "keyfile",	     1, NULL, 'k' },
	{ "sslLocalIp",	     1, NULL, 'L' },
	{ "sslLocalPort",    1, NULL, 'l' },
        { "dottedquad",	     0, NULL, 'n' },
	{ "sslProto",	     1, NULL, 'P' },
        { "pass",	     1, NULL, 'p' },
	{ "quiet",	     0, NULL, 'q' },
	{ "RedirIp",	     1, NULL, 'R' },
	{ "RedirPort",	     1, NULL, 'r' },
	{ "sslServermode",   0, NULL, 's' },
	{ "verifydir",	     1, NULL, 'T' },
	{ "verifyfile",	     1, NULL, 't' },
        { "user",	     1, NULL, 'u' },
        { "version",	     0, NULL, 'V' },
        { "verbose",	     0, NULL, 'v' },
        { NULL,	0, NULL, 0 }
      };

      c = getopt_long (argc, argv, "aT:t:k:hVsu:p:g:G:d:D:nvqCL:l:R:r:f:c:P:", long_options, &option_index);
#else
      c = getopt( argc, argv, "aT:t:k:hVsu:p:g:G:d:D:nvqCL:l:R:r:f:c:P:" );
#endif

      if (c == -1) break;	/* Exit from `while (1)' loop.  */

      switch (c)
        {

	  /* Require a Peer Certificate in SSL */
        case 'a':       
          if ( args_info->peerCert_flag )
          {
            fprintf( stderr, "%s: `--requirePeerCert' (`-a') option given more than once\n", PACKAGE);
	    exit (1);
          }
	  args_info->peerCert_given = 1;
          args_info->peerCert_flag = !(args_info->peerCert_flag);
          break;

	  /* Turns on Clientside SSL Compatibility mode */
	case 'C':	
	  if(args_info->sslClientmode_given)
            {
              fprintf (stderr, "%s: `--sslClientmode' (`-C') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->sslClientmode_given = 1;
          args_info->sslClientmode_flag = !(args_info->sslClientmode_flag);
	  break;

	  /* Certificate file for SSL in Server mode */
	case 'c':	
	  if (args_info->certFile_given)
            {
              fprintf (stderr, "%s: `--certfile' (`-c') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->certFile_given = 1;
          args_info->certFile_arg = gengetopt_strdup(optarg);
	  break;

	  /* Destination host to built the tunnel to.  */
        case 'D':	
          if (args_info->desthost_given)
            {
              fprintf (stderr, "%s: `--desthost' (`-D') option given more than once\n", PACKAGE);
              exit (1);
            }
          args_info->desthost_given = 1;
          args_info->desthost_arg = gengetopt_strdup(optarg);
          break;
	  
	  /* Destination host portnumber to built the tunnel to.  */
        case 'd':	
          if (args_info->destport_given)
            {
              fprintf (stderr, "%s: `--destport' (`-d') option given more than once\n", PACKAGE);
              exit (1);
            }
          args_info->destport_given = 1;
          args_info->destport_arg = atoi(optarg);
          break;

	  /* Run Proxytunnel on different port. */
	  case 'f':  
          if ( ( args_info->standalone_arg = atoi( optarg ) ) < 1 )
	    {
	      fprintf( stderr, "%s: Illegal port value for `--standalone' (`-f')\n", PACKAGE);
	      exit (1);
	    }
          args_info->standalone_given = 1;
          args_info->standalone_arg = atoi(optarg);
          break;

	/* HTTPS Proxy host to connect to.  */
        case 'G':
          if (args_info->proxyhost_given)
            {
              fprintf (stderr, "%s: `--proxyhost' (`-G') option given more than once\n", PACKAGE);
              exit (1);
            }
          args_info->proxyhost_given = 1;
          args_info->proxyhost_arg = gengetopt_strdup (optarg);
          break;

	  /* HTTPS Proxy host portnumber to connect to.  */
        case 'g':	
          if (args_info->proxyport_given)
            {
              fprintf (stderr, "%s: `--proxyport' (`-g') option given more than once\n", PACKAGE);
              exit (1);
            }
          args_info->proxyport_given = 1;
          args_info->proxyport_arg = atoi (optarg);
          break;

	  /* Print help and exit.  */
        case 'h':	
          cmdline_parser_print_help ();
          exit (0);


	  /* Key Certificate file for SSL */
	case 'k':	
	  if (args_info->keyFile_given)
            {
              fprintf (stderr, "%s: `--keyfile' (`-k') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->keyFile_given = 1;
          args_info->keyFile_arg = gengetopt_strdup(optarg);
	  break;

	  /* Local Host Address */
	case 'L':     
	  //add test for duplicates
          if (args_info->sslLocalIp_given)
            {
              fprintf (stderr, "%s: `--localhost' (`-L') option given more than once\n", PACKAGE);
              exit (1);
            }
          args_info->sslLocalIp_given = 1;
          args_info->sslLocalIp_arg = addressForName(optarg);
	  break;
 
	  /* Turn on resolve to sslLocalPort_given */
	case 'l':	
	  // add test for duplicates
	  if (args_info->sslLocalPort_given)
            {
              fprintf (stderr, "%s: `--sslLocalPort' (`-l') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->sslLocalPort_given = 1;
          args_info->sslLocalPort_arg = atoi (optarg);
	  break;

	  /* Turn on resolve to Dotted Quad */
	case 'n':	
	  args_info->dottedquad_flag = !(args_info->dottedquad_flag);
	  break;

	  /* Alternate Protocol for SSL.  */
	case 'P':
	  if (args_info->sslProto_given)
            {
              fprintf (stderr, "%s: `--sslProto' (`-P') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->sslProto_given = 1;
          args_info->sslProto_arg = gengetopt_strdup(optarg);;
	  break;

	  /* Password to send to HTTPS proxy for authentication.  */
        case 'p':	
          if (args_info->pass_given)
            {
              fprintf (stderr, "%s: `--pass' (`-p') option given more than once\n", PACKAGE);
              clear_args ();
              exit (1);
            }
          args_info->pass_given = 1;
          args_info->pass_arg = gengetopt_strdup (optarg);
          break;

	  /* Suppress messages -- Quiet mode */
	case 'q':	
	  args_info->quiet_flag = !(args_info->quiet_flag);
	  break;

	  /* Remote Host Address */
	case 'R':     
	  //add test for duplicates
          if (args_info->sslRedirectIp_given)
            {
              fprintf (stderr, "%s: `--sslRedirectIp' (`-R') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->sslRedirectIp_given = 1;
          args_info->sslRedirectIp_arg = addressForName(optarg);
	  break;
	  
	  /* Remote Port to redirect to */
	case 'r':       
	  // add test for duplicates
	  if (args_info->sslRedirectPort_given)
            {
              fprintf (stderr, "%s: `--sslRedirectPort' (`-r') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->sslRedirectPort_given = 1;
          args_info->sslRedirectPort_arg = atoi(optarg);
	  break;

	  /* Run in SSL server daemon */
        case 's':       
          if ( args_info->sslServermode_flag )
          {
            fprintf( stderr, "%s: `--sslServermode' (`-s') option given more than once\n", PACKAGE);
	    exit (1);
          }
          args_info->sslServermode_flag = !(args_info->sslServermode_flag);
          break;

	  /* Verify Certificate Directory for SSL */
	case 'T':	
	  if (args_info->verifyDir_given)
            {
              fprintf (stderr, "%s: `--verifydir' (`-T') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->verifyDir_given = 1;
          args_info->verifyDir_arg = gengetopt_strdup(optarg);
	  break;

	  /* Verify Certificate Directory for SSL */
	case 't':	
	  if (args_info->verifyFile_given)
            {
              fprintf (stderr, "%s: `--verifyfile' (`-t') option given more than once\n", PACKAGE);
              exit (1);
            }
	  args_info->verifyFile_given = 1;
          args_info->verifyFile_arg = gengetopt_strdup(optarg);
	  break;

	  /* Username to send to HTTPS proxy for authentication.  */
        case 'u':	
          if (args_info->user_given)
            {
              fprintf (stderr, "%s: `--user' (`-u') option given more than once\n", PACKAGE);
              exit (1);
            }
          args_info->user_given = 1;
          args_info->user_arg = gengetopt_strdup (optarg);
          break;

	  /* Print version and exit.  */
        case 'V':	
          cmdline_parser_print_version ();
          exit (0);

	  /* Turn on verbosity.  */
        case 'v':	
          args_info->verbose_flag = !(args_info->verbose_flag);
          break;

	  /* Invalid option.  */
        case '?':	
          /* `getopt_long' already printed an error message.  */
          exit (1);

	  /* bug: option not considered.  */
        default:	
          fprintf (stderr, "%s: option unknown: %c\n", PACKAGE, c);
          abort ();
        } //EOS
    } 
  return 0;
}
