/*
 * DNS KEY lookup helper
 * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
 * 
 * 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.  See <http://www.fsf.org/copyleft/gpl.txt>.
 * 
 * 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.
 */

char tncfg_c_version[] = "RCSID $Id: lwdnsq.c,v 1.4 2002/12/12 06:03:41 mcr Exp $";


#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include <unistd.h> 

#include <freeswan.h>

#include <errno.h>
#include <getopt.h>
#include <setjmp.h>
#include <ctype.h>
#include <signal.h>

#include "lwdnsq.h"

static void
usage(char *name)
{	
	fprintf(stdout,"%s --attach --virtual <virtual-device> --physical <physical-device>\n",
		name);
	exit(1);
}

static struct option const longopts[] =
{
	{"prompt", 0, 0, 'i'},
	{"single", 0, 0, 's'},
	{"debug",  0, 0, 'g'},
	{"regress",0, 0, 'X'},
	{0, 0, 0, 0}
};

/* globals */
jmp_buf getMeOut;

void sig_handler(int sig)
{
  fprintf(stderr, "Caught signal %d, cleaning up and exiting\n", sig);
  longjmp(getMeOut, 1);
}

void cmdprompt(dnskey_glob *gs)
{
	if(gs->prompt) {
		printf("dnskey>");
	}
	fflush(gs->cmdproto_out);
}

void quitprog(dnskey_glob *gs,
	      int argc,
	      char **argv)
{
	gs->done=1;
}

void setdebug(dnskey_glob *gs,
	      int argc,
	      char **argv)
{
	if(argc > 1) {
		gs->debug=strtoul(argv[1],NULL,0);
	}
	printf("0 DEBUG is %d\n",gs->debug);
}


int cmdparse(dnskey_glob *gs,
	     char *cmdline)
{
	char *argv[256];
	int   argc;
	char *arg;
	static struct cmd_entry {
		const char *cmdname;
		void (*cmdfunc)(dnskey_glob *, int, char **);
	} cmds[]={
		{"key",       lookup_key},
		{"key4",      lookup_key4},
		{"key6",      lookup_key6},
		{"txt",       lookup_txt},
		{"txt4",      lookup_txt4},
		{"txt6",      lookup_txt6},
		{"ipseckey",  lookup_ipseckey},
		{"ipseckey4", lookup_ipseckey4},
		{"ipseckey6", lookup_ipseckey6},
		{"oe4",       lookup_oe4},
		{"oe6",       lookup_oe6},
		{"vpn4",      lookup_key4},
		{"vpn6",      lookup_key6},
		{"quit",      quitprog},
		{"a",         lookup_a},
		{"aaaa",      lookup_aaaa},
		{"debug",     setdebug},
		{NULL,        NULL}};
	struct cmd_entry *ce;

	argc=0;
	while(cmdline && *cmdline!='\0' &&
	      (arg=strsep(&cmdline, " \t\n"))!=NULL) {
	  argv[argc++]=arg;
	  while(cmdline && isspace(*cmdline)) {
	    cmdline++;
	  }
	}
	argv[argc]=NULL;

	if(argc==0 || argv[0][0]=='\0') {
		return 0;
	}

	ce=cmds;
	if(strcasecmp("help", argv[0]) == 0) {
		fprintf(gs->cmdproto_out, "0 HELP\n");
		while(ce->cmdname != NULL) {
			fprintf(gs->cmdproto_out, "0 HELP %s\n", ce->cmdname);
			ce++;
		}
		cmdprompt(gs);
		return 0;
	}
	
	while(ce->cmdname != NULL) {
		if(strcasecmp(ce->cmdname, argv[0])==0) {
			(*ce->cmdfunc)(gs, argc, argv);
			cmdprompt(gs);
			return 0;
		}
		ce++;
	}

	if(ce->cmdname==NULL) {
	  cmdprompt(gs);
	}
	return 0;
}

