修改现有代码以循环遍历子网的正确方法

Correct Way To Modify Existing Code To Loop Through Subnet

我正在编写一个端口扫描器,目的是循环遍历整个子网,以查看端口 445 在任何主机上是否打开,以此作为学习 C 编程的一种方式。该程序当前的设置方式是,需要手动输入一个 IP 地址,然后扫描器就会启动。它工作完美(终于)。

我目前面临的问题:我不确定如何让这个程序扫描整个子网,而不仅仅是单个主机。在研究了这个问题之后,我相信最好的方法是剥离 IP 地址的最后一个八位字节,并让它从 1-254 循环。但是,我不完全确定如何开始处理这个问题。这是我当前的代码(大量注释以供澄清):

...
void portScan() {

struct hostent *host;
    int err, i , sock ,start , end;
    char hostname[100];
    struct sockaddr_in sa;

    //Get the hostname to scan
    printf("Enter hostname or IP to scan: ");
    gets(hostname);

    //Get start port number
    start = 445;

    //Get end port number
    end = 445;

    //Initialise the sockaddr_in structure
    strncpy((char*)&sa , "" , sizeof sa);
    sa.sin_family = AF_INET;

    //direct ip address, use it
    if(isdigit(hostname[0])) {
        sa.sin_addr.s_addr = inet_addr(hostname);
    }

    //Start the port scan loop
    printf("Starting the portscan loop : \n");

    for( i = start ; i <= end ; i++) {
        //Fill in the port number
        sa.sin_port = htons(i);

        //Create a socket of type internet
        sock = socket(AF_INET , SOCK_STREAM , 0);

        //Check whether socket was created or not
        if(sock < 0) {
            perror("\nSocket");
            exit(1);
        }
        //Connect using that socket and sockaddr structure
        err = connect(sock , (struct sockaddr*)&sa , sizeof sa);

        //not connected
        if(err < 0) {
            //printf("%s %-5d %s\r" , hostname , i, strerror(errno));
            fflush(stdout);
        }
        //connected
        else {
            printf("%-5d open\n",  i);
        }
        close(sock);
    }

    printf("\r");
    fflush(stdout);
}

TL;DR:我不确定如何修改此代码以去除 IP 地址的最后一个八位字节,并通过将最后一个八位字节替换为 1 - 254 来遍历子网。任何反馈、想法或帮助非常感谢!在 C 世界中仍然很初级。

我可以通过几种方法来解决这个问题,这很好,因为由于初始输入,您可以随意使用 IP。但是我觉得这个线程可能会为您指明正确的方向: How to increment an IP address in a loop? [C]

告诉我。

子网不只是 "the last byte"。相反,在 CIDR 中有一个前缀 length/subnet 掩码。您只需要子网中的一个地址,子网掩码无符号整数,但在主机字节顺序, (你需要在inet_addrhtonl的return值上使用ntohl将整数再次转换为可以存储到[=18中的地址=]).

  • &主机地址加上子网掩码得到网络地址。

  • |网络地址与掩码的得到广播地址。

  • 网络中的主机地址就是这2之间的所有数字,所以你可以写一个从[=21开始的for循环=] 并以 broadcast_address - 1.

  • 结尾

因此,如果您的主机地址为 10.24.41.169,前缀长度为 23,则十六进制地址为 0x0A1829A9。网络掩码的前 23 位设置为 1,其余设置为 0,即 0b11111111111111111111111000000000,即十六进制的 0xFFFFFE00

uint32_t host = 0x0A1829A9;
uint32_t netmask = 0xFFFFFE00;

现在网络地址是host & netmask:

uint32_t network_address = host & netmask;

广播地址为network_address | ~netmask;host_address | ~netmask:

uint32_t network_address = host_address | ~netmask;

子网内的地址可以通过相当无脑的for循环生成

for (uint32_t address = network_address + 1; address < broadcast_address; address ++) {
}

来自 Davis Herring,要将前缀长度转换为子网掩码,您可以使用

uint32_t netmask = ~(((uint32_t)1 << len) - 1);