// ################################################################################  
// ## Redistributions of source code must retain the above copyright notice
// ## DATE: 2011-09-22
// ## AUTHOR: SecPoint ApS  
// ## MAIL: info@secpoint.com  
// ## SITE: http://www.secpoint.com  
// ## Other scanners: http://www.secpoint.com/free-port-scan.html
// ## Other scanners: http://www.secpoint.com/free-vulnerability-scan.html
// ##
// ## LICENSE: BSD http://www.opensource.org/licenses/bsd-license.php
// ################################################################################  
// ## 1.0 initial release
// ## 1.1 you could use not only port range, but single ports and port lists (check -p option)
// ## 1.2 default port list is now not 1-1024, but is a list of frequently used ports; duplicates check
// ## 1.3 added more default ports
// ## 2.0 Added option -s for Syn Scan support

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/times.h>
#endif
#include "net.h"
#include "bestports.h"

void SynScan(char *, int);

extern long int bestPortsArray[];

#define SLEEP_TIME_MSEC			5
#define MAX_PORT_STANDARD		1024
//DET LAVER DEN SELV

#define ARP_TABLE_FILENAME		"./arp_table.txt"
#define OUI_FILENAME			"./oui.txt"
#define PORT_RES_FILENAME		"./port-numbers.txt"

#define OPTIONS_ALL_PORTS		0x00000001
#define OPTIONS_RESOLVE_PORTS	0x00000002
#define OPTIONS_RESOLVE_MAC		0x00000004
#define OPTIONS_ONFLY_HIDE		0x00000008
#define OPTIONS_SYN_SCAN		0x00000010

typedef struct _port_instance
{
	struct _port_instance *next;
	struct _port_instance *prev;
	unsigned short port;
} port_instance;

int options = 0;
static unsigned long wait_time = 1000; // msec
static unsigned long num_instances = 10;
unsigned long IP;		// ip to scan
unsigned short start_port = 1;
unsigned short end_port = MAX_PORT_STANDARD;

static unsigned short current_port;
static unsigned short current_list_num=0;
static unsigned short total_opened;
unsigned short total_scanned;
static unsigned short total_ports;
static char *port_res_file;
static port_instance *oports = NULL;
long int* port_list;//list of ports to scan

int intcmp(const void *a, const void *b)
{
    return *(const int *)a - *(const int *)b;
}
 


static unsigned long get_time()
{
#ifdef WIN32
	return GetTickCount();
#else
	struct tms tm;
	return (unsigned long)times(&tm)*10;
#endif
}

static void argument_error()
{
	printf("Argument error\n");
	exit(0);
}

long int *ExplodePorts(char *str) //port list to array
{
  int   size = 10; /* Initial size of the list.							   */
  char *newword;   /* Pointer to the new word from strtok()				   */
  int   i = 0;	 /* Our current location in words/current word number	   */
  long int*words = (long int*)malloc(sizeof(long int) * (size + 1));
  long int pNum;
  newword = strtok(str, ",");
  while(newword != 0)
  {
	if(i == size)
	{
	  size += 10;
	  words = (long int*)realloc(words, sizeof(long int) * (size + 1));
	}

	pNum = atoi(newword);
	words[i] = pNum==0?-1:pNum; //if not number  -> -1
	newword = strtok(0, ",");
	++i;
  }
  words[i] = 0; //end of array
  return words;
}

