busybox/networking/pscan.c
<<
>>
Prefs
   1/*
   2 * Pscan is a mini port scanner implementation for busybox
   3 *
   4 * Copyright 2007 Tito Ragusa <farmatito@tiscali.it>
   5 *
   6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   7 */
   8
   9//usage:#define pscan_trivial_usage
  10//usage:       "[-cb] [-p MIN_PORT] [-P MAX_PORT] [-t TIMEOUT] [-T MIN_RTT] HOST"
  11//usage:#define pscan_full_usage "\n\n"
  12//usage:       "Scan a host, print all open ports\n"
  13//usage:     "\n        -c      Show closed ports too"
  14//usage:     "\n        -b      Show blocked ports too"
  15//usage:     "\n        -p      Scan from this port (default 1)"
  16//usage:     "\n        -P      Scan up to this port (default 1024)"
  17//usage:     "\n        -t      Timeout (default 5000 ms)"
  18//usage:     "\n        -T      Minimum rtt (default 5 ms, increase for congested hosts)"
  19
  20#include "libbb.h"
  21
  22/* debugging */
  23#ifdef DEBUG_PSCAN
  24#define DMSG(...) bb_error_msg(__VA_ARGS__)
  25#define DERR(...) bb_perror_msg(__VA_ARGS__)
  26#else
  27#define DMSG(...) ((void)0)
  28#define DERR(...) ((void)0)
  29#endif
  30
  31static const char *port_name(unsigned port)
  32{
  33        struct servent *server;
  34
  35        server = getservbyport(htons(port), NULL);
  36        if (server)
  37                return server->s_name;
  38        return "unknown";
  39}
  40
  41/* We don't expect to see 1000+ seconds delay, unsigned is enough */
  42#define MONOTONIC_US() ((unsigned)monotonic_us())
  43
  44int pscan_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  45int pscan_main(int argc UNUSED_PARAM, char **argv)
  46{
  47        const char *opt_max_port = "1024";      /* -P: default max port */
  48        const char *opt_min_port = "1";         /* -p: default min port */
  49        const char *opt_timeout = "5000";       /* -t: default timeout in msec */
  50        /* We estimate rtt and wait rtt*4 before concluding that port is
  51         * totally blocked. min rtt of 5 ms may be too low if you are
  52         * scanning an Internet host behind saturated/traffic shaped link.
  53         * Rule of thumb: with min_rtt of N msec, scanning 1000 ports
  54         * will take N seconds at absolute minimum */
  55        const char *opt_min_rtt = "5";          /* -T: default min rtt in msec */
  56        const char *result_str;
  57        len_and_sockaddr *lsap;
  58        int s;
  59        unsigned opt;
  60        unsigned port, max_port, nports;
  61        unsigned closed_ports = 0;
  62        unsigned open_ports = 0;
  63        /* all in usec */
  64        unsigned timeout;
  65        unsigned min_rtt;
  66        unsigned rtt_4;
  67        unsigned start, diff;
  68
  69        opt_complementary = "=1"; /* exactly one non-option */
  70        opt = getopt32(argv, "cbp:P:t:T:", &opt_min_port, &opt_max_port, &opt_timeout, &opt_min_rtt);
  71        argv += optind;
  72        max_port = xatou_range(opt_max_port, 1, 65535);
  73        port = xatou_range(opt_min_port, 1, max_port);
  74        nports = max_port - port + 1;
  75        min_rtt = xatou_range(opt_min_rtt, 1, INT_MAX/1000 / 4) * 1000;
  76        timeout = xatou_range(opt_timeout, 1, INT_MAX/1000 / 4) * 1000;
  77        /* Initial rtt is BIG: */
  78        rtt_4 = timeout;
  79
  80        DMSG("min_rtt %u timeout %u", min_rtt, timeout);
  81
  82        lsap = xhost2sockaddr(*argv, port);
  83        printf("Scanning %s ports %u to %u\n Port\tProto\tState\tService\n",
  84                        *argv, port, max_port);
  85
  86        for (; port <= max_port; port++) {
  87                DMSG("rtt %u", rtt_4);
  88
  89                /* The SOCK_STREAM socket type is implemented on the TCP/IP protocol. */
  90                set_nport(&lsap->u.sa, htons(port));
  91                s = xsocket(lsap->u.sa.sa_family, SOCK_STREAM, 0);
  92                /* We need unblocking socket so we don't need to wait for ETIMEOUT. */
  93                /* Nonblocking connect typically "fails" with errno == EINPROGRESS */
  94                ndelay_on(s);
  95
  96                DMSG("connect to port %u", port);
  97                result_str = NULL;
  98                start = MONOTONIC_US();
  99                if (connect(s, &lsap->u.sa, lsap->len) == 0) {
 100                        /* Unlikely, for me even localhost fails :) */
 101                        DMSG("connect succeeded");
 102                        goto open;
 103                }
 104                /* Check for untypical errors... */
 105                if (errno != EAGAIN && errno != EINPROGRESS
 106                 && errno != ECONNREFUSED
 107                ) {
 108                        bb_perror_nomsg_and_die();
 109                }
 110
 111                diff = 0;
 112                while (1) {
 113                        if (errno == ECONNREFUSED) {
 114                                if (opt & 1) /* -c: show closed too */
 115                                        result_str = "closed";
 116                                closed_ports++;
 117                                break;
 118                        }
 119                        DERR("port %u errno %d @%u", port, errno, diff);
 120
 121                        if (diff > rtt_4) {
 122                                if (opt & 2) /* -b: show blocked too */
 123                                        result_str = "blocked";
 124                                break;
 125                        }
 126                        /* Can sleep (much) longer than specified delay.
 127                         * We check rtt BEFORE we usleep, otherwise
 128                         * on localhost we'll have no writes done (!)
 129                         * before we exceed (rather small) rtt */
 130                        usleep(rtt_4/8);
 131 open:
 132                        diff = MONOTONIC_US() - start;
 133                        DMSG("write to port %u @%u", port, diff - start);
 134                        if (write(s, " ", 1) >= 0) { /* We were able to write to the socket */
 135                                open_ports++;
 136                                result_str = "open";
 137                                break;
 138                        }
 139                }
 140                DMSG("out of loop @%u", diff);
 141                if (result_str)
 142                        printf("%5u" "\t" "tcp" "\t" "%s" "\t" "%s" "\n",
 143                                        port, result_str, port_name(port));
 144
 145                /* Estimate new rtt - we don't want to wait entire timeout
 146                 * for each port. *4 allows for rise in net delay.
 147                 * We increase rtt quickly (rtt_4*4), decrease slowly
 148                 * (diff is at least rtt_4/8, *4 == rtt_4/2)
 149                 * because we don't want to accidentally miss ports. */
 150                rtt_4 = diff * 4;
 151                if (rtt_4 < min_rtt)
 152                        rtt_4 = min_rtt;
 153                if (rtt_4 > timeout)
 154                        rtt_4 = timeout;
 155                /* Clean up */
 156                close(s);
 157        }
 158        if (ENABLE_FEATURE_CLEAN_UP) free(lsap);
 159
 160        printf("%d closed, %d open, %d timed out (or blocked) ports\n",
 161                                        closed_ports,
 162                                        open_ports,
 163                                        nports - (closed_ports + open_ports));
 164        return EXIT_SUCCESS;
 165}
 166