/****************
name            :       mig-telnetd-scan.c

version         :       1.1
			1.0 - basic version
			1.2 - added multihost scanning

creation date   :       10th of October 2001

author          :       no1 ( greyhats.za.net )

description     :       telnetd banner grabber

usage           :       gcc mig-telnetd-scan.c -o mig-telnetd-scan
                        ./mig-telnetd-scan

extra           :       telnet banner grabber coded with help of
                        RFC-854.
                        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                      telnetd_scan(char *ip, int time_out, int debug, FILE ** log, int log_check);
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                      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                      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:d")) != -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;
      }
    }
  }

  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:
	{
	  telnetd_scan(IP, TIMEOUT, DEBUG, &ld, log_check);
	  _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 telnetd_scan(char *ip, int time_out, int debug, FILE ** log, int logcheck)
{
  FILE                    *logs = *log;
  u_int                    check1 = 0;
  u_int                    check2 = 0;
  u_char                   telnet_packet[9];
  char                     string[1024];
  char                     banner[1024];
  char                     final_string[1024];
  int                      sockfd;
  int                      x = 0;
  int                      PORT = 23;
  fflush(stdout);
  bzero(banner, sizeof(banner));
  bzero(telnet_packet, sizeof(telnet_packet));
  bzero(string, sizeof(string));
  bzero(final_string, sizeof(final_string));
  if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    if(debug == 1)
      fprintf(stderr, "\n[%s][23]-> socket() error", 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(read_timer(sockfd, ip, PORT, time_out, debug) == 1)

      for(;;)
      {
	bzero(telnet_packet, sizeof(telnet_packet));
	if(read_timer(sockfd, ip, PORT, time_out, debug) == 1)
	{
	  if(read(sockfd, telnet_packet, 1) <= 0)
	  {
	    if(debug == 1)
	      fprintf(stderr, "\n[%s][23]-> read() failed", ip);
	    close(sockfd);
	    return (-1);
	  }
	}
	//check if server is sending a command IAC
	if(telnet_packet[0] == 255)
	{
	  //get 2 bytes after IAC that represent code for command and code for option
	  if(read_timer(sockfd, ip, PORT, time_out, debug) == 1)
	  {
	    if(read(sockfd, telnet_packet + 1, 2) <= 0)
	    {
	      if(debug == 1)
		fprintf(stderr, "\n[%s][23]-> read() failed", ip);
	      close(sockfd);
	      return (-1);
	    }
	  }
	  //if byte after IAC is 255 (IAC), then do nothing, just grab teh banner
	  if(telnet_packet[1] == 255)
	  {
	    break;
	  }
	  //if byte after IAC is 253 (DO), then we have to repply with 252 (WON't)
	  if(telnet_packet[1] == 253)
	  {
	    telnet_packet[1] = 252;
	  }
	  if(write_timer(sockfd, ip, PORT, time_out, debug) == 1)
	  {
	    if(write(sockfd, telnet_packet, 3) <= 0)
	    {
	      if(debug == 1)
		fprintf(stderr, "\n[%s][23]-> write() failed", ip);
	      close(sockfd);
	      return (-1);
	    }
	  }
	  //after this restart the loop again till server gives us wanted output 
	}
	else
	{
	  if(isprint(telnet_packet[0]))
	  {
	    strncpy(final_string, telnet_packet, 1);
	  }
	  break;
	}
      }
  if(write_timer(sockfd, ip, PORT, time_out, debug) == 1)
  {
    if(write(sockfd, "\n\n", 2) <= 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][23]-> write() failed", ip);
      close(sockfd);
      return (-1);
    }
  }
  if(read_timer(sockfd, ip, PORT, time_out, debug) == 1)
  {
    if(read(sockfd, banner, sizeof(banner)) <= 0)
    {
      if(debug == 1)
	fprintf(stderr, "\n[%s][23]-> read() failed", ip);
      close(sockfd);
      return (-1);
    }
  }
  close(sockfd);
  // format the output
  if(strlen(banner) <= 2)
  {
    if(debug == 1)
      fprintf(stderr, "[%s][23]-> no valid output\n", ip);
    close(sockfd);
    return (-1);
  }
  for(check1 = 0; check1 < strlen(banner); check1++)
  {
    if(isprint(banner[check1]))
    {
      string[check2++] = banner[check1];
    }
    else if(banner[check1] == '\n')
    {
      string[check2++] = '\n';
    }
    else if(banner[check1] == '\r')
    {
      string[check2++] = '\r';
    }
  }
  strncat(final_string, string, strlen(string));
  if(logcheck == 1)
  {
    fflush(logs);
    fprintf(logs, "[%s][23]\n", ip);
    for(; x < check2; x++)
    {
      if(isprint(final_string[x]) || final_string[x] == '\n' || final_string[x] == '\r')
      {
	if(final_string[x] == '\r')
	  final_string[x] = ' ';
	fprintf(logs, "%c", final_string[x]);
      }
    }
    fprintf(logs, "\n\n+++++++++++++\n");
    fflush(logs);
  }
  else
  {
    fflush(stdout);
    fprintf(stdout, "[%s][23]\n", ip);
    for(; x < check2; x++)
    {
      if(isprint(final_string[x]) || final_string[x] == '\n' || final_string[x] == '\r')
      {
	if(final_string[x] == '\r')
	  final_string[x] = ' ';
	fprintf(stdout, "%c", final_string[x]);
      }
    }
    fprintf(stdout, "\n\n+++++++++++++\n");
    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 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 telnetd Banner Scanner v1.1 by [0;31mno1 [0;32m*[0m\n");
  printf("[0;32m******************************************[0m\n");
  printf("\n%s [[-h <ip>] | [-i <file>]] [-o <file>] [-c <#>] [-t <#>] [-d]\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\n");
  return 0;
}

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