SCANNER简单网络扫描程序C语言多线程实现

  • 实验内容

用C语言编写一个在linux下扫描局域网内主机的程序。要求可以显示局域网内的主机名列表,IP地址列表,并可以显示哪些主机开放了哪些端口

  • 实验环境

VMware Ubuntu18虚拟机

  • 实验内容及步骤

1、程序流程图:

 2、主要函数说明:

(1)void TranAddr(char *ad)

输入一个地址,输出其局域网内的前n个地址,到address结构体中。

首先删除IP地址第三位小数点后面的字符,然后使用TranNum(将整型变量转换为字符串)和strcat函数将输入IP地址的子网的前Number_of_LAN个IP地址储存到addr[Number_of_LAN][Number_of_ports]二维结构体数组中,再使用inet_pton函数转换IP地址为二进制,最后使用gethostbyaddr函数查询IP地址对应的主机名并储存到二维结构体数组之中。

(2)void  sockconnect(struct Address *b)

输入一个Address结构体,读取IP地址和端口信息,使用connect函数尝试连接对应主机的端口,并将连接结果储存到结构体之中。

(3)void *pth_main(void *addr)

线程调用的函数,函数体就是调用sockconnect函数。

  • 实验结果

 

  • 实验中的问题及心得 

发现问题:

(1)sock: Too many open files

本地的tcp端口数量不够,而线程连接的过多

解决:增大允许打开的文件数——命令方式

ulimit -n 2048

(2)gethostbyaddr函数无法获取主机名

本地的DNS服务器出错

解决:修改/etc/hosts文件,但是不能解决根本问题

心得:在编写scaner函数的过程中,熟悉了sockaddr、sockaddr_in等结构体,了解了socket、gethostbyname、htons、connect函数的调用,学会多线程调用函数,实现多线程并行。

  • 代码

    #include <stdio.h>
    #include <stdlib.h>//包含atoi()函数,将字符串转换为整数形式
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>//包含bind()
    #include <arpa/inet.h>//包含htons()
    #include <netdb.h>//包含hostent结构定义
    #include <pthread.h>
    
    #define Number_of_LAN  16//定义要扫描的主机数量
    #define Number_of_ports 255//定义要扫描的端口数量
    
    /*struct sockaddr {
       sa_family_t sa_family;//用于指定AF_***表示使用什么协议族的ip
       char        sa_data[14];//存放ip和端口
    }*/
    struct Address{
        char hostname[20];
        char address[20];
        int port;
        int open;
    }addr[Number_of_LAN][Number_of_ports];
    
    char *TranNum(int num)//输入一个整形数组0-999,将其转换为字符串
    {
        int a , b, c;
    
        c = num % 10;
        num = num - c;
        b = num % 100;
        b = b / 10;
        num = num - b * 10;
        a = num / 100;
    
        char *strNum = (char *)malloc(4 * sizeof(char));
    
        if(a != 0)
        {
            strNum[0] = a + 48;
            strNum[1] = b + 48;
            strNum[2] = c + 48;
            strNum[3] = '\0';
        }
        else{
            if(b == 0)
            {
                strNum[0] = c + 48;
                strNum[1] = '\0';
            }
            else{
                strNum[0] = b + 48;
                strNum[1] = c + 48;
                strNum[2] = '\0';
            }
        }
    
        return strNum;
    }
    
    void TranAddr(char *ad)//输入一个地址,输出其局域网内的前n个地址,到address结构体中
    {
        char Addr[20];
        strcpy(Addr, ad);//由于函数输入的指针存在于堆栈中,不能修改,所以申请字符数组存放
    
        struct in_addr strAddr;
    	struct hostent *phost;
    
        int count, i, j, z;
        
        for(i = 0; i < Number_of_LAN; i++)
        {
            for(count = 0, z = 0; count < 3; z++)
                if(Addr[z] == '.')
                    count++;
            
            Addr[z] = '\0';
            strcpy(Addr, strcat(Addr, TranNum(i + 1)));
    
            inet_pton(AF_INET, Addr, &strAddr);//将ip地址转换为二进制
    
            phost = gethostbyaddr((const char*)&strAddr, sizeof(strAddr), AF_INET);//获取主机名
            if (phost != NULL)
                strcpy(addr[i][0].hostname, phost->h_name);
    
            for(j = 0; j < Number_of_ports; j++)
                strcpy(addr[i][j].address, Addr);
        }
    }
    
    void  sockconnect(struct Address *b)
    {
        int sockfd;
        struct sockaddr_in seraddr;
    
        char addr[20];
        strcpy(addr, b->address);
    
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd == -1)
        {
            perror("sock");
            exit(-1);
        }
    
        seraddr.sin_family = AF_INET;
        seraddr.sin_addr.s_addr = inet_addr(addr);
        seraddr.sin_port = htons(b->port);
    // 向服务器发起连接请求
        if (connect(sockfd, (struct sockaddr *)&seraddr,sizeof(seraddr)) != 0)
            b->open = 0;
        else
            b->open = 1;
        
        close(sockfd);
    }
    
    void *pth_main(void *addr)
    {
        sockconnect((struct Address*)addr);
    
        pthread_exit(0);
    }
    
    int main(int argc, char *argv[])
    {
        if(argc < 2)
        {
            printf("Missing parameter\n");
            exit(-1);
        }
        
        pthread_t pth[Number_of_LAN * Number_of_ports];
        int i, j, Ipcount, Portscount, number;
    
        TranAddr(argv[1]);
    
        for(i = 0; i < Number_of_LAN; i++)//调整连接的端口值,将没获得主机名的地址赋名为none
        {
            if(strlen(addr[i][0].hostname) == 0)
                strcpy(addr[i][0].hostname, "nameless");
            for(j = 0; j < Number_of_ports; j++)
            {
                addr[i][j].port = j + 1;
                addr[i][j].open = 0;
            }
        }
    
    
        for(number = 0; number < Number_of_LAN; number = number + 4)
        {
                for(i = number; i < number + 4; i++)
                {
                    for(j = 0; j < Number_of_ports; j++)
                    {
                        if((pthread_create(&pth[i * Number_of_ports + j], NULL, pth_main, (void *)&addr[i][j])) != 0)
                        {
                            printf("进程%d创建失败\n", i * Number_of_ports + j);
                            exit(-1);
                        }
                    }
                }
                for(i = number * Number_of_ports; i < (number + 4) * Number_of_ports; i++)
                {
                    pthread_join(pth[i], NULL);
                }
                
        }
    
    //输出
        Ipcount = 0;
        printf("主机名                  ip地址               开放端口\n");
        for(i = 0; i < Number_of_LAN; i++)
        {
            printf("%-17s       %-16s    ", addr[i][0].hostname, addr[i][0].address);
            Portscount = 0;
            for(j = 0; j < Number_of_ports; j++)
            {
                if(addr[i][j].open != 0){
                    printf(" '%d' ", addr[i][j].port);
                    Portscount ++;
                }
            }
            if(Portscount == 0)
                printf(" none");
            else    Ipcount++;
            printf("\n");
        }
        printf("共有%d个主机开放\n", Ipcount);
        return 0;
    }