/*-
 * Copyright (c) 1998-2005 Joao Cabral
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      DHIS(c)  Dynamic Host Information System Release 5
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<time.h>
#include<sys/time.h>
#include<sys/types.h>
#include<netdb.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<arpa/nameser.h>
#include<resolv.h>
#include<sys/utsname.h>
#include<unistd.h>
#include<signal.h>
#include<sys/signal.h>
#include<sys/wait.h>
#include<syslog.h>


#ifdef HAVE_SYS_PARAM_H
#include<sys/param.h>
#endif

				/* FreeBSD patch submitted by S. Kruit */

#if defined __FreeBSD__ 	/* FreeBSD has BIND built in */
#  if __FreeBSD__ >= 2		/* Test for 2.x versions */
#	include<osreldate.h>
#	if __FreeBSD_version >= 420001	
#	  include<arpa/nameser.h>
#       endif
#  endif
#else
#  if !defined BIND821
#	include<res_update.h>
#  endif
#endif

#define	DYN_TTL		60
#define	OFFLINE_IP	"192.168.255.0"

#define	DHISD_DB	"/etc/dhis/db/dhis.db"
#define	DHISE_LOG	"/etc/dhis/log/dhis-dns-engine.log"

#define	MAX_HOSTNAME	64
#define	MAX_PASS	16


typedef struct db_t {
			int id;
			unsigned char fqdn[MAX_HOSTNAME];
			struct db_t *next;
		    } db_t;

/* Function prototypes */

int db_reload(void);
int db_free(void);
int db_add(db_t *);
unsigned char *db_hostname(int);
void strtolower(unsigned char *);
void off_nl(unsigned char *);
unsigned char *line_entry(int,unsigned char *);
int msg_log(unsigned char *);
int dns_update(unsigned char *,int,short int,unsigned int,int,unsigned char *);
int mark_online(unsigned char *,unsigned char *);
int mark_offline(unsigned char *);

/* Global variables */
db_t *dbase=NULL;

/* Log function */
int msg_log(unsigned char *msg) {

	FILE *fp;

	time_t	tt;
	unsigned char buff[256];

	fp=fopen(DHISE_LOG,"a");
	while(fp==NULL) { sleep(1); fp=fopen(DHISE_LOG,"a"); }
	tt=time(NULL);
	strcpy(buff,ctime(&tt));
	off_nl(buff);
	strcat(buff," : ");
	strcat(buff,msg);
	strcat(buff,"\n");
	fputs(buff,fp);
	fclose(fp);
	return(1);
}
	
/* Misc functions */
void strtolower(unsigned char *s) { while(*s!='\0') { *s=tolower(*s); s++; } }
void off_nl(unsigned char *s) {

	while(*s!='\0' && *s!='\n' && *s!='\r') s++;
	*s='\0';
	return;
}

unsigned char *line_entry(int idx,unsigned char *buff) {

	static unsigned char b2[1024],*pb2;
	int i;

	idx--;
	b2[0]='\0';
	pb2=b2;

	while((*buff==' ' || *buff=='\t') && *buff!='\0' && *buff!='\n')
		buff++;
	if(*buff=='\0' || *buff=='\n') return(b2);
		
	for(i=0;i<idx;i++) {
		while(*buff!=' ' && *buff!='\t' && *buff!='\0' &&
			*buff!='\n') buff++;
		if(*buff=='\0' || *buff=='\n') return(b2);
		while((*buff==' ' || *buff=='\t') && *buff!='\0' && *buff!='\n')
			buff++;
		if(*buff=='\0' || *buff=='\n') return(b2);
	}
	while(*buff!=' ' && *buff!='\t' && *buff!='\0' && *buff!='\n') {
		*pb2 = *buff;
		buff++;
		pb2++;
	}
	*pb2 = '\0';
	return(b2);
}

