#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 server_acquire_creds(char *service_name, char *mechanism_name, int cred_usage)
{
  gss_buffer_desc	name_buf;
  OM_uint32		maj_stat, min_stat, lifetime;
  int			n;

  gss_name_t		server_name = NULL;
  gss_OID_set		sec_mechs = GSS_C_NO_OID_SET;
  gss_OID		doid = GSS_C_NO_OID;
  gss_buffer_desc	fullname_buf = GSS_C_EMPTY_BUFFER;
  char			*text = NULL;
  gss_cred_id_t		server_creds = GSS_C_NO_CREDENTIAL;

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

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

    maj_stat = gss_create_empty_oid_set(&min_stat, &sec_mechs);
    check("creating empty oid set", maj_stat, min_stat);

    maj_stat = gss_add_oid_set_member(&min_stat, doid, &sec_mechs);
    check("adding oid set member", maj_stat, min_stat);
  }

  maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
			      sec_mechs, cred_usage,
			      &server_creds, NULL, NULL);
  check("acquiring credentials", maj_stat, min_stat);

  /* now echo */
  maj_stat = gss_inquire_cred(&min_stat, server_creds, &server_name,
			      &lifetime, &cred_usage, &sec_mechs);
  check("inquiring credentials", maj_stat, min_stat);

  maj_stat = gss_display_name(&min_stat, server_name, &fullname_buf, &doid);
  check("getting server name", maj_stat, min_stat);

  printf("server name\t%.*s\n", fullname_buf.length, fullname_buf.value);
  printf("lifetime\t%02d:%02d:%02d\n", lifetime/3600, (lifetime%3600)/60, lifetime%60);
  printf("usage\t\t%s [%d]\n",
	 cred_usage == GSS_C_BOTH ? "GSS_C_BOTH" :
	 cred_usage == GSS_C_INITIATE ? "GSS_C_INITIATE" :
	 cred_usage == GSS_C_ACCEPT ? "GSS_C_ACCEPT" :
	 "?", cred_usage);
  for(n=0; n<sec_mechs->count; ++n) {
    maj_stat = gss_oid_to_str(&min_stat, sec_mechs->elements+n, &name_buf);
    check("converting oid to str", maj_stat, min_stat);
    printf("mechanism\t%.*s\n", name_buf.length, name_buf.value);
  };
}

main(int argc, char *argv[], char *env[])
{
  char	*security_mech = NULL;
  int	usage = GSS_C_BOTH;
  char	c;

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

  server_acquire_creds(argv[optind], security_mech, usage);

  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 }");
  fprintf(stderr, "\t-u %%s\tcredentials usage [%d]\n", GSS_C_BOTH);
  exit(2);
}
