/***********************************************************
        Copyright 1996 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/

#include "../defs.h"
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>



#include "defaults.h"
#include "bootp.h"
#include "bootpd.h"
#include "dhcpd.h"
#include "dhcp.h"
#include "hash.h"
#include "perm.h"
#include "asn1.h"
#include "snmp.h"
#include "snmp_client.h"
#include "snmp_api.h"
#include "snmp_impl.h"

int i,rc;
char *host;
char *community="public";
/* change port number if not using the default snmp agent */
unsigned short port=161;
int snmp_dump_packet=0;
int debug = 0;
char *progname = "dhcpcmd"; 	/* just to make report() happy */
boolean dont_fork = 0;			/* just to make report() happy */

typedef struct {
	char *host;
	char *community;
	unsigned short port;
	struct snmp_session *ss;
	int retries;
	int timeout;
} Snmp;
Snmp s;

typedef struct {
	oid name[64];
	oid root[64];
	int namelen,rootlen ;
	char type;
	int val_len;
	union {
		int32   *integer;
		u_char  *string;
		oid *objid;
		u_char  *byte;
		struct counter64 *counter64;
	}val;
	int status;
	int errstat;
} Var;
Var v;
int set_oid(Var *,char*);
int open_session(Snmp*);
void set_integer(Var *,unsigned long);
void set_string(Var *,char *);

/* put your dhcp servers in here */
static char *servers[]= { NULL
	};

int nservers=0;


unsigned char h2d(char c)
{
	if('0'<=c && c<='9')
		return c-'0';
	if('a'<=c && c<='f')
		return (c-'a')+10;
	return (c-'A')+10;
}
	
char *cctime(time_t t)
{
	char *c;

	c=ctime(&t);
 	c[strlen(c)-1]=0;
 	return c;
}

static char time_buf[64];
char *
uptimeStr (u_long timeticks)
{
    int seconds, minutes, hours, days;

    timeticks /= 100;
    days = timeticks / (60 * 60 * 24);
    timeticks %= (60 * 60 * 24);

    hours = timeticks / (60 * 60);
    timeticks %= (60 * 60);

    minutes = timeticks / 60;
    seconds = timeticks % 60;

    if (days == 0){
        sprintf(time_buf, "%d:%02d:%02d", hours, minutes, seconds);
    } else if (days == 1) {
        sprintf(time_buf, "%d day, %d:%02d:%02d", days, hours, minutes, seconds);
    } else {
        sprintf(time_buf, "%d days, %d:%02d:%02d", days, hours, minutes, seconds);
    }
    return time_buf;
}


