/*-
 * Copyright (c) 1999-2000 James E. Housley <jim@thehousleys.net>
 * 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.
 *
 * $Id: healthdc.c,v 1.1.2.30 2003/01/26 15:27:17 housley Exp $
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>

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

#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO)
#include "addrinfo.h"
#endif

#ifdef HAVE_LIBWRAP
#include <tcpd.h>
int allow_severity = 0;
int deny_severity = 0;
#endif /* HAVE_LIBWRAP */

#include "VERSION.h"

int main(int argc, char *argv[], char *envp[]);
void PrintNormalOutput(int , char *);
void PrintHTTPOutput(int , char *);
int OpenConnection(char *, char *, int);
int GetProtocolVersion(int sock);
int GetDaemonVersion(int sock);
void ParseLine(char *);
int hex2dec(char *ptr);
void BailOut(int code);

float VerProto = 1.0;
int VerDaemon_major = 0;
int VerDaemon_minor = 6;
int VerDaemon_sub = 0;

int tabout = 0;
int verbose = 0;
int debug = 0;
int html = 0;
int html_body = 0;

int CONTENT_LENGTH = 0;
int POST=1;

#define PrintMAX 10240
char InputLine[PrintMAX];

#define MAX_TARGETS 255
char *Target[MAX_TARGETS+1];
int TargetPtr;

char *query_string = NULL;

int
main(int argc, char *argv[], char *envp[]) {
  char ch;
  int x, y, z;
  int len;
  int sock;
  struct servent *sp;
  unsigned long port;
  char HostNameBuffer[MAXHOSTNAMELEN];
  char portstr[16];

  for (sock=0; sock<=MAX_TARGETS; sock++) {
    Target[sock] = NULL;
  }
  TargetPtr = 0;

  if ((sp = getservbyname("healthd", "tcp")) == NULL) {
    port = 1281;
    strcpy(portstr, "1281");
  } else {
    port = ntohs(sp->s_port);
    sprintf(portstr, "%ld", port);
  }
  sock = 0;
  while (envp[sock] != NULL) {
    ParseLine(envp[sock]);
    sock++;
  }
  while((ch = getopt(argc, argv, "HP:Vdhtv")) != -1) {
    switch(ch){
    case 'H':
      html = 1;
      POST = 0;
      break;

    case 'P':
      port = atoi(optarg);
      break;

    case 'V':
      fprintf(stderr, "Version %s\n", hdVERSION);
      exit(0);
      break;

    case 'd':
      debug = 1;
      break;

    case 'h':
      html = 1;
      html_body = 1;
      POST = 0;
      break;

    case 't':
      tabout = 1;
      html = 0;
      break;

    case 'v':
      verbose = 1;
      html = 0;
      break;

    default:
      fprintf(stderr, "Usage: %s [-H|h] [-P port] [-V] [-d] [-t|v] [hostname ...]\n", argv[0]);
      exit(1);
    }
  }

  while (optind < argc) {
    if ((Target[TargetPtr] = (char *)malloc(1+strlen(argv[optind]))) == NULL) {
      for (sock=0; sock<TargetPtr; sock++) {
	free(Target[TargetPtr]);
      }
      exit(1);
    }
    strcpy(Target[TargetPtr], argv[optind]);
    TargetPtr++;
    optind++;
  }
  if (html) {
    if (html_body == 0) {
      printf("Content-type: text/html\n\n<HTML><HEAD><TITLE>healthd</TITLE></HEAD><BODY>\n");
      x = 0;
      while (envp[x] != NULL) {
	x++;
      }
    }
    if (POST) {
      /*
       * The POST (origional) method was used
       */
      y = 0;
      len = 0;
      do {
	z = fgetc(stdin);
	len++;
	InputLine[y++] = z;
	if ((z == '&') || (z == EOF) || (z == '\n') || 
	    (z == '\r') || (z == ' ')) {
	  InputLine[y-1] = '\0';
	  ParseLine(InputLine);
	  y = 0;
	}
	if (CONTENT_LENGTH != 0) {
	  if (len == CONTENT_LENGTH) {
	    InputLine[y] = '\0';
	    ParseLine(InputLine);
	    break;
	  }
	}
	if (y > (PrintMAX - 4)) {
	  printf("<BR><CENTER><H1>ERROR!!!</H1><BR><BR><H2>INPUT TOO LARGE!!!!<BR>\n");
	  y = 0;
	}
      } while ((z != EOF) && (z != '\n') && (z != '\r') && (z != ' '));
    } else {
      if (query_string != NULL) {
	/*
	 * The GET method was used
	 */
	char *ptr;

	y = 0;
	len = 0;
	ptr = query_string;
	ptr += sizeof("QUERY_STRING=") - 1;
	do {
	  z = *ptr++;
	  len++;
	  InputLine[y++] = z;
	  if ((z == '&') || (z == EOF) || (z == '\n') || 
	      (z == '\r') || (z == ' ')) {
	    InputLine[y-1] = '\0';
	    ParseLine(InputLine);
	    y = 0;
	  }
	  if (*ptr == '\0') {
	    InputLine[y] = '\0';
	    ParseLine(InputLine);
	    break;
	  }
	  if (y > (PrintMAX - 4)) {
	    printf("<BR><CENTER><H1>ERROR!!!</H1><BR><BR><H2>INPUT TOO LARGE!!!!<BR>\n");
	    y = 0;
	  }
	} while ((z != '\0') && (z != '\n') && (z != '\r') && (z != ' '));
      }
    }
  }
  if (TargetPtr == 0) {
    if ((Target[TargetPtr] = (char *)malloc(1+strlen("localhost"))) == NULL) {
      for (sock=0; sock<TargetPtr; sock++) {
	free(Target[TargetPtr]);
      }
      exit(1);
    }
    strcpy(Target[TargetPtr], "localhost");
    TargetPtr++;
  }
  for (x=0; x<TargetPtr; x++) {
    if (*Target[x] == '[' && strrchr(Target[x], ']') != NULL) { 
      /* IPv6 address in [] */
      strncpy(HostNameBuffer, Target[x] + 1, strlen(Target[x]) - 2);
      HostNameBuffer[strlen(Target[x]) - 2] = '\0';
    } else {
      strncpy(HostNameBuffer, Target[x], strlen(Target[x]));
      HostNameBuffer[strlen(Target[x])] = '\0';
    }
    if ((sock = OpenConnection(HostNameBuffer, portstr, sizeof(HostNameBuffer))) == 0) {
      continue;
    }

    if (GetProtocolVersion(sock)) {
      close(sock);
      continue;
    }
    if (GetDaemonVersion(sock)) {
      close(sock);
      continue;
    }

    if (html) {
      PrintHTTPOutput(sock, HostNameBuffer);
    } else {
      PrintNormalOutput(sock, HostNameBuffer);
    }
  }

  for (sock=0; sock<=MAX_TARGETS; sock++) {
    if (Target[sock] != NULL) {
      free(Target[sock]);
    }
  }
  if (query_string != NULL) {
    free(query_string);
  }

  if ((html) && (html_body == 0)) {
    printf("</BODY></HTML>\n");
  }
  exit(0);
}