int db_reload(void) {

	FILE *fp;
	unsigned char str[1024];
	db_t *rec;

	if(dbase!=NULL) db_free();

	fp=fopen(DHISD_DB,"r");
	if(fp==NULL) return(0);
	while(fgets(str,1024,fp)!=NULL) {
	 off_nl(str);

	 if(strchr(line_entry(1,str),';')==NULL)
	 if(!strcmp(line_entry(2,str),"{")) {

		unsigned char keyword[256];
		char *cp;

		rec=(db_t *)malloc(sizeof(db_t));
		if(rec==NULL) { fclose(fp); return(0); }
		rec->id=atoi(line_entry(1,str));
		rec->fqdn[0]='\0';
		do {
		if(fgets(str,1024,fp)==NULL) { fclose(fp);free(rec);return(0);}
		off_nl(str);
		strcpy(keyword,line_entry(1,str));
		strtolower(keyword);

		if(!strcmp(keyword,"hostname")) {
			strcpy(rec->fqdn,line_entry(2,str));

			// Take leading text if comment character found
			if((cp=strchr(rec->fqdn,';'))!=NULL)
				*cp='\0';
		}



		} while(strcmp(line_entry(1,str),"}"));
		if(rec->fqdn[0]=='\0') continue;
		db_add(rec);
	 } 
	} 
	fclose(fp);
	return(1);
}

int db_free() {

	db_t *p1,*p2;

	if(dbase==NULL) return(1);
	p1=dbase;
	while(p1!=NULL) {
		p2=p1;
		p1=p1->next;
		free(p2);
	}
	dbase=NULL;
	return(1);
}

unsigned char *db_hostname(int id) {

	db_t *p1;
	static unsigned char host[MAX_HOSTNAME];
	static int lastid=0;
	
	if(lastid==id) return(host);

	p1=dbase;
	while(p1!=NULL) {
		if(p1->id==id) {
			strcpy(host,p1->fqdn);
			lastid=id;
			return(host);
		}
		p1=p1->next;
	}
	return(NULL);
}

int db_add(db_t *rec) {

	db_t *p;
	p=dbase;
	if(dbase==NULL) {
	dbase=rec;
	dbase->next=NULL;
	return(1);
	}

	while(p->next!=NULL) p=p->next;
	p->next=rec;
	p->next->next=NULL;
	return(1);
}

/* DNS functions */



int mark_online(unsigned char *host,unsigned char *addr) {

	unsigned char str[128];

	if(host==NULL) return(0);
	if(host[0]=='\0') return(0);
	if(addr==NULL) return(0);
	if(addr[0]=='\0') return(0);

	sprintf(str,"%s.",host);
	dns_update(str,DELETE,T_A,0,0,"");
	dns_update(str,ADD,T_A,DYN_TTL,strlen(addr),addr);
	return(0);
}

int mark_offline(unsigned char *host) {

	unsigned char str[128],str2[16];

	if(host==NULL) return(0);
	if(host[0]=='\0') return(0);

	strcpy(str2,OFFLINE_IP);
	sprintf(str,"%s.",host);
	dns_update(str,DELETE,T_A,0,0,"");
	dns_update(str,ADD,T_A,DYN_TTL,strlen(str2),str2);
	return(0);
}


int main() {

	unsigned char line[1024];

	db_reload();
	sprintf(line,"DHIS DNS Engine Starting");
	msg_log(line);

	while(fgets(line,1024,stdin)!=NULL) {
		off_nl(line);
		if(!strcmp(line_entry(1,line),"exit")) {
			db_free();
			exit(0);
		}
		if(!strcmp(line_entry(1,line),"reload")) {
			db_reload();
			continue;
		}
		if(!strcmp(line_entry(1,line),"add")) {
			int id;
			unsigned char addr[128];
			unsigned char logstr[128];
			id=atoi(line_entry(3,line));
			strcpy(addr,line_entry(4,line));
			mark_online(db_hostname(id),addr); 
			sprintf(logstr,"-> online %s [%s]",
				db_hostname(id),addr);
			msg_log(logstr);
			continue;
		}
		if(!strcmp(line_entry(1,line),"update")) {
			int id;
			unsigned char addr[128];
			unsigned char logstr[128];
			id=atoi(line_entry(3,line));
			strcpy(addr,line_entry(4,line));
			mark_offline(db_hostname(id));
			mark_online(db_hostname(id),addr); 
			sprintf(logstr,"-> update %s [%s]",db_hostname(id),
				addr);
			msg_log(logstr);
			continue;
		}
		if(!strcmp(line_entry(1,line),"delete")) {
			int id;
			unsigned char addr[128];
			unsigned char logstr[128];
			id=atoi(line_entry(3,line));
			strcpy(addr,line_entry(4,line));
			mark_offline(db_hostname(id));
			sprintf(logstr,"-> offline %s [%s]",db_hostname(id),
				addr);
			msg_log(logstr);
			continue;
		}
	}
	return(0);	/* Just to keep -Wall quiet */
}
