/*
 * NetworkScanner.c
 *
 * A fast network scanner that scans for a specified open port.
 *
 * It is an enhanced version of propecia.c created by Troy Robinson
 * Created : 02/05/2007
 * Author : Sandeep Kumar aka Turbo : http://students.iiit.ac.in/~sandeep_kr
 *
 * Usage: ./a.out <port> <parallel limit> <ipfile> <output file>
 * Examples of Ip in ipfile:-
 * 172.16-31.*.*
 * 192.168.36.200
 * 172.*.*.*
 * 172.16.0-255.0-255
 *
 * Sample Usage: ./a.out 21 255 IP_List.txt Out.txt
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
int ParseIPPart(char *ipPart, int n, int *s, int *e, int startCtr, int dashCtr);
int ParseIP(char *ip, int s[4], int e[4]);
int main (int argc, char *argv[])
{
    if (argc < 5)
    {
        printf("Usage: %s <port> <parallel limit> <ipfile> <output file>\n", argv[0]);
        printf("Examples of Ip in ipfile:-\n172.16-31.*.*\n192.168.36.200\n172.*.*.*\n172.16.0-255.0-255\n");
        printf("Sample Usage: %s 21 255 IP_List.txt Out.txt\n",argv[0]);
        exit(1);
    }
    int port = atoi(argv[1]); // Port to scan
    int parallelLimit = atoi(argv[2]); // Max no. parallel processes (using fork)
    char *fname = argv[3]; // input filename
    char *outFname = argv[4]; // output filename
    int procCtr = 0; // current count of parallel propcesses
    char ip[20]; // scanned ip string from input file
    int s[4],e[4]; //ip range as parsed from scanned ip string ip.
    char host[16];
    int i1,i2,i3,i4;
    int sockfd, result;
    struct sockaddr_in address;
    FILE *fp = fopen(fname,"r");
    if(fp==NULL)
    {
        perror("fopen");
        exit(0);
    }
    FILE *fw = fopen(outFname,"w");
    if(fw==NULL)
    {
        perror("fopen");
        exit(0);
    }
    while(fscanf(fp," %s",ip)!=EOF)
    {
        if(ParseIP(ip,s,e)==-1)
        {
            fprintf(stderr,"Invalid IP Range: %s\n",ip);
            continue;
        }
        //printf("Range is:%d-%d.%d-%d.%d-%d.%d-%d\n",s[0],e[0],s[1],e[1],s[2],e[2],s[3],e[3]);
        for(i1=s[0];i1<=e[0];i1++)for(i2=s[1];i2<=e[1];i2++)for(i3=s[2];i3<=e[2];i3++)for(i4=s[3];i4<=e[3];i4++)
        {
            sprintf (host, "%d.%d.%d.%d", i1,i2,i3,i4);
            if(procCtr>=parallelLimit)
            {
                wait(NULL);
                procCtr--;
            }
            int childPid = fork();
            if(childPid>=0) // fork succeeded
            {
                if (childPid == 0) //child
                {
                    address.sin_family = AF_INET;
                    address.sin_port = htons (port);
                    address.sin_addr.s_addr = inet_addr (host);
                    sockfd = socket (AF_INET, SOCK_STREAM, 0);
                    if (sockfd < 0)
                    {
                        perror ("Socket");
                        exit (2);
                    }
                    alarm (3);
                    result = connect (sockfd, (struct sockaddr *) &address, sizeof (address));
                    if (result == 0)
                    {
                        printf ("%s\n", host);
                        fprintf (fw,"%s\n", host);
                        close (sockfd);
                        exit (0);
                        continue;
                    }
                    exit (0);
                }
                else //parent
                    procCtr++;

            }
            else // fork failed
            {
                perror("fork"); /* display error message */
                exit(0);
            }
        }
    }
    fclose(fp);
    fclose(fw);
    while(procCtr>0)
    {
        wait(NULL);
        procCtr--;
    }
    close (sockfd);
    exit(0);
}
// Parses one part of an IP. An IP has 4 parts separated by dots.
int ParseIPPart(char *ipPart, int n, int *s, int *e, int starCtr, int dashCtr)
{
    int i,j;
    if(starCtr) // For *
    {
        if(n!=1)return -1;
        *s=0;*e=255;return 0;
    }
    if(dashCtr==0) // For normal number without star or dash
    {
        if(n==0||n>3)return -1;
        *s=0;for(j=0;j<n;j++)if(!isdigit(ipPart[j]))return -1;else *s=*s*10+ipPart[j]-'0';
        if(*s>255)return -1;
        *e=*s;
        return 0;
    }
    else if(dashCtr==1) // For dash
    {
        for(i=0;i<n;i++)if(ipPart[i]=='-')break;
        if(i==0||i==n-1||i>3||n-1-i>3)return -1;
        *s=0;for(j=0;j<i;j++)if(!isdigit(ipPart[j]))return -1;else *s=*s*10+ipPart[j]-'0';
        if(*s>255)return -1;
        *e=0;for(j=i+1;j<n;j++)if(!isdigit(ipPart[j]))return -1;else *e=*e*10+ipPart[j]-'0';
        if(*e>255)return -1;
        return 0;
    }
    else if(dashCtr>1) return -1;
}
// Parses an IP into the IP format: a-b.c-d.e-f.g-h
int ParseIP(char *ip, int s[4], int e[4])
{
    int last=0,partCtr=0,starCtr=0,dashCtr=0,i;
    for(i=0;1;i++)
    {
        if(ip[i]=='.'||ip[i]=='\0')
        {
            if(last==i || starCtr+dashCtr>1)return -1;
            if(ParseIPPart(ip+last,i-last,&s[partCtr],&e[partCtr],starCtr,dashCtr)==-1)return -1;
            starCtr=0,dashCtr=0;
            last = i+1;
            partCtr++;
            if(ip[i]=='\0')break;
            if(partCtr==4)return -1;
        }
        else if(isdigit(ip[i]));
        else if(ip[i]=='*')starCtr++;
        else if(ip[i]=='-')dashCtr++;
        else return-1;
    }
    if(partCtr!=4)return -1;
    return 0;
}