int 
OpenConnection(char *HostName, char *portstr, int size) {
  char *hostptr;
  struct addrinfo hints, *res, *res0;
  const char *cause = NULL;
  int error;
  int sock;

  hostptr = HostName;
  memset(&hints, 0, sizeof(hints));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = 0;
  error = getaddrinfo(hostptr, portstr, &hints, &res0);
  if (error) {
    warnx("%s: %s", hostptr, gai_strerror(error));
    if (error == EAI_SYSTEM)
      warnx("%s: %s", hostptr, strerror(errno));
    return (0);
  }

  res = res0;
  if (res->ai_canonname) {
    (void)strncpy(HostName, res->ai_canonname, size);
  }
  sock = -1;
  for (res = res0; res; res = res->ai_next) {
    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sock < 0) {
      cause = "socket";
      continue;
    }
    if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
      cause = "connect";
      close(sock);
      sock = -1;
      continue;
    }

    /* We made a connection */
    break;
  }
  if (sock < 0) {
    err(1, cause);
  }
  freeaddrinfo(res0);
  return(sock);
}

void
PrintNormalOutput(int sock, char *HostNameBuffer) {
  int x;
  int rval;
  char query[16];
  char seprt[8];
  char buf[1024];

  if (verbose) {
    sprintf(query, "GTV");
    sprintf(seprt, ", ");
  } else {
    sprintf(query, "GET");
    sprintf(seprt, "\t");
  }

  printf("%s%s", HostNameBuffer, seprt);
  for (x=1; x<=3; x++) {
    sprintf(buf, "%s T%d", query, x);
    if (write(sock, buf, strlen(buf)) < 0) {
      perror("sending stream message");
    }
    bzero(buf, sizeof(buf));
    if ((rval = read(sock, buf, 1024)) < 0) {
      perror("reading stream message");
    }
    if (rval == 0) {
      printf("Ending connections\n");
      goto lost_connection;
    } else {
      printf("%s%s", buf, seprt);
    }
  }
  for (x=1; x<=3; x++) {
    sprintf(buf, "%s S%d", query, x);
    if (write(sock, buf, strlen(buf)) < 0) {
      perror("sending stream message");
    }
    bzero(buf, sizeof(buf));
    if ((rval = read(sock, buf, 1024)) < 0) {
      perror("reading stream message");
    }
    if (rval == 0) {
      printf("Ending connections\n");
      goto lost_connection;
    } else {
      printf("%s%s", buf, seprt);
    }
  }
  for (x=1; x<=7; x++) {
    sprintf(buf, "%s V%d", query, x);
    if (write(sock, buf, strlen(buf)) < 0) {
      perror("sending stream message");
    }
    bzero(buf, sizeof(buf));
    if ((rval = read(sock, buf, 1024)) < 0) {
      perror("reading stream message");
    }
    if (rval == 0) {
      printf("Ending connections\n");
      goto lost_connection;
    } else {
      printf("%s%s", buf, seprt);
    }
  }
lost_connection:
  close(sock);
  printf("\n");
}