static int parse_arguments(int argc, char *argv[])
{
	int i;
	char *p, *t;
	int nPlast;
	int nP;
	int pcount;
	
	IP = net_get_iip(argv[1]);
	if (IP == 0 || IP == 0xFFFFFFFF)
	{
		printf("Incorrect IP address: %s\n", argv[1]);
		exit(0);
	}
	for (i = 2; i < argc; i++)
	{
		if (argv[i][0] != '-') continue;
		switch (argv[i][1])
		{
		case 'p':
			if (argv[i][2] != 0) p = &argv[i][2];
			else
			{
				i++;
				if (i == argc) argument_error();
				p = argv[i];
			}
			t = p;
			while ( (*t != '-') && (*t != ',')) 
			{
				t++;
				if (*t == 0) 
				{
					if (atoi(p)!=0) //only one port number
					{
						port_list = (long int  *)malloc(sizeof(long int ) * (2));
						port_list[0]=atoi(p);
						port_list[1]=0;
						start_port = 0;
						break;
					}
					argument_error();
				}
			}
			if (*t=='-')
			{
				*t = 0; t++;
				start_port = atoi(p);
				end_port = atoi(t);
				
				if (start_port == 0 || end_port == 0 || end_port < start_port)
				{
					printf("Incorrect ports, starting = %d, ending = %d\n", start_port, end_port);
					exit(0);
				}
				port_list = (long int  *)malloc(sizeof(long int ) * (end_port-start_port+1+1));
				nPlast=0;
				for (nP=0; nP<= end_port-start_port ; nP++)
				{
					port_list[nP]=start_port+nP;
					nPlast++;
				}
				port_list[nPlast]=0;
				start_port = 0;
				
			}
			else
				if (*t==',')
				{
					port_list = ExplodePorts(p);
					start_port = 0;
				}

			break;
		case 's':
			options |= OPTIONS_SYN_SCAN;
			break;
		case 'n':
			if (argv[i][2] != 0) p = &argv[i][2];
			else
			{
				i++;
				if (i == argc) argument_error();
				p = argv[i];
			}
			num_instances = atoi(p);
			break;
		case 'w':
			if (argv[i][2] != 0) p = &argv[i][2];
			else
			{
				i++;
				if (i == argc) argument_error();
				p = argv[i];
			}
			wait_time = atoi(p);
			break;
		case 'a':
			options |= OPTIONS_ALL_PORTS;
			break;
		case 'r':
			options |= OPTIONS_RESOLVE_PORTS;
			break;
		case 'M':
			options |= OPTIONS_RESOLVE_MAC;
			break;
		case 'h':
			options |= OPTIONS_ONFLY_HIDE;
			break;
		default:
			break;
		}
	}

	if (start_port!=0)
	{ //taking "best ports"
		//port_list = (long int  *)malloc(sizeof(long int ) * (MAX_PORT_STANDARD-1+1));
		//for (int nP=0; nP<= MAX_PORT_STANDARD-1 ; nP++)
		//{
		//	port_list[nP]=1+nP;
		//}
		port_list = bestPortsArray;

		start_port=-1;
	}
	//sorting and removing duplicates:
	pcount=0;
	while(port_list[pcount] != 0)  ++pcount;
	//printf("%d\n",  pcount); 
	qsort(port_list, pcount, sizeof(long int), intcmp);
	//will remove dups from tail of array one by one (replacing with -1)
	for (i=pcount; i>0 ; i-- )
	{
		if ((port_list[i]==port_list[i-1])  &&(port_list[i]!=-1))
		{
			printf("Duplicate port found in list: %d\n",(int)port_list[i]);
			port_list[i]=-1;
		}
	}

	return pcount;
}

static void print_general()
{
puts("#");
puts("SecPoint Port scanner tool   (c) www.SecPoint.com\nv. 2.0"); 
puts("The Portable Penetrator - Wifi Receovery - Vulnerability Scanner");
puts("http://www.secpoint.com/portable-penetrator.html");
}

static void print_help()
{	
	puts("Usage: IP [options]\n");
	puts("Options:");
	puts("\t-p <start>-<end>\tStarting and ending port\n\t\t\t(default: only frequent ports - check readme)");
	puts("\tOR -p <port_list>\tPorts list with commas\n\t\t\t(like -p 25,137,139)");
	puts("\t-s \t\tPerforms a SYN scan. Default is Connect scan");
	puts("\t-n <inst>\tNumber of simultaneous TCP port scan instances\n\t\t\t(default: 10)"); 
	puts("\t-w <time>\tMax waiting time for each port in milliseconds, only\n\t\t\tfor Connect scans (default: 1000)");
	puts("\t-a\t\tReport all ports (OPENED, CLOSED and TIMEDOUT)");
	puts("\t-r\t\tResolve ports to known services");
	puts("\t-M\t\tMAC lookup of remote IP");
	puts("\t-h\t\tHide on-fly port displaying (display at the end)\n");
	printf("Command line example: 192.168.1.1 -p 1-65535 -n 20 -w 2000 -r -M\n\n");
}

