/***********************************************************/
/* PORTSCANNER.C by Tennessee Carmel-Veilleux              */
/* Contact info: (I speak french and english :)            */
/* e-mail: veilleux@ameth.org                              */
/* www: http://www.ameth.org/~veilleux 			   */
/*                                                         */
/* Version 1.2                                             */
/* The author of this program offers no waranty at all     */
/* about the correct execution of this software material.  */
/* Furthermore, the author can NOT be held responsible for */
/* any physical or moral damage caused by the use of this  */
/* software.                                               */
/*                                                         */
/* -> This program will scan for TCP ports listening on a  */
/*    remote or local host inside the range you give to it.*/
/*    I offer no warranty over the accuracy though :)      */
/*    There are 3 verbose modes: No info, service info, and*/
/*    full info. No info is good of you only want the list */
/*    of the ports, no more info. The best mode is Full    */
/*    info, as you get error information,etc. The main     */
/*    output is STDOUT, and ALL the errors go to STDERR.   */
/*                                                         */
/*    History: v1.0 : August 9 1998 -> First release       */
/*             v1.2 : August 19 1998 -> Major release      */
/***********************************************************/

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/time.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#define VERSION "1.2"

int sock = -1; /* Main socket */
struct sockaddr_in address, address2; /* Address structure */
struct sockaddr *sub_address; /* Address in subnet */
int result; /* Temporary results for operations */
u_short current_port = 0; /* Current port being scanned */
u_short base_port = 1; /* Base port to start scanning from */
u_short end_port = 1024; /* Port number to end scanning at */
u_char start_address = 1; /* Subnet address to start at */
u_char end_address = 254; /* Subnet address to finish at */
int verbose = 0; /* Level of verbosity */
struct hostent *host_info; /* Host information structure */
struct servent *service_info; /* Service information structure */
char addr[1024]; /* Address to connect to, 1024 chars max */
int strobe = 0; /* 1 if we want strobe scanning */
int subnet = 0; /* 1 if we want subnet scanning */
int i; /* Counter variable */
	    
void port_scan(void);
void print_usage(int finish);
void print_help(int finish);
void print_info(int finish);
	    
void print_usage(int finish) {
    fprintf(stderr,
"Usage: portscan [-b start] [-e end] [-bm start] [-em end] [-v[v]] [-a] [-s] [-i] [-? | h] <address>\n");
    if (finish) 
     exit(1);
}

void print_help(int finish) {
 print_usage(0);
 fprintf(stderr,"\n-b start: Specify the port at which we begin scanning\n");
 fprintf(stderr,"-e end: Specify the port at which we stop scanning\n");
 fprintf(stderr,"-bm: Subnet machine number at which to start scanning [dfl=1]\n");
 fprintf(stderr,"-em: Subnet machine number at which to stop scanning [dfl=254]\n");  
 fprintf(stderr,"-v: Level 1 of verbosity, basic information\n");
 fprintf(stderr,"-vv: Level 2 of verbosity, full information report\n");
 fprintf(stderr,"-a: if we want subnet scanning (start at .1, end at .254)\n");
 fprintf(stderr,"-s: strobe scanning: scan only ports found in /etc/services, much faster\n");
 fprintf(stderr,"-? or -h: print this help text\n");
 fprintf(stderr,"-i: Copyright and version information\n");
 if (finish) exit(1);
} 

void print_info(int finish) {       
 fprintf(stderr,"portscanner v%s was written by Tennessee Carmel-Veilleux\n",VERSION);
 fprintf(stderr,"This version compiled on %s at %sEST\n",__DATE__,__TIME__);
 fprintf(stderr,"This program is licensed under the GPL. See www.gnu.org for more info on the license\n");
 if (finish) exit(1);
}
       