void
PrintHTTPOutput(int sock, char *HostNameBuffer) {
  int x;
  int rval;
  char buf[1024];
  int active;
  int Ival;
  float Fval;

  printf("<CENTER><H1>%s</H1></CENTER>\n", HostNameBuffer);
  printf("<CENTER><TABLE BORDER=2>\n");
  if (VerProto < 2.0) {
    for (x=1; x<=3; x++) {
      sprintf(buf, "GTV T%d", x);
      if (write(sock, buf, strlen(buf)) < 0) {
	perror("sending stream message");
      }
      bzero(buf, sizeof(buf));
      if ((rval = read(sock, buf, 1024)) < 0) {
	perror("reading stream message");
      }
      if (rval == 0) {
	printf("Ending connections\n");
	goto lost_connection;
      } else {
	sscanf(buf, "%f|%d|", &Fval, &active);
	if (active) {
	  printf("<TR><TD>Temp #%d</TD><TD>%4.1f</TD></TR>\n", x, Fval);
	}
      }
    }
    for (x=1; x<=3; x++) {
      sprintf(buf, "GTV S%d", x);
      if (write(sock, buf, strlen(buf)) < 0) {
	perror("sending stream message");
      }
      bzero(buf, sizeof(buf));
      if ((rval = read(sock, buf, 1024)) < 0) {
	perror("reading stream message");
      }
      if (rval == 0) {
	printf("Ending connections\n");
	goto lost_connection;
      } else {
	sscanf(buf, "%d|%d|", &Ival, &active);
	if (active) {
	  printf("<TR><TD>Fan Speed #%d</TD><TD>%d</TD></TR>\n", x, Ival);
	}
      }
    }
    for (x=1; x<=7; x++) {
      sprintf(buf, "GTV V%d", x);
      if (write(sock, buf, strlen(buf)) < 0) {
	perror("sending stream message");
      }
      bzero(buf, sizeof(buf));
      if ((rval = read(sock, buf, 1024)) < 0) {
	perror("reading stream message");
      }
      if (rval == 0) {
	printf("Ending connections\n");
	goto lost_connection;
      } else {
	sscanf(buf, "%f|%d|", &Fval, &active);
	if (active) {
	  printf("<TR><TD>Voltage #%d</TD><TD>%4.2f</TD></TR>\n", x, Fval);
	}
      }
    }
    printf("</TABLE></CENTER>\n");
  } else {
    /*
     * Version 2.0 or newer
     */
    for (x=1; x<=3; x++) {
      sprintf(buf, "CFG T%d_ACT", x);
      if (write(sock, buf, strlen(buf)) < 0) {
	perror("sending stream message");
      }
      bzero(buf, sizeof(buf));
      if ((rval = read(sock, buf, 1024)) < 0) {
	perror("reading stream message");
      }
      if (rval == 0) {
	printf("Ending connections\n");
	goto lost_connection;
      }
      if (strncasecmp(buf, "yes", strlen("yes")) == 0) {
	sprintf(buf, "CFG T%d_LBL", x);
	if (write(sock, buf, strlen(buf)) < 0) {
	  perror("sending stream message");
	}
	bzero(buf, sizeof(buf));
	if ((rval = read(sock, buf, 1024)) < 0) {
	  perror("reading stream message");
	}
	if (rval == 0) {
	  printf("Ending connections\n");
	  goto lost_connection;
	} else {
	  printf("<TR><TD ALIGN=RIGHT>%s</TD>", buf);
	}
	sprintf(buf, "GET T%d", x);
	if (write(sock, buf, strlen(buf)) < 0) {
	  perror("sending stream message");
	}
	bzero(buf, sizeof(buf));
	if ((rval = read(sock, buf, 1024)) < 0) {
	  perror("reading stream message");
	}
	if (rval == 0) {
	  printf("Ending connections\n");
	  goto lost_connection;
	} else {
	    printf("<TD>%s &deg;C</TD></TR>\n", buf);
	}
      }
    }
    for (x=1; x<=3; x++) {
      sprintf(buf, "CFG F%d_ACT", x);
      if (write(sock, buf, strlen(buf)) < 0) {
	perror("sending stream message");
      }
      bzero(buf, sizeof(buf));
      if ((rval = read(sock, buf, 1024)) < 0) {
	perror("reading stream message");
      }
      if (rval == 0) {
	printf("Ending connections\n");
	goto lost_connection;
      }
      if (strncasecmp(buf, "yes", strlen("yes")) == 0) {
	sprintf(buf, "CFG F%d_LBL", x);
	if (write(sock, buf, strlen(buf)) < 0) {
	  perror("sending stream message");
	}
	bzero(buf, sizeof(buf));
	if ((rval = read(sock, buf, 1024)) < 0) {
	  perror("reading stream message");
	}
	if (rval == 0) {
	  printf("Ending connections\n");
	  goto lost_connection;
	} else {
	  printf("<TR><TD ALIGN=RIGHT>%s</TD>", buf);
	}
	sprintf(buf, "GET S%d", x);
	if (write(sock, buf, strlen(buf)) < 0) {
	  perror("sending stream message");
	}
	bzero(buf, sizeof(buf));
	if ((rval = read(sock, buf, 1024)) < 0) {
	  perror("reading stream message");
	}
	if (rval == 0) {
	  printf("Ending connections\n");
	  goto lost_connection;
	} else {
	    printf("<TD>%s</TD></TR>\n", buf);
	}
      }
    }
    for (x=1; x<=7; x++) {
      sprintf(buf, "CFG V%d_ACT", x);
      if (write(sock, buf, strlen(buf)) < 0) {
	perror("sending stream message");
      }
      bzero(buf, sizeof(buf));
      if ((rval = read(sock, buf, 1024)) < 0) {
	perror("reading stream message");
      }
      if (rval == 0) {
	printf("Ending connections\n");
	goto lost_connection;
      }
      if (strncasecmp(buf, "yes", strlen("yes")) == 0) {
	sprintf(buf, "CFG V%d_LBL", x);
	if (write(sock, buf, strlen(buf)) < 0) {
	  perror("sending stream message");
	}
	bzero(buf, sizeof(buf));
	if ((rval = read(sock, buf, 1024)) < 0) {
	  perror("reading stream message");
	}
	if (rval == 0) {
	  printf("Ending connections\n");
	  goto lost_connection;
	} else {
	  printf("<TR><TD ALIGN=RIGHT>%s</TD>", buf);
	}
	sprintf(buf, "GET V%d", x);
	if (write(sock, buf, strlen(buf)) < 0) {
	  perror("sending stream message");
	}
	bzero(buf, sizeof(buf));
	if ((rval = read(sock, buf, 1024)) < 0) {
	  perror("reading stream message");
	}
	if (rval == 0) {
	  printf("Ending connections\n");
	  goto lost_connection;
	} else {
	    printf("<TD>%s</TD></TR>\n", buf);
	}
      }
    }
    printf("</TABLE></CENTER>\n");
  }
lost_connection:
  close(sock);
  printf("\n");
}

