diff --git a/libnetutil/netutil.cc b/libnetutil/netutil.cc index 48e653416..47c66ecba 100644 --- a/libnetutil/netutil.cc +++ b/libnetutil/netutil.cc @@ -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; diff --git a/tcpip.cc b/tcpip.cc index b8dc88309..3653b20b8 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -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 }