#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>

static
void gss_errmsg1(OM_uint32 stat, int code)
{
  OM_uint32             ignore;
  uint                  err_ctx = 0;
  gss_buffer_desc       line = GSS_C_EMPTY_BUFFER;

  do {
    (void)gss_display_status(&ignore, stat, code, GSS_C_NULL_OID, &err_ctx, &line);
    printf("\t%.*s\n", line.length, line.value);
    (void)gss_release_buffer(&ignore, &line);
  } while(err_ctx);
}

static
void check(char *s, OM_uint32 maj_stat, OM_uint32 min_stat)
{
  if (maj_stat == GSS_S_COMPLETE) return;
  printf("GSS-API error %s\n", s);
  gss_errmsg1(maj_stat, GSS_C_GSS_CODE);
  gss_errmsg1(min_stat, GSS_C_MECH_CODE);
  exit(1);
}

static
void client_establish_context(char *service_name, char *mechanism_name)
{ 
  gss_buffer_desc       name_buf;
  gss_buffer_t          token_ptr;
  OM_uint32             maj_stat, min_stat;

  gss_buffer_desc       send_tok = GSS_C_EMPTY_BUFFER;
  gss_name_t            target_name = NULL;
  gss_OID               secoid = GSS_C_NO_OID;
  gss_ctx_id_t		*gss_context;

  token_ptr = GSS_C_NO_BUFFER;
  *gss_context = GSS_C_NO_CONTEXT;

  name_buf.value = service_name;
  name_buf.length = strlen(service_name) + 1;
  maj_stat = gss_import_name(&min_stat, &name_buf,
                             (gss_OID)gss_nt_service_name, &target_name);
  check("parsing name", maj_stat, min_stat);

  name_buf.value = mechanism_name;
  name_buf.length = strlen(mechanism_name) + 1;
  maj_stat = gss_str_to_oid(&min_stat, &name_buf, &secoid);
  check("parsing security oid", maj_stat, min_stat);

  maj_stat =
    gss_init_sec_context(&min_stat,
                         GSS_C_NO_CREDENTIAL,
                         gss_context,
                         target_name,
                         secoid,
                         GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
                         0,
                         NULL,  /* no channel bindings */
                         token_ptr,
                         NULL,  /* ignore mech type */
                         &send_tok,
                         NULL,  /* ignore ret_flags */
                         NULL); /* ignore time_rec */

  check("initializing context", maj_stat, min_stat);
}


main(int argc, char *argv[], char *env[])
{
  char  *security_mech = "{ 1 3 5 1 5 2 }";
  char  c;

  while((c = getopt(argc, argv, "m:")) != EOF)
    switch (c) {
    case 'm':
      security_mech = optarg;
      break;
    default:
      goto Usage;
    }
  if (optind != argc-1) goto Usage;

  client_establish_context(argv[optind], security_mech);

  exit(0);

 Usage:
  fprintf(stderr, "usage: %s [Options]\n", argv[0]);
  fprintf(stderr, "\nOptions:\n");
  fprintf(stderr, "\t-m %%s\tsecurity mechansim [\"%s\"]\n", "{ 1 3 5 1 5 2 }");
  exit(2);
}