int 
GetProtocolVersion(int sock) {
  char buf[1024];
  int rval;
  float value;

  VerProto = 1.0;
  sprintf(buf, "VER P");
  if (write(sock, buf, strlen(buf)) < 0) {
    perror("sending stream message");
  }
  bzero(buf, sizeof(buf));
  if ((rval = read(sock, buf, 1024)) < 0) {
    perror("reading stream message");
  }
  if (rval == 0) {
    printf("Ending connections\n");
    return 1;
  } else {
    if (strncmp(buf, "ERROR", 5) != 0) {
      if (sscanf(buf, "%f", &value) == 1) {
	VerProto = value;
      }
    }
  }
  return 0;
}

int 
GetDaemonVersion(int sock) {
  char buf[1024];
  int rval;
  int major, minor, sub;

  VerDaemon_major = 0;
  VerDaemon_minor = 6;
  VerDaemon_sub = 0;
  sprintf(buf, "VER d");
  if (write(sock, buf, strlen(buf)) < 0) {
    perror("sending stream message");
  }
  bzero(buf, sizeof(buf));
  if ((rval = read(sock, buf, 1024)) < 0) {
    perror("reading stream message");
  }
  if (rval == 0) {
    printf("Ending connections\n");
    return 1;
  } else {
    if (strncmp(buf, "ERROR", 5) != 0) {
      if (sscanf(buf, "%d.%d.%d", &major, &minor, &sub) == 3) {
	VerDaemon_major = major;
	VerDaemon_minor = minor;
	VerDaemon_sub = sub;
      }
    }
  }
  return 0;
}

