1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00

Switch from pcap_next to pcap_next_ex to check for errors

This change supports #1394. When the network goes down, Nmap ought to
stop instead of slowing to an infinite crawl.
This commit is contained in:
dmiller
2020-01-13 17:14:45 +00:00
parent dcf4aa9f78
commit 00cb4d22d9
2 changed files with 63 additions and 35 deletions

View File

@@ -955,7 +955,7 @@ int my_pcap_get_selectable_fd(pcap_t *p) {
fd is selectable? If not, it's possible for the fd to become selectable, then
for pcap_dispatch to buffer two or more frames, and return only the first one
Because select doesn't know about pcap's buffer, the fd does not become
selectable again, even though another pcap_next would succeed. On these
selectable again, even though another pcap_next_ex would succeed. On these
platforms, we must do a non-blocking read from the fd before doing a select
on the fd.
@@ -4172,7 +4172,7 @@ int datalink_offset(int datalink)
the function returns 0 and the output parameters are undefined. */
static int read_reply_pcap(pcap_t *pd, long to_usec,
bool (*accept_callback)(const unsigned char *, const struct pcap_pkthdr *, int, size_t),
unsigned char **p, struct pcap_pkthdr *head, struct timeval *rcvdtime,
const unsigned char **p, struct pcap_pkthdr **head, struct timeval *rcvdtime,
int *datalink, size_t *offset)
{
static int warning = 0;
@@ -4207,6 +4207,7 @@ static int read_reply_pcap(pcap_t *pd, long to_usec,
do {
*p = NULL;
int pcap_status = 0;
/* It may be that protecting this with !pcap_selectable_fd_one_to_one is not
necessary, that it is always safe to do a nonblocking read in this way on
all platforms. But I have only tested it on Solaris. */
@@ -4217,22 +4218,32 @@ static int read_reply_pcap(pcap_t *pd, long to_usec,
assert(nonblock == 0);
rc = pcap_setnonblock(pd, 1, NULL);
assert(rc == 0);
*p = (u8 *) pcap_next(pd, head);
pcap_status = pcap_next_ex(pd, head, p);
rc = pcap_setnonblock(pd, nonblock, NULL);
assert(rc == 0);
}
if (*p == NULL) {
/* Nonblocking pcap_next didn't get anything. */
if (pcap_status == PCAP_ERROR) {
// TODO: Gracefully end the scan.
netutil_fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd));
}
if (pcap_status == 0 || *p == NULL) {
/* Nonblocking pcap_next_ex didn't get anything. */
if (pcap_select(pd, to_usec) == 0)
timedout = 1;
else
*p = (u8 *) pcap_next(pd, head);
pcap_status = pcap_next_ex(pd, head, p);
}
if (*p != NULL && accept_callback(*p, head, *datalink, *offset)) {
if (pcap_status == PCAP_ERROR) {
// TODO: Gracefully end the scan.
netutil_fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd));
}
if (pcap_status == 1 && *p != NULL && accept_callback(*p, *head, *datalink, *offset)) {
break;
} else if (*p == NULL) {
} else if (pcap_status == 0 || *p == NULL) {
/* Should we timeout? */
if (to_usec == 0) {
timedout = 1;
@@ -4264,9 +4275,9 @@ static int read_reply_pcap(pcap_t *pd, long to_usec,
gettimeofday(&tv_end, NULL);
*rcvdtime = tv_end;
#else
rcvdtime->tv_sec = head->ts.tv_sec;
rcvdtime->tv_usec = head->ts.tv_usec;
assert(head->ts.tv_sec);
rcvdtime->tv_sec = (*head)->ts.tv_sec;
rcvdtime->tv_usec = (*head)->ts.tv_usec;
assert((*head)->ts.tv_sec);
#endif
}
@@ -4308,8 +4319,8 @@ int read_arp_reply_pcap(pcap_t *pd, u8 *sendermac,
struct in_addr *senderIP, long to_usec,
struct timeval *rcvdtime,
void (*trace_callback)(int, const u8 *, u32, struct timeval *)) {
unsigned char *p;
struct pcap_pkthdr head;
const unsigned char *p;
struct pcap_pkthdr *head;
int datalink;
size_t offset;
int rc;
@@ -4360,8 +4371,8 @@ int read_ns_reply_pcap(pcap_t *pd, u8 *sendermac,
struct sockaddr_in6 *senderIP, long to_usec,
struct timeval *rcvdtime,
void (*trace_callback)(int, const u8 *, u32, struct timeval *)) {
unsigned char *p;
struct pcap_pkthdr head;
const unsigned char *p;
struct pcap_pkthdr *head;
int datalink;
size_t offset;
int rc;

View File

@@ -1551,8 +1551,9 @@ char *readipv4_pcap(pcap_t *pd, unsigned int *len, long to_usec,
char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
struct timeval *rcvdtime, struct link_header *linknfo, bool validate) {
unsigned int offset = 0;
struct pcap_pkthdr head;
char *p;
struct pcap_pkthdr *head;
const unsigned char *p;
int pcap_status = 0;
int datalink;
int timedout = 0;
struct timeval tv_start, tv_end;
@@ -1651,18 +1652,22 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
break;
#endif
default:
p = (char *) pcap_next(pd, &head);
if (head.caplen == 0) {
pcap_status = pcap_next_ex(pd, &head, &p);
if (pcap_status == 0) {
/* Lets sleep a brief time and try again to increase the chance of seeing
a real packet ... */
usleep(500000);
p = (char *) pcap_next(pd, &head);
pcap_status = pcap_next_ex(pd, &head, &p);
}
if (head.caplen > 100000) {
fatal("FATAL: %s: bogus caplen from libpcap (%d) on interface type %d", __func__, head.caplen, datalink);
if (pcap_status != 1 || !head) {
// No packet captured
fatal("FATAL: Unknown datalink type (%d).", datalink);
}
error("FATAL: Unknown datalink type (%d). Caplen: %d; Packet:", datalink, head.caplen);
nmap_hexdump((unsigned char *) p, head.caplen);
if (head->caplen > 100000 || !p) {
fatal("FATAL: %s: bogus caplen from libpcap (%d) on interface type %d", __func__, head->caplen, datalink);
}
error("FATAL: Unknown datalink type (%d). Caplen: %d; Packet:", datalink, head->caplen);
nmap_hexdump(p, head->caplen);
exit(1);
}
@@ -1673,6 +1678,7 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
do {
p = NULL;
pcap_status = 0;
/* It may be that protecting this with !pcap_selectable_fd_one_to_one is not
necessary, that it is always safe to do a nonblocking read in this way on
all platforms. But I have only tested it on Solaris. */
@@ -1683,21 +1689,32 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
assert(nonblock == 0);
rc = pcap_setnonblock(pd, 1, NULL);
assert(rc == 0);
p = (char *) pcap_next(pd, &head);
pcap_status = pcap_next_ex(pd, &head, &p);
rc = pcap_setnonblock(pd, nonblock, NULL);
assert(rc == 0);
}
if (p == NULL) {
/* Nonblocking pcap_next didn't get anything. */
if (pcap_status == PCAP_ERROR) {
// TODO: Gracefully end the scan.
// For now, consider a pcap read error to be fatal.
fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd));
}
if (pcap_status == 0 || p == NULL) {
/* Nonblocking pcap_next_ex didn't get anything. */
if (pcap_select(pd, to_usec) == 0)
timedout = 1;
else
p = (char *) pcap_next(pd, &head);
pcap_status = pcap_next_ex(pd, &head, &p);
}
if (p) {
if (head.caplen <= offset) {
if (pcap_status == PCAP_ERROR) {
// TODO: Gracefully end the scan.
fatal("Error from pcap_next_ex: %s\n", pcap_geterr(pd));
}
if (pcap_status == 1 && p) {
if (head->caplen <= offset) {
*len = 0;
return NULL;
}
@@ -1709,7 +1726,7 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
}
p += offset;
}
if (!p) {
else {
/* Should we timeout? */
if (to_usec == 0) {
timedout = 1;
@@ -1726,7 +1743,7 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
*len = 0;
return NULL;
}
*len = head.caplen - offset;
*len = head->caplen - offset;
if (*len > alignedbufsz) {
alignedbuf = (char *) safe_realloc(alignedbuf, *len);
alignedbufsz = *len;
@@ -1752,9 +1769,9 @@ char *readip_pcap(pcap_t *pd, unsigned int *len, long to_usec,
gettimeofday(&tv_end, NULL);
*rcvdtime = tv_end;
#else
rcvdtime->tv_sec = head.ts.tv_sec;
rcvdtime->tv_usec = head.ts.tv_usec;
assert(head.ts.tv_sec);
rcvdtime->tv_sec = head->ts.tv_sec;
rcvdtime->tv_usec = head->ts.tv_usec;
assert(head->ts.tv_sec);
#endif
}