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