lomaster
lomaster

Reputation: 31

Port scanning c++ TCP method, sometimes the function hangs on some IPs

Port scanning c++ method TCP, sometimes function hangs on some IPs. Here is the problem, I have the main function where the packet is sent, and port handling.

int send_tcp_packet(int sock, const struct sockaddr_in* target_address, int timeout_ms) {
    int ret = connect(sock, (struct sockaddr*)target_address, sizeof(*target_address));
    if (ret < 0 && errno != EINPROGRESS){
        return PORT_CLOSED;
    }
    if (ret == -1) {
        int error_code = 0;
        #ifdef _WIN32
        error_code = WSAGetLastError();
        #else
        error_code = errno;
        #endif
        if (error_code == EINPROGRESS || error_code == EINTR) {
            fd_set read_fds, write_fds;
            FD_ZERO(&read_fds);
            FD_ZERO(&write_fds);
            FD_SET(sock, &read_fds);
            FD_SET(sock, &write_fds);

            struct timeval tv;
            tv.tv_sec = timeout_ms / 1000;
            tv.tv_usec = (timeout_ms % 1000) * 1000;

            int result = select(sock + 1, &read_fds, &write_fds, NULL, &tv);
            if (result == -1) {
                perror("select");
                return PORT_ERROR;
            }
            else if (result == 0) {
                int err;
                socklen_t err_len = sizeof(err);
                if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&err, &err_len) != 0) {
                    perror("getsockopt");
                    return PORT_ERROR;
                }
                if (err == ECONNREFUSED || err == ETIMEDOUT) {
                    return PORT_CLOSED;
                }
                else if (err == EHOSTUNREACH || err == EADDRINUSE) {
                    return PORT_ERROR;
                }
                else {
                    char *errstr = strerror(err);
                    return PORT_FILTERED;
                }
            }
            else {
                if (FD_ISSET(sock, &write_fds) || FD_ISSET(sock, &read_fds)) {
                    return PORT_OPEN;
                } else {
                    return PORT_CLOSED;
                }
            }
        }
        else {
            char *errstr = strerror(error_code);
            return PORT_ERROR;
        }
    }
    else {
        close(sock);
        return PORT_OPEN;
    }
}

** Function for enabling non-blocking mode:**

void set_non_blocking(int sock) {
#ifdef _WIN32
    u_long iMode = 1;
    ioctlsocket(sock, FIONBIO, &iMode);
#else
    int flags = fcntl(sock, F_GETFL, 0);
    if (flags == -1) {
        return;
    }

    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
        return;
    }
#endif
}

** And the function that binds it and others, such as opening a socket, closing and enabling non-blocking mode:**

int tcp_scan_port(const char* host, int port, int timeout_ms){
    int sock = create_socket();
    set_non_blocking(sock);

    struct sockaddr_in target_address;
    memset(&target_address, 0, sizeof(target_address));
    target_address.sin_family = AF_INET;
    target_address.sin_port = htons(port);
    inet_pton(AF_INET, host, &target_address.sin_addr);

    int result = send_tcp_packet(sock, &target_address, timeout_ms);
    close_socket(sock);
    return result;
}

Where can the problem be? Generally, the scanning occurs in the threads, to work with them I use future.

But through gdb I understand that it is not because of them, but hangs on one of these functions, how can it be fixed, or at least why it happens. I have no explanation.

Scanning random ip. And then it just stops and the function hangs. The IPs are not connected to this in any way.

Upvotes: 1

Views: 60

Answers (1)

lomaster
lomaster

Reputation: 31

I did not fully understand what the problem was, but my timeout did not work, I just rewrote the main function, and added processing already in it. This is what came out.

int tcp_scan_port(const std::string& ip, int port, int timeout_ms) {
    const char* addr = ip.c_str();
    struct sockaddr_in addr_s;
    struct timeval tv;

    int so_error;
    int fd=-1;

    struct timespec tstart={0,0}, tend={0,0};
    fd_set read_fds, write_fds;

    addr_s.sin_family = AF_INET;
    addr_s.sin_addr.s_addr = inet_addr(addr);
    addr_s.sin_port = htons(port);

    clock_gettime(CLOCK_MONOTONIC, &tstart);

    fd = create_socket(); 
    set_non_blocking(fd);

    int rc = connect(fd, (struct sockaddr *)&addr_s, sizeof(addr_s));

    if (rc == 0) {
        clock_gettime(CLOCK_MONOTONIC, &tend);
        close_socket(fd);
        return PORT_OPEN;
    }
    else if (rc == -1 && errno == EINPROGRESS) {
        FD_ZERO(&read_fds);
        FD_ZERO(&write_fds);
        FD_SET(fd, &read_fds);
        FD_SET(fd, &write_fds);

        tv.tv_sec = timeout_ms / 1000;
        tv.tv_usec = (timeout_ms % 1000) * 1000;

        rc = select(fd + 1, &read_fds, &write_fds, NULL, &tv);
        if (rc > 0) {
            socklen_t len = sizeof(so_error);
            getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_error, &len);
            if (so_error == 0) {
                clock_gettime(CLOCK_MONOTONIC, &tend);
                close(fd);
                return PORT_OPEN;
            }
            else {
                close_socket(fd);
                return PORT_FILTERED;
            }
        } 
        else if (rc == 0) {
            close(fd);
            return PORT_CLOSED;
        }
    }

    close_socket(fd); 
    return PORT_ERROR;
}

Upvotes: 0

Related Questions