/*
	< Multi-Thread Port Scanner >
 
	date(dd/mm/yyyy) - 28/09/2008
 
	code by hkpco (Chanam Park)
	chanam.park@hkpco.kr, http://hkpco.kr/
 
	compile:
		gcc -o mthrdscan mthrdscan.c -lpthread -D_REENTRANT -Wall
*/
 
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
 
#define MAX_THRD 8
#define DEFAULT_SZ 16
 
void *sock_chk( void *th_arg );
void sig_shoot( void *pth_id );
int save_state( int port );
int compare_port( const void *a, const void *b );
void get_time( char *t_buf, int size );
void notice_msg( void );
 
int port_cnt, begin, end;
int sigcnt;
int *open_p, memcnt, max_sz = DEFAULT_SZ;
char target[1024];
 
pthread_t get_pth, main_thrd;
pthread_mutex_t mutx_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t sig_mutx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t c_wait = PTHREAD_COND_INITIALIZER;
pthread_cond_t c_same = PTHREAD_COND_INITIALIZER;
 
int main( int argc, char **argv )
{
	int cnt;
	int thrd_id[MAX_THRD];
	char t_buf[1024] = {0x00,};
	struct servent *p_info;
	pthread_t pth[MAX_THRD];
 
	if( argc < 2 ) {
		notice_msg();
		fprintf( stderr, "\n%s [target] (begin_port) (end_port)\n", argv[0] );
		fprintf( stderr, "* default range: 1~1024\n\n" );
 
		return -1;
	}
 
	notice_msg();
 
	get_time( t_buf, sizeof(t_buf) -1 );
	printf( "\n>> Scanning Time - %s\n\n", t_buf );
 
	strncpy( target, argv[1], sizeof(target) -1 ); 
 
	if( argc == 4 ) {
		begin = atoi(argv[2]);
		end = atoi(argv[3]);
	}
	else if( argc == 2 ) {
		begin = 1;
		end = 1024;
	}
 
	if( (begin < 0 || begin > 65535) || (end < 0 || end > 65535) ) {
		fprintf( stderr, "Check your range\n\n" );
		return -1;
	}
 
	open_p = (int *)calloc( max_sz, sizeof(int) );
	if( open_p == NULL ) {
		perror( "calloc()" );
		return -1;
	}
 
	main_thrd = pthread_self();
	port_cnt = begin;
	for( cnt = 0 ; cnt < MAX_THRD ; cnt++ )
	{
		thrd_id[cnt] = pthread_create( &pth[cnt], NULL, (void *)sock_chk, (void *)NULL );
		if( thrd_id[cnt] < 0 )
			perror( "pthread_create()" );
 
		pthread_detach(pth[cnt]);
	}
 
	// run threads nearly at a time
	for( cnt = 0 ; cnt < MAX_THRD ; cnt++ )
		pthread_cond_signal(&c_same);
 
	pthread_cond_wait(&c_wait, &sig_mutx);
 
	pthread_mutex_destroy(&mutx_lock);
	pthread_mutex_destroy(&sig_mutx);
	pthread_cond_destroy(&c_wait);
	pthread_cond_destroy(&c_same);
 
	// sorting and printing
	qsort( (void *)open_p, memcnt, sizeof(int), compare_port );
	for( cnt = 0 ; cnt < memcnt ; cnt++ )
	{
		p_info = getservbyport( htons(open_p[cnt]), "tcp" );
		printf( "%d\topen\t(%s)\n", open_p[cnt], (p_info==NULL)?"unknown":(p_info->s_name) );
	}
	printf("\n");
 
	free(open_p);
	return 0;
}
 
