#define _POSIX_C_SOURCE 200809L
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

static int
parse_port(const char *text)
{
    long port;
    if (!*text || strspn(text, "0123456789") != strlen(text)) return -1;
    errno = 0;
    port = strtol(text, NULL, 10);
    if (errno || port < 1 || port > 65535) return -1;
    return (int)port;
}

static int64_t
now_ms(void)
{
    struct timespec ts;
    if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) return -1;
    return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}

/* Return 1 for open, 0 for refused/unreachable/timed out, or -1 for failure. */
static int
connect_port(const struct sockaddr_in *address)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int result = -1, error, saved;
    int64_t deadline;
    if (fd < 0) return -1;
    if (fd >= FD_SETSIZE) { errno = EMFILE; goto done; }
    do {
        error = fcntl(fd, F_SETFL, O_NONBLOCK);
    } while (error < 0 && errno == EINTR);
    if (error < 0) goto done;
    if (connect(fd, (const struct sockaddr *)address, sizeof(*address)) == 0) {
        result = 1;
        goto done;
    }
    if (errno != EINPROGRESS && errno != EINTR && errno != EALREADY)
        goto failed;
    deadline = now_ms();
    if (deadline < 0) goto done;
    deadline += 1000;
    for (;;) {
        fd_set writable;
        struct timeval timeout;
        socklen_t size = sizeof(error);
        int64_t now = now_ms(), remaining;
        if (now < 0) goto done;
        remaining = deadline - now;
        if (remaining <= 0) { result = 0; goto done; }
        FD_ZERO(&writable);
        FD_SET(fd, &writable);
        timeout.tv_sec = (time_t)(remaining / 1000);
        timeout.tv_usec = (suseconds_t)((remaining % 1000) * 1000);
        error = select(fd + 1, NULL, &writable, NULL, &timeout);
        if (error < 0 && errno == EINTR) continue;
        if (error < 0) goto done;
        if (error == 0) { result = 0; goto done; }
        do {
            saved = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &size);
        } while (saved < 0 && errno == EINTR);
        if (saved < 0) goto done;
        if (error == 0) { result = 1; goto done; }
        errno = error;
        break;
    }
failed:
    switch (errno) {
    case ECONNREFUSED:
    case ECONNRESET:
    case ETIMEDOUT:
    case EHOSTUNREACH:
    case ENETUNREACH:
        result = 0;
        break;
    }
done:
    saved = errno;
    close(fd);
    errno = saved;
    return result;
}

int
main(int argc, char **argv)
{
    struct addrinfo hints = {0}, *addresses;
    struct sockaddr_in address;
    int first, last, port, result;
    if (argc != 4) {
        fprintf(stderr, "usage: %s host start-port end-port\n", argv[0]);
        return 1;
    }
    first = parse_port(argv[2]);
    last = parse_port(argv[3]);
    if (first < 0 || last < 0 || first > last) {
        fprintf(stderr, "invalid port range: %s %s\n", argv[2], argv[3]);
        return 1;
    }
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    result = getaddrinfo(argv[1], NULL, &hints, &addresses);
    if (result != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result));
        return 1;
    }
    address = *(const struct sockaddr_in *)addresses->ai_addr;
    freeaddrinfo(addresses);
    for (port = first; port <= last; ++port) {
        address.sin_port = htons((unsigned short)port);
        result = connect_port(&address);
        if (result < 0) {
            fprintf(stderr, "port %d: %s\n", port, strerror(errno));
            return 1;
        }
        if (result && printf("%d open\n", port) < 0) {
            perror("stdout");
            return 1;
        }
    }
    if (fflush(stdout) == EOF) { perror("stdout"); return 1; }
    return 0;
}