static void print_config()
{
	char *yes = "YES", *no = "NO";
	printf("\nScanning IP: %s\n", net_get_aip(IP));
	//printf("Port range: %u - %u\n", start_port, end_port);
	//int nP=0;
	//while(port_list[nP] != 0)
	//{
	//	printf("%d\n", port_list[nP]); 
	//	++nP;				   
	//}
	printf("Instances: %u, waiting time: %u\n", (unsigned int)num_instances, (unsigned int)wait_time);
	printf("Syn scan: %s\nReport all ports: %s, Resolve ports: %s\nMAC lookup: %s, Hide on-fly port displaying: %s\n\n", 
		options & OPTIONS_SYN_SCAN ? yes : no, 
		options & OPTIONS_ALL_PORTS ? yes : no, 
		options & OPTIONS_RESOLVE_PORTS ? yes : no,
		options & OPTIONS_RESOLVE_MAC ? yes : no,
		options & OPTIONS_ONFLY_HIDE ? yes : no);
}

static void print_status()
{
//	printf("Scanned: %u/%u (%d%%), opened: %u\r", total_scanned, total_ports, (total_scanned * 100 / total_ports), total_opened);
//	printf(" opened: %u\r", total_opened);
#ifndef WIN32
	fflush(stdout);
#endif
}

void print_open_port(unsigned short port, char *service_str)
{
//	if (!service_str) printf("%u tcp open                                 \n", port);
	if (!service_str) printf("%u/tcp open\n", port);
	else
	{
		int i, len = 60 - strlen(service_str);
		printf("%u/tcp open  %s", port, service_str);
		for (i = 0; i < len; i++) 
		printf(" ");
		printf("\n");
	}
}

static void process_open_port(unsigned short port)
{
	if (options & OPTIONS_RESOLVE_PORTS)
	{
		char buff[16], *p;
		sprintf(buff, "%u/tcp", port);
		p = strstr(port_res_file, buff);
		if (!p) print_open_port(port, NULL);
		else
		{
			char *t, tmp;
			int len = strlen(buff);
			p += len;
			t = p;
			while (*t == ' ') t++;
			p = t;
			while (*t != '\n') t++;
			tmp = *t;
			*t = 0;
			print_open_port(port, p);
			*t = tmp;
		}
	}
	else print_open_port(port, NULL);
}

static void save_open_port(unsigned short port)
{
	port_instance *pi, *ps = oports;

	pi = (port_instance *)malloc(sizeof(port_instance));
	pi->port = port;

	if (ps)
	{
		while (ps->next)
		{
			if (ps->port > port) break;
			ps = ps->next;
		}

		if (!ps->next && ps->port < port)
		{
			ps->next = pi;
			pi->prev = ps;
			pi->next = NULL;
			return;
		}
		
		if (!ps->prev)
		{
			ps->prev = pi;
			pi->next = ps;
			pi->prev = NULL;
			oports = pi;
		}
		else
		{
			pi->prev = ps->prev;
			pi->prev->next = pi;
			pi->next = ps;
			ps->prev = pi;
		}
	}
	else 
	{
		oports = pi;
		oports->next = NULL;
		oports->prev = NULL;
	}
}


void on_response(port_response resp, unsigned short port)
{
//	total_scanned++;

	switch (resp)
	{
	case CLOSED:
		if (options & OPTIONS_ALL_PORTS && !(options & OPTIONS_ONFLY_HIDE)) 
			printf("%u/tcp closed\n", port);
		break;
	case OPENED:
		if (options & OPTIONS_ONFLY_HIDE) save_open_port(port);
		else process_open_port(port);
		total_opened++;
		break;
	case TIMEDOUT:
		if (options & OPTIONS_ALL_PORTS && !(options & OPTIONS_ONFLY_HIDE)) 
			printf("%u/tcp timedout\n", port);
		break;
	default:
		break;
	}

	print_status();

	return;
}

void add_next_port()
{
	total_scanned++;

	if (port_list[current_list_num]!=0)
	{
		while (port_list[current_list_num]==-1)
		{
			current_list_num++;
			if (port_list[current_list_num]==0) break;//last port in list
		}
		net_add(IP, (unsigned short)port_list[current_list_num], get_time());
		current_port++;
		current_list_num++;
	}
}