int cmdread(dnskey_glob *gs,
	    char  *buf,
	    int    len)
{
	unsigned char *nl;
	int   cmdlen;

	cmdlen=0;

	/* 
	 * have to handle partial reads and multiple commands
	 * per read, since this may in fact be a file or a pipe.
	 */
	if((gs->cmdloc + len + 1) > sizeof(gs->cmdbuf)) {
		fprintf(stderr, "command '%.*s...' is too long, discarding!\n",
			40, buf);
		fflush(stdout);
		
		gs->cmdloc=0;
		return 0;
	}
	memcpy(gs->cmdbuf+gs->cmdloc, buf, len);
	gs->cmdloc+=len;
	gs->cmdbuf[gs->cmdloc]='\0';

	while((nl = strchr(gs->cmdbuf, '\n')) != NULL) {
		/* found a newline, so turn it into a \0, and process the
		 * command, and then we will pull the rest of the buffer
		 * up.
		 */
		*nl='\0';
		cmdlen= nl - gs->cmdbuf +1;

		cmdparse(gs, gs->cmdbuf);

		memmove(gs->cmdbuf, gs->cmdbuf+cmdlen, cmdlen);
		gs->cmdloc -= cmdlen;
	}
	return 1;
}

int
main(int argc, char *argv[])
{
	char *program_name;
	dnskey_glob gs;
	int c;

	memset(&gs, 0, sizeof(dnskey_glob));

#if 0
	printf("PID: %d\n", getpid());
	sleep(60);
#endif

	program_name = argv[0];
	gs.concurrent = 1;

	while((c = getopt_long_only(argc, argv, "gsiX", longopts, 0)) != EOF) {
		switch(c) {
		case 'g':
			gs.debug++;
			break;
		case 's':
			gs.concurrent=0;
			break;
		case 'i':
			gs.prompt=1;
			break;
		case 'X':
			gs.regress++;
			break;

		default:
			usage(program_name);
			break;
		}
	}

	if(isatty(0)) {
		gs.prompt=1;
	}

	/* do various bits of setup */
	if(setjmp(getMeOut)!=0) {
		signal(SIGINT,  SIG_DFL);
		signal(SIGPIPE, SIG_IGN);
		
		/* cleanup_crap(); */
		
		exit(1);
	}
	
	if(signal(SIGINT, sig_handler) < 0)
		perror("Setting handler for SIGINT");
	
	if(signal(SIGPIPE, sig_handler) < 0)
		perror("Setting handler for SIGINT");
	
	cmdprompt(&gs);

	gs.done = 0;
	gs.cmdproto_out = stdout;
	gs.l_fds[0].events=POLLIN;
	gs.l_fds[0].fd=0;
	gs.l_nfds= 1;

	while(!gs.done)
	{
		int    timeout;
		char   buf[128];
		int    n;
		int    rlen;

		timeout=-1;

		if(gs.debug > 1) {
			fprintf(stderr, "invoking poll(,%d,) with %s",
				gs.l_nfds,
				timeout>0 ? "waittime" : "no wait");

		}

		n = poll(gs.l_fds, gs.l_nfds, timeout);

		while(n>0) {
			if((gs.l_fds[0].revents & POLLIN) == POLLIN) {
				rlen=read(0, buf, 128);
				if(rlen > 0) {
					cmdread(&gs, buf, rlen);
				}
				if(rlen == 0) {
					/* EOF, die */
					gs.done=1;
				}
				n--;
			} 
			if((gs.l_fds[0].revents & POLLHUP) == POLLHUP) {
				gs.done=1;
				break;
			}
		}
	}

	signal(SIGINT,  SIG_DFL);
	signal(SIGPIPE, SIG_IGN);
  
	exit(0);
}
	
/*
 * $Log: lwdnsq.c,v $
 * Revision 1.4  2002/12/12 06:03:41  mcr
 * 	added --regress option to force times to be regular
 *
 * Revision 1.3  2002/11/25 18:37:48  mcr
 * 	make sure that we exit cleanly upon EOF.
 *
 * Revision 1.2  2002/11/16 02:53:53  mcr
 * 	lwdnsq - with new contract added.
 *
 * Revision 1.1  2002/10/30 02:25:31  mcr
 * 	renamed version of files from dnskey/
 *
 * Revision 1.3  2002/10/09 20:14:16  mcr
 * 	make sure to flush stdout at the right time - do it regardless
 * 	of whether or not we are printing prompts.
 *
 * Revision 1.2  2002/09/30 18:55:54  mcr
 * 	skeleton for dnskey helper program.
 *
 * Revision 1.1  2002/09/30 16:50:23  mcr
 * 	documentation for "dnskey" helper
 *
 * Local variables:
 * c-file-style: "linux"
 * c-basic-offset: 2
 * End:
 *
 */