void *sock_chk( void *th_arg )
{
	int sockfd, port, flag = 0, chk = 0;
	char host[1024] = {0x00,}, tmp[512] = {0x00,};
	struct sockaddr_in sock;
	struct hostent hst, *host_st;
	pthread_t mypth;
 
	int vbit, rt, err_s, errl, err_r;
	fd_set r_fd, w_fd;
	struct timeval tv;
 
	// this code is waiting until all threads are created
	pthread_mutex_lock(&mutx_lock);
	pthread_cond_wait(&c_same, &mutx_lock);
	pthread_mutex_unlock(&mutx_lock);
 
	// reentrant function for gethostbyname
	gethostbyname_r( target, &hst, tmp, sizeof(tmp) -1, &host_st, &err_r );
	if( host == NULL )
		flag = -1;
 
	mypth = pthread_self();
	pthread_cleanup_push( sig_shoot, (void *)mypth );
 
	while( !flag )
	{
		// @fetch a port, mutex routine
		pthread_mutex_lock(&mutx_lock);
		if( (port_cnt < begin) || (port_cnt > end) ) {
			pthread_mutex_unlock(&mutx_lock);
			break;
		}
 
		port = port_cnt;
		port_cnt++;
		pthread_mutex_unlock(&mutx_lock);
		// fetch a port, mutex routine@
 
		sockfd = socket( PF_INET, SOCK_STREAM, 0 );
		if( sockfd < 0 ) {
			perror( "socket() in thread" );
 
			flag = -1;
			break;
		}
 
		bzero( sock.sin_zero, sizeof(sock.sin_zero) );
		sock.sin_family = AF_INET;
		sock.sin_port = htons(port);
		sock.sin_addr = *((struct in_addr *)host_st->h_addr);
 
		vbit = fcntl( sockfd, F_GETFL );
		fcntl( sockfd, F_SETFL, vbit | O_NONBLOCK );
		connect( sockfd, (struct sockaddr *)&sock, sizeof(sock) );
 
		FD_ZERO(&r_fd);
		FD_ZERO(&w_fd);
		FD_SET( sockfd, &r_fd );
		FD_SET( sockfd, &w_fd );
 
		while(1)
		{
			tv.tv_sec = 5;
			tv.tv_usec = 0;
 
			rt = select( sockfd +1, (fd_set *)&r_fd, (fd_set *)&w_fd, (fd_set *)NULL, (struct timeval *)&tv );
			if( rt <= 0 ) {
				chk = 0x00;
				break;
			}	
 
			if( FD_ISSET( sockfd, &r_fd ) || FD_ISSET( sockfd, &w_fd ) ) {
				errl = sizeof(err_s);
 
				if( getsockopt( sockfd, SOL_SOCKET, SO_ERROR, &err_s, (socklen_t *)&errl ) == 0 ) {
					if( err_s == 0 ) {
						chk = 1;
						break;
					}
				}
 
				break;
			}
		}
 
		if( chk ) {
			if( save_state(port) < 0 )
				fprintf( stderr, "save_state() error\n" );
		}
 
		chk = 0;
		close(sockfd);
	}
 
	pthread_exit((void *)flag);
	pthread_cleanup_pop(0);
}
 
void sig_shoot( void *pth_id )
{
	pthread_mutex_lock(&sig_mutx);
 
	sigcnt++;
	if( sigcnt == MAX_THRD )
		pthread_cond_signal(&c_wait);
 
	pthread_mutex_unlock(&sig_mutx);
}
 
int save_state( int port )
{
	if( memcnt < max_sz ) {
		open_p[memcnt] = port;
		memcnt++;
	}
	else {
		max_sz = max_sz * 2;
		open_p = realloc( open_p, max_sz * sizeof(int) );
		if( open_p == NULL ) {
			perror( "realloc()" );
			return -1;
		}
 
		open_p[memcnt] = port;
		memcnt++;
	}
 
	return 0;
}
 
int compare_port( const void *a, const void *b )
{
	if( (int *)a < (int *)b )
		return -1;
 
	else if( (int *)a > (int *)b )
		return 1;
 
	else
		return 0;
}
 
void get_time( char *t_buf, int size )
{
	char *l_tm;
	time_t timeval;
	struct tm *t;
 
	time( &timeval );
	t = localtime( &timeval );
 
	l_tm = asctime(t);
	l_tm[ strlen(l_tm) -1 ] = '\0';
	// remove "\n"
 
	strncpy( t_buf, l_tm, size );
}
 
void notice_msg( void )
{
	printf("\n");
	printf( "===================================\n" );
	printf( "< simple multi-thread portscanner >\n" );
	printf( "    code by hkpco (ChanAm Park)    \n" );
	printf( "===================================\n" );
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              