mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Move a lot of raw/connect scanning routines.
This commit is contained in:
2577
scan_engine.cc
2577
scan_engine.cc
File diff suppressed because it is too large
Load Diff
@@ -757,5 +757,21 @@ private:
|
||||
individual host */
|
||||
enum ultra_timing_type { TIMING_HOST, TIMING_GROUP };
|
||||
|
||||
const char *pspectype2ascii(int type);
|
||||
|
||||
void ultrascan_port_probe_update(UltraScanInfo *USI, HostScanStats *hss,
|
||||
std::list<UltraProbe *>::iterator probeI,
|
||||
int newstate, struct timeval *rcvdtime,
|
||||
bool adjust_timing_hint = true);
|
||||
|
||||
void ultrascan_host_probe_update(UltraScanInfo *USI, HostScanStats *hss,
|
||||
std::list<UltraProbe *>::iterator probeI,
|
||||
int newstate, struct timeval *rcvdtime,
|
||||
bool adjust_timing_hint = true);
|
||||
|
||||
void ultrascan_ping_update(UltraScanInfo *USI, HostScanStats *hss,
|
||||
std::list<UltraProbe *>::iterator probeI,
|
||||
struct timeval *rcvdtime,
|
||||
bool adjust_timing = true);
|
||||
#endif /* SCAN_ENGINE_H */
|
||||
|
||||
|
||||
@@ -123,4 +123,420 @@
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "nmap_error.h"
|
||||
#include "Target.h"
|
||||
#include "scan_engine_connect.h"
|
||||
#include "libnetutil/netutil.h" /* for max_sd() */
|
||||
#include "NmapOps.h"
|
||||
|
||||
extern NmapOps o;
|
||||
|
||||
/* Sets this UltraProbe as type UP_CONNECT, preparing to connect to given
|
||||
port number*/
|
||||
void UltraProbe::setConnect(u16 portno) {
|
||||
type = UP_CONNECT;
|
||||
probes.CP = new ConnectProbe();
|
||||
mypspec.type = PS_CONNECTTCP;
|
||||
mypspec.proto = IPPROTO_TCP;
|
||||
mypspec.pd.tcp.dport = portno;
|
||||
mypspec.pd.tcp.flags = TH_SYN;
|
||||
}
|
||||
|
||||
ConnectScanInfo::ConnectScanInfo() {
|
||||
maxValidSD = -1;
|
||||
numSDs = 0;
|
||||
if (o.max_parallelism > 0) {
|
||||
maxSocketsAllowed = o.max_parallelism;
|
||||
} else {
|
||||
/* Subtracting 10 from max_sd accounts for
|
||||
stdin
|
||||
stdout
|
||||
stderr
|
||||
/dev/tty
|
||||
/var/run/utmpx, which is opened on Mac OS X at least
|
||||
-oG log file
|
||||
-oN log file
|
||||
-oS log file
|
||||
-oX log file
|
||||
perhaps another we've forgotten. */
|
||||
maxSocketsAllowed = max_sd() - 10;
|
||||
if (maxSocketsAllowed < 5)
|
||||
maxSocketsAllowed = 5;
|
||||
}
|
||||
maxSocketsAllowed = MIN(maxSocketsAllowed, FD_SETSIZE - 10);
|
||||
FD_ZERO(&fds_read);
|
||||
FD_ZERO(&fds_write);
|
||||
FD_ZERO(&fds_except);
|
||||
}
|
||||
|
||||
/* Nothing really to do here. */
|
||||
ConnectScanInfo::~ConnectScanInfo() {}
|
||||
|
||||
/* Watch a socket descriptor (add to fd_sets and maxValidSD). Returns
|
||||
true if the SD was absent from the list, false if you tried to
|
||||
watch an SD that was already being watched. */
|
||||
bool ConnectScanInfo::watchSD(int sd) {
|
||||
assert(sd >= 0);
|
||||
if (!checked_fd_isset(sd, &fds_read)) {
|
||||
checked_fd_set(sd, &fds_read);
|
||||
checked_fd_set(sd, &fds_write);
|
||||
checked_fd_set(sd, &fds_except);
|
||||
numSDs++;
|
||||
if (sd > maxValidSD)
|
||||
maxValidSD = sd;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear SD from the fd_sets and maxValidSD. Returns true if the SD
|
||||
was in the list, false if you tried to clear an sd that wasn't
|
||||
there in the first place. */
|
||||
bool ConnectScanInfo::clearSD(int sd) {
|
||||
assert(sd >= 0);
|
||||
if (checked_fd_isset(sd, &fds_read)) {
|
||||
checked_fd_clr(sd, &fds_read);
|
||||
checked_fd_clr(sd, &fds_write);
|
||||
checked_fd_clr(sd, &fds_except);
|
||||
assert(numSDs > 0);
|
||||
numSDs--;
|
||||
if (sd == maxValidSD)
|
||||
maxValidSD--;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ConnectProbe::ConnectProbe() {
|
||||
sd = -1;
|
||||
}
|
||||
|
||||
ConnectProbe::~ConnectProbe() {
|
||||
if (sd > 0)
|
||||
close(sd);
|
||||
sd = -1;
|
||||
}
|
||||
|
||||
static void handleConnectResult(UltraScanInfo *USI, HostScanStats *hss,
|
||||
std::list<UltraProbe *>::iterator probeI,
|
||||
int connect_errno,
|
||||
bool destroy_probe=false) {
|
||||
bool adjust_timing = true;
|
||||
int newportstate = PORT_UNKNOWN;
|
||||
int newhoststate = HOST_UNKNOWN;
|
||||
reason_t current_reason = ER_NORESPONSE;
|
||||
UltraProbe *probe = *probeI;
|
||||
struct sockaddr_storage local;
|
||||
socklen_t local_len = sizeof(struct sockaddr_storage);
|
||||
struct sockaddr_storage remote;
|
||||
size_t remote_len;
|
||||
|
||||
switch (connect_errno) {
|
||||
case 0:
|
||||
newhoststate = HOST_UP;
|
||||
newportstate = PORT_OPEN;
|
||||
current_reason = ER_CONACCEPT;
|
||||
break;
|
||||
case EACCES:
|
||||
/* Apparently this can be caused by dest unreachable admin
|
||||
prohibited messages sent back, at least from IPv6
|
||||
hosts */
|
||||
newhoststate = HOST_DOWN;
|
||||
newportstate = PORT_FILTERED;
|
||||
current_reason = ER_ADMINPROHIBITED;
|
||||
break;
|
||||
/* This can happen on localhost, successful/failing connection immediately
|
||||
in non-blocking mode. */
|
||||
case ECONNREFUSED:
|
||||
newhoststate = HOST_UP;
|
||||
newportstate = PORT_CLOSED;
|
||||
current_reason = ER_CONREFUSED;
|
||||
break;
|
||||
case EAGAIN:
|
||||
log_write(LOG_STDOUT, "Machine %s MIGHT actually be listening on probe port %d\n", hss->target->targetipstr(), USI->ports->syn_ping_ports[probe->dport()]);
|
||||
/* Fall through. */
|
||||
#ifdef WIN32
|
||||
case WSAENOTCONN:
|
||||
#endif
|
||||
newhoststate = HOST_UP;
|
||||
current_reason = ER_CONACCEPT;
|
||||
break;
|
||||
#ifdef ENOPROTOOPT
|
||||
case ENOPROTOOPT:
|
||||
#endif
|
||||
case EHOSTUNREACH:
|
||||
newhoststate = HOST_DOWN;
|
||||
newportstate = PORT_FILTERED;
|
||||
current_reason = ER_HOSTUNREACH;
|
||||
break;
|
||||
#ifdef WIN32
|
||||
case WSAEADDRNOTAVAIL:
|
||||
#endif
|
||||
case ETIMEDOUT:
|
||||
case EHOSTDOWN:
|
||||
newhoststate = HOST_DOWN;
|
||||
/* It could be the host is down, or it could be firewalled. We
|
||||
will go on the safe side & assume port is closed ... on second
|
||||
thought, lets go firewalled! and see if it causes any trouble */
|
||||
newportstate = PORT_FILTERED;
|
||||
current_reason = ER_NORESPONSE;
|
||||
break;
|
||||
case ENETUNREACH:
|
||||
newhoststate = HOST_DOWN;
|
||||
newportstate = PORT_FILTERED;
|
||||
current_reason = ER_NETUNREACH;
|
||||
break;
|
||||
case ENETDOWN:
|
||||
case ENETRESET:
|
||||
case ECONNABORTED:
|
||||
fatal("Strange SO_ERROR from connection to %s (%d - '%s') -- bailing scan", hss->target->targetipstr(), connect_errno, strerror(connect_errno));
|
||||
break;
|
||||
default:
|
||||
error("Strange read error from %s (%d - '%s')", hss->target->targetipstr(), connect_errno, strerror(connect_errno));
|
||||
break;
|
||||
}
|
||||
if (probe->isPing() && newhoststate != HOST_UNKNOWN ) {
|
||||
ultrascan_ping_update(USI, hss, probeI, &USI->now, adjust_timing);
|
||||
} else if (USI->ping_scan && newhoststate != HOST_UNKNOWN) {
|
||||
ultrascan_host_probe_update(USI, hss, probeI, newhoststate, &USI->now, adjust_timing);
|
||||
hss->target->reason.reason_id = current_reason;
|
||||
/* If the host is up, we can forget our other probes. */
|
||||
if (newhoststate == HOST_UP)
|
||||
hss->destroyAllOutstandingProbes();
|
||||
} else if (!USI->ping_scan && newportstate != PORT_UNKNOWN) {
|
||||
/* Save these values so we can use them after
|
||||
ultrascan_port_probe_update deletes probe. */
|
||||
u8 protocol = probe->protocol();
|
||||
u16 dport = probe->dport();
|
||||
/* Check for self-connected probe */
|
||||
if (getsockname(probe->CP()->sd, (struct sockaddr*)&local, &local_len) == 0
|
||||
&& hss->target->TargetSockAddr(&remote, &remote_len) == 0) {
|
||||
if (sockaddr_storage_cmp(&local, &remote) == 0 && (
|
||||
(local.ss_family == AF_INET &&
|
||||
((struct sockaddr_in*)&local)->sin_port == htons(dport))
|
||||
#if HAVE_IPV6
|
||||
|| (local.ss_family == AF_INET6 &&
|
||||
((struct sockaddr_in6*)&local)->sin6_port == htons(dport))
|
||||
#endif
|
||||
)) {
|
||||
if (o.debugging) {
|
||||
log_write(LOG_STDOUT, "Detected likely self-connect on port %d\n", probe->dport());
|
||||
}
|
||||
/* It's not really timed out, but this is a simple way to retry the
|
||||
* probe. It shouldn't affect timing too much, since this is quite
|
||||
* rare (should average one per scan, for localhost -p 0-65535 scans
|
||||
* only) */
|
||||
hss->markProbeTimedout(probeI);
|
||||
}
|
||||
else {
|
||||
ultrascan_port_probe_update(USI, hss, probeI, newportstate, &USI->now, adjust_timing);
|
||||
hss->target->ports.setStateReason(dport, protocol, current_reason, 0, NULL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
gh_perror("getsockname or TargetSockAddr failed");
|
||||
}
|
||||
} else if (destroy_probe) {
|
||||
hss->destroyOutstandingProbe(probeI);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the socket lingering so we will RST connections instead of wasting
|
||||
bandwidth with the four-step close. Set the source address if needed. Bind to
|
||||
a specific interface if needed. */
|
||||
static void init_socket(int sd) {
|
||||
static int bind_failed = 0;
|
||||
struct linger l;
|
||||
struct sockaddr_storage ss;
|
||||
size_t sslen;
|
||||
|
||||
l.l_onoff = 1;
|
||||
l.l_linger = 0;
|
||||
|
||||
if (setsockopt(sd, SOL_SOCKET, SO_LINGER, (const char *) &l, sizeof(l)) != 0) {
|
||||
error("Problem setting socket SO_LINGER, errno: %d", socket_errno());
|
||||
perror("setsockopt");
|
||||
}
|
||||
if (o.spoofsource && !bind_failed) {
|
||||
o.SourceSockAddr(&ss, &sslen);
|
||||
if (::bind(sd, (struct sockaddr*)&ss, sslen) != 0) {
|
||||
error("%s: Problem binding source address (%s), errno: %d", __func__, inet_socktop(&ss), socket_errno());
|
||||
perror("bind");
|
||||
bind_failed = 1;
|
||||
}
|
||||
}
|
||||
errno = 0;
|
||||
if (!socket_bindtodevice(sd, o.device)) {
|
||||
/* EPERM is expected when not running as root. */
|
||||
if (errno != EPERM) {
|
||||
error("Problem binding to interface %s, errno: %d", o.device, socket_errno());
|
||||
perror("socket_bindtodevice");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is NOT a ping probe, set pingseq to 0. Otherwise it will be the
|
||||
ping sequence number (they start at 1). The probe sent is returned. */
|
||||
UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss,
|
||||
u16 destport, u8 tryno, u8 pingseq) {
|
||||
|
||||
UltraProbe *probe = new UltraProbe();
|
||||
std::list<UltraProbe *>::iterator probeI;
|
||||
int rc;
|
||||
int connect_errno = 0;
|
||||
struct sockaddr_storage sock;
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) &sock;
|
||||
#if HAVE_IPV6
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &sock;
|
||||
#endif
|
||||
size_t socklen;
|
||||
ConnectProbe *CP;
|
||||
|
||||
probe->tryno = tryno;
|
||||
probe->pingseq = pingseq;
|
||||
/* First build the probe */
|
||||
probe->setConnect(destport);
|
||||
CP = probe->CP();
|
||||
/* Initiate the connection */
|
||||
CP->sd = socket(o.af(), SOCK_STREAM, IPPROTO_TCP);
|
||||
if (CP->sd == -1)
|
||||
pfatal("Socket creation in %s", __func__);
|
||||
unblock_socket(CP->sd);
|
||||
init_socket(CP->sd);
|
||||
set_ttl(CP->sd, o.ttl);
|
||||
if (o.ipoptionslen)
|
||||
set_ipoptions(CP->sd, o.ipoptions, o.ipoptionslen);
|
||||
if (hss->target->TargetSockAddr(&sock, &socklen) != 0) {
|
||||
fatal("Failed to get target socket address in %s", __func__);
|
||||
}
|
||||
if (sin->sin_family == AF_INET)
|
||||
sin->sin_port = htons(probe->pspec()->pd.tcp.dport);
|
||||
#if HAVE_IPV6
|
||||
else sin6->sin6_port = htons(probe->pspec()->pd.tcp.dport);
|
||||
#endif
|
||||
probe->sent = USI->now;
|
||||
/* We don't record a byte count for connect probes. */
|
||||
hss->probeSent(0);
|
||||
rc = connect(CP->sd, (struct sockaddr *)&sock, socklen);
|
||||
gettimeofday(&USI->now, NULL);
|
||||
if (rc == -1)
|
||||
connect_errno = socket_errno();
|
||||
PacketTrace::traceConnect(IPPROTO_TCP, (sockaddr *) &sock, socklen, rc,
|
||||
connect_errno, &USI->now);
|
||||
/* This counts as probe being sent, so update structures */
|
||||
hss->probes_outstanding.push_back(probe);
|
||||
probeI = hss->probes_outstanding.end();
|
||||
probeI--;
|
||||
USI->gstats->num_probes_active++;
|
||||
hss->num_probes_active++;
|
||||
|
||||
/* It would be convenient if the connect() call would never succeed
|
||||
or permanently fail here, so related code cood all be localized
|
||||
elsewhere. But the reality is that connect() MAY be finished now. */
|
||||
|
||||
if (rc == -1 && (connect_errno == EINPROGRESS || connect_errno == EAGAIN)) {
|
||||
USI->gstats->CSI->watchSD(CP->sd);
|
||||
} else {
|
||||
handleConnectResult(USI, hss, probeI, connect_errno, true);
|
||||
probe = NULL;
|
||||
}
|
||||
gettimeofday(&USI->now, NULL);
|
||||
return probe;
|
||||
}
|
||||
|
||||
/* Does a select() call and handles all of the results. This handles both host
|
||||
discovery (ping) scans and port scans. Even if stime is now, it tries a very
|
||||
quick select() just in case. Returns true if at least one good result
|
||||
(generally a port state change) is found, false if it times out instead */
|
||||
bool do_one_select_round(UltraScanInfo *USI, struct timeval *stime) {
|
||||
fd_set fds_rtmp, fds_wtmp, fds_xtmp;
|
||||
int selectres;
|
||||
struct timeval timeout;
|
||||
int timeleft;
|
||||
ConnectScanInfo *CSI = USI->gstats->CSI;
|
||||
int sd;
|
||||
std::list<HostScanStats *>::iterator hostI;
|
||||
HostScanStats *host;
|
||||
UltraProbe *probe = NULL;
|
||||
int optval;
|
||||
recvfrom6_t optlen = sizeof(int);
|
||||
int numGoodSD = 0;
|
||||
int err = 0;
|
||||
|
||||
do {
|
||||
timeleft = TIMEVAL_MSEC_SUBTRACT(*stime, USI->now);
|
||||
if (timeleft < 0)
|
||||
timeleft = 0;
|
||||
fds_rtmp = USI->gstats->CSI->fds_read;
|
||||
fds_wtmp = USI->gstats->CSI->fds_write;
|
||||
fds_xtmp = USI->gstats->CSI->fds_except;
|
||||
timeout.tv_sec = timeleft / 1000;
|
||||
timeout.tv_usec = (timeleft % 1000) * 1000;
|
||||
|
||||
if (CSI->numSDs) {
|
||||
selectres = select(CSI->maxValidSD + 1, &fds_rtmp, &fds_wtmp,
|
||||
&fds_xtmp, &timeout);
|
||||
err = socket_errno();
|
||||
} else {
|
||||
/* Apparently Windows returns an WSAEINVAL if you select without watching any SDs. Lame. We'll usleep instead in that case */
|
||||
usleep(timeleft * 1000);
|
||||
selectres = 0;
|
||||
}
|
||||
} while (selectres == -1 && err == EINTR);
|
||||
|
||||
gettimeofday(&USI->now, NULL);
|
||||
|
||||
if (selectres == -1)
|
||||
pfatal("select failed in %s()", __func__);
|
||||
|
||||
if (!selectres)
|
||||
return false;
|
||||
|
||||
/* Yay! Got at least one response back -- loop through outstanding probes
|
||||
and find the relevant ones. Note the peculiar structure of the loop--we
|
||||
iterate through both incompleteHosts and completedHosts, because global
|
||||
timing pings are sent to hosts in completedHosts. */
|
||||
std::list<HostScanStats *>::iterator incompleteHostI, completedHostI;
|
||||
incompleteHostI = USI->incompleteHosts.begin();
|
||||
completedHostI = USI->completedHosts.begin();
|
||||
while ((incompleteHostI != USI->incompleteHosts.end()
|
||||
|| completedHostI != USI->completedHosts.end())
|
||||
&& numGoodSD < selectres) {
|
||||
if (incompleteHostI != USI->incompleteHosts.end())
|
||||
hostI = incompleteHostI++;
|
||||
else
|
||||
hostI = completedHostI++;
|
||||
|
||||
host = *hostI;
|
||||
if (host->num_probes_active == 0)
|
||||
continue;
|
||||
|
||||
std::list<UltraProbe *>::iterator nextProbeI;
|
||||
for (std::list<UltraProbe *>::iterator probeI = host->probes_outstanding.begin(), end = host->probes_outstanding.end();
|
||||
probeI != end && numGoodSD < selectres && host->num_probes_outstanding() > 0; probeI = nextProbeI) {
|
||||
/* handleConnectResult may remove the probe at probeI, which invalidates
|
||||
* the iterator. We copy and increment it here instead of in the for-loop
|
||||
* statement to avoid incrementing an invalid iterator */
|
||||
nextProbeI = probeI;
|
||||
nextProbeI++;
|
||||
probe = *probeI;
|
||||
assert(probe->type == UltraProbe::UP_CONNECT);
|
||||
sd = probe->CP()->sd;
|
||||
/* Let see if anything has happened! */
|
||||
if (sd >= 0 && (checked_fd_isset(sd, &fds_rtmp) ||
|
||||
checked_fd_isset(sd, &fds_wtmp) ||
|
||||
checked_fd_isset(sd, &fds_xtmp))) {
|
||||
numGoodSD++;
|
||||
if (getsockopt(sd, SOL_SOCKET, SO_ERROR, (char *) &optval,
|
||||
&optlen) != 0)
|
||||
optval = socket_errno(); /* Stupid Solaris ... */
|
||||
|
||||
handleConnectResult(USI, host, probeI, optval);
|
||||
}
|
||||
}
|
||||
}
|
||||
return numGoodSD;
|
||||
}
|
||||
|
||||
@@ -123,4 +123,8 @@
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "scan_engine.h"
|
||||
|
||||
UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss,
|
||||
u16 destport, u8 tryno, u8 pingseq);
|
||||
bool do_one_select_round(UltraScanInfo *USI, struct timeval *stime);
|
||||
|
||||
2149
scan_engine_raw.cc
2149
scan_engine_raw.cc
File diff suppressed because it is too large
Load Diff
@@ -123,4 +123,19 @@
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include "scan_engine.h"
|
||||
#include "Target.h"
|
||||
#include <vector>
|
||||
|
||||
void increment_base_port();
|
||||
int get_ping_pcap_result(UltraScanInfo *USI, struct timeval *stime);
|
||||
void begin_sniffer(UltraScanInfo *USI, std::vector<Target *> &Targets);
|
||||
UltraProbe *sendArpScanProbe(UltraScanInfo *USI, HostScanStats *hss,
|
||||
u8 tryno, u8 pingseq);
|
||||
UltraProbe *sendNDScanProbe(UltraScanInfo *USI, HostScanStats *hss,
|
||||
u8 tryno, u8 pingseq);
|
||||
UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
|
||||
const probespec *pspec, u8 tryno, u8 pingseq);
|
||||
bool get_arp_result(UltraScanInfo *USI, struct timeval *stime);
|
||||
bool get_ns_result(UltraScanInfo *USI, struct timeval *stime);
|
||||
bool get_pcap_result(UltraScanInfo *USI, struct timeval *stime);
|
||||
|
||||
Reference in New Issue
Block a user