/* Aspd Address Search Protocol Daemon                                     */
/* Copyright (C) 1996-2001 Stenio Brunetta                                 */
/*                                                                         */
/*  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.                                    */
/*                                                                         */
/*  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.                           */
/*                                                                         */
/*  You should have received a copy of the GNU General Public License      */
/*  along with this program; if not, write to the Free Software            */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/*                                                                         */
/*  Author's email address: stenio@brunettaeperin.it                       */

static char rcsid[] = \
           "$Id: aspd.c,v 1.39 2001/02/01 19:27:26 ste Exp $";

#include "asp.h"

static int rep = 5;
static int hname_len;
static char hname[MSGSIZE] = "";
static char* interface = NULL;

char* prog_name;

int main (int argc, char *argv[]){ 
  int asp_server(void);
  int asp_daemon(void);
  void parse_cmdline(int, char*[]);
  char *p;

  prog_name = *argv;

  parse_cmdline(argc, argv);

  if(!strcmp(hname, "")){

    /* Get our hostname */
    if(gethostname(hname, MSGSIZE) == -1)
      asp_perror("gethostname");	/* MSGSIZE < strlen(hostname)+1 */

    /* Se gethostname() ritorna il FQDN viene troncato al solo
       nome dell'host */
    if((p = strchr(hname, '.')) != NULL){
      *p = '\0';
    }
  }

  hname_len = strlen(hname);

  /* See how the program is invoked */
  p = strrchr(argv[0], '/');

  if(p == NULL) p = argv[0];
  else p++;

  if(strcmp(p, "in.aspd") == 0) return asp_server();
  else return asp_daemon();

}

int asp_server(void){
  char rmesg[MSGSIZE], last_mesg[MSGSIZE];
  struct sockaddr_in from;
  struct in_addr last_addr;
  int i, size = sizeof(from);
  
  bzero(rmesg, MSGSIZE);
  memset(&(from.sin_addr), 0, sizeof(struct in_addr));

  openlog(prog_name, LOG_PID, LOG_DAEMON);

  while(input_timeout(0, 60, 0) == 1){

    last_addr.s_addr = from.sin_addr.s_addr;
    strcpy(last_mesg, rmesg);
    bzero(rmesg, MSGSIZE);

    /* force zero termination */
    if(recvfrom(0, rmesg, MSGSIZE-1, 0, \
		(struct sockaddr *)&from, &size) == -1){
#ifdef ENABLE_DEBUG
      syslog(LOG_WARNING, "recvfrom: %s", \
	     strerror(errno));
#endif
      continue;
    }

    /* Check if it is a re-sended msg */
    if(from.sin_addr.s_addr != last_addr.s_addr || strcmp(rmesg, last_mesg))
      syslog(LOG_NOTICE, "%s searching for %s", \
	     inet_ntoa(from.sin_addr), rmesg);

    if(!strcmp(hname, rmesg))
      for(i=0; i < rep; i++)
#ifdef ENABLE_DEBUG
	if(sendto(1, hname, hname_len, 0, (struct sockaddr *)&from, \
		  sizeof(struct sockaddr)) == -1 && i == 0)
	  syslog(LOG_WARNING, "sendto: %s", \
		 strerror(errno));
#else
	  sendto(1, hname, hname_len, 0, (struct sockaddr *)&from, \
		  sizeof(struct sockaddr));
#endif
  }

  closelog();  
  return 0;	
}
	  
