C port scanner snippet

//      hostcheck-ng.c
//
//      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 (https://phocean.net).\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;
}