// hostcheck-ng.c
//
// Copyright 2010 phocean <jc@phocean.net>
// Copyright 2010 phocean <jc@phocean.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
//
// Compilation :
// gcc hostcheck-ng.c -o hostcheck-ng -pthread -g -O1
// - with debug :
// gcc hostcheck-ng.c -o hostcheck-ng -pthread -g -O1 -DDEBUG
#include <stdio.h> /* stderr, stdout */
#include <errno.h> /* errno */
#include <netdb.h> /* hostent struct, gethostbyname() */
#include <string.h> /* memset */
#include <sys/socket.h> /* socket handling */
#include <arpa/inet.h> /* inet_ntoa() to format IP address */
#include <netinet/in.h> /* in_addr structure */
#include <assert.h> /* assert */
#include <fcntl.h> /* socket non blocking */
#include <stdlib.h> /* malloc, free, etc. */
#include <pthread.h> /* threading */
#define MAX_THREADS 3
#define IPBUF 16
struct myHost
{
int port;
char ip[IPBUF];
int state;
};
/* resolve FQDN */
char *resolv(char *name) {
struct in_addr h_addr; /* internet address */
struct hostent *host; /* host information */
if ( (host = gethostbyname(name) ) == NULL) {
printf( "(!) Wrong parameter, exiting\n" );
return NULL;
}
h_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
#ifdef DEBUG
printf ("(-) %s resolved as %s\n",name,inet_ntoa(h_addr));
#endif
return (inet_ntoa(h_addr));
}
/* socket functions */
int init_socket(struct sockaddr_in *addr, char *ip, int *port)
{
int fd;
/* create TCP INET socket */
fd = socket( AF_INET, SOCK_STREAM, 0 );
/* Initialize structure content */
memset( addr, 0, sizeof(*addr) );
addr->sin_family = AF_INET;
addr->sin_port = htons ( *port );
//printf("%d\n%d\n", *port, addr->sin_port);
addr->sin_addr.s_addr = inet_addr( ip );
return(fd);
}
/* set socket as non-blocking */
int set_nonblock(int fd)
{
int flags;
flags = fcntl(fd,F_GETFL,0);
assert(flags != -1);
if ( (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) {
perror("(!) Error setting non-blocking socket");
return 1;
}
return 0;
}
/* socket timeout option */
int set_options(int fd, fd_set *rst, struct timeval *tm)
{
//struct timeval tv; /*timeout */
/* Options */
tm->tv_sec = 2;
tm->tv_usec = 0;
if ( (setsockopt( fd, SOL_SOCKET, SO_RCVTIMEO, tm, sizeof(*tm))) != 0) {
perror("(!) Error setting socket options");
return 1;
}
FD_ZERO(rst);
FD_SET(fd, rst);
return 0;
}
/* scan thread */
void *scanThread( void *arg )
{
struct myHost *args = arg;
int port = args->port;
char *ip = args->ip;
/* file descriptor and socket structure */
int connFd, ret, ret2, valopt, lon;
//struct hostent *host; /* host information */
struct sockaddr_in *servaddr;
struct timeval tv; /*timeout */
fd_set rset;
connFd = ret = ret2 = valopt = lon = 0;
/* detachable thread */
//pthread_detach( pthread_self() );
servaddr = (struct sockaddr_in*)malloc(sizeof(*servaddr));
if (servaddr == NULL) {
printf( "(!) %s - Error allocating %ld bytes of memory\n",ip,sizeof(*servaddr));
goto bad;
}
connFd = init_socket( servaddr, ip, &port );
/* set socket as non-blocking */
if ( (set_nonblock(connFd)) != 0 )
goto bad;
/* set options (timeout) */
if ( (set_options( connFd, &rset, &tv)) != 0)
goto bad;
/* Connect */
#ifdef DEBUG
printf( "(-) %s - Trying to open port %d\n", ip, port );
#endif
ret = connect ( connFd, (struct sockaddr *)servaddr, sizeof(*servaddr) );
/* not connected yet */
if (ret < 0) {
/* errno : operation in progress */
if (errno == EINPROGRESS) {
#ifdef DEBUG
fprintf(stderr, "(-) %s - EINPROGRESS in connect() - selecting\n",ip);
#endif
/* select loop */
do {
ret2 = select(connFd+1, NULL, &rset, NULL, &tv);
/* error which is not an interruption */
if (ret2 < 0 && errno != EINTR) {
fprintf(stderr, "(!) %s - Error from select()\n", ip, errno, strerror(errno));
goto bad;
}
/* socket selected : host up */
else if (ret2 > 0) {
// Socket selected for write
lon = sizeof(int);
if (getsockopt(connFd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
fprintf(stderr, "(!) %s - Error in getsockopt() %d - %s\n", ip, errno, strerror(errno));
goto bad;
}
// Check the value returned...
/*if (valopt) {
fprintf(stderr, "Error in delayed connection() %d - %s\n", valopt, strerror(valopt));
//free_exit( connFd, servaddr, 1 );
//return NULL;
}*/
#ifdef DEBUG
printf("(-) %s:%d is open (selected)\n",ip,port);
#endif
args->state=1;
break;
}
/* timeout case */
else {
#ifdef DEBUG
printf("(!) %s:%d is down (timeout)\n",ip,port);
#endif
args->state=0;
break;
}
} while (1);
}
/* any other errno : host down */
else {
#ifdef DEBUG
printf("(!) %s:%d is down (connect error)\n",ip,port);
#endif
args->state=0;
}
}
/* connected */
else if (ret == 0) {
#ifdef DEBUG
printf("(-) %s:%d is up\n",ip,port);
#endif
args->state=1;
}
/* should not get here */
else {
printf("(-) Unexpected error\n");
}
close(connFd);
free(servaddr);
pthread_exit( NULL );
bad:
if (connFd)
close(connFd);
if (servaddr)
free(servaddr);
pthread_exit((void *)1);
}
int main( int argc, char *argv[] )
{
int status,nbHosts,i,j;
int posHost=0;
int numHost=0;
pthread_t threadIds[MAX_THREADS];
struct myHost *hosts;
/* Arg sanity check */
if (argc < 3) {
printf("Usage: %s <dst ip> <dst port>\n", argv[0]);
printf("\nCoded by phocean (/).\n");
exit(-1);
}
nbHosts = (argc-1)/2;
hosts = (struct myHost*)calloc(nbHosts,sizeof(struct myHost));
/* resolv hostname */
for (i=0,j=0; i < argc - 1; i=i+2, j++) {
hosts[j].port = atoi(argv[i+2]);
strncpy(hosts[j].ip, resolv(argv[i+1]), sizeof(hosts[j].ip)-1);
hosts[j].ip[IPBUF] = '\0';
}
#ifdef DEBUG
printf ("(-) Going to scan:\n");
for (i = 0; i < nbHosts; i++) {
printf ("\t%s:%d\n",hosts[i].ip,hosts[i].port);
}
#endif
/* for all hosts */
printf ("(-) Scanning...\n");
/* Loop for processing all hosts */
while (numHost < nbHosts) {
/* save the first host processed */
posHost = numHost;
/* Loop for the configured number of threads */
i = 0;
while (i < MAX_THREADS && i < (nbHosts - numHost)) {
/* Loop to create the max number of threads minus the already processed hosts */
do {
if ( (pthread_create( &threadIds[i],NULL,scanThread,&hosts[numHost])) != 0 )
printf( "(!) Thread %d failed to start\n", (int)threadIds[i] );
i++; numHost++;
} while (i < (nbHosts - posHost - 1) && i < MAX_THREADS);
/* joining threads */
for (j = 0; j < i; j++) {
if ( (pthread_join(threadIds[j],(void **)&status)) != 0 )
printf( "(!) Thread %d - Error joining\n", (int)threadIds[j] );
else if (status == 1) {
printf( "(!) Thread %d terminated in error, status = %d\n", (int)threadIds[j], status );
}
else {
#ifdef DEBUG
printf( "(-) Thread %d - done, status = %d\n", (int)threadIds[j], status );
#endif
}
}
}
}
printf ("(+) Scan result:\n");
for (i = 0; i < nbHosts; i++) {
if (hosts[i].state == 0)
printf ("\t%s:%d\tCLOSED\n",hosts[i].ip,hosts[i].port);
else if (hosts[i].state == 1)
printf ("\t%s:%d\tOPEN\n",hosts[i].ip,hosts[i].port);
else
printf ("\t%s:%d\tERROR\n",hosts[i].ip,hosts[i].port);
}
printf ("(-) All done\n");
free(hosts);
return 0;
}