int main(int argc, char **argv)
{ 
   if (argc < 2) {
    print_usage(1);
   }
   
   switch (argc) {
     case 2: if (!strcmp(argv[1],"-i")) {
              print_info(1);
             } else if ((!strcmp(argv[1],"-?")) || (!strcmp(argv[1],"-h"))) {
              print_help(1);
             } else { 
		break;
	     }	
		
     default: for (i = 1; i < argc - 1; i++) {
       if (!strcmp(argv[i],"-v")) { 
        verbose = 1;
       } else 
       if (!strcmp(argv[i],"-vv")) {
        verbose = 2;
       } else
       if (!strcmp(argv[i],"-i")) {
        print_info(0);
       } else
       if ((!strcmp(argv[i],"-h")) || (!strcmp(argv[1],"-?"))) {
        print_help(0);
       } else
       if (!strcmp(argv[i],"-a")) {
        subnet = 1;
       } else
       if (!strcmp(argv[i],"-s")) {
        strobe = 1;
       } else
       if (!strcmp(argv[i],"-b")) {
        if ((i+1) > (argc - 1)) { 
         print_usage(1);
        } else {
           base_port = (u_short)atoi(argv[i+1]);
           i++;
        }
       } else 
       if (!strcmp(argv[i],"-e")) {
        if ((i+1) > (argc - 1)) {
         print_usage(1);
        } else {
           end_port = (u_short)atoi(argv[i+1]);
           i++;
        }
       } else
      if (!strcmp(argv[i],"-bm")) {
       if ((i+1) > (argc - 1)) {
	 print_usage(1);
       } else {
	  start_address = (u_char)atoi(argv[i+1]);
	  i++;
       }
      } else 
      if (!strcmp(argv[i],"-em")) {
        if ((i+1) > (argc - 1)) {
         print_usage(1);
        } else {
           end_address = (u_char)atoi(argv[i+1]);
           i++;
        }
       }
     }
    }     
    
   if ((base_port > end_port) || ((short)base_port < 0)) { 
     fprintf(stderr,"Bad port range : start=%d end=%d !\n", base_port, end_port);
     exit(1);
   }
   
   if ((start_address > end_address)) { 
     fprintf(stderr,"Bad address range : start=%d end=%d !\n", start_address, end_address);
     exit(1);
   }
   
   if (subnet) printf("Subnet scanning enabled\n");
    
    bzero((char *)&address, sizeof(address));
    address.sin_family = AF_INET;
    
      
    strncpy(addr,argv[argc - 1],1023);
    addr[1023] = 0;
      
    if (verbose == 2) printf("Resolving: %s ->",addr);
    if ((host_info = gethostbyname(addr)))
    {
      bcopy(host_info->h_addr, (char *)&address.sin_addr,host_info->h_length);
      if (verbose == 2) printf(" resolved\n");
    } 
   else if ((address.sin_addr.s_addr = inet_addr(argv[1])) == INADDR_NONE)
   {
            fprintf(stderr,"Could not get %s host entry !\n",argv[1]);
            printf(" NOT resolved !!!\n");
            exit(1);
   }
   else if (verbose == 2) printf(" address valid\n");
   
  if (subnet) { 
   if (verbose == 2) printf("Starting SUBNET port scanning\n");
   for (i=start_address; i <= end_address; i++) {
      sub_address = (struct sockaddr *)&address;
      sub_address->sa_data[5] = i;
      
      if (verbose == 2) printf("Current address: ");
      
      printf("%d.%d.%d.%d\n",(u_char)sub_address->sa_data[2],(u_char)sub_address->sa_data[3],
	                     (u_char)sub_address->sa_data[4],(u_char)sub_address->sa_data[5]);
      
      bcopy(sub_address,&address2,sizeof(address2));	
   
      if (verbose == 2) {
            printf("Port range: %d to %d\n",base_port,end_port);
      }
      port_scan();
      printf("#\n");
   }
   if (verbose == 2) printf("SUBNET scanning completed\n");
  } else {
      sub_address = (struct sockaddr *)&address;
      
      if (verbose == 2) printf("Current address: ");
      
      printf("%d.%d.%d.%d\n",(u_char)sub_address->sa_data[2],(u_char)sub_address->sa_data[3],
	                     (u_char)sub_address->sa_data[4],(u_char)sub_address->sa_data[5]);
      
      bcopy(sub_address,&address2,sizeof(address2));	
   
      if (verbose == 2) {
            printf("Port range: %d to %d\n",base_port,end_port);
      }
      port_scan();
      printf("#\n");
  }
   exit(0);
}

void port_scan(void) {
   int finished = 0; /* Set to 1 when strobe scanning is finished */
   int goodproto = 0; /* set to 1 if protocol returned in service info structure can be scanned */
   
   if ((strobe) && (verbose == 2)) { 
       printf("Strobe scanning started...\n");
       setservent(1);
   }
   
   while (((base_port + current_port) <= end_port) || !finished) {
/*  fprintf(stderr,"Trying port: %d\n",base_port+current_port); */
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if (sock == -1)
     {
	// fprintf(stderr, "Error assigning master socket: %s\n",sys_errlist[errno]);
	fprintf(stderr, "Error assigning master socket: sys_errlist[errno]\n");
	exit(-1);
     } 
   
    if (strobe) {
     while(!goodproto) {
      service_info = getservent();
      if (!service_info) break;
      if (!strcmp(service_info->s_proto,"tcp") && (ntohs(service_info->s_port) <= end_port) && (ntohs(service_info->s_port) >= base_port)) {
       goodproto = 1;
       break;
      }
     }
     if (!goodproto) break;
       
     if (!service_info) {
       finished = 1;
       break;
     }
     if (goodproto) address2.sin_port = service_info->s_port;
     goodproto = 0;
    } else address2.sin_port = htons(base_port+current_port);

    if (!finished)
    if (connect(sock, (struct sockaddr *)&address2, sizeof(address2)) == 0) {
     switch (verbose) {
      case 0: if (strobe)
	       printf("%d\n",ntohs(service_info->s_port));
              else
	       printf("%d\n",base_port+current_port);
              break;
      case 1: if (!strobe) {
	       service_info = getservbyport(htons(base_port+current_port),"tcp");
               if (!service_info) {
                printf("%d -> service name unknown\n",base_port+current_port);
               } else {
                printf("%d -> %s\n",base_port+current_port,service_info->s_name);
               }
              } else printf("%d -> %s\n",ntohs(service_info->s_port),service_info->s_name);
              break; 
      case 2: if (!strobe) {
	       service_info = getservbyport(htons(base_port+current_port),"tcp");
               if (!service_info) {
                printf("Port %d found. Service name unknown\n",base_port+current_port);
               } else {
                printf("Port %d found. Service name: %s\n",base_port+current_port,service_info->s_name);
               }
              }  else {
		printf("Port %d found. Service name: %s\n",ntohs(service_info->s_port),service_info->s_name);
              }
              break; 
     } 
    }  else if (errno == 113) {
         fprintf(stderr,"No route to host !\n");
         if (!subnet) {
	  close(sock);
	  exit(1);
	 }
       } 
/*     fprintf(stderr,"Error %d connecting socket %d to port %d: %s\n",
     errno,sock,base_port+current_port,sys_errlist[errno]); */ 
     close(sock);
     current_port++;
     if (base_port+current_port >= end_port) {
       finished = 1;
     }
    /* fprintf(stderr,"current_port: %d,b: %d,e:%d,b+c:%d\n", current_port, base_port,end_port,base_port+current_port); */
   }

  if (verbose == 2) printf("Port scan finished !\n");
  if (strobe) endservent();
}