int asp_daemon(void){				   
  int sock, make_dsocket(int), \
    i, from_size;
  void handler(int);
  struct sockaddr_in from;
  struct servent* psServent;
  char rmesg[MSGSIZE];
  pid_t cpid;
  
  if((psServent = getservbyname("asp", "udp")) == NULL){
    fprintf(stderr, "%s: getservbyname: Protocol not found\n", prog_name);
    exit(EXIT_FAILURE);
  }
  
  sock = make_dsocket(ntohs(psServent->s_port));
  
  if((cpid = fork()) != 0){ /* parent */
    if(cpid == -1)
		asp_perror("fork");
    else
		exit(EXIT_SUCCESS);
  }
  
  signal(SIGTERM, handler);
  signal(SIGHUP, handler);
  signal(SIGINT, handler);
  
  openlog(prog_name, LOG_PID, LOG_DAEMON);
  syslog(LOG_INFO, "Started");
  
  from_size = sizeof(from);
  
  while(1){
    /* pulisce il buffer prima di ogni messaggio */
    bzero(rmesg, MSGSIZE);
    
    /* force zero termination */
    if(recvfrom(sock, rmesg, MSGSIZE-1, 0, \
		(struct sockaddr *)&from, &from_size) == -1){
#ifdef ENABLE_DEBUG
      syslog(LOG_WARNING, "recvfrom: %s", \
	     strerror(errno));
#endif
      continue;
    }
    
    syslog(LOG_NOTICE, "%s searching for %s", \
	   inet_ntoa(from.sin_addr), rmesg);
    
    if(!strcmp(hname, rmesg))
      for(i=0; i < rep; i++)
#ifdef ENABLE_DEBUG
	if(sendto(sock, hname, hname_len, 0, (struct sockaddr *)&from, \
		  sizeof(struct sockaddr)) == -1 && i == 0)
	  syslog(LOG_WARNING, "sendto: %s", \
		 strerror(errno));
#else
    sendto(sock, hname, hname_len, 0, (struct sockaddr *)&from, \
	   sizeof(struct sockaddr));
#endif
  }
}

RETSIGTYPE handler(int sig){
  syslog(LOG_INFO, "%s", \
	 strsignal(sig));
  raise(sig);
}

int make_dsocket(int port){
  int sock ;
  struct sockaddr_in *name;
  struct ifreq ifr;

  /* Crea il socket */
  sock = socket(PF_INET, SOCK_DGRAM, 0);

  if(sock < 0) asp_perror("socket");

  /* Assegna il nome al socket */
  name = (struct sockaddr_in *)&ifr.ifr_addr;
  name->sin_family = AF_INET;


  /* Se invocato con l'opzione -i assegna il nome con l'indirizzo
     dell'interfaccia specificata al socket */
  if(interface != NULL){
    
    bzero(&ifr, sizeof(ifr));
    strcpy(ifr.ifr_name, interface);
    
    if(ioctl(sock, SIOCGIFADDR, &ifr) < 0) asp_perror("ioctl");

  } else name->sin_addr.s_addr = htonl(INADDR_ANY);

  name->sin_port = htons(port);

  if(bind(sock, (struct sockaddr *)name, sizeof(*name)) < 0)
    asp_perror("bind");

  return sock;
}

void about(){
  fprintf(stderr, "Copyright (C) 1996-2001 Stenio Brunetta\n" \
	  "%s\n", rcsid);
  exit(EXIT_SUCCESS);
}

void use(void){
  fprintf(stderr, "Usage: %s [-r repetitions] [-i interface] [hostname]\n" \
	  "%s -V\n", prog_name, prog_name);
  exit(EXIT_FAILURE);
}

void parse_cmdline(int argc, char* argv[]){
  int c;

  /* we don't want getopt to write error messages */
  opterr = 0;
  while((c = getopt(argc, argv, "i:r:V")) != -1){
    switch(c){
    case 'V':
      if(argc > 2 || strcmp(argv[argc-1], "-V"))
	use();
      else about();
      break;
    case 'r': /* repetitions */
      if(bound_check(optarg, 1, 65535, &rep) != 0){
        fprintf(stderr, "%s: bad repetition number\n", argv[0]);
        exit(EXIT_FAILURE);
      }
    case 'i': /* interface */
      interface = optarg;
      break;
    default:
      use();
      break;
    }
  }
  if(optind == argc-1){
    bzero(hname, MSGSIZE);
    /* Force zero termination */
    strncpy(hname, argv[optind++], MSGSIZE-1);
  }
  if(optind != argc) use();
}