void ConnectScan()
{
	unsigned long i;

	struct_init(on_response, wait_time);

	total_opened = 0;
	total_ports = end_port - start_port + 1;
	current_port = start_port;
	for (i = 0; i < num_instances; i++)
	{
		if (port_list[current_list_num]==0) break;//last port in list
		while (port_list[current_list_num]==-1)
		{
			current_list_num++;
			if (port_list[current_list_num]==0) break;//last port in list
		}
		net_add(IP, (unsigned short)port_list[current_list_num], get_time());
		current_port++;
		current_list_num++;
		//if (current_port > end_port || current_port == 0) break;
	}

	print_status();

	while (net_check(get_time())) 
	{
#ifdef WIN32
		Sleep(SLEEP_TIME_MSEC);
#else
		usleep(SLEEP_TIME_MSEC * 1000);
#endif
	}
	
	net_uninit();
}

char *read_file(char *file)
{
	FILE *f;
	long size;
	char *buff;
	int r;

	f = fopen(file, "rb");
	if (!f)
	{
		printf("Failed to open %s file\n", file);
		exit(0);
	}

	fseek(f, 0, SEEK_END);
	size = ftell(f);
	fseek(f, 0, SEEK_SET);
	buff = (char *)malloc((unsigned int)size);

	r = fread(buff, 1, (unsigned int)size, f);
	fclose(f);

	return buff;
}

void resolve_MAC(char *mac)
{
	int i;
	char *p, *b = read_file(OUI_FILENAME);
	mac[8] = 0;
	for (i = 0; i < 8; i++) if (mac[i] >= 'a' && mac[i] <= 'f') mac[i] &= 0xDF;
	p = strstr(b, mac);
	if (p)
	{
		char *t;
		p += 18;
		t = p;
		while (*t != '\n') t++;
		*t = 0;
		printf(" %s\n", p);
	}
	else printf("Adapter manufacturer: UNKNOWN\n");
	free(b);
}

void print_MAC()
{
	int r;
	char buff[256], ips[16], *p, *b;

	strcpy(ips, net_get_aip(IP));

#ifdef WIN32
	sprintf(buff, "arp -a %s > "ARP_TABLE_FILENAME, ips);
#else
	sprintf(buff, "arp -a | grep %s | awk '{ print $4 }' > "ARP_TABLE_FILENAME, ips);
#endif
	r = system(buff);

	b = read_file(ARP_TABLE_FILENAME);
#ifdef WIN32
	p = strstr(b, ips);
#else
	for (p = b; *p; p++) 
	{
		if (*p == ':') *p = '-';
		if (*p == '\n') *p = 0;
	}
	p = b;
#endif
	if (!p) printf("ARP table contains no information about IP: %s\nMAC address cannot be resolved!\n", ips);
	else
		{
#ifdef WIN32
		char *t;
		while (*p != ' ') p++;
		t = p;
		while (*t == ' ') t++;
		p = t;
		while (*t != ' ') t++;
		*t = 0;
#endif
		printf("MAC Address: %s", p);
		resolve_MAC(p);
	}
	free(b);

#ifdef WIN32
	system("erase "ARP_TABLE_FILENAME);
#else
	r = system("rm "ARP_TABLE_FILENAME);
#endif
}

int main(int argc, char *argv[])
{
	int PortCount;
	unsigned long time;

	print_general();

	if (argc < 2)
	{
		print_help();
		return 0;
	}

	PortCount = parse_arguments(argc, argv);

	print_config();

	net_init();

	if (options & OPTIONS_RESOLVE_PORTS) port_res_file = read_file(PORT_RES_FILENAME);

	total_scanned = 0;
	time = get_time();

	if (options & OPTIONS_SYN_SCAN)
		SynScan(argv[1], PortCount);
	else
		ConnectScan();

	time = get_time() - time;

	if (options & OPTIONS_ONFLY_HIDE)
	{
		port_instance *tmp, *pi = oports;
		while (pi)
		{
			tmp = pi->next;
			process_open_port(pi->port);
			free(pi);
			pi = tmp;
		}
	}

	printf("\nTotal ports scanned: %d\nTotal ports open: %d\n", total_scanned, total_opened);
	printf("\n# Scan time: %u.%03u seconds\n\n", (unsigned int)(time / 1000), (unsigned int)(time % 1000));

	if (options & OPTIONS_RESOLVE_PORTS) free(port_res_file);
	if (options & OPTIONS_RESOLVE_MAC) print_MAC();

	net_end();

	return 0;
}