int 
hex2dec(char *ptr) {
  int rtn;

  if (('0' <= ptr[0]) && (ptr[0] <= '9'))
    rtn = ptr[0] -'0';
  else if (('A' <= ptr[0]) && (ptr[0] <= 'F'))
    rtn = ptr[0] -'A' + 10;
  else if (('a' <= ptr[0]) && (ptr[0] <= 'f'))
    rtn = ptr[0] -'a' + 10;
  else 
    rtn = 0;
  rtn *= 16;
  if (('0' <= ptr[1]) && (ptr[1] <= '9'))
    rtn += ptr[1] -'0';
  else if (('A' <= ptr[1]) && (ptr[1] <= 'F'))
    rtn += ptr[1] -'A' + 10;
  else if (('a' <= ptr[1]) && (ptr[1] <= 'f'))
    rtn += ptr[1] -'a' + 10;
  else 
    rtn += 0;
  return rtn;
}

void 
BailOut(int code) {
  if (html) {
    printf("<BR><CENTER><H1>ERROR!!!</H1><BR><BR><H2>");
  }
  printf("Internal Error #%d\n", code);
  if (html) {
    printf("</HTML>\n\n");
  }
  exit(code);
}

void 
ParseLine(char *line) {
  int x, y, len;

  if (strncmp("QUERY_STRING=", line, strlen("QUERY_STRING=")) == 0) {
    html = 1;
    if (strlen(line) != strlen("QUERY_STRING=")) {
      if ((query_string = (char *)malloc(1+strlen(line))) != NULL) {
	strcpy(query_string, line);
      } else {
	BailOut(1007);
      }
    }
  }
  else if (strncmp("HTTP_REFERER=", line, strlen("HTTP_REFERER=")) == 0) {
    html = 1;
  }
  else if (strncmp("REQUEST_METHOD=", line, strlen("REQUEST_METHOD=")) == 0) {
    html = 1;
    if (strlen(line) != strlen("REQUEST_METHOD=")) {
      if (strncmp("REQUEST_METHOD=GET", line, strlen("REQUEST_METHOD=GET")) == 0) {
	POST = 0;
      }
    }
  }
  else if (strncmp("HTTP_CONTENT_LENGTH=", line, strlen("HTTP_CONTENT_LENGTH=")) == 0) {
    int length;

    html = 1;
    if (sscanf(line, "HTTP_CONTENT_LENGTH=%d", &length) == 1) {
      CONTENT_LENGTH = length;
    }
  }
  else if (strncmp("CONTENT_LENGTH=", line, strlen("CONTENT_LENGTH=")) == 0) {
    int length;

    html = 1;
    if (sscanf(line, "CONTENT_LENGTH=%d", &length) == 1) {
      CONTENT_LENGTH = length;
    }
  }
  else if (strncmp("SERVER_ADMIN=", line, strlen("SERVER_ADMIN=")) == 0) {
    html = 1;
  } else {
    /*
     * Process the rest
     */
    len = strlen(line);
    y = 0;
    for (x=0; x<=len; x++) {
      if (line[x] == '+') {
	line[y++] = ' ';
      }
      else if (line[x] == '%') {
	line[y++] = hex2dec(line+(x+1));
	x += 2;
	if ((line[y-2] == 0x0D) && (line[y-1] == 0x0A)) {
	  line[y-2] = 0x0A;
	  y -= 1;
	}
      } else {
	line[y++] = line[x];
      }
    }
    y--;
    line[y] = '\0';
    if (strncmp("HEALTHD_TARGET=", line, strlen("HEALTHD_TARGET=")) == 0) {
      if (strlen(line) != strlen("HEALTHD_TARGET=")) {
	/* Not the first, already data in user->uname */
	if ((Target[TargetPtr] = (char *)malloc(1+strlen(line))) != NULL) {
	  strcpy(Target[TargetPtr], line+sizeof("HEALTHD_TARGET"));
	  TargetPtr++;
	} else {
	  BailOut(1007);
	}
      }
    }
  }
}


syntax highlighted by Code2HTML, v. 0.9.1