aboutsummaryrefslogtreecommitdiff
path: root/testprogs
diff options
context:
space:
mode:
authorJoseph Mingrone <jrm@FreeBSD.org>2023-03-27 18:45:17 +0000
committerJoseph Mingrone <jrm@FreeBSD.org>2023-03-27 18:45:17 +0000
commit35af88c96350eb786f1198dfb6b29a171016e6bf (patch)
treee883c1f8391d5ca1afd57abd8ed9d2cd7c274b0b /testprogs
parent20616273d52132557e786a8aea1637be4c218a08 (diff)
Diffstat (limited to 'testprogs')
-rw-r--r--testprogs/CMakeLists.txt13
-rw-r--r--testprogs/Makefile.in54
-rw-r--r--testprogs/can_set_rfmon_test.c1
-rw-r--r--testprogs/capturetest.c89
-rw-r--r--testprogs/filtertest.c44
-rw-r--r--testprogs/findalldevstest-perf.c97
-rw-r--r--testprogs/findalldevstest.c29
-rw-r--r--testprogs/fuzz/CMakeLists.txt43
-rw-r--r--testprogs/fuzz/fuzz_both.c101
-rw-r--r--testprogs/fuzz/fuzz_both.options2
-rw-r--r--testprogs/fuzz/fuzz_filter.c43
-rw-r--r--testprogs/fuzz/fuzz_filter.options2
-rw-r--r--testprogs/fuzz/fuzz_pcap.c80
-rw-r--r--testprogs/fuzz/fuzz_pcap.options2
-rw-r--r--testprogs/fuzz/onefile.c54
-rw-r--r--testprogs/nonblocktest.c187
-rw-r--r--testprogs/opentest.c19
-rw-r--r--testprogs/reactivatetest.c1
-rw-r--r--testprogs/selpolltest.c66
-rw-r--r--testprogs/threadsignaltest.c58
-rw-r--r--testprogs/valgrindtest.c34
-rwxr-xr-xtestprogs/visopts.py317
-rw-r--r--testprogs/writecaptest.c556
23 files changed, 1794 insertions, 98 deletions
diff --git a/testprogs/CMakeLists.txt b/testprogs/CMakeLists.txt
index b8ef9b7d0510..567f42aa6ed7 100644
--- a/testprogs/CMakeLists.txt
+++ b/testprogs/CMakeLists.txt
@@ -19,6 +19,10 @@ macro(add_test_executable _executable)
target_link_libraries(${_executable}
${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES})
endif(WIN32)
+ if(NOT "${LINKER_FLAGS}" STREQUAL "")
+ set_target_properties(${_executable} PROPERTIES
+ LINK_FLAGS "${LINKER_FLAGS}")
+ endif()
add_dependencies(testprogs ${_executable})
endmacro()
@@ -26,8 +30,10 @@ add_test_executable(can_set_rfmon_test)
add_test_executable(capturetest)
add_test_executable(filtertest)
add_test_executable(findalldevstest)
+add_test_executable(findalldevstest-perf)
add_test_executable(opentest)
add_test_executable(reactivatetest)
+add_test_executable(writecaptest)
if(NOT WIN32)
add_test_executable(selpolltest)
@@ -35,6 +41,11 @@ endif()
add_test_executable(threadsignaltest ${CMAKE_THREAD_LIBS_INIT})
-if(NOT WIN32)
+# Same as in configure.ac.
+if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+ CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
+ CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_test_executable(valgrindtest)
endif()
+
+add_subdirectory(fuzz)
diff --git a/testprogs/Makefile.in b/testprogs/Makefile.in
index ec0a47208464..f195693713f9 100644
--- a/testprogs/Makefile.in
+++ b/testprogs/Makefile.in
@@ -38,6 +38,7 @@ mandir = @mandir@
# VPATH
srcdir = @srcdir@
+top_srcdir = @top_srcdir@
VPATH = @srcdir@
#
@@ -62,7 +63,6 @@ LDFLAGS = @LDFLAGS@ ${CROSSFLAGS}
DYEXT = @DYEXT@
V_RPATH_OPT = @V_RPATH_OPT@
DEPENDENCY_CFLAG = @DEPENDENCY_CFLAG@
-EXTRA_NETWORK_LIBS=@EXTRA_NETWORK_LIBS@
# Standard CFLAGS for building test programs
FULL_CFLAGS = $(CCOPT) $(INCLS) $(DEFS) $(CFLAGS)
@@ -79,14 +79,17 @@ INSTALL_DATA = @INSTALL_DATA@
$(CC) $(FULL_CFLAGS) -c $(srcdir)/$*.c
SRC = @VALGRINDTEST_SRC@ \
- capturetest.c \
can_set_rfmon_test.c \
+ capturetest.c \
filtertest.c \
+ findalldevstest-perf.c \
findalldevstest.c \
opentest.c \
+ nonblocktest.c \
reactivatetest.c \
selpolltest.c \
- threadsignaltest.c
+ threadsignaltest.c \
+ writecaptest.c
TESTS = $(SRC:.c=)
@@ -98,31 +101,56 @@ CLEANFILES = $(OBJ) $(TESTS)
all: $(TESTS)
capturetest: $(srcdir)/capturetest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o capturetest $(srcdir)/capturetest.c ../libpcap.a $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o capturetest $(srcdir)/capturetest.c \
+ ../libpcap.a $(LIBS)
can_set_rfmon_test: $(srcdir)/can_set_rfmon_test.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o can_set_rfmon_test $(srcdir)/can_set_rfmon_test.c ../libpcap.a $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o can_set_rfmon_test \
+ $(srcdir)/can_set_rfmon_test.c \
+ ../libpcap.a $(LIBS)
filtertest: $(srcdir)/filtertest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o filtertest $(srcdir)/filtertest.c ../libpcap.a $(EXTRA_NETWORK_LIBS) $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o filtertest $(srcdir)/filtertest.c \
+ ../libpcap.a $(LIBS)
findalldevstest: $(srcdir)/findalldevstest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest $(srcdir)/findalldevstest.c ../libpcap.a $(EXTRA_NETWORK_LIBS) $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest \
+ $(srcdir)/findalldevstest.c \
+ ../libpcap.a $(LIBS)
+
+findalldevstest-perf: $(srcdir)/findalldevstest-perf.c ../libpcap.a
+ $(CC) $(FULL_CFLAGS) -I. -L. -o findalldevstest-perf \
+ $(srcdir)/findalldevstest-perf.c \
+ ../libpcap.a $(LIBS)
opentest: $(srcdir)/opentest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c ../libpcap.a $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o opentest $(srcdir)/opentest.c \
+ ../libpcap.a $(LIBS)
+
+nonblocktest: $(srcdir)/nonblocktest.c ../libpcap.a
+ $(CC) $(FULL_CFLAGS) -I. -L. -o nonblocktest $(srcdir)/nonblocktest.c \
+ ../libpcap.a $(LIBS)
reactivatetest: $(srcdir)/reactivatetest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest $(srcdir)/reactivatetest.c ../libpcap.a $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o reactivatetest \
+ $(srcdir)/reactivatetest.c ../libpcap.a $(LIBS)
selpolltest: $(srcdir)/selpolltest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o selpolltest $(srcdir)/selpolltest.c ../libpcap.a $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o selpolltest $(srcdir)/selpolltest.c \
+ ../libpcap.a $(LIBS)
threadsignaltest: $(srcdir)/threadsignaltest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o threadsignaltest $(srcdir)/threadsignaltest.c ../libpcap.a $(LIBS) $(PTHREAD_LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o threadsignaltest \
+ $(srcdir)/threadsignaltest.c \
+ ../libpcap.a $(LIBS) $(PTHREAD_LIBS)
valgrindtest: $(srcdir)/valgrindtest.c ../libpcap.a
- $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c ../libpcap.a $(LIBS)
+ $(CC) $(FULL_CFLAGS) -I. -L. -o valgrindtest $(srcdir)/valgrindtest.c \
+ ../libpcap.a $(LIBS)
+
+writecaptest: $(srcdir)/writecaptest.c ../libpcap.a
+ $(CC) $(FULL_CFLAGS) -I. -L. -o writecaptest $(srcdir)/writecaptest.c \
+ ../libpcap.a $(LIBS)
clean:
rm -f $(CLEANFILES)
@@ -141,4 +169,4 @@ tags: $(TAGFILES)
ctags -wtd $(TAGFILES)
depend:
- ../$(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC)
+ $(MKDEP) -c "$(CC)" -m "$(DEPENDENCY_CFLAG)" -s "$(srcdir)" $(CFLAGS) $(DEFS) $(INCLS) $(SRC)
diff --git a/testprogs/can_set_rfmon_test.c b/testprogs/can_set_rfmon_test.c
index f6188ba1399e..96816f9b977c 100644
--- a/testprogs/can_set_rfmon_test.c
+++ b/testprogs/can_set_rfmon_test.c
@@ -70,7 +70,6 @@ main(int argc, char **argv)
else
error("%s: pcap_can_set_rfmon failed: %s", argv[1],
pcap_statustostr(status));
- return 1;
}
printf("%s: Monitor mode %s be set\n", argv[1], status ? "can" : "cannot");
return 0;
diff --git a/testprogs/capturetest.c b/testprogs/capturetest.c
index d625cb4ab2b7..4eadd3367968 100644
--- a/testprogs/capturetest.c
+++ b/testprogs/capturetest.c
@@ -38,6 +38,9 @@ The Regents of the University of California. All rights reserved.\n";
#include <unistd.h>
#endif
#include <errno.h>
+#ifndef _WIN32
+ #include <signal.h>
+#endif
#include <sys/types.h>
#include <pcap.h>
@@ -58,6 +61,37 @@ static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
static char *copy_argv(char **);
static pcap_t *pd;
+#ifndef _WIN32
+static int breaksigint = 0;
+#endif
+
+#ifndef _WIN32
+static void
+sigint_handler(int signum _U_)
+{
+ if (breaksigint)
+ pcap_breakloop(pd);
+}
+#endif
+
+#ifdef _WIN32
+/*
+ * We don't have UN*X-style signals, so we don't have anything to test.
+ */
+#define B_OPTION ""
+#define R_OPTION ""
+#define S_OPTION ""
+#else
+/*
+ * We do have UN*X-style signals (we assume that "not Windows" means "UN*X").
+ */
+#define B_OPTION "b"
+#define R_OPTION "r"
+#define S_OPTION "s"
+#endif
+
+#define COMMAND_OPTIONS B_OPTION "i:mn" R_OPTION S_OPTION "t:"
+#define USAGE_OPTIONS "-" B_OPTION "mn" R_OPTION S_OPTION
int
main(int argc, char **argv)
@@ -69,6 +103,10 @@ main(int argc, char **argv)
int timeout = 1000;
int immediate = 0;
int nonblock = 0;
+#ifndef _WIN32
+ int sigrestart = 0;
+ int catchsigint = 0;
+#endif
pcap_if_t *devlist;
bpf_u_int32 localnet, netmask;
struct bpf_program fcode;
@@ -83,9 +121,15 @@ main(int argc, char **argv)
program_name = argv[0];
opterr = 0;
- while ((op = getopt(argc, argv, "i:mnt:")) != -1) {
+ while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) {
switch (op) {
+#ifndef _WIN32
+ case 'b':
+ breaksigint = 1;
+ break;
+#endif
+
case 'i':
device = optarg;
break;
@@ -98,6 +142,16 @@ main(int argc, char **argv)
nonblock = 1;
break;
+#ifndef _WIN32
+ case 'r':
+ sigrestart = 1;
+ break;
+
+ case 's':
+ catchsigint = 1;
+ break;
+#endif
+
case 't':
longarg = strtol(optarg, &p, 10);
if (p == optarg || *p != '\0') {
@@ -132,6 +186,28 @@ main(int argc, char **argv)
pcap_freealldevs(devlist);
}
*ebuf = '\0';
+
+#ifndef _WIN32
+ /*
+ * If we were told to catch SIGINT, do so.
+ */
+ if (catchsigint) {
+ struct sigaction action;
+
+ action.sa_handler = sigint_handler;
+ sigemptyset(&action.sa_mask);
+
+ /*
+ * Should SIGINT interrupt, or restart, system calls?
+ */
+ action.sa_flags = sigrestart ? SA_RESTART : 0;
+
+ if (sigaction(SIGINT, &action, NULL) == -1)
+ error("Can't catch SIGINT: %s\n",
+ strerror(errno));
+ }
+#endif
+
pd = pcap_create(device, ebuf);
if (pd == NULL)
error("%s", ebuf);
@@ -188,6 +264,10 @@ main(int argc, char **argv)
if (status != 0) {
printf("%d packets seen, %d packets counted after pcap_dispatch returns\n",
status, packet_count);
+ struct pcap_stat ps;
+ pcap_stats(pd, &ps);
+ printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n",
+ ps.ps_recv, ps.ps_drop, ps.ps_ifdrop);
}
}
if (status == -2) {
@@ -197,13 +277,14 @@ main(int argc, char **argv)
* Print an extra newline, just in case.
*/
putchar('\n');
+ printf("Broken out of loop from SIGINT handler\n");
}
(void)fflush(stdout);
if (status == -1) {
/*
* Error. Report it.
*/
- (void)fprintf(stderr, "%s: pcap_loop: %s\n",
+ (void)fprintf(stderr, "%s: pcap_dispatch: %s\n",
program_name, pcap_geterr(pd));
}
pcap_close(pd);
@@ -223,7 +304,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
static void
usage(void)
{
- (void)fprintf(stderr, "Usage: %s [ -mn ] [ -i interface ] [ -t timeout] [expression]\n",
+ (void)fprintf(stderr, "Usage: %s [ " USAGE_OPTIONS " ] [ -i interface ] [ -t timeout] [expression]\n",
program_name);
exit(1);
}
@@ -271,7 +352,7 @@ static char *
copy_argv(register char **argv)
{
register char **p;
- register u_int len = 0;
+ register size_t len = 0;
char *buf;
char *src, *dst;
diff --git a/testprogs/filtertest.c b/testprogs/filtertest.c
index 7e2d6d6e186d..15556d045cf7 100644
--- a/testprogs/filtertest.c
+++ b/testprogs/filtertest.c
@@ -36,6 +36,7 @@ The Regents of the University of California. All rights reserved.\n";
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
+#include <limits.h>
#ifdef _WIN32
#include "getopt.h"
#include "unix.h"
@@ -56,6 +57,8 @@ The Regents of the University of California. All rights reserved.\n";
#include "pcap/funcattrs.h"
+#define MAXIMUM_SNAPLEN 262144
+
#ifdef BDEBUG
/*
* We have pcap_set_optimizer_debug() and pcap_set_print_dot_graph() in
@@ -99,11 +102,21 @@ read_infile(char *fname)
if (fstat(fd, &buf) < 0)
error("can't stat %s: %s", fname, pcap_strerror(errno));
+ /*
+ * _read(), on Windows, has an unsigned int byte count and an
+ * int return value, so we can't handle a file bigger than
+ * INT_MAX - 1 bytes (and have no reason to do so; a filter *that*
+ * big will take forever to compile). (The -1 is for the '\0' at
+ * the end of the string.)
+ */
+ if (buf.st_size > INT_MAX - 1)
+ error("%s is larger than %d bytes; that's too large", fname,
+ INT_MAX - 1);
cp = malloc((u_int)buf.st_size + 1);
if (cp == NULL)
error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
fname, pcap_strerror(errno));
- cc = read(fd, cp, (u_int)buf.st_size);
+ cc = (int)read(fd, cp, (u_int)buf.st_size);
if (cc < 0)
error("read %s: %s", fname, pcap_strerror(errno));
if (cc != buf.st_size)
@@ -163,7 +176,7 @@ static char *
copy_argv(register char **argv)
{
register char **p;
- register u_int len = 0;
+ register size_t len = 0;
char *buf;
char *src, *dst;
@@ -196,13 +209,14 @@ main(int argc, char **argv)
char *cp;
int op;
int dflag;
+#ifdef BDEBUG
int gflag;
+#endif
char *infile;
int Oflag;
- long snaplen;
+ int snaplen;
char *p;
int dlt;
- int have_fcode = 0;
bpf_u_int32 netmask = PCAP_NETMASK_UNKNOWN;
char *cmdbuf;
pcap_t *pd;
@@ -214,11 +228,13 @@ main(int argc, char **argv)
#endif /* _WIN32 */
dflag = 1;
+#ifdef BDEBUG
gflag = 0;
+#endif
infile = NULL;
Oflag = 1;
- snaplen = 68;
+ snaplen = MAXIMUM_SNAPLEN;
if ((cp = strrchr(argv[0], '/')) != NULL)
program_name = cp + 1;
@@ -272,13 +288,19 @@ main(int argc, char **argv)
case 's': {
char *end;
+ long long_snaplen;
- snaplen = strtol(optarg, &end, 0);
+ long_snaplen = strtol(optarg, &end, 0);
if (optarg == end || *end != '\0'
- || snaplen < 0 || snaplen > 65535)
+ || long_snaplen < 0
+ || long_snaplen > MAXIMUM_SNAPLEN)
error("invalid snaplen %s", optarg);
- else if (snaplen == 0)
- snaplen = 65535;
+ else {
+ if (snaplen == 0)
+ snaplen = MAXIMUM_SNAPLEN;
+ else
+ snaplen = (int)long_snaplen;
+ }
break;
}
@@ -317,7 +339,6 @@ main(int argc, char **argv)
if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
error("%s", pcap_geterr(pd));
- have_fcode = 1;
if (!bpf_validate(fcode.bf_insns, fcode.bf_len))
warn("Filter doesn't pass validation");
@@ -337,8 +358,7 @@ main(int argc, char **argv)
bpf_dump(&fcode, dflag);
free(cmdbuf);
- if (have_fcode)
- pcap_freecode (&fcode);
+ pcap_freecode (&fcode);
pcap_close(pd);
exit(0);
}
diff --git a/testprogs/findalldevstest-perf.c b/testprogs/findalldevstest-perf.c
new file mode 100644
index 000000000000..16f53cdc5967
--- /dev/null
+++ b/testprogs/findalldevstest-perf.c
@@ -0,0 +1,97 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef _WIN32
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+#else
+ #include <unistd.h>
+ #include <sys/resource.h>
+#endif
+
+#include <pcap.h>
+
+#include "varattrs.h"
+#include "pcap/funcattrs.h"
+#include "portability.h"
+
+int main(int argc _U_, char **argv _U_)
+{
+ pcap_if_t *alldevs;
+ int exit_status = 0;
+ char errbuf[PCAP_ERRBUF_SIZE+1];
+#ifdef _WIN32
+ FILETIME start_ktime, start_utime, end_ktime, end_utime;
+ FILETIME dummy1, dummy2;
+ ULARGE_INTEGER start_kticks, end_kticks, start_uticks, end_uticks;
+ ULONGLONG ktime, utime, tottime;
+#else
+ struct rusage start_rusage, end_rusage;
+ struct timeval ktime, utime, tottime;
+#endif
+
+#ifdef _WIN32
+ if (!GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2,
+ &start_ktime, &start_utime))
+ {
+ fprintf(stderr, "GetProcessTimes() fails at start\n");
+ exit(1);
+ }
+ start_kticks.LowPart = start_ktime.dwLowDateTime;
+ start_kticks.HighPart = start_ktime.dwHighDateTime;
+ start_uticks.LowPart = start_utime.dwLowDateTime;
+ start_uticks.HighPart = start_utime.dwHighDateTime;
+#else
+ if (getrusage(RUSAGE_SELF, &start_rusage) == -1) {
+ fprintf(stderr, "getrusage() fails at start\n");
+ exit(1);
+ }
+#endif
+ for (int i = 0; i < 500; i++)
+ {
+ if (pcap_findalldevs(&alldevs, errbuf) == -1)
+ {
+ fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf);
+ exit(1);
+ }
+ pcap_freealldevs(alldevs);
+ }
+
+#ifdef _WIN32
+ if (!GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2,
+ &end_ktime, &end_utime))
+ {
+ fprintf(stderr, "GetProcessTimes() fails at end\n");
+ exit(1);
+ }
+ end_kticks.LowPart = end_ktime.dwLowDateTime;
+ end_kticks.HighPart = end_ktime.dwHighDateTime;
+ end_uticks.LowPart = end_utime.dwLowDateTime;
+ end_uticks.HighPart = end_utime.dwHighDateTime;
+ ktime = end_kticks.QuadPart - start_kticks.QuadPart;
+ utime = end_uticks.QuadPart - start_uticks.QuadPart;
+ tottime = ktime + utime;
+ printf("Total CPU secs: kernel %g, user %g, total %g\n",
+ ((double)ktime) / 10000000.0,
+ ((double)utime) / 10000000.0,
+ ((double)tottime) / 10000000.0);
+#else
+ if (getrusage(RUSAGE_SELF, &end_rusage) == -1) {
+ fprintf(stderr, "getrusage() fails at end\n");
+ exit(1);
+ }
+ timersub(&end_rusage.ru_stime, &start_rusage.ru_stime, &ktime);
+ timersub(&end_rusage.ru_utime, &start_rusage.ru_utime, &utime);
+ timeradd(&ktime, &utime, &tottime);
+ printf("Total CPU secs: kernel %g, user %g, total %g\n",
+ (double)ktime.tv_sec + ((double)ktime.tv_usec / 1000000.0),
+ (double)utime.tv_sec + ((double)utime.tv_usec / 1000000.0),
+ (double)tottime.tv_sec + ((double)tottime.tv_usec / 1000000.0));
+#endif
+ exit(exit_status);
+}
diff --git a/testprogs/findalldevstest.c b/testprogs/findalldevstest.c
index e535e25462fb..062932090381 100644
--- a/testprogs/findalldevstest.c
+++ b/testprogs/findalldevstest.c
@@ -19,6 +19,7 @@
#include <pcap.h>
+#include "varattrs.h"
#include "pcap/funcattrs.h"
static int ifprint(pcap_if_t *d);
@@ -94,7 +95,11 @@ getpass(const char *prompt)
}
#endif
+#ifdef ENABLE_REMOTE
int main(int argc, char **argv)
+#else
+int main(int argc _U_, char **argv _U_)
+#endif
{
pcap_if_t *alldevs;
pcap_if_t *d;
@@ -152,8 +157,24 @@ int main(int argc, char **argv)
{
if (pcap_lookupnet(alldevs->name, &net, &mask, errbuf) < 0)
{
- fprintf(stderr,"Error in pcap_lookupnet: %s\n",errbuf);
- exit_status = 2;
+ /*
+ * XXX - this doesn't distinguish between "a real error
+ * occurred" and "this interface doesn't *have* an IPv4
+ * address". The latter shouldn't be treated as an error.
+ *
+ * We look for the interface name, followed by a colon and
+ * a space, and, if we find it,w e see if what follows it
+ * is "no IPv4 address assigned".
+ */
+ size_t devnamelen = strlen(alldevs->name);
+ if (strncmp(errbuf, alldevs->name, devnamelen) == 0 &&
+ strncmp(errbuf + devnamelen, ": ", 2) == 0 &&
+ strcmp(errbuf + devnamelen + 2, "no IPv4 address assigned") == 0)
+ printf("Preferred device is not on an IPv4 network\n");
+ else {
+ fprintf(stderr,"Error in pcap_lookupnet: %s\n",errbuf);
+ exit_status = 2;
+ }
}
else
{
@@ -300,12 +321,12 @@ static int ifprint(pcap_if_t *d)
#define IPTOSBUFFERS 12
static char *iptos(bpf_u_int32 in)
{
- static char output[IPTOSBUFFERS][3*4+3+1];
+ static char output[IPTOSBUFFERS][sizeof("255.255.255.255")];
static short which;
u_char *p;
p = (u_char *)&in;
which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);
- sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ snprintf(output[which], sizeof(output[which]), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
return output[which];
}
diff --git a/testprogs/fuzz/CMakeLists.txt b/testprogs/fuzz/CMakeLists.txt
new file mode 100644
index 000000000000..67250cca4349
--- /dev/null
+++ b/testprogs/fuzz/CMakeLists.txt
@@ -0,0 +1,43 @@
+add_executable(fuzz_pcap onefile.c fuzz_pcap.c)
+target_link_libraries(fuzz_pcap ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES})
+if(NOT "${SANITIZER_FLAGS}" STREQUAL "")
+ set_target_properties(fuzz_pcap PROPERTIES
+ LINK_FLAGS "${SANITIZER_FLAGS}")
+endif()
+
+add_executable(fuzz_filter onefile.c fuzz_filter.c)
+target_link_libraries(fuzz_filter ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES})
+if(NOT "${SANITIZER_FLAGS}" STREQUAL "")
+ set_target_properties(fuzz_filter PROPERTIES
+ LINK_FLAGS "${SANITIZER_FLAGS}")
+endif()
+
+add_executable(fuzz_both onefile.c fuzz_both.c)
+target_link_libraries(fuzz_both ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES})
+if(NOT "${SANITIZER_FLAGS}" STREQUAL "")
+ set_target_properties(fuzz_both PROPERTIES
+ LINK_FLAGS "${SANITIZER_FLAGS}")
+endif()
+
+if(ENABLE_REMOTE AND "$ENV{CFLAGS}" MATCHES "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION")
+add_executable(fuzz_rclient onefile.c fuzz_rclient.c)
+target_link_libraries(fuzz_rclient ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES})
+if(NOT "${SANITIZER_FLAGS}" STREQUAL "")
+ set_target_properties(fuzz_rclient PROPERTIES
+ LINK_FLAGS "${SANITIZER_FLAGS}")
+endif()
+
+add_executable(fuzz_rserver onefile.c fuzz_rserver.c ../../rpcapd/daemon.c)
+check_function_exists(crypt HAVE_CRYPT_IN_SYSTEM_LIBRARIES)
+if(HAVE_CRYPT_IN_SYSTEM_LIBRARIES)
+ set(HAVE_CRYPT TRUE)
+else(HAVE_CRYPT_IN_SYSTEM_LIBRARIES)
+ set(PCAP_LINK_LIBRARIES ${PCAP_LINK_LIBRARIES} crypt)
+endif(HAVE_CRYPT_IN_SYSTEM_LIBRARIES)
+target_link_libraries(fuzz_rserver ${ARGN} ${LIBRARY_NAME}_static ${PCAP_LINK_LIBRARIES})
+
+if(NOT "${SANITIZER_FLAGS}" STREQUAL "")
+ set_target_properties(fuzz_rserver PROPERTIES
+ LINK_FLAGS "${SANITIZER_FLAGS}")
+endif()
+endif(ENABLE_REMOTE AND "$ENV{CFLAGS}" MATCHES "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION")
diff --git a/testprogs/fuzz/fuzz_both.c b/testprogs/fuzz/fuzz_both.c
new file mode 100644
index 000000000000..59e3d404103d
--- /dev/null
+++ b/testprogs/fuzz/fuzz_both.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pcap/pcap.h>
+
+FILE * outfile = NULL;
+
+static int bufferToFile(const char * name, const uint8_t *Data, size_t Size) {
+ FILE * fd;
+ if (remove(name) != 0) {
+ if (errno != ENOENT) {
+ printf("failed remove, errno=%d\n", errno);
+ return -1;
+ }
+ }
+ fd = fopen(name, "wb");
+ if (fd == NULL) {
+ printf("failed open, errno=%d\n", errno);
+ return -2;
+ }
+ if (fwrite (Data, 1, Size, fd) != Size) {
+ fclose(fd);
+ return -3;
+ }
+ fclose(fd);
+ return 0;
+}
+
+void fuzz_openFile(const char * name) {
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ outfile = fopen(name, "w");
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ pcap_t * pkts;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ const u_char *pkt;
+ struct pcap_pkthdr *header;
+ int r;
+ size_t filterSize;
+ char * filter;
+ struct bpf_program bpf;
+
+
+ //initialize output file
+ if (outfile == NULL) {
+ outfile = fopen("/dev/null", "w");
+ if (outfile == NULL) {
+ return 0;
+ }
+ }
+
+ if (Size < 1) {
+ return 0;
+ }
+ filterSize = Data[0];
+ if (Size < 1+filterSize || filterSize == 0) {
+ return 0;
+ }
+
+ //rewrite buffer to a file as libpcap does not have buffer inputs
+ if (bufferToFile("/tmp/fuzz.pcap", Data+1+filterSize, Size-(1+filterSize)) < 0) {
+ return 0;
+ }
+
+ //initialize structure
+ pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf);
+ if (pkts == NULL) {
+ fprintf(outfile, "Couldn't open pcap file %s\n", errbuf);
+ return 0;
+ }
+
+ filter = malloc(filterSize);
+ memcpy(filter, Data+1, filterSize);
+ //null terminate string
+ filter[filterSize-1] = 0;
+
+ if (pcap_compile(pkts, &bpf, filter, 1, PCAP_NETMASK_UNKNOWN) == 0) {
+ //loop over packets
+ r = pcap_next_ex(pkts, &header, &pkt);
+ while (r > 0) {
+ //checks filter
+ fprintf(outfile, "packet length=%d/%d filter=%d\n",header->caplen, header->len, pcap_offline_filter(&bpf, header, pkt));
+ r = pcap_next_ex(pkts, &header, &pkt);
+ }
+ //close structure
+ pcap_close(pkts);
+ pcap_freecode(&bpf);
+ }
+ else {
+ pcap_close(pkts);
+ }
+ free(filter);
+
+ return 0;
+}
diff --git a/testprogs/fuzz/fuzz_both.options b/testprogs/fuzz/fuzz_both.options
new file mode 100644
index 000000000000..0824b19fab47
--- /dev/null
+++ b/testprogs/fuzz/fuzz_both.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65535
diff --git a/testprogs/fuzz/fuzz_filter.c b/testprogs/fuzz/fuzz_filter.c
new file mode 100644
index 000000000000..de350672797f
--- /dev/null
+++ b/testprogs/fuzz/fuzz_filter.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pcap/pcap.h>
+
+void fuzz_openFile(const char * name){
+ //do nothing
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ pcap_t * pkts;
+ struct bpf_program bpf;
+ char * filter;
+
+ //we need at least 1 byte for linktype
+ if (Size < 1) {
+ return 0;
+ }
+
+ //initialize structure snaplen = 65535
+ pkts = pcap_open_dead(Data[Size-1], 0xFFFF);
+ if (pkts == NULL) {
+ printf("pcap_open_dead failed\n");
+ return 0;
+ }
+ filter = malloc(Size);
+ memcpy(filter, Data, Size);
+ //null terminate string
+ filter[Size-1] = 0;
+
+ if (pcap_compile(pkts, &bpf, filter, 1, PCAP_NETMASK_UNKNOWN) == 0) {
+ pcap_setfilter(pkts, &bpf);
+ pcap_close(pkts);
+ pcap_freecode(&bpf);
+ }
+ else {
+ pcap_close(pkts);
+ }
+ free(filter);
+
+ return 0;
+}
diff --git a/testprogs/fuzz/fuzz_filter.options b/testprogs/fuzz/fuzz_filter.options
new file mode 100644
index 000000000000..9fda93fcb340
--- /dev/null
+++ b/testprogs/fuzz/fuzz_filter.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 4096
diff --git a/testprogs/fuzz/fuzz_pcap.c b/testprogs/fuzz/fuzz_pcap.c
new file mode 100644
index 000000000000..fba5312fcaf2
--- /dev/null
+++ b/testprogs/fuzz/fuzz_pcap.c
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <pcap/pcap.h>
+
+FILE * outfile = NULL;
+
+static int bufferToFile(const char * name, const uint8_t *Data, size_t Size) {
+ FILE * fd;
+ if (remove(name) != 0) {
+ if (errno != ENOENT) {
+ printf("failed remove, errno=%d\n", errno);
+ return -1;
+ }
+ }
+ fd = fopen(name, "wb");
+ if (fd == NULL) {
+ printf("failed open, errno=%d\n", errno);
+ return -2;
+ }
+ if (fwrite (Data, 1, Size, fd) != Size) {
+ fclose(fd);
+ return -3;
+ }
+ fclose(fd);
+ return 0;
+}
+
+void fuzz_openFile(const char * name) {
+ if (outfile != NULL) {
+ fclose(outfile);
+ }
+ outfile = fopen(name, "w");
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ pcap_t * pkts;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ const u_char *pkt;
+ struct pcap_pkthdr *header;
+ struct pcap_stat stats;
+ int r;
+
+ //initialize output file
+ if (outfile == NULL) {
+ outfile = fopen("/dev/null", "w");
+ if (outfile == NULL) {
+ return 0;
+ }
+ }
+
+ //rewrite buffer to a file as libpcap does not have buffer inputs
+ if (bufferToFile("/tmp/fuzz.pcap", Data, Size) < 0) {
+ return 0;
+ }
+
+ //initialize structure
+ pkts = pcap_open_offline("/tmp/fuzz.pcap", errbuf);
+ if (pkts == NULL) {
+ fprintf(outfile, "Couldn't open pcap file %s\n", errbuf);
+ return 0;
+ }
+
+ //loop over packets
+ r = pcap_next_ex(pkts, &header, &pkt);
+ while (r > 0) {
+ //TODO pcap_offline_filter
+ fprintf(outfile, "packet length=%d/%d\n",header->caplen, header->len);
+ r = pcap_next_ex(pkts, &header, &pkt);
+ }
+ if (pcap_stats(pkts, &stats) == 0) {
+ fprintf(outfile, "number of packets=%d\n", stats.ps_recv);
+ }
+ //close structure
+ pcap_close(pkts);
+
+ return 0;
+}
diff --git a/testprogs/fuzz/fuzz_pcap.options b/testprogs/fuzz/fuzz_pcap.options
new file mode 100644
index 000000000000..0824b19fab47
--- /dev/null
+++ b/testprogs/fuzz/fuzz_pcap.options
@@ -0,0 +1,2 @@
+[libfuzzer]
+max_len = 65535
diff --git a/testprogs/fuzz/onefile.c b/testprogs/fuzz/onefile.c
new file mode 100644
index 000000000000..690a63bdc433
--- /dev/null
+++ b/testprogs/fuzz/onefile.c
@@ -0,0 +1,54 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+void fuzz_openFile(const char * name);
+
+int main(int argc, char** argv)
+{
+ FILE * fp;
+ uint8_t *Data;
+ size_t Size;
+
+ if (argc == 3) {
+ fuzz_openFile(argv[2]);
+ } else if (argc != 2) {
+ return 1;
+ }
+ //opens the file, get its size, and reads it into a buffer
+ fp = fopen(argv[1], "rb");
+ if (fp == NULL) {
+ return 2;
+ }
+ if (fseek(fp, 0L, SEEK_END) != 0) {
+ fclose(fp);
+ return 2;
+ }
+ Size = ftell(fp);
+ if (Size == (size_t) -1) {
+ fclose(fp);
+ return 2;
+ }
+ if (fseek(fp, 0L, SEEK_SET) != 0) {
+ fclose(fp);
+ return 2;
+ }
+ Data = malloc(Size);
+ if (Data == NULL) {
+ fclose(fp);
+ return 2;
+ }
+ if (fread(Data, Size, 1, fp) != 1) {
+ fclose(fp);
+ free(Data);
+ return 2;
+ }
+
+ //launch fuzzer
+ LLVMFuzzerTestOneInput(Data, Size);
+ free(Data);
+ fclose(fp);
+ return 0;
+}
+
diff --git a/testprogs/nonblocktest.c b/testprogs/nonblocktest.c
new file mode 100644
index 000000000000..72700a3b64a1
--- /dev/null
+++ b/testprogs/nonblocktest.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "varattrs.h"
+
+/*
+ * Tests for pcap_set_nonblock / pcap_get_nonblock:
+ * - idempotency
+ * - set/get are symmetric
+ * - get returns the same before/after activate
+ * - pcap_breakloop works after setting nonblock on and then off
+ *
+ * Really this is meant to
+ * be run manually under strace, to check for extra
+ * calls to eventfd or close.
+ */
+#include <pcap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static pcap_t *pd;
+static char *program_name = "nonblocktest";
+/* Forwards */
+static void PCAP_NORETURN usage(void);
+static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2);
+static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
+
+/* VARARGS */
+static void
+error(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "%s: ", program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ exit(1);
+ /* NOTREACHED */
+}
+
+/* VARARGS */
+static void
+warning(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "%s: WARNING: ", program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "Usage: %s [ -i interface ]\n",
+ program_name);
+ exit(1);
+}
+
+static void
+breakme(u_char *user _U_, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
+{
+ warning("using pcap_breakloop()");
+ pcap_breakloop(pd);
+}
+
+int
+main(int argc, char **argv)
+{
+ int status, op, i, ret;
+ char *device;
+ pcap_if_t *devlist;
+ char ebuf[PCAP_ERRBUF_SIZE];
+
+ device = NULL;
+ while ((op = getopt(argc, argv, "i:sptnq")) != -1) {
+ switch (op) {
+
+ case 'i':
+ device = optarg;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (device == NULL) {
+ if (pcap_findalldevs(&devlist, ebuf) == -1)
+ error("%s", ebuf);
+ if (devlist == NULL)
+ error("no interfaces available for capture");
+ device = strdup(devlist->name);
+ warning("listening on %s", device);
+ pcap_freealldevs(devlist);
+ }
+ *ebuf = '\0';
+ pd = pcap_create(device, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+ else if (*ebuf)
+ warning("%s", ebuf);
+ /* set nonblock before activate */
+ if (pcap_setnonblock(pd, 1, ebuf) < 0)
+ error("pcap_setnonblock failed: %s", ebuf);
+ /* getnonblock just returns "not activated yet" */
+ ret = pcap_getnonblock(pd, ebuf);
+ if (ret != PCAP_ERROR_NOT_ACTIVATED)
+ error("pcap_getnonblock unexpectedly succeeded");
+ if ((status = pcap_activate(pd)) < 0)
+ error("pcap_activate failed");
+ ret = pcap_getnonblock(pd, ebuf);
+ if (ret != 1)
+ error( "pcap_getnonblock did not return nonblocking" );
+
+ /* Set nonblock multiple times, ensure with strace that it's a noop */
+ for (i=0; i<10; i++) {
+ if (pcap_setnonblock(pd, 1, ebuf) < 0)
+ error("pcap_setnonblock failed: %s", ebuf);
+ ret = pcap_getnonblock(pd, ebuf);
+ if (ret != 1)
+ error( "pcap_getnonblock did not return nonblocking" );
+ }
+ /* Set block multiple times, ensure with strace that it's a noop */
+ for (i=0; i<10; i++) {
+ if (pcap_setnonblock(pd, 0, ebuf) < 0)
+ error("pcap_setnonblock failed: %s", ebuf);
+ ret = pcap_getnonblock(pd, ebuf);
+ if (ret != 0)
+ error( "pcap_getnonblock did not return blocking" );
+ }
+
+ /* Now pcap_loop forever, with a callback that
+ * uses pcap_breakloop to get out of forever */
+ pcap_loop(pd, -1, breakme, NULL);
+
+ /* Now test that pcap_setnonblock fails if we can't open the
+ * eventfd. */
+ if (pcap_setnonblock(pd, 1, ebuf) < 0)
+ error("pcap_setnonblock failed: %s", ebuf);
+ while (1) {
+ ret = open("/dev/null", O_RDONLY);
+ if (ret < 0)
+ break;
+ }
+ ret = pcap_setnonblock(pd, 0, ebuf);
+ if (ret == 0)
+ error("pcap_setnonblock succeeded even though file table is full");
+ else
+ warning("pcap_setnonblock failed as expected: %s", ebuf);
+}
diff --git a/testprogs/opentest.c b/testprogs/opentest.c
index bad38eb0e9fb..a441dda1268f 100644
--- a/testprogs/opentest.c
+++ b/testprogs/opentest.c
@@ -45,7 +45,7 @@ The Regents of the University of California. All rights reserved.\n";
#include "portability.h"
#endif
-#define MAXIMUM_SNAPLEN 65535
+#define MAXIMUM_SNAPLEN 262144
static char *program_name;
@@ -81,7 +81,7 @@ main(int argc, char **argv)
switch (op) {
case 'i':
- device = optarg;
+ device = strdup(optarg);
break;
case 'I':
@@ -95,13 +95,19 @@ main(int argc, char **argv)
case 's': {
char *end;
+ long long_snaplen;
- snaplen = strtol(optarg, &end, 0);
+ long_snaplen = strtol(optarg, &end, 0);
if (optarg == end || *end != '\0'
- || snaplen < 0 || snaplen > MAXIMUM_SNAPLEN)
+ || long_snaplen < 0
+ || long_snaplen > MAXIMUM_SNAPLEN)
error("invalid snaplen %s", optarg);
- else if (snaplen == 0)
- snaplen = MAXIMUM_SNAPLEN;
+ else {
+ if (snaplen == 0)
+ snaplen = MAXIMUM_SNAPLEN;
+ else
+ snaplen = (int)long_snaplen;
+ }
break;
}
@@ -186,6 +192,7 @@ main(int argc, char **argv)
else
printf("%s opened successfully\n", device);
}
+ free(device);
pcap_close(pd);
exit(status < 0 ? 1 : 0);
}
diff --git a/testprogs/reactivatetest.c b/testprogs/reactivatetest.c
index d7f3e322c175..a9c987aa5015 100644
--- a/testprogs/reactivatetest.c
+++ b/testprogs/reactivatetest.c
@@ -51,7 +51,6 @@ main(void)
if (pd == NULL) {
error("Neither lo0 nor lo could be opened: %s",
ebuf);
- return 2;
}
}
status = pcap_activate(pd);
diff --git a/testprogs/selpolltest.c b/testprogs/selpolltest.c
index 329281dc24bc..ab7f8f462a0a 100644
--- a/testprogs/selpolltest.c
+++ b/testprogs/selpolltest.c
@@ -69,13 +69,13 @@ main(int argc, char **argv)
register int op;
bpf_u_int32 localnet, netmask;
register char *cp, *cmdbuf, *device;
- int doselect, dopoll, dotimeout, dononblock;
+ int doselect, dopoll, dotimeout, dononblock, quiet;
const char *mechanism;
struct bpf_program fcode;
char ebuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devlist;
- int selectable_fd;
- struct timeval *required_timeout;
+ int selectable_fd = -1;
+ const struct timeval *required_timeout;
int status;
int packet_count;
@@ -85,13 +85,14 @@ main(int argc, char **argv)
mechanism = NULL;
dotimeout = 0;
dononblock = 0;
+ quiet = 0;
if ((cp = strrchr(argv[0], '/')) != NULL)
program_name = cp + 1;
else
program_name = argv[0];
opterr = 0;
- while ((op = getopt(argc, argv, "i:sptn")) != -1) {
+ while ((op = getopt(argc, argv, "i:sptnq")) != -1) {
switch (op) {
case 'i':
@@ -116,6 +117,10 @@ main(int argc, char **argv)
dononblock = 1;
break;
+ case 'q':
+ quiet = 1;
+ break;
+
default:
usage();
/* NOTREACHED */
@@ -196,6 +201,7 @@ main(int argc, char **argv)
for (;;) {
fd_set setread, setexcept;
struct timeval seltimeout;
+ struct timeval *timeoutp;
FD_ZERO(&setread);
if (selectable_fd != -1) {
@@ -203,6 +209,7 @@ main(int argc, char **argv)
FD_ZERO(&setexcept);
FD_SET(selectable_fd, &setexcept);
}
+ required_timeout = pcap_get_required_select_timeout(pd);
if (dotimeout) {
seltimeout.tv_sec = 0;
if (required_timeout != NULL &&
@@ -210,37 +217,34 @@ main(int argc, char **argv)
seltimeout.tv_usec = required_timeout->tv_usec;
else
seltimeout.tv_usec = 1000;
- status = select(selectable_fd + 1, &setread,
- NULL, &setexcept, &seltimeout);
+ timeoutp = &seltimeout;
} else if (required_timeout != NULL) {
seltimeout = *required_timeout;
- status = select(selectable_fd + 1, &setread,
- NULL, &setexcept, &seltimeout);
+ timeoutp = &seltimeout;
} else {
- status = select((selectable_fd == -1) ?
- 0 : selectable_fd + 1, &setread,
- NULL, &setexcept, NULL);
+ timeoutp = NULL;
}
+ status = select((selectable_fd == -1) ?
+ 0 : selectable_fd + 1, &setread, NULL, &setexcept,
+ timeoutp);
if (status == -1) {
printf("Select returns error (%s)\n",
strerror(errno));
} else {
- if (selectable_fd == -1) {
- if (status != 0)
- printf("Select returned a descriptor\n");
- } else {
+ if (!quiet) {
if (status == 0)
printf("Select timed out: ");
- else
+ else{
printf("Select returned a descriptor: ");
- if (FD_ISSET(selectable_fd, &setread))
- printf("readable, ");
- else
- printf("not readable, ");
- if (FD_ISSET(selectable_fd, &setexcept))
- printf("exceptional condition\n");
- else
- printf("no exceptional condition\n");
+ if (FD_ISSET(selectable_fd, &setread))
+ printf("readable, ");
+ else
+ printf("not readable, ");
+ if (FD_ISSET(selectable_fd, &setexcept))
+ printf("exceptional condition\n");
+ else
+ printf("no exceptional condition\n");
+ }
}
packet_count = 0;
status = pcap_dispatch(pd, -1, countme,
@@ -268,11 +272,12 @@ main(int argc, char **argv)
fd.fd = selectable_fd;
fd.events = POLLIN;
+ required_timeout = pcap_get_required_select_timeout(pd);
if (dotimeout)
polltimeout = 1;
else if (required_timeout != NULL &&
required_timeout->tv_usec >= 1000)
- polltimeout = required_timeout->tv_usec/1000;
+ polltimeout = (int)(required_timeout->tv_usec/1000);
else
polltimeout = -1;
status = poll(&fd, (selectable_fd == -1) ? 0 : 1, polltimeout);
@@ -280,10 +285,7 @@ main(int argc, char **argv)
printf("Poll returns error (%s)\n",
strerror(errno));
} else {
- if (selectable_fd == -1) {
- if (status != 0)
- printf("Poll returned a descriptor\n");
- } else {
+ if (!quiet) {
if (status == 0)
printf("Poll timed out\n");
else {
@@ -349,7 +351,7 @@ main(int argc, char **argv)
/*
* Error. Report it.
*/
- (void)fprintf(stderr, "%s: pcap_loop: %s\n",
+ (void)fprintf(stderr, "%s: pcap_dispatch: %s\n",
program_name, pcap_geterr(pd));
}
pcap_close(pd);
@@ -367,7 +369,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
static void
usage(void)
{
- (void)fprintf(stderr, "Usage: %s [ -sptn ] [ -i interface ] [expression]\n",
+ (void)fprintf(stderr, "Usage: %s [ -sptnq ] [ -i interface ] [expression]\n",
program_name);
exit(1);
}
@@ -415,7 +417,7 @@ static char *
copy_argv(register char **argv)
{
register char **p;
- register u_int len = 0;
+ register size_t len = 0;
char *buf;
char *src, *dst;
diff --git a/testprogs/threadsignaltest.c b/testprogs/threadsignaltest.c
index a60bb49523fb..c9ade76fe5b5 100644
--- a/testprogs/threadsignaltest.c
+++ b/testprogs/threadsignaltest.c
@@ -157,7 +157,7 @@ capture_thread_func(THREAD_FUNC_ARG_TYPE arg)
} else
printf("No packets seen by pcap_dispatch\n");
}
- if (status == -2) {
+ if (status == PCAP_ERROR_BREAK) {
/*
* We got interrupted, so perhaps we didn't
* manage to finish a line we were printing.
@@ -167,11 +167,11 @@ capture_thread_func(THREAD_FUNC_ARG_TYPE arg)
printf("Loop got broken\n");
}
(void)fflush(stdout);
- if (status == -1) {
+ if (status == PCAP_ERROR) {
/*
* Error. Report it.
*/
- (void)fprintf(stderr, "%s: pcap_loop: %s\n",
+ (void)fprintf(stderr, "%s: pcap_dispatch: %s\n",
program_name, pcap_geterr(pd));
}
return 0;
@@ -182,7 +182,7 @@ main(int argc, char **argv)
{
register int op;
register char *cp, *cmdbuf, *device;
- int immediate = 0;
+ int do_wakeup = 1;
pcap_if_t *devlist;
bpf_u_int32 localnet, netmask;
struct bpf_program fcode;
@@ -200,13 +200,17 @@ main(int argc, char **argv)
program_name = argv[0];
opterr = 0;
- while ((op = getopt(argc, argv, "i:")) != -1) {
+ while ((op = getopt(argc, argv, "i:n")) != -1) {
switch (op) {
case 'i':
device = optarg;
break;
+ case 'n':
+ do_wakeup = 0;
+ break;
+
default:
usage();
/* NOTREACHED */
@@ -229,12 +233,6 @@ main(int argc, char **argv)
if (status != 0)
error("%s: pcap_set_snaplen failed: %s",
device, pcap_statustostr(status));
- if (immediate) {
- status = pcap_set_immediate_mode(pd, 1);
- if (status != 0)
- error("%s: pcap_set_immediate_mode failed: %s",
- device, pcap_statustostr(status));
- }
status = pcap_set_timeout(pd, 5*60*1000);
if (status != 0)
error("%s: pcap_set_timeout failed: %s",
@@ -280,21 +278,39 @@ main(int argc, char **argv)
error("Can't create capture thread: %s", strerror(status));
#endif
sleep_secs(60);
+ printf("Doing pcap_breakloop()\n");
pcap_breakloop(pd);
+ if (do_wakeup) {
+ /*
+ * Force a wakeup in the capture thread.
+ *
+ * On some platforms, with some devices,, pcap_breakloop()
+ * can't do that itself. On Windows, poke the device's
+ * event handle; on UN*X, send a SIGUSR1 to the thread.
+ */
+#ifdef _WIN32
+ printf("Setting event\n");
+ if (!SetEvent(pcap_getevent(pd)))
+ error("Can't set event for pcap_t: %s",
+ win32_strerror(GetLastError()));
+#else
+ printf("Sending SIGUSR1\n");
+ status = pthread_kill(capture_thread, SIGUSR1);
+ if (status != 0)
+ warning("Can't interrupt capture thread: %s",
+ strerror(status));
+#endif
+ }
+
+ /*
+ * Now wait for the capture thread to terminate.
+ */
#ifdef _WIN32
- printf("Setting event\n");
- if (!SetEvent(pcap_getevent(pd)))
- error("Can't set event for pcap_t: %s",
- win32_strerror(GetLastError()));
if (WaitForSingleObject(capture_thread, INFINITE) == WAIT_FAILED)
error("Wait for thread termination failed: %s",
win32_strerror(GetLastError()));
CloseHandle(capture_thread);
#else
- printf("Sending SIGUSR1\n");
- status = pthread_kill(capture_thread, SIGUSR1);
- if (status != 0)
- warning("Can't interrupt capture thread: %s", strerror(status));
status = pthread_join(capture_thread, &retval);
if (status != 0)
error("Wait for thread termination failed: %s",
@@ -317,7 +333,7 @@ countme(u_char *user, const struct pcap_pkthdr *h _U_, const u_char *sp _U_)
static void
usage(void)
{
- (void)fprintf(stderr, "Usage: %s [ -m ] [ -i interface ] [ -t timeout] [expression]\n",
+ (void)fprintf(stderr, "Usage: %s [ -n ] [ -i interface ] [ expression ]\n",
program_name);
exit(1);
}
@@ -365,7 +381,7 @@ static char *
copy_argv(register char **argv)
{
register char **p;
- register u_int len = 0;
+ register size_t len = 0;
char *buf;
char *src, *dst;
diff --git a/testprogs/valgrindtest.c b/testprogs/valgrindtest.c
index 104ef6a9feaa..55055ca3d9ba 100644
--- a/testprogs/valgrindtest.c
+++ b/testprogs/valgrindtest.c
@@ -59,6 +59,7 @@ The Regents of the University of California. All rights reserved.\n";
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
+#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@@ -99,6 +100,23 @@ The Regents of the University of California. All rights reserved.\n";
#endif
+/*
+ * Squelch a warning.
+ *
+ * We include system headers to be able to directly set the filter to
+ * a program with uninitialized content, to make sure what we're testing
+ * is Valgrind's checking of the system call to set the filter, and we
+ * also include <pcap.h> to open the device in the first place, and that
+ * means that we may get collisions between their definitions of
+ * BPF_STMT and BPF_JUMP - and do, in fact, get them on Linux (the
+ * definitions may be semantically the same, but that's not sufficient to
+ * avoid the warnings, as the preprocessor doesn't know that u_short is
+ * just unsigned short).
+ *
+ * So we undefine BPF_STMT and BPF_JUMP to avoid the warning.
+ */
+#undef BPF_STMT
+#undef BPF_JUMP
#include <pcap.h>
static char *program_name;
@@ -132,11 +150,21 @@ read_infile(char *fname)
if (fstat(fd, &buf) < 0)
error("can't stat %s: %s", fname, pcap_strerror(errno));
+ /*
+ * _read(), on Windows, has an unsigned int byte count and an
+ * int return value, so we can't handle a file bigger than
+ * INT_MAX - 1 bytes (and have no reason to do so; a filter *that*
+ * big will take forever to compile). (The -1 is for the '\0' at
+ * the end of the string.)
+ */
+ if (buf.st_size > INT_MAX - 1)
+ error("%s is larger than %d bytes; that's too large", fname,
+ INT_MAX - 1);
cp = malloc((u_int)buf.st_size + 1);
if (cp == NULL)
error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
fname, pcap_strerror(errno));
- cc = read(fd, cp, (u_int)buf.st_size);
+ cc = (int)read(fd, cp, (u_int)buf.st_size);
if (cc < 0)
error("read %s: %s", fname, pcap_strerror(errno));
if (cc != buf.st_size)
@@ -196,7 +224,7 @@ static char *
copy_argv(register char **argv)
{
register char **p;
- register u_int len = 0;
+ register size_t len = 0;
char *buf;
char *src, *dst;
@@ -421,7 +449,7 @@ usage(void)
(void)fprintf(stderr, "%s, with %s\n", program_name,
pcap_lib_version());
(void)fprintf(stderr,
- "Usage: %s [-aI] [ -F file ] [ -I interface ] [ expression ]\n",
+ "Usage: %s [-aI] [ -F file ] [ -i interface ] [ expression ]\n",
program_name);
exit(1);
}
diff --git a/testprogs/visopts.py b/testprogs/visopts.py
new file mode 100755
index 000000000000..97eafffff4f0
--- /dev/null
+++ b/testprogs/visopts.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+
+"""
+This program parses the output from pcap_compile() to visualize the CFG after
+each optimize phase.
+
+Usage guide:
+1. Enable optimizer debugging code when configure libpcap,
+ and build libpcap & the test programs
+ ./configure --enable-optimizer-dbg
+ make
+ make testprogs
+2. Run filtertest to compile BPF expression and produce the CFG as a
+ DOT graph, save to output a.txt
+ testprogs/filtertest -g EN10MB host 192.168.1.1 > a.txt
+3. Send a.txt to this program's standard input
+ cat a.txt | testprogs/visopts.py
+ (Graphviz must be installed)
+4. Step 2&3 can be merged:
+ testprogs/filtertest -g EN10MB host 192.168.1.1 | testprogs/visopts.py
+5. The standard output is something like this:
+ generated files under directory: /tmp/visopts-W9ekBw
+ the directory will be removed when this programs finished.
+ open this link: http://localhost:39062/expr1.html
+6. Open the URL at the 3rd line in a browser.
+
+Note:
+1. The CFG is translated to SVG images, expr1.html embeds them as external
+ documents. If you open expr1.html as local file using file:// protocol, some
+ browsers will deny such requests so the web page will not work properly.
+ For Chrome, you can run it using the following command to avoid this:
+ chromium --disable-web-security
+ That's why this program starts a localhost HTTP server.
+2. expr1.html uses jQuery from https://ajax.googleapis.com, so it needs Internet
+ access to work.
+"""
+
+import sys, os
+import string
+import subprocess
+import json
+
+html_template = string.Template("""
+<html>
+ <head>
+ <title>BPF compiler optimization phases for $expr </title>
+ <style type="text/css">
+ .hc {
+ /* half width container */
+ display: inline-block;
+ float: left;
+ width: 50%;
+ }
+ </style>
+
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"/></script>
+ <!--script type="text/javascript" src="./jquery.min.js"/></script-->
+ <script type="text/javascript">
+ var expr = '$expr';
+ var exprid = 1;
+ var gcount = $gcount;
+ var logs = JSON.parse('$logs');
+ logs[gcount] = "";
+
+ var leftsvg = null;
+ var rightsvg = null;
+
+ function gurl(index) {
+ index += 1;
+ if (index < 10)
+ s = "00" + index;
+ else if (index < 100)
+ s = "0" + index;
+ else
+ s = "" + index;
+ return "./expr" + exprid + "_g" + s + ".svg"
+ }
+
+ function annotate_svgs() {
+ if (!leftsvg || !rightsvg) return;
+
+ $$.each([$$(leftsvg), $$(rightsvg)], function() {
+ $$(this).find("[id|='block'][opacity]").each(function() {
+ $$(this).removeAttr('opacity');
+ });
+ });
+
+ $$(leftsvg).find("[id|='block']").each(function() {
+ var has = $$(rightsvg).find("#" + this.id).length != 0;
+ if (!has) $$(this).attr("opacity", "0.4");
+ else {
+ $$(this).click(function() {
+ var target = $$(rightsvg).find("#" + this.id);
+ var offset = $$("#rightsvgc").offset().top + target.position().top;
+ window.scrollTo(0, offset);
+ target.focus();
+ });
+ }
+ });
+ $$(rightsvg).find("[id|='block']").each(function() {
+ var has = $$(leftsvg).find("#" + this.id).length != 0;
+ if (!has) $$(this).attr("opacity", "0.4");
+ else {
+ $$(this).click(function() {
+ var target = $$(leftsvg).find("#" + this.id);
+ var offset = $$("#leftsvgc").offset().top + target.position().top;
+ window.scrollTo(0, offset);
+ target.focus();
+ });
+ }
+ });
+ }
+
+ function init_svgroot(svg) {
+ svg.setAttribute("width", "100%");
+ svg.setAttribute("height", "100%");
+ }
+ function wait_leftsvg() {
+ if (leftsvg) return;
+ var doc = document.getElementById("leftsvgc").getSVGDocument();
+ if (doc == null) {
+ setTimeout(wait_leftsvg, 500);
+ return;
+ }
+ leftsvg = doc.documentElement;
+ //console.log(leftsvg);
+ // initialize it
+ init_svgroot(leftsvg);
+ annotate_svgs();
+ }
+ function wait_rightsvg() {
+ if (rightsvg) return;
+ var doc = document.getElementById("rightsvgc").getSVGDocument();
+ if (doc == null) {
+ setTimeout(wait_rightsvg, 500);
+ return;
+ }
+ rightsvg = doc.documentElement;
+ //console.log(rightsvg);
+ // initialize it
+ init_svgroot(rightsvg);
+ annotate_svgs();
+ }
+ function load_left(index) {
+ var url = gurl(index);
+ var frag = "<embed id='leftsvgc' type='image/svg+xml' pluginspage='https://www.adobe.com/svg/viewer/install/' src='" + url + "'/>";
+ $$("#lsvg").html(frag);
+ $$("#lcomment").html(logs[index]);
+ $$("#lsvglink").attr("href", url);
+ leftsvg = null;
+ wait_leftsvg();
+ }
+ function load_right(index) {
+ var url = gurl(index);
+ var frag = "<embed id='rightsvgc' type='image/svg+xml' pluginspage='https://www.adobe.com/svg/viewer/install/' src='" + url + "'/>";
+ $$("#rsvg").html(frag);
+ $$("#rcomment").html(logs[index]);
+ $$("#rsvglink").attr("href", url);
+ rightsvg = null;
+ wait_rightsvg();
+ }
+
+ $$(document).ready(function() {
+ for (var i = 0; i < gcount; i++) {
+ var opt = "<option value='" + i + "'>loop" + i + " -- " + logs[i] + "</option>";
+ $$("#lselect").append(opt);
+ $$("#rselect").append(opt);
+ }
+ var on_selected = function() {
+ var index = parseInt($$(this).children("option:selected").val());
+ if (this.id == "lselect")
+ load_left(index);
+ else
+ load_right(index);
+ }
+ $$("#lselect").change(on_selected);
+ $$("#rselect").change(on_selected);
+
+ $$("#backward").click(function() {
+ var index = parseInt($$("#lselect option:selected").val());
+ if (index <= 0) return;
+ $$("#lselect").val(index - 1).change();
+ $$("#rselect").val(index).change();
+ });
+ $$("#forward").click(function() {
+ var index = parseInt($$("#rselect option:selected").val());
+ if (index >= gcount - 1) return;
+ $$("#lselect").val(index).change();
+ $$("#rselect").val(index + 1).change();
+ });
+
+ if (gcount >= 1) $$("#lselect").val(0).change();
+ if (gcount >= 2) $$("#rselect").val(1).change();
+ });
+ </script>
+ </head>
+ <body style="width: 96%">
+ <div>
+ <h1>$expr</h1>
+ <div style="text-align: center;">
+ <button id="backward" type="button">&lt;&lt;</button>
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <button id="forward" type="button">&gt;&gt;</button>
+ </div>
+ </div>
+ <br/>
+ <div style="clear: both;">
+ <div class="hc lc">
+ <select id="lselect"></select>
+ <a id="lsvglink" target="_blank">open this svg in browser</a>
+ <p id="lcomment"></p>
+ </div>
+ <div class="hc rc">
+ <select id="rselect"></select>
+ <a id="rsvglink" target="_blank">open this svg in browser</a>
+ <p id="rcomment"></p>
+ </div>
+ </div>
+ <br/>
+ <div style="clear: both;">
+ <div id="lsvg" class="hc lc"></div>
+ <div id="rsvg" class="hc rc"></div>
+ </div>
+ </body>
+</html>
+""")
+
+def write_html(expr, gcount, logs):
+ logs = map(lambda s: s.strip().replace("\n", "<br/>"), logs)
+
+ global html_template
+ html = html_template.safe_substitute(expr=expr.encode("string-escape"), gcount=gcount, logs=json.dumps(logs).encode("string-escape"))
+ with file("expr1.html", "wt") as f:
+ f.write(html)
+
+def render_on_html(infile):
+ expr = None
+ gid = 1
+ log = ""
+ dot = ""
+ indot = 0
+ logs = []
+
+ for line in infile:
+ if line.startswith("machine codes for filter:"):
+ expr = line[len("machine codes for filter:"):].strip()
+ break
+ elif line.startswith("digraph BPF {"):
+ indot = 1
+ dot = line
+ elif indot:
+ dot += line
+ if line.startswith("}"):
+ indot = 2
+ else:
+ log += line
+
+ if indot == 2:
+ try:
+ p = subprocess.Popen(['dot', '-Tsvg'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ except OSError as ose:
+ print "Failed to run 'dot':", ose
+ print "(Is Graphviz installed?)"
+ exit(1)
+
+ svg = p.communicate(dot)[0]
+ with file("expr1_g%03d.svg" % (gid), "wt") as f:
+ f.write(svg)
+
+ logs.append(log)
+ gid += 1
+ log = ""
+ dot = ""
+ indot = 0
+
+ if indot != 0:
+ #unterminated dot graph for expression
+ return False
+ if expr is None:
+ # BPF parser encounter error(s)
+ return False
+ write_html(expr, gid - 1, logs)
+ return True
+
+def run_httpd():
+ import SimpleHTTPServer
+ import SocketServer
+
+ class MySocketServer(SocketServer.TCPServer):
+ allow_reuse_address = True
+ Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
+ httpd = MySocketServer(("localhost", 0), Handler)
+ print "open this link: http://localhost:%d/expr1.html" % (httpd.server_address[1])
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt as e:
+ pass
+
+def main():
+ import tempfile
+ import atexit
+ import shutil
+ os.chdir(tempfile.mkdtemp(prefix="visopts-"))
+ atexit.register(shutil.rmtree, os.getcwd())
+ print "generated files under directory: %s" % os.getcwd()
+ print " the directory will be removed when this program has finished."
+
+ if not render_on_html(sys.stdin):
+ return 1
+ run_httpd()
+ return 0
+
+if __name__ == "__main__":
+ if '-h' in sys.argv or '--help' in sys.argv:
+ print __doc__
+ exit(0)
+ exit(main())
diff --git a/testprogs/writecaptest.c b/testprogs/writecaptest.c
new file mode 100644
index 000000000000..4db532c6e621
--- /dev/null
+++ b/testprogs/writecaptest.c
@@ -0,0 +1,556 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "varattrs.h"
+
+#ifndef lint
+static const char copyright[] _U_ =
+ "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
+The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+#ifdef _WIN32
+ #include "getopt.h"
+#else
+ #include <unistd.h>
+#endif
+#include <errno.h>
+#ifndef _WIN32
+ #include <signal.h>
+#endif
+#include <sys/types.h>
+
+#include <pcap.h>
+
+#include "pcap/funcattrs.h"
+
+#ifdef _WIN32
+ #include "portability.h"
+#endif
+
+static char *program_name;
+
+/* Forwards */
+static void PCAP_NORETURN usage(void);
+static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2);
+static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
+static char *copy_argv(char **);
+
+static pcap_t *pd;
+
+#ifdef _WIN32
+static BOOL WINAPI
+stop_capture(DWORD ctrltype _U_)
+{
+ pcap_breakloop(pd);
+ return TRUE;
+}
+#else
+static void
+stop_capture(int signum _U_)
+{
+ pcap_breakloop(pd);
+}
+#endif
+
+static long
+parse_interface_number(const char *device)
+{
+ const char *p;
+ long devnum;
+ char *end;
+
+ /*
+ * Search for a colon, terminating any scheme at the beginning
+ * of the device.
+ */
+ p = strchr(device, ':');
+ if (p != NULL) {
+ /*
+ * We found it. Is it followed by "//"?
+ */
+ p++; /* skip the : */
+ if (strncmp(p, "//", 2) == 0) {
+ /*
+ * Yes. Search for the next /, at the end of the
+ * authority part of the URL.
+ */
+ p += 2; /* skip the // */
+ p = strchr(p, '/');
+ if (p != NULL) {
+ /*
+ * OK, past the / is the path.
+ */
+ device = p + 1;
+ }
+ }
+ }
+ devnum = strtol(device, &end, 10);
+ if (device != end && *end == '\0') {
+ /*
+ * It's all-numeric, but is it a valid number?
+ */
+ if (devnum <= 0) {
+ /*
+ * No, it's not an ordinal.
+ */
+ error("Invalid adapter index");
+ }
+ return (devnum);
+ } else {
+ /*
+ * It's not all-numeric; return -1, so our caller
+ * knows that.
+ */
+ return (-1);
+ }
+}
+
+static char *
+find_interface_by_number(long devnum)
+{
+ pcap_if_t *dev, *devlist;
+ long i;
+ char ebuf[PCAP_ERRBUF_SIZE];
+ char *device;
+ int status;
+
+ status = pcap_findalldevs(&devlist, ebuf);
+ if (status < 0)
+ error("%s", ebuf);
+ /*
+ * Look for the devnum-th entry in the list of devices (1-based).
+ */
+ for (i = 0, dev = devlist; i < devnum-1 && dev != NULL;
+ i++, dev = dev->next)
+ ;
+ if (dev == NULL)
+ error("Invalid adapter index");
+ device = strdup(dev->name);
+ pcap_freealldevs(devlist);
+ return (device);
+}
+
+static pcap_t *
+open_interface(const char *device, int snaplen_set, int snaplen, char *ebuf)
+{
+ pcap_t *pc;
+ int status;
+ char *cp;
+
+ pc = pcap_create(device, ebuf);
+ if (pc == NULL) {
+ /*
+ * If this failed with "No such device", that means
+ * the interface doesn't exist; return NULL, so that
+ * the caller can see whether the device name is
+ * actually an interface index.
+ */
+ if (strstr(ebuf, "No such device") != NULL)
+ return (NULL);
+ error("%s", ebuf);
+ }
+ if (snaplen_set) {
+ status = pcap_set_snaplen(pc, snaplen);
+ if (status != 0)
+ error("%s: pcap_set_snaplen failed: %s",
+ device, pcap_statustostr(status));
+ }
+ status = pcap_set_timeout(pc, 100);
+ if (status != 0)
+ error("%s: pcap_set_timeout failed: %s",
+ device, pcap_statustostr(status));
+ status = pcap_activate(pc);
+ if (status < 0) {
+ /*
+ * pcap_activate() failed.
+ */
+ cp = pcap_geterr(pc);
+ if (status == PCAP_ERROR)
+ error("%s", cp);
+ else if (status == PCAP_ERROR_NO_SUCH_DEVICE) {
+ /*
+ * Return an error for our caller to handle.
+ */
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)",
+ device, pcap_statustostr(status), cp);
+ } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0')
+ error("%s: %s\n(%s)", device,
+ pcap_statustostr(status), cp);
+ else
+ error("%s: %s", device,
+ pcap_statustostr(status));
+ pcap_close(pc);
+ return (NULL);
+ } else if (status > 0) {
+ /*
+ * pcap_activate() succeeded, but it's warning us
+ * of a problem it had.
+ */
+ cp = pcap_geterr(pc);
+ if (status == PCAP_WARNING)
+ warning("%s", cp);
+ else if (status == PCAP_WARNING_PROMISC_NOTSUP &&
+ *cp != '\0')
+ warning("%s: %s\n(%s)", device,
+ pcap_statustostr(status), cp);
+ else
+ warning("%s: %s", device,
+ pcap_statustostr(status));
+ }
+ return (pc);
+}
+
+#define COMMAND_OPTIONS "DLi:s:w:y:"
+
+int
+main(int argc, char **argv)
+{
+ int op;
+ char *cp, *cmdbuf = NULL, *device, *end, *savefile = NULL;
+ int snaplen = 0;
+ int snaplen_set = 0;
+ pcap_if_t *devlist;
+ long devnum;
+ int show_interfaces = 0;
+ int show_dlt_types = 0;
+ int ndlts;
+ int *dlts;
+ bpf_u_int32 localnet, netmask;
+ struct bpf_program fcode;
+ char ebuf[PCAP_ERRBUF_SIZE];
+#ifndef _WIN32
+ struct sigaction action;
+#endif
+ int dlt;
+ const char *dlt_name = NULL;
+ int status;
+ pcap_dumper_t *pdd;
+
+ device = NULL;
+ if ((cp = strrchr(argv[0], '/')) != NULL)
+ program_name = cp + 1;
+ else
+ program_name = argv[0];
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) {
+ switch (op) {
+
+ case 'D':
+ show_interfaces = 1;
+ break;
+
+ case 'L':
+ show_dlt_types = 1;
+ break;
+
+ case 'i':
+ device = optarg;
+ break;
+
+ case 's':
+ snaplen = (int)strtol(optarg, &end, 0);
+ if (optarg == end || *end != '\0' || snaplen < 0)
+ error("invalid snaplen %s (must be >= 0)",
+ optarg);
+ snaplen_set = 1;
+ break;
+
+ case 'w':
+ savefile = optarg;
+ break;
+
+ case 'y':
+ dlt_name = optarg;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (show_interfaces) {
+ pcap_if_t *dev;
+ int i;
+
+ if (pcap_findalldevs(&devlist, ebuf) < 0)
+ error("%s", ebuf);
+ for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
+ printf("%d.%s", i+1, dev->name);
+ if (dev->description != NULL)
+ printf(" (%s)", dev->description);
+ printf("\n");
+ }
+ pcap_freealldevs(devlist);
+ return (0);
+ }
+
+ if (device == NULL) {
+ if (pcap_findalldevs(&devlist, ebuf) == -1)
+ error("%s", ebuf);
+ if (devlist == NULL)
+ error("no interfaces available for capture");
+ device = strdup(devlist->name);
+ pcap_freealldevs(devlist);
+ }
+ if (show_dlt_types) {
+ pd = pcap_create(device, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+ status = pcap_activate(pd);
+ if (status < 0) {
+ /*
+ * pcap_activate() failed.
+ */
+ error("%s: %s\n(%s)", device,
+ pcap_statustostr(status), pcap_geterr(pd));
+ }
+ ndlts = pcap_list_datalinks(pd, &dlts);
+ if (ndlts < 0) {
+ /*
+ * pcap_list_datalinks() failed.
+ */
+ error("%s: %s\n(%s)", device,
+ pcap_statustostr(status), pcap_geterr(pd));
+ }
+ for (int i = 0; i < ndlts; i++) {
+ dlt_name = pcap_datalink_val_to_name(dlts[i]);
+ if (dlt_name == NULL)
+ printf("DLT %d", dlts[i]);
+ else
+ printf("%s", dlt_name);
+ printf("\n");
+ }
+ pcap_free_datalinks(dlts);
+ pcap_close(pd);
+ return 0;
+ }
+
+ if (savefile == NULL)
+ error("no savefile specified");
+
+ *ebuf = '\0';
+
+ pd = open_interface(device, snaplen_set, snaplen, ebuf);
+ if (pd == NULL) {
+ /*
+ * That failed because the interface couldn't be found.
+ *
+ * If we can get a list of interfaces, and the interface name
+ * is purely numeric, try to use it as a 1-based index
+ * in the list of interfaces.
+ */
+ devnum = parse_interface_number(device);
+ if (devnum == -1) {
+ /*
+ * It's not a number; just report
+ * the open error and fail.
+ */
+ error("%s", ebuf);
+ }
+
+ /*
+ * OK, it's a number; try to find the
+ * interface with that index, and try
+ * to open it.
+ *
+ * find_interface_by_number() exits if it
+ * couldn't be found.
+ */
+ device = find_interface_by_number(devnum);
+ pd = open_interface(device, snaplen_set, snaplen, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+ }
+
+ if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
+ localnet = 0;
+ netmask = 0;
+ warning("%s", ebuf);
+ }
+
+ if (dlt_name != NULL) {
+ dlt = pcap_datalink_name_to_val(dlt_name);
+ if (dlt == PCAP_ERROR)
+ error("%s isn't a valid DLT name", dlt_name);
+ if (pcap_set_datalink(pd, dlt) == PCAP_ERROR)
+ error("%s: %s", device, pcap_geterr(pd));
+ }
+
+ /*
+ * Don't set a filter unless we were given one on the
+ * command line; if capturing doesn't work, or doesn't
+ * use the snapshot length, without a filter, that's
+ * a bug.
+ */
+ if (optind < argc) {
+ cmdbuf = copy_argv(&argv[optind]);
+
+ if (pcap_compile(pd, &fcode, cmdbuf, 1, netmask) < 0)
+ error("%s", pcap_geterr(pd));
+
+ if (pcap_setfilter(pd, &fcode) < 0)
+ error("%s", pcap_geterr(pd));
+ }
+
+ pdd = pcap_dump_open(pd, savefile);
+ if (pdd == NULL)
+ error("%s", pcap_geterr(pd));
+
+#ifdef _WIN32
+ SetConsoleCtrlHandler(stop_capture, TRUE);
+#else
+ action.sa_handler = stop_capture;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = 0;
+ if (sigaction(SIGINT, &action, NULL) == -1)
+ error("Can't catch SIGINT: %s\n", strerror(errno));
+#endif
+
+ printf("Listening on %s, link-type ", device);
+ dlt = pcap_datalink(pd);
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ if (dlt_name == NULL)
+ printf("DLT %d", dlt);
+ else
+ printf("%s", dlt_name);
+ printf("\n");
+ for (;;) {
+ status = pcap_dispatch(pd, -1, pcap_dump, (u_char *)pdd);
+ if (status < 0)
+ break;
+ if (status != 0) {
+ printf("%d packets seen\n", status);
+ struct pcap_stat ps;
+ pcap_stats(pd, &ps);
+ printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n",
+ ps.ps_recv, ps.ps_drop, ps.ps_ifdrop);
+ }
+ }
+ if (status == -2) {
+ /*
+ * We got interrupted, so perhaps we didn't
+ * manage to finish a line we were printing.
+ * Print an extra newline, just in case.
+ */
+ putchar('\n');
+ printf("Broken out of loop from SIGINT handler\n");
+ }
+ (void)fflush(stdout);
+ if (status == -1) {
+ /*
+ * Error. Report it.
+ */
+ (void)fprintf(stderr, "%s: pcap_dispatch: %s\n",
+ program_name, pcap_geterr(pd));
+ }
+ pcap_close(pd);
+ if (cmdbuf != NULL) {
+ pcap_freecode(&fcode);
+ free(cmdbuf);
+ }
+ exit(status == -1 ? 1 : 0);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "Usage: %s -D -L [ -i interface ] [ -s snaplen ] [ -w file ] [ -y dlt ] [expression]\n",
+ program_name);
+ exit(1);
+}
+
+/* VARARGS */
+static void
+error(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "%s: ", program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ exit(1);
+ /* NOTREACHED */
+}
+
+/* VARARGS */
+static void
+warning(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "%s: WARNING: ", program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+}
+
+/*
+ * Copy arg vector into a new buffer, concatenating arguments with spaces.
+ */
+static char *
+copy_argv(register char **argv)
+{
+ register char **p;
+ register size_t len = 0;
+ char *buf;
+ char *src, *dst;
+
+ p = argv;
+ if (*p == 0)
+ return 0;
+
+ while (*p)
+ len += strlen(*p++) + 1;
+
+ buf = (char *)malloc(len);
+ if (buf == NULL)
+ error("copy_argv: malloc");
+
+ p = argv;
+ dst = buf;
+ while ((src = *p++) != NULL) {
+ while ((*dst++ = *src++) != '\0')
+ ;
+ dst[-1] = ' ';
+ }
+ dst[-1] = '\0';
+
+ return buf;
+}