/****************
name            :       mig-identd-scanner.c

version         :       1.0

creation date   :       10th of October 2002

author          :       no1 ( greyhats.za.net )

description     :       identd version scanner with
			multiple host scanning capability

usage           :       gcc mig-identd-scanner.c -o mig-identd-scanner
                        ./mig-identd-scanner 

extra           :       if you have any comments or ideas,
                        mail me at no1@greyhats.za.net or msg me at
                        http://greyhats.za.net/guestbook/
****************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
int                      ident_scan(char *ip, int time_out, int debug, FILE ** log, int log_check, int multi);
int                      Connect(int fd, char *ip, int port, int time_out, int debug);
int                      read_timer(int fd, char *ip, int port, int time_out, int debug);
int                      write_timer(int fd, char *ip, int port, int time_out, int debug);
int                      log_format(char *result, char *buffer, char *ip, char *port);
int                      usage(char *arg);
int                      check = 0;
int main(int argc, char **argv)
{
  int                      TIMEOUT = 10;
  int                      CHILDREN = 10;
  int                      DEBUG = 0;
  int                      i = 0;
  int                      log_check = 0;
  int                      flag = 0;
  int                      multiline = 0;
  int                      status;
  FILE                    *fp;
  FILE                    *ld;
  char                     IP[16] = "127.0.0.1";
  char                     INPUT[256] = "./input318";
  char                     OUTPUT[256] = "./log";
  char                     opt;
  while((opt = getopt(argc, argv, "h:i:o:c:t:dm")) != -1)
  {
    switch (opt)
    {
      case 'h':		// ip
      {
	flag++;
	bzero(IP, sizeof(IP));
	strcpy(IP, optarg);
	remove("./input318");
	fp = fopen(INPUT, "w");
	fprintf(fp, "%s\n", IP);
	fclose(fp);
	fp = fopen(INPUT, "r");
	break;
      }
      case 'i':		// file with ips
      {
	flag++;
	bzero(INPUT, sizeof(INPUT));
	strcpy(INPUT, optarg);
	fp = fopen(INPUT, "r");
	break;
      }
      case 'o':		// log file (stdout if not used)
      {
	log_check = 1;
	strcpy(OUTPUT, optarg);
	ld = fopen(OUTPUT, "w");
	break;
      }
      case 'c':		// number of children
      {
	CHILDREN = atoi(optarg);
	break;
      }
      case 't':		// timeout value for connect/read/write
      {
	TIMEOUT = atoi(optarg);
	break;
      }
      case 'd':		// debuging output
      {
	DEBUG = 1;
	break;
      }
      case 'm':		// multi line mode
      {
	multiline = 1;
	break;
      }
    }
  }

  if((flag == 2) || (flag == 0))
  {
    usage(argv[0]);
    exit(1);
  }
  bzero(IP, sizeof(IP));
  while((fgets(IP, sizeof(IP), fp)) != NULL)
  {
      i++;
      IP[strlen(IP) - 1] = '\0';
      switch (fork())
      {
	case 0:
	{
	  ident_scan(IP, TIMEOUT, DEBUG, &ld, log_check, multiline);
	  _exit(0);
	  break;
	}
	case -1:
	{
	  perror("fork error");
	  _exit(0);
	  break;
	}
	default:
	{
	  if(i > CHILDREN - 2)
	  {
	    wait(&status);
	    i--;
	  }
	  break;
	}
      }
    bzero(IP, sizeof(IP));
  }
  remove("./input318");
  return 0;
}
int ident_scan(char *ip, int time_out, int debug, FILE ** log, int logcheck, int multi)
{
  FILE                    *logs = *log;
  int                      sockfd;
  char                     string[] = "VERSION\n";
  char                     answer[256];
  char                     buffer[256];
  char                     port_str[5];
  int                      PORT = 113;
  char                    *p_buffer;
  p_buffer = buffer;
  bzero(answer, sizeof(answer));
  bzero(buffer, sizeof(buffer));
  bzero(port_str, sizeof(port_str));
  sprintf(port_str, "%d", PORT);
  if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][113]-> socket() error\n", ip);
    return (-1);
  }
  if(Connect(sockfd, ip, PORT, time_out, debug) == -1)
  {
    close(sockfd);
    return (-1);
  }
  if(write_timer(sockfd, ip, PORT, time_out, debug) == 1)
  {
    if(write(sockfd, string, sizeof(string)) <= 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][113]-> write() failed", ip);
      close(sockfd);
      return (-1);
    }
  }
  if(read_timer(sockfd, ip, PORT, time_out, debug) == 1)
  {
    if(read(sockfd, answer, 256) <= 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][113]-> read() failed", ip);
      close(sockfd);
      return (-1);
    }
  }
  if(multi == 0)
  {
    strcat(buffer, answer);
  }
  else
  {
    log_format(answer, p_buffer, ip, port_str);
  }
  if(logcheck == 1)
  {
    fprintf(logs, "[%s][113] %s", ip, buffer);
  }
  else
  {
    fprintf(stdout, "[%s][113] %s", ip, buffer);
  }
  if(logcheck == 1)
    fflush(logs);
  else
    fflush(stdout);
  return (0);
}
int Connect(int fd, char *ip, int port, int time_out, int debug)
{
  int                      flags;
  int                      select_status;
  fd_set                   connect_read, connect_write;
  struct timeval           timeout;
  int                      getsockopt_length = 0;
  int                      getsockopt_error = 0;
  struct sockaddr_in       server;
  bzero(&server, sizeof(server));
  server.sin_family = AF_INET;
  inet_pton(AF_INET, ip, &server.sin_addr);
  server.sin_port = htons(port);
  if((flags = fcntl(fd, F_GETFL, 0)) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error getting socket flags\n", ip, port);
    close(fd);
    return (-1);
  }
  if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket non-blocking\n", ip, port);
    close(fd);
    return (-1);
  }

  timeout.tv_sec = time_out;
  timeout.tv_usec = 0;
  FD_ZERO(&connect_read);
  FD_ZERO(&connect_write);
  FD_SET(fd, &connect_read);
  FD_SET(fd, &connect_write);
  if((connect(fd, (struct sockaddr *) &server, sizeof(server))) < 0)
  {
    if(errno != EINPROGRESS)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][%d]-> connect() error\n", ip, port);
      close(fd);
      return (-1);
    }
  }
  else
  {
    if(fcntl(fd, F_SETFL, flags) < 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket flags to original state\n", ip, port);
      close(fd);
      return (-1);
    }
    return (1);
  }
  select_status = select(fd + 1, &connect_read, &connect_write, NULL, &timeout);
  if(select_status == 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> connect() timed out\n", ip, port);
    close(fd);
    return (-1);
  }
  if(select_status == -1)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> select() error on connect()\n", ip, port);
    close(fd);
    return (-1);
  }
  if(FD_ISSET(fd, &connect_read) || FD_ISSET(fd, &connect_write))
  {
    if(FD_ISSET(fd, &connect_read) && FD_ISSET(fd, &connect_write))
    {
      getsockopt_length = sizeof(getsockopt_error);
      if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &getsockopt_error, &getsockopt_length) < 0)
      {
	errno = ETIMEDOUT;
	if(debug == 1)
	  fprintf(stderr, "\n[%s][%d]-> getsockopt() timed out on connect()\n", ip, port);
	close(fd);
	return (-1);
      }
      if(getsockopt_error == 0)
      {
	if(fcntl(fd, F_SETFL, flags) < 0)
	{
	  if(debug == 1)
	    fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket flags to original state\n", ip, port);
	  close(fd);
	  return (-1);
	}
	return (1);
      }
      else
      {
	errno = getsockopt_error;
	if(debug == 1)
	  fprintf(stderr, "\n[%s][%d]-> getsockopt() error on connect()\n", ip, port);
	close(fd);
	return (-1);
      }
    }
  }
  else
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> socket not readable or writable\n", ip, port);
    close(fd);
    return (-1);
  }
  if(fcntl(fd, F_SETFL, flags) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket flags to original state\n", ip, port);
    close(fd);
    return (-1);
  }
  return (1);
}
int read_timer(int fd, char *ip, int port, int time_out, int debug)
{
  int                      flags;
  int                      select_status;
  fd_set                   fdread;
  struct timeval           timeout;
  if((flags = fcntl(fd, F_GETFL, 0)) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error getting socket flags\n", ip, port);
    close(fd);
    return (-1);
  }
  if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket non-blocking\n", ip, port);
    close(fd);
    return (-1);
  }
  timeout.tv_sec = time_out;
  timeout.tv_usec = 0;
  FD_ZERO(&fdread);
  FD_SET(fd, &fdread);
  select_status = select(fd + 1, &fdread, NULL, NULL, &timeout);
  if(select_status == 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> read() timed out\n", ip, port);
    close(fd);
    return (-1);
  }
  if(select_status == -1)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> select() error on read()\n", ip, port);
    close(fd);
    return (-1);
  }
  if(FD_ISSET(fd, &fdread))
  {
    if(fcntl(fd, F_SETFL, flags) < 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket flags to original state\n", ip, port);
      close(fd);
      return (-1);
    }
    return (1);
  }
  else
  {
    errno = ETIMEDOUT;
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> select() timed out on read()\n", ip, port);
    close(fd);
    return (-1);
  }
}
int log_format(char *result, char *buffer, char *ip, char *port)
{
  char                     x;
  char                     buf[1];
  char                     ip_port[50];
  int                      counter;
  bzero(ip_port, sizeof(ip_port));
  if(atoi(port) != 25)
  {
    sprintf(ip_port, "[%s][%s] ", ip, port);
  }
  else
  {
    strcat(ip_port, buffer);
    bzero(buffer, sizeof(buffer));
  }
  for(counter = 0; counter <= strlen(result); counter++)
  {
    bzero(buf, sizeof(buf));
    x = result[counter];
    sprintf(buf, "%c", x);
    strcat(buf, "\0");
    strcat(buffer, buf);
    if((result[counter] == '\n') && (result[counter + 1] != '\0'))
    {
      if(result[counter + 1] == '\n' && result[counter + 2] == '\0')
      {
      }
      else
      {
	strcat(buffer, ip_port);
      }
    }
  }
  return (0);
}
int write_timer(int fd, char *ip, int port, int time_out, int debug)
{
  int                      flags;
  int                      select_status;
  fd_set                   fdwrite;
  struct timeval           timeout;
  if((flags = fcntl(fd, F_GETFL, 0)) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error getting socket flags\n", ip, port);
    close(fd);
    return (-1);
  }
  if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket non-blocking\n", ip, port);
    close(fd);
    return (-1);
  }
  timeout.tv_sec = time_out;
  timeout.tv_usec = 0;
  FD_ZERO(&fdwrite);
  FD_SET(fd, &fdwrite);
  select_status = select(fd + 1, NULL, &fdwrite, NULL, &timeout);
  if(select_status == 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> write() timed out\n", ip, port);
    close(fd);
    return (-1);
  }
  if(select_status == -1)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> select() error on write()\n", ip, port);
    close(fd);
    return (-1);
  }
  if(FD_ISSET(fd, &fdwrite))
  {
    if(fcntl(fd, F_SETFL, flags) < 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][%d]-> fcntl() error setting socket flags to original state\n", ip, port);
      close(fd);
      return (-1);
    }
    return (1);
  }
  else
  {
    errno = ETIMEDOUT;
    if(debug == 1)
      fprintf(stderr, "\n[%s][%d]-> select() timed out on write()\n", ip, port);
    close(fd);
    return (-1);
  }
}
int usage(char *arg)
{
  printf("\n[0;32m******************************************[0m\n");
  printf("[0;32m* MIG identd Version Scanner v1.0 by [0;31mno1 [0;32m*[0m\n");
  printf("[0;32m******************************************[0m\n");
  printf("\n%s [[-h <ip>] | [-i <file>]] [-o <file>] [-c <#>] [-t <#>] [-d] [-m]\n", arg);
  printf("\n [-h]\tsingle ip address to scan\n");
  printf(" [-i]\tfile with ip addresses to scan\n");
  printf(" [-o]\tlog file (defult: stdout)\n");
  printf(" [-c]\tnumber of children to spawn (default: 10)\n");
  printf(" [-t]\tconnect/read/write timeout value (default: 10)\n");
  printf(" [-d]\tfor debuging output (default: off)\n");
  printf(" [-m]\tfor multiline scanning (default: single line scanning)\n\n");
  return 0;
}

/*******************/
// greyhats.za.net //
/*******************/