void help(int argc, char **argv)
{
	if(argc==0){
		printf("dhcpcmd command [opt1] [opt2] .....\n");
		printf("   Commands are:\n");
		printf("*   delete  \t- Deletes an entry permanently from the database\n");
		printf("    dynamic \t- Information on dynamic addresses\n");
		printf("*   flush   \t- Flushs information to disk on the server(s)\n");
		printf("*   gc      \t- Force servers to garbage collect\n");
		printf("    getinfo \t- Get information on a specific machine\n");
		printf("    help    \t- Get syntax for a specific command\n");
		printf("    leases  \t- List of active leases (DHCP and Dynamic bootp)\n");
		printf("*   readtab \t- Force the server(s) to read the bootptab\n");
		printf("*   readconf\t- Force the server(s) to read the configuration file\n");
		printf("*   release \t- Force the server to release a DHCP lease or any\n\t\t  Dynamically assigned address\n");
		printf("    status  \t- Get uptime and other information from the server(s)\n");
		printf("    unknown \t- Get list of unknown hardware addresses from the server\n");
		printf("\n\nUnless specified with the -s option, the default DHCP server to query is\n\t%s\n",servers[0]);
		printf("\n* - These commands forces the server to perform an action. They require a\n");
		printf("    private community string. If the -s option is not used with these commands,\n");
		printf("    the operation is applied to the servers in the order\n");
		printf("    %s %s %s\n",servers[0],servers[1],servers[2]);
		exit(0);
	} else {
 		if(!strncmp("st",*argv,2))
			printf("dhcpcmd status [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("fl",*argv,2))
			printf("dhcpcmd flush [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("readt",*argv,5))
			printf("dhcpcmd readtab [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("gc",*argv,2))
			printf("dhcpcmd gc [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("rel",*argv,3))
			printf("dhcpcmd release [-s server] [-c community] [-p port] ip-address\n");
 		else if(!strncmp("dy",*argv,2))
			printf("dhcpcmd dynamic [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("de",*argv,2))
			printf("dhcpcmd delete [-s server] [-c community] [-p port] client-identifier\n");
 		else if(!strncmp("l",*argv,1))
			printf("dhcpcmd leases [-n] [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("readc",*argv,5))
			printf("dhcpcmd readconfig [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("u",*argv,1))
			printf("dhcpcmd unknown [-n] [-s server] [-c community] [-p port]\n");
 		else if(!strncmp("ge",*argv,2)) {
			printf("dhcpcmd getinfo [-n] [-s server] [-c community] [-p port] client-identifier\n");
			printf("dhcpcmd getinfo [-n] [-s server] [-c community] [-p port] hardware-address\n");
			printf("dhcpcmd getinfo [-n] [-s server] [-c community] [-p port] ip-address\n");
			printf("dhcpcmd getinfo [-n] [-s server] [-c community] [-p port] hostname\n");
		 } else
			help(0,NULL);
	}

	exit(0);
}


#define readtab(a,b) simplecmd(a,b,".1.3.6.1.4.1.3.2.3.2.1.0",1)
#define readconf(a,b) simplecmd(a,b,".1.3.6.1.4.1.3.2.3.2.1.0",2)
#define flush(a,b) simplecmd(a,b,".1.3.6.1.4.1.3.2.3.2.1.0",3)
#define gc(a,b) simplecmd(a,b,".1.3.6.1.4.1.3.2.3.2.1.0",4)
void simplecmd(int argc,char **argv,char *objid, int value)
{

	char **targv;
	int targc;
	int sflag,delay;

	targc=argc;
	targv=argv;
	argc--;
	argv++;

	sflag=0;
	delay=20;
	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': sflag++; argv++; argc--; host=*argv; break;
		case 'd':  argv++; argc--; delay=atoi(*argv); break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}

	s.host=host;
	s.community=community;
	s.port=port;
	s.retries=0;
	s.timeout=5000000L;
	if(sflag){
	rc=open_session(&s);
	if(rc==1){
	  printf("Couldn't open host %s\n",host);
	  exit(1);
	}
	set_oid(&v,objid);
	set_integer(&v,value);
	rc=snmp_set(&s,&v);
	if(rc){
		printf("The snmp agent does not contain the CMU DHCP Mib\n");
		exit(1);
	}
	} else {

	setvbuf(stdout,NULL,_IONBF,0);
	for(i=0;i<nservers;i++){
	s.host=servers[i];
	rc=open_session(&s);
	if(rc==1){
	  printf("Couldn't open host %s\n",host);
	  exit(1);
	}
	set_oid(&v,objid);
	set_integer(&v,value);
	rc=snmp_set(&s,&v);
	if(rc){
		if(v.status==STAT_SUCCESS && v.errstat==SNMP_ERR_NOSUCHNAME)
			printf("Access denied\n");
		else
			printf("Error contacting server %s\n",servers[i]);
	} 
	do{
	sleep(delay);
	printf(".");
	}while(snmp_get(&s,&v));
	printf("Operation complete on %s\n",servers[i]);
	snmp_close(s.ss);

	}
	}

		
	exit(0);
}


#define WALK(s,v) \
	rc=snmp_walk(s,v); \
	if(rc){	\
		printf("Error in getting data from host\n"); \
		exit(1); \
		}

void status(int argc,char **argv)
{

	unsigned long start,uptime,tab,conf,discover,request,offer,ack;
	unsigned long nack,decline,release;
	unsigned long expired,reclaimed,garbage,bootpreq,bootprpl,inform, leases;
	char **targv,*ver,*tabf,*conff,*uptimestr;
	int targc;

	bzero(&v,sizeof(v));
	bzero(&s,sizeof(s));
	targc=argc;
	targv=argv;
	argc--;
	argv++;
	host=servers[0];

	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': argv++; argc--; host=*argv; break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}

	s.host=host;
s.community=community;
	s.port=port;
	rc=open_session(&s);
	if(rc==1){
	  printf("Couldn't open host %s\n",host);
	  exit(1);
	}
	set_oid(&v,".1.3.6.1.4.1.3.2.3.1");
	start=time(0);
	rc=snmp_walk(&s,&v);
	if(rc){
		printf("The snmp agent does not contain the CMU DHCP Mib\n");
		exit(1);
	}

	uptime=*v.val.integer;
	WALK(&s,&v);
	ver=(char*)malloc(v.val_len+1);
	bcopy(v.val.string,ver,v.val_len);
	ver[v.val_len]=0;
	WALK(&s,&v);
	tab=*v.val.integer;
	WALK(&s,&v);
	conf=*v.val.integer;
	WALK(&s,&v);
	garbage=*v.val.integer;
	WALK(&s,&v);
	discover=*v.val.integer;
	WALK(&s,&v);
	offer=*v.val.integer;
	WALK(&s,&v);
	request=*v.val.integer;
	WALK(&s,&v);
	decline=*v.val.integer;
	WALK(&s,&v);
	ack=*v.val.integer;
	WALK(&s,&v);
	nack=*v.val.integer;
	WALK(&s,&v);
	release=*v.val.integer;
	WALK(&s,&v);
	inform=*v.val.integer;
	WALK(&s,&v);
	bootpreq=*v.val.integer;
	WALK(&s,&v);
	bootprpl=*v.val.integer;
	WALK(&s,&v);
	leases=*v.val.integer;
	WALK(&s,&v);
	expired=*v.val.integer;
	WALK(&s,&v);
	reclaimed=*v.val.integer;
	set_oid(&v,".1.3.6.1.4.1.3.2.3.2.2.0");
	rc=snmp_get(&s,&v);
	tabf=(char*)malloc(v.val_len+1);
	bcopy(v.val.string,tabf,v.val_len);
	tabf[v.val_len]=0;
	if(rc)
		exit(1);
	set_oid(&v,".1.3.6.1.4.1.3.2.3.2.3.0");
	rc=snmp_get(&s,&v);
	conff=(char*)malloc(v.val_len+1);
	bcopy(v.val.string,conff,v.val_len);
	conff[v.val_len]=0;
	if(rc)
		exit(1);
	uptimestr=uptimeStr(uptime);
	printf("Version=%s started at %s , uptime %s\n",ver,cctime(start-(uptime/100)),uptimestr);
	printf("tabfile=%s  last modified at %s\n",tabf,cctime(tab));
	printf("confile=%s  last modified at %s\n",conff,cctime(conf));
	if(garbage>(INFINITY-2))
		printf("No garbage collect scheduled\n");
	else
		printf("Next garbage collect: %s\n",cctime(garbage));
	printf("Active leases=%d\nTotal # of leases which have expired=%d Reclaimed dynamic ip's=%d\n",leases,expired,reclaimed);
	printf("DHCP:\tdiscovers=%d requests=%d declines=%d releases=%d informs=%d\n",
		discover ,request,decline,release,inform);
	printf("     \toffers=%d acks=%d nacks=%d\n",offer,ack,nack);
	printf("BOOTP:\trequests=%d replys=%d\n",bootpreq,bootprpl);
   

	exit(0);
}


void release(int argc, char *argv[])
{
	char buf[256],*cp;
	struct in_addr addr;
	struct hostent *hp;
	char **targv;
	int targc;

	targc=argc;
	targv=argv;
	argc--;
	argv++;
	host=servers[0];

	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': argv++; argc--; host=*argv; break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}

	if(argc<1)
			help(targc,targv);
	s.port=port;
	s.host=host;
	s.community=community;
	rc=open_session(&s);
	if(rc==1){
	  printf("Couldn't open host %s\n",host);
	  exit(1);
	}

	cp=*argv;
	
	addr.s_addr=inet_addr(cp);
	if(addr.s_addr == -1){
		hp=gethostbyname(cp);
		if(hp)
			addr.s_addr=*((unsigned long*)hp->h_addr);
		else{
			printf("Could not resolve host %s",cp);
			exit(1);
		}
	}
	sprintf(buf,".1.3.6.1.4.1.3.2.3.2.7.%s",inet_ntoa(addr));
	
	set_oid(&v,buf);
	set_string(&v,"");
	rc=snmp_set(&s,&v);
	if(rc){
		if(v.status==STAT_SUCCESS && v.errstat==SNMP_ERR_NOSUCHNAME)
			printf("Access denied\n");
		else
			printf("Error contacting server %s\n",host);
	} else
		printf("Successfully released %s\n",inet_ntoa(addr));
	exit(rc);

}
void delete_perm(int argc, char *argv[])
{
	char buf[256],*cp,**targv;
	unsigned char cliid[64],cliidlen;
	struct lastbind *p;
	int targc;
	if(argc<1)
			help(0,NULL);
	host=servers[0];
	targv=argv;
	targc=argc;
	argc--;
	argv++;


	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		default: help(targc,targv);
		}
		argc--;
		argv++;
	}
	if(argc!=1)
		help(targc,targv);
	s.host=host;
	s.community=community;
	s.port=port;
	rc=open_session(&s);
	if(rc==1){
	  printf("Couldn't open host %s\n",host);
	  exit(1);
	}
	cp=*argv;
	cliidlen=0;
	if(*cp=='"'){
		cp=strchr(*argv,'"');
		if(cp)
			*cp=(char)NULL;
		strcpy(buf,*argv);
		cliidlen=strlen(buf);
	} else {
		while(*cp && isxdigit(*cp)){
			cliid[cliidlen]=(h2d(*cp) <<4)&0xf0;
			cp++;
			if(!*cp)
				continue;
			cliid[cliidlen++]|=h2d(*cp)&0xf;
			cp++;
			while(*cp && !isxdigit(*cp))cp++;
		}
	}
	
	
	sprintf(buf,".1.3.6.1.4.1.3.2.3.2.6");
	for(i=0;i<cliidlen;i++)
		sprintf(buf+strlen(buf),".%d",cliid[i]);
	
	set_oid(&v,buf);
	set_string(&v,"");
	rc=snmp_set(&s,&v);
	if(rc)
		printf("Error delete entry in server %s\n",host);
	exit(rc);

}
void getinfo(int argc, char *argv[])
{
	char buf[256],*cp,**targv;
	unsigned char *ucp,name_flag,cliidlen;
	struct lastbind *p;
	struct hostent *hp;
	int targc,cliid[64];

	targc=argc;
	targv=argv;
	argc--;
	argv++;
	host=servers[0];
	name_flag=0;

	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': argv++; argc--; host=*argv; break;
		case 'n': name_flag=1; break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}
	if(argc!=1)
		help(targc,targv);
	s.port=port;
	s.host=host;
	s.community=community;
	rc=open_session(&s);
	if(rc==1){
	  printf("Couldn't open host %s\n",host);
	  exit(1);
	}
	cp=*argv;
	cliidlen=0;
		rc=sscanf(cp,"%u.%u.%u.%u",cliid,cliid+1,cliid+2,cliid+3);
		if(rc!=4){
			hp=gethostbyname(cp);
			if(hp){
				cliid[0]=0xff&hp->h_addr[0];
				cliid[1]=0xff&hp->h_addr[1];
				cliid[2]=0xff&hp->h_addr[2];
				cliid[3]=0xff&hp->h_addr[3];
				cliidlen=4;
			}
		} else
			cliidlen=4;
	if(cliidlen==4){
		sprintf(buf,".1.3.6.1.4.1.3.2.3.2.101");
		for(i=0;i<cliidlen;i++)
			sprintf(buf+strlen(buf),".%d",cliid[i]);
		set_oid(&v,buf);
		rc=snmp_get(&s,&v);
	} else {
		
	if(*cp=='"'){
		cp=strchr(*argv,'"');
		if(cp)
			*cp=(char)NULL;
		strcpy(buf,*argv);
		cliidlen=strlen(buf);
	} else {
		while(*cp && isxdigit(*cp)){
			cliid[cliidlen]=(h2d(*cp) <<4)&0xf0;
			cp++;
			if(!*cp)
				continue;
			cliid[cliidlen++]|=h2d(*cp)&0xf;
			cp++;
			while(*cp && !isxdigit(*cp))cp++;
		}
	}
	
	
	sprintf(buf,".1.3.6.1.4.1.3.2.3.2.100");
	for(i=0;i<cliidlen;i++)
		sprintf(buf+strlen(buf),".%d",cliid[i]);
	
	set_oid(&v,buf);
	rc=snmp_get(&s,&v);
	if(rc && !v.status && v.errstat==SNMP_ERR_NOSUCHNAME && cliidlen==6){
		/* try ethernet */
		sprintf(buf,".1.3.6.1.4.1.3.2.3.2.100.1");
		for(i=0;i<cliidlen;i++)
			sprintf(buf+strlen(buf),".%d",cliid[i]);
		
		set_oid(&v,buf);
		rc=snmp_get(&s,&v);
	}
	if(rc && !v.status && v.errstat==SNMP_ERR_NOSUCHNAME && cliidlen==6){
		/* try token-ring */
		sprintf(buf,".1.3.6.1.4.1.3.2.3.2.100.6");
		for(i=0;i<cliidlen;i++)
			sprintf(buf+strlen(buf),".%d",cliid[i]);
		
		set_oid(&v,buf);
		rc=snmp_get(&s,&v);
	}
	}


	if(rc){
		printf("The client specified does not exist in the servers database\n");
		exit(1);
	}

	p=data2bind(v.val.string,v.val_len);
	if(!p){
		printf("corrupt packet sent by server\n");
		exit(1);
	}


	printf("Hardware type: ");
	switch(p->cliidhw->data[0]){
	case 0x1:printf("ethernet"); break;
	case 0x6:printf("token ring"); break;
	case 0xFE:printf("PCTCP SLIP"); break;
	default: printf("%02X",p->cliidhw->data[0]);
	}
	buf[0]=(char)NULL;
	for(i=1;i<(p->cliidhw->length-1);i++)
		sprintf(buf+strlen(buf),"%02X-",p->cliidhw->data[i]);
	sprintf(buf+strlen(buf),"%02X",p->cliidhw->data[i]);

	printf(" \tAddress: %s\n",buf);
	printf("Address source: ");
	switch(p->code){
	case BOOTP_FLAG: printf("Dynamic Bootp\n"); break;
	case DHCP_FLAG: printf("Dynamic DHCP\n"); break;
	case RESERVED_FLAG: printf("Reserved\n"); break;
	case ROAMING_FLAG: printf("Roaming\n");	break;
	case SYNCH_FLAG: printf("Another DHCP server\n");	break;
	default:
		printf("Error\n");
		break;
	}
	if(p->expire)
		if(p->expire<time(0))
			printf("Expired at %s\n",cctime(p->expire));
		else
			printf("Lease is still active. expires: %s\n",cctime(p->expire));

	printf("Last sent an acknowledgement at %s\n",cctime(p->last_touch));
	printf("Total number of acknowledgements sent %d\n",p->replys);
	if(p->vend){
		if(p->vend->length==4){
			if(*(u_long*)p->vend->data == 0x37020000)
				printf("Last seen as a Windows 95 client\n");
			else if(*(u_long*)p->vend->data == 0x63825363)
				printf("Last seen as a unknown standard RFC1043 BOOTP client\n");
			else
				printf("Unknown Vendorid (%8X). Report to administrator of this program\n",*(u_long*)p->vend->data);
		} else if(p->vend->length>4 && *(u_long*)p->vend->data == 0x63825363){
				i=p->vend->length-4;
				ucp=p->vend->data+4;
				switch(ucp[0]){
				case 197:
				case 198:
				case 199:
				case 200:
					printf("PCTCP: client\n");
					while(i>0 && *ucp!=TAG_END){
						bzero(buf,sizeof(buf));
						switch(*ucp){
						case 199:bcopy((char*)ucp+2,buf,ucp[1]);
								printf("Description: %s\n",buf);
								break;
						case 200:bcopy((char*)ucp+2,buf,ucp[1]);
								printf("User: %s\n",buf);
								break;
						default:;
						}
						i-=ucp[1];
						ucp++;
						ucp+=1+*ucp;
					}
					break;
				case 255:
					printf("Last seen as possibly a Macintosh\n");
					break;
				default:
				printf("Unknown Vendorid (");
				for(i=0;i<p->vend->length;i++)
					printf("%02X",p->vend->data[i]);
				printf(").\nReport to administrator of this program\n");
				}
		}
	} else 
		printf("No vendor data: Possibly Windows NT\n");
			
	printf("IP address %s",inet_ntoa(p->last_ip));
	if(!name_flag){
		hp=gethostbyaddr((char*)&p->last_ip,4,AF_INET);
		if(hp)
			printf(" (%s)",hp->h_name);
	}
	printf("\n");
	exit(0);
}



static void active_leases(int argc, char *argv[])
{
	char **targv,buf[256],*cp,*type;
	char name_flag;
	Var exp, cl, addr, code;
	unsigned char *ip;
	struct hostent *hp;
	int targc;


	targc=argc;
	targv=argv;
	argc--;
	argv++;
	host=servers[0];
	name_flag=0;

	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': argv++; argc--; host=*argv; break;
		case 'n': name_flag=1; break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}
	if(argc)
		help(targc,targv);


 bzero(&cl,sizeof(cl));
 bzero(&addr,sizeof(addr));
 bzero(&code,sizeof(code));
 bzero(&exp,sizeof(exp));
 s.host=host;
 s.port=port;
 s.community=community;
 if(open_session(&s)){
   printf("Couldn't open host %s\n",s.host);
   exit(1);
 }


 set_oid(&cl,".1.3.6.1.4.1.3.2.3.3.1.1");
 set_oid(&addr,".1.3.6.1.4.1.3.2.3.3.1.2");
 set_oid(&exp,".1.3.6.1.4.1.3.2.3.3.1.3");
 set_oid(&code,".1.3.6.1.4.1.3.2.3.3.1.4");
 while(!snmp_walk(&s,&cl) && !snmp_walk(&s,&addr) &&!snmp_walk(&s,&exp) &&!snmp_walk(&s,&code) ){
    for(i=0,rc=0;i<cl.val_len;i++)
        rc|=!iscntrl(cl.val.string[i]);

    bzero(buf,sizeof(buf));
    if(rc){
		if(cl.val_len==7){
	sprintf(buf,"Hardware type: ");
	switch(cl.val.string[0]){
	case 0x1:sprintf(buf+strlen(buf),"ethernet"); break;
	case 0x6:sprintf(buf+strlen(buf),"token ring"); break;
	case 0xFE:sprintf(buf+strlen(buf),"PCTCP SLIP"); break;
	default: sprintf(buf+strlen(buf),"%02X",cl.val.string[0]);
	}
	sprintf(buf+strlen(buf)," \tAddress: ");
	for(i=1;i<cl.val_len;i++)
		sprintf(buf+strlen(buf),"%02X",cl.val.string[i]);
	} else {
        buf[0]='0';
        buf[1]='x';
        for(i=0;i<cl.val_len;i++)
            sprintf(buf+strlen(buf),"%02X",(unsigned char)cl.val.string[i]);
			}
    } else {
        buf[0]='"';
        bcopy(cl.val.string,buf+1,cl.val_len*sizeof(char));
        buf[cl.val_len+1]='"';
    }

		
    switch(*code.val.integer){
    case 1: type="reserved"; break;
    case 2: type="dynamic dhcp"; break;
    case 3: type="dynamic bootp"; break;
    case 4: type="roaming"; break;
    default: type="unknown";
    }

   
    ip=(unsigned char*)addr.val.string;
    if(!name_flag)
        hp=gethostbyaddr((char*) ip,4,2);
    if(!name_flag && hp)
        printf("%u.%u.%u.%u    \t(%s)\n",ip[0],ip[1],ip[2],ip[3],hp->h_name);
    else
        printf("%u.%u.%u.%u\n",ip[0],ip[1],ip[2],ip[3]);
    printf("\tAddress source: %s\n",type);
	printf("\t%s\n",buf);
	if(*exp.val.integer>0xfffffff0)
		printf("\tNever expires\n");
	else
		printf("\tExpires at %s\n",cctime(*exp.val.integer));

	}

 exit (0);
}
	
void unknownfn(int argc, char *argv[])
{
	char **targv,buf[256],*cp;
	char name_flag;
	Var len,type,addr,last,gw,fromip;
	unsigned char *ip;
	struct hostent *hp;
	int targc;


	targc=argc;
	targv=argv;
	argc--;
	argv++;
	host=servers[0];
	name_flag=0;

	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': argv++; argc--; host=*argv; break;
		case 'n': name_flag++; break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}
	if(argc)
		help(targc,targv);


 bzero(&len,sizeof(len));
 bzero(&type,sizeof(type));
 bzero(&addr,sizeof(type));
 bzero(&last,sizeof(last));
 bzero(&gw,sizeof(gw));
 bzero(&fromip,sizeof(fromip));
 s.port=port;
 s.host=host;
 s.community=community;
 rc=open_session(&s);
 if(rc==1){
   printf("Couldn't open host %s\n",argv[1]);
   exit(1);
 }

 set_oid(&addr,".1.3.6.1.4.1.3.2.3.12.1.1");
 set_oid(&type,".1.3.6.1.4.1.3.2.3.12.1.2");
 set_oid(&len,".1.3.6.1.4.1.3.2.3.12.1.3");
 set_oid(&last,".1.3.6.1.4.1.3.2.3.12.1.4");
 set_oid(&gw,".1.3.6.1.4.1.3.2.3.12.1.5");
 set_oid(&fromip,".1.3.6.1.4.1.3.2.3.12.1.6");
 while(!snmp_walk(&s,&addr) && !snmp_walk(&s,&type) && !snmp_walk(&s,&len) && !snmp_walk(&s,&last) && !snmp_walk(&s,&gw) && !snmp_walk(&s,&fromip)){
	if((*type.val.integer==1) && (*len.val.integer==16)){
		printf("Microsoft RAS proxy DHCP client\n");
	}else {
	printf("Hardware type : ");
	switch(*type.val.integer){
	case 1: printf("ethernet");break;
	case 6: printf("token-ring");break;
	case 0xFE: printf("PCTCP SLIP");break;
	default: printf("Unknown (%02X)",*type.val.integer);
	}
	printf("\n");

	printf("Hardware Length: %d\n",*len.val.integer);
	}

	for(i=0;(i+1)<*len.val.integer && (i+1)<BP_CHADDR_LEN;i++)
		printf("%02X-",addr.val.string[i]);
	printf("%02X\n",addr.val.byte[i]);
	printf("Last seen request %s\n",cctime(*last.val.integer));
	printf("Relay Agent: ");
	if(!*gw.val.integer)
		printf("not forwarded by a BOOTP relay agent\n");
	else{
		printf("%d.%d.%d.%d",
			gw.val.byte[0],gw.val.byte[1],gw.val.byte[2],gw.val.byte[3]);
		if(!name_flag){
		hp=gethostbyaddr((char*) gw.val.byte,4,AF_INET);
		if(hp)
			printf(" (%s)\n",hp->h_name);
		else
			printf("\n");
		}
	}
	
	if(*fromip.val.integer && (*fromip.val.integer!=*gw.val.integer)){
		printf("Sent from address %d.%d.%d.%d",
			fromip.val.byte[0],fromip.val.byte[1],fromip.val.byte[2],fromip.val.byte[3]);
		if(!name_flag){
		hp=gethostbyaddr((char*) fromip.val.byte,4,AF_INET);
		if(hp)
			printf(" (%s)\n",hp->h_name);
		else
			printf("\n");
			}
	}



	printf("\n");
	}

 exit (0);
}

void dynamic(int argc, char *argv[])
{
	char **targv,buf[256];
	u_char *cp;
	char name_flag;
	Var name,network,mask,code,freeips;
	unsigned char *ip;
	struct hostent *hp;
	int targc;


	targc=argc;
	targv=argv;
	argc--;
	argv++;
	host=servers[0];

	while(argc>=2 && argv[0][0]=='-'){
		switch(argv[0][1]){
		case 'c': argv++; argc--; community=*argv; break;
		case 'p': argv++; argc--; port=atoi(*argv); break;
		case 's': argv++; argc--; host=*argv; break;
		default:
			help(targc,targv);
		}
		argc--;
		argv++;
	}
	if(argc)
		help(targc,targv);


 s.port=port;
 s.host=host;
 s.community=community;
 rc=open_session(&s);
 if(rc==1){
   printf("Couldn't open host %s\n",argv[1]);
   exit(1);
 }


 set_oid(&name,".1.3.6.1.4.1.3.2.3.5.1.1.1");
 set_oid(&network,".1.3.6.1.4.1.3.2.3.5.1.1.2");
 set_oid(&mask,".1.3.6.1.4.1.3.2.3.5.1.1.3");
 set_oid(&code,".1.3.6.1.4.1.3.2.3.5.1.1.4");
 set_oid(&freeips,".1.3.6.1.4.1.3.2.3.5.1.1.5");
 while(!snmp_walk(&s,&name) && !snmp_walk(&s,&network) && !snmp_walk(&s,&mask) && !snmp_walk(&s,&code) && !snmp_walk(&s,&freeips)){

  cp=name.val.string;
  printf("%s %d.%d.%d.%d mask:%d.%d.%d.%d\n",cp,
	network.val.byte[0],network.val.byte[1],network.val.byte[2],network.val.byte[3],
	mask.val.byte[0],mask.val.byte[1],mask.val.byte[2],mask.val.byte[3]);
	
	printf("Access: ");
	if(*code.val.integer&ROAMING_ACCESS)
		printf(" Roaming");
	if((*code.val.integer&0x3) ==(DHCP_ACCESS|BOOTP_ACCESS))
		printf(" DHCP/BOOTP");
	else {
		if(*code.val.integer&DHCP_ACCESS)
			printf(" DHCP");
		if(*code.val.integer&BOOTP_ACCESS)
			printf(" BOOTP");
	}
	if(*code.val.integer&REG_ACCESS)
		printf(" Registered");
 	printf("\n# of remaining dynamic ip's free: %d\n\n",*freeips.val.integer);
}

 exit(0);
}



int main(int argc, char *argv[])
{

 if(argc<2){
        help(0,NULL);
        exit(0);
 }

init_mib();
 bzero(&s,sizeof(s));
 bzero(&v,sizeof(v));
 if(!strncmp("st",argv[1],2))
        status(argc-1,argv+1);
 if(!strncmp("ge",argv[1],2))
        getinfo(argc-1,argv+1);
 if(!strncmp("fl",argv[1],2))
        flush(argc-1,argv+1);
 if(!strncmp("gc",argv[1],2))
        gc(argc-1,argv+1);
 if(!strncmp("ch",argv[1],2))
        readtab(argc-1,argv+1);
 if(!strncmp("readt",argv[1],5))
        readtab(argc-1,argv+1);
 if(!strncmp("readc",argv[1],5))
        readconf(argc-1,argv+1);
 if(!strncmp("de",argv[1],2))
        delete_perm(argc-1,argv+1);
 if(!strncmp("rel",argv[1],3))
        release(argc-1,argv+1);
 if(!strncmp("l",argv[1],1))
        active_leases(argc-1,argv+1);
 if(!strncmp("u",argv[1],1))
        unknownfn(argc-1,argv+1);
 if(!strncmp("dy",argv[1],1))
        dynamic(argc-1,argv+1);
 if(!strncmp("h",argv[1],1))
        help(argc-2,argv+2);
  help(0,NULL);
 exit(0);
}

int open_session(Snmp *s){

	struct snmp_session session;

	bzero(&session,sizeof(session));
	if(!s->community)
		s->community="public";
	session.peername=s->host;
	session.version = SNMP_VERSION_1;
	session.community = (u_char *)s->community;
	session.community_len = strlen(s->community);
	session.remote_port=s->port;
	if(s->retries)
		session.retries = s->retries;
	else
		session.retries = SNMP_DEFAULT_RETRIES;
	if(s->timeout)
		session.timeout = s->timeout;
	else
		session.timeout = SNMP_DEFAULT_TIMEOUT;
	session.authenticator = NULL;
	snmp_synch_setup(&session);
	s->ss=snmp_open(&session);;
	if (s->ss == NULL)
		return 1;

	return 0;
}



int set_oid(Var *v, char *objid)
{
	int rc;

	v->namelen=64;
	rc=read_objid(objid,v->name,&v->namelen);
	v->rootlen=v->namelen;
	bcopy(v->name,v->root,v->namelen*sizeof(oid));
	return rc;
}

void set_integer(Var *v, unsigned long value)
{

	v->type=INTEGER;
	v->val_len=4;
	if(v->val.string)
		free(v->val.string);
	v->val.string=(u_char*)malloc(4);
	*(unsigned long*)v->val.integer=value;
}
void set_string(Var *v, char *str)
{

	v->type=STRING;
	v->val_len=strlen(str);
	if(v->val.string)
		free(v->val.string);
	v->val.string=(u_char*)malloc(v->val_len);
	bcopy(str,v->val.string,v->val_len);
}


int snmp_get(Snmp *s, Var *v)
{
	struct variable_list *vars;
	int         rc, count, status;
	struct snmp_pdu *pdu, *response;

	pdu = snmp_pdu_create(GET_REQ_MSG);

	snmp_add_null_var(pdu, v->name, v->namelen);


retry:
	v->status=snmp_synch_response(s->ss, pdu, &response);
	if(v->status==STAT_SUCCESS){
		v->errstat=response->errstat;
		if (response->errstat == SNMP_ERR_NOERROR) {
			for (vars = response->variables; vars; vars = vars->next_variable) {
                if ((v->namelen==vars->name_length) &&
						!bcmp(v->name, vars->name,v->namelen*sizeof(oid))) {
					if(v->val.string)
						free(v->val.string);
					v->val.string=(u_char*)malloc(vars->val_len);
					v->val_len=vars->val_len;
					bcopy(vars->val.string,v->val.string,v->val_len);
					v->type=vars->type;
				}
			}
            if ((pdu = snmp_fix_pdu(response, GET_REQ_MSG)) != NULL)
                goto retry;
			snmp_free_pdu(response);
			return 0;
			}
           snmp_free_pdu(response);
            return 1;

		} else 
		return 1;

}



int snmp_walk(Snmp *s, Var *v)
{
	struct variable_list *vars;
	int         rc, count, status;
	struct snmp_pdu *pdu, *response;

	pdu = snmp_pdu_create(GETNEXT_REQ_MSG);

	snmp_add_null_var(pdu, v->name, v->namelen);


retry:
	v->status=snmp_synch_response(s->ss, pdu, &response);
	if(v->status==STAT_SUCCESS){
		v->errstat=response->errstat;
			if (response->errstat == SNMP_ERR_NOERROR) {
				for (vars = response->variables; vars; vars = vars->next_variable) {
                if ((v->rootlen > vars->name_length) ||
						bcmp(v->root, vars->name,v->rootlen*sizeof(oid))) {
					snmp_free_pdu(response);
                    return 1;
				}
					if(v->val.string)
						free(v->val.string);
					v->val.string=(u_char*)malloc(vars->val_len);
					v->val_len=vars->val_len;
					bcopy(vars->val.string,v->val.string,v->val_len);
					v->type=vars->type;
					v->namelen=vars->name_length;
					bcopy(vars->name,v->name,v->namelen*sizeof(oid));
			}
            if ((pdu = snmp_fix_pdu(response, GETNEXT_REQ_MSG)) != NULL)
                goto retry;
			snmp_free_pdu(response);
			return 0;
			}
           snmp_free_pdu(response);
            return 1;

		} else 
		return 1;

}




int snmp_set(Snmp *s, Var *v)
{
	struct variable_list *vars;
	int         rc, count, status;
	struct snmp_pdu *pdu, *response;

	pdu = snmp_pdu_create(SET_REQ_MSG);

	snmp_add_var(pdu, v->name, v->namelen,v->type, v->val.string,v->val_len);


retry:
	v->status=snmp_synch_response(s->ss, pdu, &response);
	if(v->status==STAT_SUCCESS){
		v->errstat=response->errstat;
			if (response->errstat == SNMP_ERR_NOERROR) {
           		snmp_free_pdu(response);
            	return 0;
			}
			
            if ((pdu = snmp_fix_pdu(response, SET_REQ_MSG)) != NULL)
                goto retry;
			snmp_free_pdu(response);
			return 1;

		} else 
		return 1;

}

	
