diff options
author | Haibo Huang <hhb@google.com> | 2018-07-23 17:26:52 -0700 |
---|---|---|
committer | Haibo Huang <hhb@google.com> | 2018-07-24 09:44:25 -0700 |
commit | 165065aed040e9658cb43fc1194d6799c9227043 (patch) | |
tree | 8b959bf9d2b44f2b12c86dfdc5810210d587e99e /pcap.c | |
parent | ba51ee392e9b2d5dd04faaf695b1da82488d0323 (diff) | |
download | libpcap-165065aed040e9658cb43fc1194d6799c9227043.tar.gz |
Update libpcap to 1.9.0
Test: compile
Change-Id: I5b036bc4544b3150629b943d3a7896d7dc03fec9
Diffstat (limited to 'pcap.c')
-rw-r--r-- | pcap.c | 2390 |
1 files changed, 2009 insertions, 381 deletions
@@ -32,23 +32,28 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include <config.h> #endif -#ifdef _WIN32 -#include <pcap-stdinc.h> -#else /* _WIN32 */ -#if HAVE_INTTYPES_H -#include <inttypes.h> -#elif HAVE_STDINT_H -#include <stdint.h> +#include <pcap-types.h> +#ifndef _WIN32 +#include <sys/param.h> +#ifndef MSDOS +#include <sys/file.h> #endif -#ifdef HAVE_SYS_BITYPES_H -#include <sys/bitypes.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> #endif -#include <sys/types.h> + +struct mbuf; /* Squelch compiler warnings on some platforms for */ +struct rtentry; /* declarations in <net/if.h> */ +#include <net/if.h> +#include <netinet/in.h> #endif /* _WIN32 */ +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -57,6 +62,11 @@ #endif #include <fcntl.h> #include <errno.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#else +#define INT_MAX 2147483647 +#endif #ifdef HAVE_OS_PROTO_H #include "os-proto.h" @@ -68,6 +78,8 @@ #include "pcap-int.h" +#include "optimize.h" + #ifdef HAVE_DAG_API #include "pcap-dag.h" #endif /* HAVE_DAG_API */ @@ -100,13 +112,89 @@ #include "pcap-netfilter-linux.h" #endif +#ifdef PCAP_SUPPORT_NETMAP +#include "pcap-netmap.h" +#endif + #ifdef PCAP_SUPPORT_DBUS #include "pcap-dbus.h" #endif +#ifdef PCAP_SUPPORT_RDMASNIFF +#include "pcap-rdmasniff.h" +#endif + +#ifdef _WIN32 +/* + * DllMain(), required when built as a Windows DLL. + */ +BOOL WINAPI DllMain( + HANDLE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved +) +{ + return (TRUE); +} + +/* + * Start WinSock. + * Exported in case some applications using WinPcap called it, + * even though it wasn't exported. + */ +int +wsockinit(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + static int err = -1; + static int done = 0; + + if (done) + return (err); + + wVersionRequested = MAKEWORD( 1, 1); + err = WSAStartup( wVersionRequested, &wsaData ); + atexit ((void(*)(void))WSACleanup); + done = 1; + + if ( err != 0 ) + err = -1; + return (err); +} + +/* + * This is the exported function; new programs should call this. + */ +int +pcap_wsockinit(void) +{ + return (wsockinit()); +} +#endif /* _WIN32 */ + +/* + * String containing the library version. + * Not explicitly exported via a header file - the right API to use + * is pcap_lib_version() - but some programs included it, so we + * provide it. + * + * We declare it here, right before defining it, to squelch any + * warnings we might get from compilers about the lack of a + * declaration. + */ +PCAP_API char pcap_version[]; +PCAP_API_DEF char pcap_version[] = PACKAGE_VERSION; + static int pcap_not_initialized(pcap_t *pcap) { + if (pcap->activated) { + /* A module probably forgot to set the function pointer */ + (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "This operation isn't properly handled by that device"); + return (PCAP_ERROR); + } /* in case the caller doesn't check for PCAP_ERROR_NOT_ACTIVATED */ (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); @@ -118,6 +206,12 @@ pcap_not_initialized(pcap_t *pcap) static void * pcap_not_initialized_ptr(pcap_t *pcap) { + if (pcap->activated) { + /* A module probably forgot to set the function pointer */ + (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "This operation isn't properly handled by that device"); + return (NULL); + } (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); return (NULL); @@ -126,6 +220,12 @@ pcap_not_initialized_ptr(pcap_t *pcap) static HANDLE pcap_getevent_not_initialized(pcap_t *pcap) { + if (pcap->activated) { + /* A module probably forgot to set the function pointer */ + (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "This operation isn't properly handled by that device"); + return (INVALID_HANDLE_VALUE); + } (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); return (INVALID_HANDLE_VALUE); @@ -134,6 +234,12 @@ pcap_getevent_not_initialized(pcap_t *pcap) static u_int pcap_sendqueue_transmit_not_initialized(pcap_t *pcap, pcap_send_queue* queue, int sync) { + if (pcap->activated) { + /* A module probably forgot to set the function pointer */ + (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "This operation isn't properly handled by that device"); + return (0); + } (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); return (0); @@ -142,6 +248,12 @@ pcap_sendqueue_transmit_not_initialized(pcap_t *pcap, pcap_send_queue* queue, in static PAirpcapHandle pcap_get_airpcap_handle_not_initialized(pcap_t *pcap) { + if (pcap->activated) { + /* A module probably forgot to set the function pointer */ + (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "This operation isn't properly handled by that device"); + return (NULL); + } (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); return (NULL); @@ -190,8 +302,8 @@ pcap_list_tstamp_types(pcap_t *p, int **tstamp_typesp) *tstamp_typesp = (int*)calloc(sizeof(**tstamp_typesp), p->tstamp_type_count); if (*tstamp_typesp == NULL) { - (void)pcap_snprintf(p->errbuf, sizeof(p->errbuf), - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "malloc"); return (PCAP_ERROR); } (void)memcpy(*tstamp_typesp, p->tstamp_type_list, @@ -295,8 +407,15 @@ pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, return (p->read_op(p, 1, p->oneshot_callback, (u_char *)&s)); } +/* + * Implementation of a pcap_if_list_t. + */ +struct pcap_if_list { + pcap_if_t *beginning; +}; + static struct capture_source_type { - int (*findalldevs_op)(pcap_if_t **, char *); + int (*findalldevs_op)(pcap_if_list_t *, char *); pcap_t *(*create_op)(const char *, char *, int *); } capture_source_types[] = { #ifdef HAVE_DAG_API @@ -323,9 +442,15 @@ static struct capture_source_type { #ifdef PCAP_SUPPORT_NETFILTER { netfilter_findalldevs, netfilter_create }, #endif +#ifdef PCAP_SUPPORT_NETMAP + { pcap_netmap_findalldevs, pcap_netmap_create }, +#endif #ifdef PCAP_SUPPORT_DBUS { dbus_findalldevs, dbus_create }, #endif +#ifdef PCAP_SUPPORT_RDMASNIFF + { rdmasniff_findalldevs, rdmasniff_create }, +#endif { NULL, NULL } }; @@ -339,34 +464,1502 @@ int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf) { size_t i; + pcap_if_list_t devlist; /* * Find all the local network interfaces on which we * can capture. */ - if (pcap_platform_finddevs(alldevsp, errbuf) == -1) + devlist.beginning = NULL; + if (pcap_platform_finddevs(&devlist, errbuf) == -1) { + /* + * Failed - free all of the entries we were given + * before we failed. + */ + if (devlist.beginning != NULL) + pcap_freealldevs(devlist.beginning); + *alldevsp = NULL; return (-1); + } /* * Ask each of the non-local-network-interface capture * source types what interfaces they have. */ for (i = 0; capture_source_types[i].findalldevs_op != NULL; i++) { - if (capture_source_types[i].findalldevs_op(alldevsp, errbuf) == -1) { + if (capture_source_types[i].findalldevs_op(&devlist, errbuf) == -1) { /* * We had an error; free the list we've been * constructing. */ - if (*alldevsp != NULL) { - pcap_freealldevs(*alldevsp); - *alldevsp = NULL; + if (devlist.beginning != NULL) + pcap_freealldevs(devlist.beginning); + *alldevsp = NULL; + return (-1); + } + } + + /* + * Return the first entry of the list of all devices. + */ + *alldevsp = devlist.beginning; + return (0); +} + +static struct sockaddr * +dup_sockaddr(struct sockaddr *sa, size_t sa_length) +{ + struct sockaddr *newsa; + + if ((newsa = malloc(sa_length)) == NULL) + return (NULL); + return (memcpy(newsa, sa, sa_length)); +} + +/* + * Construct a "figure of merit" for an interface, for use when sorting + * the list of interfaces, in which interfaces that are up are superior + * to interfaces that aren't up, interfaces that are up and running are + * superior to interfaces that are up but not running, and non-loopback + * interfaces that are up and running are superior to loopback interfaces, + * and interfaces with the same flags have a figure of merit that's higher + * the lower the instance number. + * + * The goal is to try to put the interfaces most likely to be useful for + * capture at the beginning of the list. + * + * The figure of merit, which is lower the "better" the interface is, + * has the uppermost bit set if the interface isn't running, the bit + * below that set if the interface isn't up, the bit below that set + * if the interface is a loopback interface, and the interface index + * in the 29 bits below that. (Yes, we assume u_int is 32 bits.) + */ +static u_int +get_figure_of_merit(pcap_if_t *dev) +{ + const char *cp; + u_int n; + + if (strcmp(dev->name, "any") == 0) { + /* + * Give the "any" device an artificially high instance + * number, so it shows up after all other non-loopback + * interfaces. + */ + n = 0x1FFFFFFF; /* 29 all-1 bits */ + } else { + /* + * A number at the end of the device name string is + * assumed to be an instance number. Add 1 to the + * instance number, and use 0 for "no instance + * number", so we don't put "no instance number" + * devices and "instance 0" devices together. + */ + cp = dev->name + strlen(dev->name) - 1; + while (cp-1 >= dev->name && *(cp-1) >= '0' && *(cp-1) <= '9') + cp--; + if (*cp >= '0' && *cp <= '9') + n = atoi(cp) + 1; + else + n = 0; + } + if (!(dev->flags & PCAP_IF_RUNNING)) + n |= 0x80000000; + if (!(dev->flags & PCAP_IF_UP)) + n |= 0x40000000; + + /* + * Give non-wireless interfaces that aren't disconnected a better + * figure of merit than interfaces that are disconnected, as + * "disconnected" should indicate that the interface isn't + * plugged into a network and thus won't give you any traffic. + * + * For wireless interfaces, it means "associated with a network", + * which we presume not to necessarily prevent capture, as you + * might run the adapter in some flavor of monitor mode. + */ + if (!(dev->flags & PCAP_IF_WIRELESS) && + (dev->flags & PCAP_IF_CONNECTION_STATUS) == PCAP_IF_CONNECTION_STATUS_DISCONNECTED) + n |= 0x20000000; + + /* + * Sort loopback devices after non-loopback devices, *except* for + * disconnected devices. + */ + if (dev->flags & PCAP_IF_LOOPBACK) + n |= 0x10000000; + + return (n); +} + +#ifndef _WIN32 +/* + * Try to get a description for a given device. + * Returns a mallocated description if it could and NULL if it couldn't. + * + * XXX - on FreeBSDs that support it, should it get the sysctl named + * "dev.{adapter family name}.{adapter unit}.%desc" to get a description + * of the adapter? Note that "dev.an.0.%desc" is "Aironet PC4500/PC4800" + * with my Cisco 350 card, so the name isn't entirely descriptive. The + * "dev.an.0.%pnpinfo" has a better description, although one might argue + * that the problem is really a driver bug - if it can find out that it's + * a Cisco 340 or 350, rather than an old Aironet card, it should use + * that in the description. + * + * Do NetBSD, DragonflyBSD, or OpenBSD support this as well? FreeBSD + * and OpenBSD let you get a description, but it's not generated by the OS, + * it's set with another ioctl that ifconfig supports; we use that to get + * a description in FreeBSD and OpenBSD, but if there is no such + * description available, it still might be nice to get some description + * string based on the device type or something such as that. + * + * In macOS, the System Configuration framework can apparently return + * names in 10.4 and later. + * + * It also appears that freedesktop.org's HAL offers an "info.product" + * string, but the HAL specification says it "should not be used in any + * UI" and "subsystem/capability specific properties" should be used + * instead and, in any case, I think HAL is being deprecated in + * favor of other stuff such as DeviceKit. DeviceKit doesn't appear + * to have any obvious product information for devices, but maybe + * I haven't looked hard enough. + * + * Using the System Configuration framework, or HAL, or DeviceKit, or + * whatever, would require that libpcap applications be linked with + * the frameworks/libraries in question. That shouldn't be a problem + * for programs linking with the shared version of libpcap (unless + * you're running on AIX - which I think is the only UN*X that doesn't + * support linking a shared library with other libraries on which it + * depends, and having an executable linked only with the first shared + * library automatically pick up the other libraries when started - + * and using HAL or whatever). Programs linked with the static + * version of libpcap would have to use pcap-config with the --static + * flag in order to get the right linker flags in order to pick up + * the additional libraries/frameworks; those programs need that anyway + * for libpcap 1.1 and beyond on Linux, as, by default, it requires + * -lnl. + * + * Do any other UN*Xes, or desktop environments support getting a + * description? + */ +static char * +#ifdef SIOCGIFDESCR +get_if_description(const char *name) +{ + char *description = NULL; + int s; + struct ifreq ifrdesc; +#ifndef IFDESCRSIZE + size_t descrlen = 64; +#else + size_t descrlen = IFDESCRSIZE; +#endif /* IFDESCRSIZE */ + + /* + * Get the description for the interface. + */ + memset(&ifrdesc, 0, sizeof ifrdesc); + strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name); + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s >= 0) { +#ifdef __FreeBSD__ + /* + * On FreeBSD, if the buffer isn't big enough for the + * description, the ioctl succeeds, but the description + * isn't copied, ifr_buffer.length is set to the description + * length, and ifr_buffer.buffer is set to NULL. + */ + for (;;) { + free(description); + if ((description = malloc(descrlen)) != NULL) { + ifrdesc.ifr_buffer.buffer = description; + ifrdesc.ifr_buffer.length = descrlen; + if (ioctl(s, SIOCGIFDESCR, &ifrdesc) == 0) { + if (ifrdesc.ifr_buffer.buffer == + description) + break; + else + descrlen = ifrdesc.ifr_buffer.length; + } else { + /* + * Failed to get interface description. + */ + free(description); + description = NULL; + break; + } + } else + break; + } +#else /* __FreeBSD__ */ + /* + * The only other OS that currently supports + * SIOCGIFDESCR is OpenBSD, and it has no way + * to get the description length - it's clamped + * to a maximum of IFDESCRSIZE. + */ + if ((description = malloc(descrlen)) != NULL) { + ifrdesc.ifr_data = (caddr_t)description; + if (ioctl(s, SIOCGIFDESCR, &ifrdesc) != 0) { + /* + * Failed to get interface description. + */ + free(description); + description = NULL; + } + } +#endif /* __FreeBSD__ */ + close(s); + if (description != NULL && strlen(description) == 0) { + /* + * Description is empty, so discard it. + */ + free(description); + description = NULL; + } + } + +#ifdef __FreeBSD__ + /* + * For FreeBSD, if we didn't get a description, and this is + * a device with a name of the form usbusN, label it as a USB + * bus. + */ + if (description == NULL) { + if (strncmp(name, "usbus", 5) == 0) { + /* + * OK, it begins with "usbus". + */ + long busnum; + char *p; + + errno = 0; + busnum = strtol(name + 5, &p, 10); + if (errno == 0 && p != name + 5 && *p == '\0' && + busnum >= 0 && busnum <= INT_MAX) { + /* + * OK, it's a valid number that's not + * bigger than INT_MAX. Construct + * a description from it. + */ + static const char descr_prefix[] = "USB bus number "; + size_t descr_size; + + /* + * Allow enough room for a 32-bit bus number. + * sizeof (descr_prefix) includes the + * terminating NUL. + */ + descr_size = sizeof (descr_prefix) + 10; + description = malloc(descr_size); + if (description != NULL) { + pcap_snprintf(description, descr_size, + "%s%ld", descr_prefix, busnum); + } + } + } + } +#endif + return (description); +#else /* SIOCGIFDESCR */ +get_if_description(const char *name _U_) +{ + return (NULL); +#endif /* SIOCGIFDESCR */ +} + +/* + * Look for a given device in the specified list of devices. + * + * If we find it, return a pointer to its entry. + * + * If we don't find it, attempt to add an entry for it, with the specified + * IFF_ flags and description, and, if that succeeds, return a pointer to + * the new entry, otherwise return NULL and set errbuf to an error message. + */ +pcap_if_t * +find_or_add_if(pcap_if_list_t *devlistp, const char *name, + bpf_u_int32 if_flags, get_if_flags_func get_flags_func, char *errbuf) +{ + bpf_u_int32 pcap_flags; + + /* + * Convert IFF_ flags to pcap flags. + */ + pcap_flags = 0; +#ifdef IFF_LOOPBACK + if (if_flags & IFF_LOOPBACK) + pcap_flags |= PCAP_IF_LOOPBACK; +#else + /* + * We don't have IFF_LOOPBACK, so look at the device name to + * see if it looks like a loopback device. + */ + if (name[0] == 'l' && name[1] == 'o' && + (isdigit((unsigned char)(name[2])) || name[2] == '\0') + pcap_flags |= PCAP_IF_LOOPBACK; +#endif +#ifdef IFF_UP + if (if_flags & IFF_UP) + pcap_flags |= PCAP_IF_UP; +#endif +#ifdef IFF_RUNNING + if (if_flags & IFF_RUNNING) + pcap_flags |= PCAP_IF_RUNNING; +#endif + + /* + * Attempt to find an entry for this device; if we don't find one, + * attempt to add one. + */ + return (find_or_add_dev(devlistp, name, pcap_flags, + get_flags_func, get_if_description(name), errbuf)); +} + +/* + * Look for a given device in the specified list of devices. + * + * If we find it, then, if the specified address isn't null, add it to + * the list of addresses for the device and return 0. + * + * If we don't find it, attempt to add an entry for it, with the specified + * IFF_ flags and description, and, if that succeeds, add the specified + * address to its list of addresses if that address is non-null, and + * return 0, otherwise return -1 and set errbuf to an error message. + * + * (We can get called with a null address because we might get a list + * of interface name/address combinations from the underlying OS, with + * the address being absent in some cases, rather than a list of + * interfaces with each interface having a list of addresses, so this + * call may be the only call made to add to the list, and we want to + * add interfaces even if they have no addresses.) + */ +int +add_addr_to_if(pcap_if_list_t *devlistp, const char *name, + bpf_u_int32 if_flags, get_if_flags_func get_flags_func, + struct sockaddr *addr, size_t addr_size, + struct sockaddr *netmask, size_t netmask_size, + struct sockaddr *broadaddr, size_t broadaddr_size, + struct sockaddr *dstaddr, size_t dstaddr_size, + char *errbuf) +{ + pcap_if_t *curdev; + + /* + * Check whether the device exists and, if not, add it. + */ + curdev = find_or_add_if(devlistp, name, if_flags, get_flags_func, + errbuf); + if (curdev == NULL) { + /* + * Error - give up. + */ + return (-1); + } + + if (addr == NULL) { + /* + * There's no address to add; this entry just meant + * "here's a new interface". + */ + return (0); + } + + /* + * "curdev" is an entry for this interface, and we have an + * address for it; add an entry for that address to the + * interface's list of addresses. + */ + return (add_addr_to_dev(curdev, addr, addr_size, netmask, + netmask_size, broadaddr, broadaddr_size, dstaddr, + dstaddr_size, errbuf)); +} +#endif /* _WIN32 */ + +/* + * Add an entry to the list of addresses for an interface. + * "curdev" is the entry for that interface. + */ +int +add_addr_to_dev(pcap_if_t *curdev, + struct sockaddr *addr, size_t addr_size, + struct sockaddr *netmask, size_t netmask_size, + struct sockaddr *broadaddr, size_t broadaddr_size, + struct sockaddr *dstaddr, size_t dstaddr_size, + char *errbuf) +{ + pcap_addr_t *curaddr, *prevaddr, *nextaddr; + + /* + * Allocate the new entry and fill it in. + */ + curaddr = (pcap_addr_t *)malloc(sizeof(pcap_addr_t)); + if (curaddr == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + + curaddr->next = NULL; + if (addr != NULL && addr_size != 0) { + curaddr->addr = (struct sockaddr *)dup_sockaddr(addr, addr_size); + if (curaddr->addr == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + free(curaddr); + return (-1); + } + } else + curaddr->addr = NULL; + + if (netmask != NULL && netmask_size != 0) { + curaddr->netmask = (struct sockaddr *)dup_sockaddr(netmask, netmask_size); + if (curaddr->netmask == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + if (curaddr->addr != NULL) + free(curaddr->addr); + free(curaddr); + return (-1); + } + } else + curaddr->netmask = NULL; + + if (broadaddr != NULL && broadaddr_size != 0) { + curaddr->broadaddr = (struct sockaddr *)dup_sockaddr(broadaddr, broadaddr_size); + if (curaddr->broadaddr == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + if (curaddr->netmask != NULL) + free(curaddr->netmask); + if (curaddr->addr != NULL) + free(curaddr->addr); + free(curaddr); + return (-1); + } + } else + curaddr->broadaddr = NULL; + + if (dstaddr != NULL && dstaddr_size != 0) { + curaddr->dstaddr = (struct sockaddr *)dup_sockaddr(dstaddr, dstaddr_size); + if (curaddr->dstaddr == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + if (curaddr->broadaddr != NULL) + free(curaddr->broadaddr); + if (curaddr->netmask != NULL) + free(curaddr->netmask); + if (curaddr->addr != NULL) + free(curaddr->addr); + free(curaddr); + return (-1); + } + } else + curaddr->dstaddr = NULL; + + /* + * Find the end of the list of addresses. + */ + for (prevaddr = curdev->addresses; prevaddr != NULL; prevaddr = nextaddr) { + nextaddr = prevaddr->next; + if (nextaddr == NULL) { + /* + * This is the end of the list. + */ + break; + } + } + + if (prevaddr == NULL) { + /* + * The list was empty; this is the first member. + */ + curdev->addresses = curaddr; + } else { + /* + * "prevaddr" is the last member of the list; append + * this member to it. + */ + prevaddr->next = curaddr; + } + + return (0); +} + +/* + * Look for a given device in the specified list of devices. + * + * If we find it, return 0 and set *curdev_ret to point to it. + * + * If we don't find it, attempt to add an entry for it, with the specified + * flags and description, and, if that succeeds, return 0, otherwise + * return -1 and set errbuf to an error message. + */ +pcap_if_t * +find_or_add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, + get_if_flags_func get_flags_func, const char *description, char *errbuf) +{ + pcap_if_t *curdev; + + /* + * Is there already an entry in the list for this device? + */ + curdev = find_dev(devlistp, name); + if (curdev != NULL) { + /* + * Yes, return it. + */ + return (curdev); + } + + /* + * No, we didn't find it. + */ + + /* + * Try to get additional flags for the device. + */ + if ((*get_flags_func)(name, &flags, errbuf) == -1) { + /* + * Failed. + */ + return (NULL); + } + + /* + * Now, try to add it to the list of devices. + */ + return (add_dev(devlistp, name, flags, description, errbuf)); +} + +/* + * Look for a given device in the specified list of devices, and return + * the entry for it if we find it or NULL if we don't. + */ +pcap_if_t * +find_dev(pcap_if_list_t *devlistp, const char *name) +{ + pcap_if_t *curdev; + + /* + * Is there an entry in the list for this device? + */ + for (curdev = devlistp->beginning; curdev != NULL; + curdev = curdev->next) { + if (strcmp(name, curdev->name) == 0) { + /* + * We found it, so, yes, there is. No need to + * add it. Provide the entry we found to our + * caller. + */ + return (curdev); + } + } + + /* + * No. + */ + return (NULL); +} + +/* + * Attempt to add an entry for a device, with the specified flags + * and description, and, if that succeeds, return 0 and return a pointer + * to the new entry, otherwise return NULL and set errbuf to an error + * message. + * + * If we weren't given a description, try to get one. + */ +pcap_if_t * +add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, + const char *description, char *errbuf) +{ + pcap_if_t *curdev, *prevdev, *nextdev; + u_int this_figure_of_merit, nextdev_figure_of_merit; + + curdev = malloc(sizeof(pcap_if_t)); + if (curdev == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (NULL); + } + + /* + * Fill in the entry. + */ + curdev->next = NULL; + curdev->name = strdup(name); + if (curdev->name == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + free(curdev); + return (NULL); + } + if (description == NULL) { + /* + * We weren't handed a description for the interface. + */ + curdev->description = NULL; + } else { + /* + * We were handed a description; make a copy. + */ + curdev->description = strdup(description); + if (curdev->description == NULL) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + free(curdev->name); + free(curdev); + return (NULL); + } + } + curdev->addresses = NULL; /* list starts out as empty */ + curdev->flags = flags; + + /* + * Add it to the list, in the appropriate location. + * First, get the "figure of merit" for this interface. + */ + this_figure_of_merit = get_figure_of_merit(curdev); + + /* + * Now look for the last interface with an figure of merit + * less than or equal to the new interface's figure of merit. + * + * We start with "prevdev" being NULL, meaning we're before + * the first element in the list. + */ + prevdev = NULL; + for (;;) { + /* + * Get the interface after this one. + */ + if (prevdev == NULL) { + /* + * The next element is the first element. + */ + nextdev = devlistp->beginning; + } else + nextdev = prevdev->next; + + /* + * Are we at the end of the list? + */ + if (nextdev == NULL) { + /* + * Yes - we have to put the new entry after "prevdev". + */ + break; + } + + /* + * Is the new interface's figure of merit less + * than the next interface's figure of merit, + * meaning that the new interface is better + * than the next interface? + */ + nextdev_figure_of_merit = get_figure_of_merit(nextdev); + if (this_figure_of_merit < nextdev_figure_of_merit) { + /* + * Yes - we should put the new entry + * before "nextdev", i.e. after "prevdev". + */ + break; + } + + prevdev = nextdev; + } + + /* + * Insert before "nextdev". + */ + curdev->next = nextdev; + + /* + * Insert after "prevdev" - unless "prevdev" is null, + * in which case this is the first interface. + */ + if (prevdev == NULL) { + /* + * This is the first interface. Make it + * the first element in the list of devices. + */ + devlistp->beginning = curdev; + } else + prevdev->next = curdev; + return (curdev); +} + +/* + * Free a list of interfaces. + */ +void +pcap_freealldevs(pcap_if_t *alldevs) +{ + pcap_if_t *curdev, *nextdev; + pcap_addr_t *curaddr, *nextaddr; + + for (curdev = alldevs; curdev != NULL; curdev = nextdev) { + nextdev = curdev->next; + + /* + * Free all addresses. + */ + for (curaddr = curdev->addresses; curaddr != NULL; curaddr = nextaddr) { + nextaddr = curaddr->next; + if (curaddr->addr) + free(curaddr->addr); + if (curaddr->netmask) + free(curaddr->netmask); + if (curaddr->broadaddr) + free(curaddr->broadaddr); + if (curaddr->dstaddr) + free(curaddr->dstaddr); + free(curaddr); + } + + /* + * Free the name string. + */ + free(curdev->name); + + /* + * Free the description string, if any. + */ + if (curdev->description != NULL) + free(curdev->description); + + /* + * Free the interface. + */ + free(curdev); + } +} + +/* + * pcap-npf.c has its own pcap_lookupdev(), for compatibility reasons, as + * it actually returns the names of all interfaces, with a NUL separator + * between them; some callers may depend on that. + * + * MS-DOS has its own pcap_lookupdev(), but that might be useful only + * as an optimization. + * + * In all other cases, we just use pcap_findalldevs() to get a list of + * devices, and pick from that list. + */ +#if !defined(HAVE_PACKET32) && !defined(MSDOS) +/* + * Return the name of a network interface attached to the system, or NULL + * if none can be found. The interface must be configured up; the + * lowest unit number is preferred; loopback is ignored. + */ +char * +pcap_lookupdev(char *errbuf) +{ + pcap_if_t *alldevs; +#ifdef _WIN32 + /* + * Windows - use the same size as the old WinPcap 3.1 code. + * XXX - this is probably bigger than it needs to be. + */ + #define IF_NAMESIZE 8192 +#else + /* + * UN*X - use the system's interface name size. + * XXX - that might not be large enough for capture devices + * that aren't regular network interfaces. + */ + /* for old BSD systems, including bsdi3 */ + #ifndef IF_NAMESIZE + #define IF_NAMESIZE IFNAMSIZ + #endif +#endif + static char device[IF_NAMESIZE + 1]; + char *ret; + + if (pcap_findalldevs(&alldevs, errbuf) == -1) + return (NULL); + + if (alldevs == NULL || (alldevs->flags & PCAP_IF_LOOPBACK)) { + /* + * There are no devices on the list, or the first device + * on the list is a loopback device, which means there + * are no non-loopback devices on the list. This means + * we can't return any device. + * + * XXX - why not return a loopback device? If we can't + * capture on it, it won't be on the list, and if it's + * on the list, there aren't any non-loopback devices, + * so why not just supply it as the default device? + */ + (void)strlcpy(errbuf, "no suitable device found", + PCAP_ERRBUF_SIZE); + ret = NULL; + } else { + /* + * Return the name of the first device on the list. + */ + (void)strlcpy(device, alldevs->name, sizeof(device)); + ret = device; + } + + pcap_freealldevs(alldevs); + return (ret); +} +#endif /* !defined(HAVE_PACKET32) && !defined(MSDOS) */ + +#if !defined(_WIN32) && !defined(MSDOS) +/* + * We don't just fetch the entire list of devices, search for the + * particular device, and use its first IPv4 address, as that's too + * much work to get just one device's netmask. + * + * If we had an API to get attributes for a given device, we could + * use that. + */ +int +pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, + char *errbuf) +{ + register int fd; + register struct sockaddr_in *sin4; + struct ifreq ifr; + + /* + * The pseudo-device "any" listens on all interfaces and therefore + * has the network address and -mask "0.0.0.0" therefore catching + * all traffic. Using NULL for the interface is the same as "any". + */ + if (!device || strcmp(device, "any") == 0 +#ifdef HAVE_DAG_API + || strstr(device, "dag") != NULL +#endif +#ifdef HAVE_SEPTEL_API + || strstr(device, "septel") != NULL +#endif +#ifdef PCAP_SUPPORT_BT + || strstr(device, "bluetooth") != NULL +#endif +#ifdef PCAP_SUPPORT_USB + || strstr(device, "usbmon") != NULL +#endif +#ifdef HAVE_SNF_API + || strstr(device, "snf") != NULL +#endif +#ifdef PCAP_SUPPORT_NETMAP + || strncmp(device, "netmap:", 7) == 0 + || strncmp(device, "vale", 4) == 0 +#endif + ) { + *netp = *maskp = 0; + return 0; + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "socket"); + return (-1); + } + memset(&ifr, 0, sizeof(ifr)); +#ifdef linux + /* XXX Work around Linux kernel bug */ + ifr.ifr_addr.sa_family = AF_INET; +#endif + (void)strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) { + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "%s: no IPv4 address assigned", device); + } else { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "SIOCGIFADDR: %s", device); + } + (void)close(fd); + return (-1); + } + sin4 = (struct sockaddr_in *)&ifr.ifr_addr; + *netp = sin4->sin_addr.s_addr; + memset(&ifr, 0, sizeof(ifr)); +#ifdef linux + /* XXX Work around Linux kernel bug */ + ifr.ifr_addr.sa_family = AF_INET; +#endif + (void)strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFNETMASK, (char *)&ifr) < 0) { + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "SIOCGIFNETMASK: %s", device); + (void)close(fd); + return (-1); + } + (void)close(fd); + *maskp = sin4->sin_addr.s_addr; + if (*maskp == 0) { + if (IN_CLASSA(*netp)) + *maskp = IN_CLASSA_NET; + else if (IN_CLASSB(*netp)) + *maskp = IN_CLASSB_NET; + else if (IN_CLASSC(*netp)) + *maskp = IN_CLASSC_NET; + else { + (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "inet class for 0x%x unknown", *netp); + return (-1); + } + } + *netp &= *maskp; + return (0); +} +#endif /* !defined(_WIN32) && !defined(MSDOS) */ + +#ifdef ENABLE_REMOTE +#include "pcap-rpcap.h" + +/* + * Extract a substring from a string. + */ +static char * +get_substring(const char *p, size_t len, char *ebuf) +{ + char *token; + + token = malloc(len + 1); + if (token == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (NULL); + } + memcpy(token, p, len); + token[len] = '\0'; + return (token); +} + +/* + * Parse a capture source that might be a URL. + * + * If the source is not a URL, *schemep, *userinfop, *hostp, and *portp + * are set to NULL, *pathp is set to point to the source, and 0 is + * returned. + * + * If source is a URL, and the URL refers to a local device (a special + * case of rpcap:), *schemep, *userinfop, *hostp, and *portp are set + * to NULL, *pathp is set to point to the device name, and 0 is returned. + * + * If source is a URL, and it's not a special case that refers to a local + * device, and the parse succeeds: + * + * *schemep is set to point to an allocated string containing the scheme; + * + * if user information is present in the URL, *userinfop is set to point + * to an allocated string containing the user information, otherwise + * it's set to NULL; + * + * if host information is present in the URL, *hostp is set to point + * to an allocated string containing the host information, otherwise + * it's set to NULL; + * + * if a port number is present in the URL, *portp is set to point + * to an allocated string containing the port number, otherwise + * it's set to NULL; + * + * *pathp is set to point to an allocated string containing the + * path; + * + * and 0 is returned. + * + * If the parse fails, ebuf is set to an error string, and -1 is returned. + */ +static int +pcap_parse_source(const char *source, char **schemep, char **userinfop, + char **hostp, char **portp, char **pathp, char *ebuf) +{ + char *colonp; + size_t scheme_len; + char *scheme; + const char *endp; + size_t authority_len; + char *authority; + char *parsep, *atsignp, *bracketp; + char *userinfo, *host, *port, *path; + + /* + * Start out returning nothing. + */ + *schemep = NULL; + *userinfop = NULL; + *hostp = NULL; + *portp = NULL; + *pathp = NULL; + + /* + * RFC 3986 says: + * + * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + * + * hier-part = "//" authority path-abempty + * / path-absolute + * / path-rootless + * / path-empty + * + * authority = [ userinfo "@" ] host [ ":" port ] + * + * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + * + * Step 1: look for the ":" at the end of the scheme. + * A colon in the source is *NOT* sufficient to indicate that + * this is a URL, as interface names on some platforms might + * include colons (e.g., I think some Solaris interfaces + * might). + */ + colonp = strchr(source, ':'); + if (colonp == NULL) { + /* + * The source is the device to open. + * Return a NULL pointer for the scheme, user information, + * host, and port, and return the device as the path. + */ + *pathp = strdup(source); + if (*pathp == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + return (0); + } + + /* + * All schemes must have "//" after them, i.e. we only support + * hier-part = "//" authority path-abempty, not + * hier-part = path-absolute + * hier-part = path-rootless + * hier-part = path-empty + * + * We need that in order to distinguish between a local device + * name that happens to contain a colon and a URI. + */ + if (strncmp(colonp + 1, "//", 2) != 0) { + /* + * The source is the device to open. + * Return a NULL pointer for the scheme, user information, + * host, and port, and return the device as the path. + */ + *pathp = strdup(source); + if (*pathp == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + return (0); + } + + /* + * XXX - check whether the purported scheme could be a scheme? + */ + + /* + * OK, this looks like a URL. + * Get the scheme. + */ + scheme_len = colonp - source; + scheme = malloc(scheme_len + 1); + if (scheme == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + memcpy(scheme, source, scheme_len); + scheme[scheme_len] = '\0'; + + /* + * Treat file: specially - take everything after file:// as + * the pathname. + */ + if (pcap_strcasecmp(scheme, "file") == 0) { + *schemep = scheme; + *pathp = strdup(colonp + 3); + if (*pathp == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + return (0); + } + + /* + * The WinPcap documentation says you can specify a local + * interface with "rpcap://{device}"; we special-case + * that here. If the scheme is "rpcap", and there are + * no slashes past the "//", we just return the device. + * + * XXX - %-escaping? + */ + if (pcap_strcasecmp(scheme, "rpcap") == 0 && + strchr(colonp + 3, '/') == NULL) { + /* + * Local device. + * + * Return a NULL pointer for the scheme, user information, + * host, and port, and return the device as the path. + */ + free(scheme); + *pathp = strdup(colonp + 3); + if (*pathp == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (-1); + } + return (0); + } + + /* + * OK, now start parsing the authority. + * Get token, terminated with / or terminated at the end of + * the string. + */ + authority_len = strcspn(colonp + 3, "/"); + authority = get_substring(colonp + 3, authority_len, ebuf); + if (authority == NULL) { + /* + * Error. + */ + free(scheme); + return (-1); + } + endp = colonp + 3 + authority_len; + + /* + * Now carve the authority field into its components. + */ + parsep = authority; + + /* + * Is there a userinfo field? + */ + atsignp = strchr(parsep, '@'); + if (atsignp != NULL) { + /* + * Yes. + */ + size_t userinfo_len; + + userinfo_len = atsignp - parsep; + userinfo = get_substring(parsep, userinfo_len, ebuf); + if (userinfo == NULL) { + /* + * Error. + */ + free(authority); + free(scheme); + return (-1); + } + parsep = atsignp + 1; + } else { + /* + * No. + */ + userinfo = NULL; + } + + /* + * Is there a host field? + */ + if (*parsep == '\0') { + /* + * No; there's no host field or port field. + */ + host = NULL; + port = NULL; + } else { + /* + * Yes. + */ + size_t host_len; + + /* + * Is it an IP-literal? + */ + if (*parsep == '[') { + /* + * Yes. + * Treat verything up to the closing square + * bracket as the IP-Literal; we don't worry + * about whether it's a valid IPv6address or + * IPvFuture. + */ + bracketp = strchr(parsep, ']'); + if (bracketp == NULL) { + /* + * There's no closing square bracket. + */ + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + "IP-literal in URL doesn't end with ]"); + free(userinfo); + free(authority); + free(scheme); + return (-1); + } + if (*(bracketp + 1) != '\0' && + *(bracketp + 1) != ':') { + /* + * There's extra crud after the + * closing square bracketn. + */ + pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + "Extra text after IP-literal in URL"); + free(userinfo); + free(authority); + free(scheme); + return (-1); + } + host_len = (bracketp - 1) - parsep; + host = get_substring(parsep + 1, host_len, ebuf); + if (host == NULL) { + /* + * Error. + */ + free(userinfo); + free(authority); + free(scheme); + return (-1); + } + parsep = bracketp + 1; + } else { + /* + * No. + * Treat everything up to a : or the end of + * the string as the host. + */ + host_len = strcspn(parsep, ":"); + host = get_substring(parsep, host_len, ebuf); + if (host == NULL) { + /* + * Error. + */ + free(userinfo); + free(authority); + free(scheme); + return (-1); + } + parsep = parsep + host_len; + } + + /* + * Is there a port field? + */ + if (*parsep == ':') { + /* + * Yes. It's the rest of the authority field. + */ + size_t port_len; + + parsep++; + port_len = strlen(parsep); + port = get_substring(parsep, port_len, ebuf); + if (port == NULL) { + /* + * Error. + */ + free(host); + free(userinfo); + free(authority); + free(scheme); + return (-1); + } + } else { + /* + * No. + */ + port = NULL; + } + } + free(authority); + + /* + * Everything else is the path. Strip off the leading /. + */ + if (*endp == '\0') + path = strdup(""); + else + path = strdup(endp + 1); + if (path == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + free(port); + free(host); + free(userinfo); + free(scheme); + return (-1); + } + *schemep = scheme; + *userinfop = userinfo; + *hostp = host; + *portp = port; + *pathp = path; + return (0); +} + +int +pcap_createsrcstr(char *source, int type, const char *host, const char *port, + const char *name, char *errbuf) +{ + switch (type) { + + case PCAP_SRC_FILE: + strlcpy(source, PCAP_SRC_FILE_STRING, PCAP_BUF_SIZE); + if (name != NULL && *name != '\0') { + strlcat(source, name, PCAP_BUF_SIZE); + return (0); + } else { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The file name cannot be NULL."); + return (-1); + } + + case PCAP_SRC_IFREMOTE: + strlcpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); + if (host != NULL && *host != '\0') { + if (strchr(host, ':') != NULL) { + /* + * The host name contains a colon, so it's + * probably an IPv6 address, and needs to + * be included in square brackets. + */ + strlcat(source, "[", PCAP_BUF_SIZE); + strlcat(source, host, PCAP_BUF_SIZE); + strlcat(source, "]", PCAP_BUF_SIZE); + } else + strlcat(source, host, PCAP_BUF_SIZE); + + if (port != NULL && *port != '\0') { + strlcat(source, ":", PCAP_BUF_SIZE); + strlcat(source, port, PCAP_BUF_SIZE); } + + strlcat(source, "/", PCAP_BUF_SIZE); + } else { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The host name cannot be NULL."); return (-1); } + + if (name != NULL && *name != '\0') + strlcat(source, name, PCAP_BUF_SIZE); + + return (0); + + case PCAP_SRC_IFLOCAL: + strlcpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); + + if (name != NULL && *name != '\0') + strlcat(source, name, PCAP_BUF_SIZE); + + return (0); + + default: + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The interface type is not valid."); + return (-1); + } +} + +int +pcap_parsesrcstr(const char *source, int *type, char *host, char *port, + char *name, char *errbuf) +{ + char *scheme, *tmpuserinfo, *tmphost, *tmpport, *tmppath; + + /* Initialization stuff */ + if (host) + *host = '\0'; + if (port) + *port = '\0'; + if (name) + *name = '\0'; + + /* Parse the source string */ + if (pcap_parse_source(source, &scheme, &tmpuserinfo, &tmphost, + &tmpport, &tmppath, errbuf) == -1) { + /* + * Fail. + */ + return (-1); + } + + if (scheme == NULL) { + /* + * Local device. + */ + if (name && tmppath) + strlcpy(name, tmppath, PCAP_BUF_SIZE); + if (type) + *type = PCAP_SRC_IFLOCAL; + free(tmppath); + free(tmpport); + free(tmphost); + free(tmpuserinfo); + return (0); + } + + if (strcmp(scheme, "rpcap") == 0) { + /* + * rpcap:// + * + * pcap_parse_source() has already handled the case of + * rpcap://device + */ + if (host && tmphost) { + if (tmpuserinfo) + pcap_snprintf(host, PCAP_BUF_SIZE, "%s@%s", + tmpuserinfo, tmphost); + else + strlcpy(host, tmphost, PCAP_BUF_SIZE); + } + if (port && tmpport) + strlcpy(port, tmpport, PCAP_BUF_SIZE); + if (name && tmppath) + strlcpy(name, tmppath, PCAP_BUF_SIZE); + if (type) + *type = PCAP_SRC_IFREMOTE; + free(tmppath); + free(tmpport); + free(tmphost); + free(tmpuserinfo); + free(scheme); + return (0); } + if (strcmp(scheme, "file") == 0) { + /* + * file:// + */ + if (name && tmppath) + strlcpy(name, tmppath, PCAP_BUF_SIZE); + if (type) + *type = PCAP_SRC_FILE; + free(tmppath); + free(tmpport); + free(tmphost); + free(tmpuserinfo); + free(scheme); + return (0); + } + + /* + * Neither rpcap: nor file:; just treat the entire string + * as a local device. + */ + if (name) + strlcpy(name, source, PCAP_BUF_SIZE); + if (type) + *type = PCAP_SRC_IFLOCAL; + free(tmppath); + free(tmpport); + free(tmphost); + free(tmpuserinfo); + free(scheme); return (0); } +#endif pcap_t * pcap_create(const char *device, char *errbuf) @@ -400,8 +1993,9 @@ pcap_create(const char *device, char *errbuf) length = wcslen((wchar_t *)device); device_str = (char *)malloc(length + 1); if (device_str == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, + PCAP_ERRBUF_SIZE, errno, + "malloc"); return (NULL); } @@ -412,8 +2006,8 @@ pcap_create(const char *device, char *errbuf) device_str = strdup(device); } if (device_str == NULL) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); return (NULL); } @@ -463,6 +2057,18 @@ pcap_create(const char *device, char *errbuf) return (p); } +/* + * Set nonblocking mode on an unactivated pcap_t; this sets a flag + * checked by pcap_activate(), which sets the mode after calling + * the activate routine. + */ +static int +pcap_setnonblock_unactivated(pcap_t *p, int nonblock) +{ + p->opt.nonblock = nonblock; + return (0); +} + static void initialize_ops(pcap_t *p) { @@ -477,7 +2083,6 @@ initialize_ops(pcap_t *p) p->setdirection_op = (setdirection_op_t)pcap_not_initialized; p->set_datalink_op = (set_datalink_op_t)pcap_not_initialized; p->getnonblock_op = (getnonblock_op_t)pcap_not_initialized; - p->setnonblock_op = (setnonblock_op_t)pcap_not_initialized; p->stats_op = (stats_op_t)pcap_not_initialized; #ifdef _WIN32 p->stats_ex_op = (stats_ex_op_t)pcap_not_initialized_ptr; @@ -519,24 +2124,38 @@ pcap_alloc_pcap_t(char *ebuf, size_t size) * plus a structure following it of size "size". The * structure following it is a private data structure * for the routines that handle this pcap_t. + * + * The structure following it must be aligned on + * the appropriate alignment boundary for this platform. + * We align on an 8-byte boundary as that's probably what + * at least some platforms do, even with 32-bit integers, + * and because we can't be sure that some values won't + * require 8-byte alignment even on platforms with 32-bit + * integers. */ - chunk = malloc(sizeof (pcap_t) + size); +#define PCAP_T_ALIGNED_SIZE ((sizeof(pcap_t) + 7) & ~0x7) + chunk = malloc(PCAP_T_ALIGNED_SIZE + size); if (chunk == NULL) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); return (NULL); } - memset(chunk, 0, sizeof (pcap_t) + size); + memset(chunk, 0, PCAP_T_ALIGNED_SIZE + size); /* * Get a pointer to the pcap_t at the beginning. */ p = (pcap_t *)chunk; -#ifndef _WIN32 +#ifdef _WIN32 + p->handle = INVALID_HANDLE_VALUE; /* not opened yet */ +#else /* _WIN32 */ p->fd = -1; /* not opened yet */ +#ifndef MSDOS p->selectable_fd = -1; -#endif + p->required_select_timeout = NULL; +#endif /* MSDOS */ +#endif /* _WIN32 */ if (size == 0) { /* No private data was requested. */ @@ -546,7 +2165,7 @@ pcap_alloc_pcap_t(char *ebuf, size_t size) * Set the pointer to the private data; that's the structure * of size "size" following the pcap_t. */ - p->priv = (void *)(chunk + sizeof (pcap_t)); + p->priv = (void *)(chunk + PCAP_T_ALIGNED_SIZE); } return (p); @@ -569,10 +2188,17 @@ pcap_create_common(char *ebuf, size_t size) */ p->can_set_rfmon_op = pcap_cant_set_rfmon; + /* + * If pcap_setnonblock() is called on a not-yet-activated + * pcap_t, default to setting a flag and turning + * on non-blocking mode when activated. + */ + p->setnonblock_op = pcap_setnonblock_unactivated; + initialize_ops(p); /* put in some defaults*/ - p->snapshot = MAXIMUM_SNAPLEN; /* max packet size */ + p->snapshot = 0; /* max packet size unspecified */ p->opt.timeout = 0; /* no timeout specified */ p->opt.buffer_size = 0; /* use the platform's default */ p->opt.promisc = 0; @@ -580,6 +2206,15 @@ pcap_create_common(char *ebuf, size_t size) p->opt.immediate = 0; p->opt.tstamp_type = -1; /* default to not setting time stamp type */ p->opt.tstamp_precision = PCAP_TSTAMP_PRECISION_MICRO; + /* + * Platform-dependent options. + */ +#ifdef __linux__ + p->opt.protocol = 0; +#endif +#ifdef _WIN32 + p->opt.nocapture_local = 0; +#endif /* * Start out with no BPF code generation flags set. @@ -605,16 +2240,6 @@ pcap_set_snaplen(pcap_t *p, int snaplen) { if (pcap_check_activated(p)) return (PCAP_ERROR_ACTIVATED); - - /* - * Turn invalid values, or excessively large values, into - * the maximum allowed value. - * - * If some application really *needs* a bigger snapshot - * length, we should just increase MAXIMUM_SNAPLEN. - */ - if (snaplen <= 0 || snaplen > MAXIMUM_SNAPLEN) - snaplen = MAXIMUM_SNAPLEN; p->snapshot = snaplen; return (0); } @@ -785,9 +2410,25 @@ pcap_activate(pcap_t *p) if (pcap_check_activated(p)) return (PCAP_ERROR_ACTIVATED); status = p->activate_op(p); - if (status >= 0) + if (status >= 0) { + /* + * If somebody requested non-blocking mode before + * calling pcap_activate(), turn it on now. + */ + if (p->opt.nonblock) { + status = p->setnonblock_op(p, 1); + if (status < 0) { + /* + * Failed. Undo everything done by + * the activate operation. + */ + p->cleanup_op(p); + initialize_ops(p); + return (status); + } + } p->activated = 1; - else { + } else { if (p->errbuf[0] == '\0') { /* * No error message supplied by the activate routine; @@ -813,6 +2454,53 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er { pcap_t *p; int status; +#ifdef ENABLE_REMOTE + char host[PCAP_BUF_SIZE + 1]; + char port[PCAP_BUF_SIZE + 1]; + char name[PCAP_BUF_SIZE + 1]; + int srctype; + + /* + * Retrofit - we have to make older applications compatible with + * remote capture. + * So we're calling pcap_open_remote() from here; this is a very + * dirty hack. + * Obviously, we cannot exploit all the new features; for instance, + * we cannot send authentication, we cannot use a UDP data connection, + * and so on. + */ + if (pcap_parsesrcstr(device, &srctype, host, port, name, errbuf)) + return (NULL); + + if (srctype == PCAP_SRC_IFREMOTE) { + /* + * Although we already have host, port and iface, we prefer + * to pass only 'device' to pcap_open_rpcap(), so that it has + * to call pcap_parsesrcstr() again. + * This is less optimized, but much clearer. + */ + return (pcap_open_rpcap(device, snaplen, + promisc ? PCAP_OPENFLAG_PROMISCUOUS : 0, to_ms, + NULL, errbuf)); + } + if (srctype == PCAP_SRC_FILE) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown URL scheme \"file\""); + return (NULL); + } + if (srctype == PCAP_SRC_IFLOCAL) { + /* + * If it starts with rpcap://, that refers to a local device + * (no host part in the URL). Remove the rpcap://, and + * fall through to the regular open path. + */ + if (strncmp(device, PCAP_SRC_IF_STRING, strlen(PCAP_SRC_IF_STRING)) == 0) { + size_t len = strlen(device) - strlen(PCAP_SRC_IF_STRING) + 1; + + if (len > 0) + device += strlen(PCAP_SRC_IF_STRING); + } + } +#endif /* ENABLE_REMOTE */ p = pcap_create(device, errbuf); if (p == NULL) @@ -946,8 +2634,8 @@ pcap_list_datalinks(pcap_t *p, int **dlt_buffer) */ *dlt_buffer = (int*)malloc(sizeof(**dlt_buffer)); if (*dlt_buffer == NULL) { - (void)pcap_snprintf(p->errbuf, sizeof(p->errbuf), - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "malloc"); return (PCAP_ERROR); } **dlt_buffer = p->linktype; @@ -955,8 +2643,8 @@ pcap_list_datalinks(pcap_t *p, int **dlt_buffer) } else { *dlt_buffer = (int*)calloc(sizeof(**dlt_buffer), p->dlt_count); if (*dlt_buffer == NULL) { - (void)pcap_snprintf(p->errbuf, sizeof(p->errbuf), - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "malloc"); return (PCAP_ERROR); } (void)memcpy(*dlt_buffer, p->dlt_list, @@ -1271,6 +2959,15 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(WATTSTOPPER_DLM, "WattStopper Digital Lighting Management (DLM) and Legrand Nitoo Open protocol"), DLT_CHOICE(ISO_14443, "ISO 14443 messages"), DLT_CHOICE(RDS, "IEC 62106 Radio Data System groups"), + DLT_CHOICE(USB_DARWIN, "USB with Darwin header"), + DLT_CHOICE(OPENFLOW, "OpenBSD DLT_OPENFLOW"), + DLT_CHOICE(SDLC, "IBM SDLC frames"), + DLT_CHOICE(TI_LLN_SNIFFER, "TI LLN sniffer frames"), + DLT_CHOICE(VSOCK, "Linux vsock"), + DLT_CHOICE(NORDIC_BLE, "Nordic Semiconductor Bluetooth LE sniffer frames"), + DLT_CHOICE(DOCSIS31_XRA31, "Excentis XRA-31 DOCSIS 3.1 RF sniffer frames"), + DLT_CHOICE(ETHERNET_MPACKET, "802.3br mPackets"), + DLT_CHOICE(DISPLAYPORT_AUX, "DisplayPort AUX channel monitoring data"), DLT_CHOICE_SENTINEL }; @@ -1393,6 +3090,14 @@ pcap_minor_version(pcap_t *p) return (p->version_minor); } +int +pcap_bufsize(pcap_t *p) +{ + if (!p->activated) + return (PCAP_ERROR_NOT_ACTIVATED); + return (p->bufsize); +} + FILE * pcap_file(pcap_t *p) { @@ -1405,8 +3110,8 @@ pcap_fileno(pcap_t *p) #ifndef _WIN32 return (p->fd); #else - if (p->adapter != NULL) - return ((int)(DWORD)p->adapter->hFile); + if (p->handle != INVALID_HANDLE_VALUE) + return ((int)(DWORD)p->handle); else return (PCAP_ERROR); #endif @@ -1418,6 +3123,12 @@ pcap_get_selectable_fd(pcap_t *p) { return (p->selectable_fd); } + +struct timeval * +pcap_get_required_select_timeout(pcap_t *p) +{ + return (p->required_select_timeout); +} #endif void @@ -1437,14 +3148,18 @@ pcap_getnonblock(pcap_t *p, char *errbuf) { int ret; - ret = p->getnonblock_op(p, errbuf); + ret = p->getnonblock_op(p); if (ret == -1) { /* - * In case somebody depended on the bug wherein - * the error message was put into p->errbuf - * by pcap_getnonblock_fd(). + * The get nonblock operation sets p->errbuf; this + * function *shouldn't* have had a separate errbuf + * argument, as it didn't need one, but I goofed + * when adding it. + * + * We copy the error message to errbuf, so callers + * can find it in either place. */ - strlcpy(p->errbuf, errbuf, PCAP_ERRBUF_SIZE); + strlcpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); } return (ret); } @@ -1455,14 +3170,14 @@ pcap_getnonblock(pcap_t *p, char *errbuf) */ #if !defined(_WIN32) && !defined(MSDOS) int -pcap_getnonblock_fd(pcap_t *p, char *errbuf) +pcap_getnonblock_fd(pcap_t *p) { int fdflags; fdflags = fcntl(p->fd, F_GETFL, 0); if (fdflags == -1) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "F_GETFL: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "F_GETFL"); return (-1); } if (fdflags & O_NONBLOCK) @@ -1477,14 +3192,18 @@ pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf) { int ret; - ret = p->setnonblock_op(p, nonblock, errbuf); + ret = p->setnonblock_op(p, nonblock); if (ret == -1) { /* - * In case somebody depended on the bug wherein - * the error message was put into p->errbuf - * by pcap_setnonblock_fd(). + * The set nonblock operation sets p->errbuf; this + * function *shouldn't* have had a separate errbuf + * argument, as it didn't need one, but I goofed + * when adding it. + * + * We copy the error message to errbuf, so callers + * can find it in either place. */ - strlcpy(p->errbuf, errbuf, PCAP_ERRBUF_SIZE); + strlcpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); } return (ret); } @@ -1497,14 +3216,14 @@ pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf) * needs to do some additional work.) */ int -pcap_setnonblock_fd(pcap_t *p, int nonblock, char *errbuf) +pcap_setnonblock_fd(pcap_t *p, int nonblock) { int fdflags; fdflags = fcntl(p->fd, F_GETFL, 0); if (fdflags == -1) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "F_GETFL: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "F_GETFL"); return (-1); } if (nonblock) @@ -1512,8 +3231,8 @@ pcap_setnonblock_fd(pcap_t *p, int nonblock, char *errbuf) else fdflags &= ~O_NONBLOCK; if (fcntl(p->fd, F_SETFL, fdflags) == -1) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "F_SETFL: %s", - pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "F_SETFL"); return (-1); } return (0); @@ -1532,7 +3251,7 @@ pcap_win32_err_to_str(DWORD error, char *errbuf) size_t errlen; char *p; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf, + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf, PCAP_ERRBUF_SIZE, NULL); /* @@ -1617,9 +3336,9 @@ pcap_strerror(int errnum) #ifdef HAVE_STRERROR #ifdef _WIN32 static char errbuf[PCAP_ERRBUF_SIZE]; - errno_t errno; - errno = strerror_s(errbuf, PCAP_ERRBUF_SIZE, errnum); - if (errno != 0) /* errno = 0 if successful */ + errno_t err = strerror_s(errbuf, PCAP_ERRBUF_SIZE, errnum); + + if (err != 0) /* err = 0 if successful */ strlcpy(errbuf, "strerror_s() error", PCAP_ERRBUF_SIZE); return (errbuf); #else @@ -1666,14 +3385,6 @@ pcap_stats(pcap_t *p, struct pcap_stat *ps) return (p->stats_op(p, ps)); } -static int -pcap_stats_dead(pcap_t *p, struct pcap_stat *ps _U_) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Statistics aren't available from a pcap_open_dead pcap_t"); - return (-1); -} - #ifdef _WIN32 struct pcap_stat * pcap_stats_ex(pcap_t *p, int *pcap_stat_size) @@ -1687,86 +3398,36 @@ pcap_setbuff(pcap_t *p, int dim) return (p->setbuff_op(p, dim)); } -static int -pcap_setbuff_dead(pcap_t *p, int dim) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "The kernel buffer size cannot be set on a pcap_open_dead pcap_t"); - return (-1); -} - int pcap_setmode(pcap_t *p, int mode) { return (p->setmode_op(p, mode)); } -static int -pcap_setmode_dead(pcap_t *p, int mode) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "impossible to set mode on a pcap_open_dead pcap_t"); - return (-1); -} - int pcap_setmintocopy(pcap_t *p, int size) { return (p->setmintocopy_op(p, size)); } -static int -pcap_setmintocopy_dead(pcap_t *p, int size) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "The mintocopy parameter cannot be set on a pcap_open_dead pcap_t"); - return (-1); -} - HANDLE pcap_getevent(pcap_t *p) { return (p->getevent_op(p)); } -static HANDLE -pcap_getevent_dead(pcap_t *p) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "A pcap_open_dead pcap_t has no event handle"); - return (INVALID_HANDLE_VALUE); -} - int pcap_oid_get_request(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp) { return (p->oid_get_request_op(p, oid, data, lenp)); } -static int -pcap_oid_get_request_dead(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, - size_t *lenp _U_) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "An OID get request cannot be performed on a pcap_open_dead pcap_t"); - return (PCAP_ERROR); -} - int pcap_oid_set_request(pcap_t *p, bpf_u_int32 oid, const void *data, size_t *lenp) { return (p->oid_set_request_op(p, oid, data, lenp)); } -static int -pcap_oid_set_request_dead(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, - size_t *lenp _U_) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "An OID set request cannot be performed on a pcap_open_dead pcap_t"); - return (PCAP_ERROR); -} - pcap_send_queue * pcap_sendqueue_alloc(u_int memsize) { @@ -1822,56 +3483,24 @@ pcap_sendqueue_transmit(pcap_t *p, pcap_send_queue *queue, int sync) return (p->sendqueue_transmit_op(p, queue, sync)); } -static u_int -pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue, int sync) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Packets cannot be transmitted on a pcap_open_dead pcap_t"); - return (0); -} - int pcap_setuserbuffer(pcap_t *p, int size) { return (p->setuserbuffer_op(p, size)); } -static int -pcap_setuserbuffer_dead(pcap_t *p, int size) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "The user buffer cannot be set on a pcap_open_dead pcap_t"); - return (-1); -} - int pcap_live_dump(pcap_t *p, char *filename, int maxsize, int maxpacks) { return (p->live_dump_op(p, filename, maxsize, maxpacks)); } -static int -pcap_live_dump_dead(pcap_t *p, char *filename, int maxsize, int maxpacks) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); - return (-1); -} - int pcap_live_dump_ended(pcap_t *p, int sync) { return (p->live_dump_ended_op(p, sync)); } -static int -pcap_live_dump_ended_dead(pcap_t *p, int sync) -{ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); - return (-1); -} - PAirpcapHandle pcap_get_airpcap_handle(pcap_t *p) { @@ -1884,12 +3513,6 @@ pcap_get_airpcap_handle(pcap_t *p) } return (handle); } - -static PAirpcapHandle -pcap_get_airpcap_handle_dead(pcap_t *p) -{ - return (NULL); -} #endif /* @@ -2012,66 +3635,6 @@ pcap_cleanup_live_common(pcap_t *p) #endif } -static void -pcap_cleanup_dead(pcap_t *p _U_) -{ - /* Nothing to do. */ -} - -pcap_t * -pcap_open_dead_with_tstamp_precision(int linktype, int snaplen, u_int precision) -{ - pcap_t *p; - - switch (precision) { - - case PCAP_TSTAMP_PRECISION_MICRO: - case PCAP_TSTAMP_PRECISION_NANO: - break; - - default: - return NULL; - } - p = malloc(sizeof(*p)); - if (p == NULL) - return NULL; - memset (p, 0, sizeof(*p)); - p->snapshot = snaplen; - p->linktype = linktype; - p->opt.tstamp_precision = precision; - p->stats_op = pcap_stats_dead; -#ifdef _WIN32 - p->stats_ex_op = (stats_ex_op_t)pcap_not_initialized_ptr; - p->setbuff_op = pcap_setbuff_dead; - p->setmode_op = pcap_setmode_dead; - p->setmintocopy_op = pcap_setmintocopy_dead; - p->getevent_op = pcap_getevent_dead; - p->oid_get_request_op = pcap_oid_get_request_dead; - p->oid_set_request_op = pcap_oid_set_request_dead; - p->sendqueue_transmit_op = pcap_sendqueue_transmit_dead; - p->setuserbuffer_op = pcap_setuserbuffer_dead; - p->live_dump_op = pcap_live_dump_dead; - p->live_dump_ended_op = pcap_live_dump_ended_dead; - p->get_airpcap_handle_op = pcap_get_airpcap_handle_dead; -#endif - p->cleanup_op = pcap_cleanup_dead; - - /* - * A "dead" pcap_t never requires special BPF code generation. - */ - p->bpf_codegen_flags = 0; - - p->activated = 1; - return (p); -} - -pcap_t * -pcap_open_dead(int linktype, int snaplen) -{ - return (pcap_open_dead_with_tstamp_precision(linktype, snaplen, - PCAP_TSTAMP_PRECISION_MICRO)); -} - /* * API compatible with WinPcap's "send a packet" routine - returns -1 * on error, 0 otherwise. @@ -2123,160 +3686,251 @@ pcap_offline_filter(const struct bpf_program *fp, const struct pcap_pkthdr *h, return (0); } -#include "pcap_version.h" +static int +pcap_can_set_rfmon_dead(pcap_t *p) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Rfmon mode doesn't apply on a pcap_open_dead pcap_t"); + return (PCAP_ERROR); +} + +static int +pcap_read_dead(pcap_t *p, int cnt _U_, pcap_handler callback _U_, + u_char *user _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Packets aren't available from a pcap_open_dead pcap_t"); + return (-1); +} + +static int +pcap_inject_dead(pcap_t *p, const void *buf _U_, size_t size _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Packets can't be sent on a pcap_open_dead pcap_t"); + return (-1); +} + +static int +pcap_setfilter_dead(pcap_t *p, struct bpf_program *fp _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A filter cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} + +static int +pcap_setdirection_dead(pcap_t *p, pcap_direction_t d _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The packet direction cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} + +static int +pcap_set_datalink_dead(pcap_t *p, int dlt _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The link-layer header type cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} + +static int +pcap_getnonblock_dead(pcap_t *p) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); + return (-1); +} + +static int +pcap_setnonblock_dead(pcap_t *p, int nonblock _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); + return (-1); +} + +static int +pcap_stats_dead(pcap_t *p, struct pcap_stat *ps _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Statistics aren't available from a pcap_open_dead pcap_t"); + return (-1); +} #ifdef _WIN32 +struct pcap_stat * +pcap_stats_ex_dead(pcap_t *p, int *pcap_stat_size _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Statistics aren't available from a pcap_open_dead pcap_t"); + return (NULL); +} -static char *full_pcap_version_string; +static int +pcap_setbuff_dead(pcap_t *p, int dim) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The kernel buffer size cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} -#ifdef HAVE_VERSION_H -/* - * libpcap being built for Windows, as part of a WinPcap/Npcap source - * tree. Include version.h from that source tree to get the WinPcap/Npcap - * version. - * - * XXX - it'd be nice if we could somehow generate the WinPcap version number - * when building WinPcap. (It'd be nice to do so for the packet.dll version - * number as well.) - */ -#include "../../version.h" +static int +pcap_setmode_dead(pcap_t *p, int mode) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "impossible to set mode on a pcap_open_dead pcap_t"); + return (-1); +} -static const char wpcap_version_string[] = WINPCAP_VER_STRING; -static const char pcap_version_string_fmt[] = - WINPCAP_PRODUCT_NAME " version %s, based on %s"; -static const char pcap_version_string_packet_dll_fmt[] = - WINPCAP_PRODUCT_NAME " version %s (packet.dll version %s), based on %s"; +static int +pcap_setmintocopy_dead(pcap_t *p, int size) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The mintocopy parameter cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} -const char * -pcap_lib_version(void) +static HANDLE +pcap_getevent_dead(pcap_t *p) { - char *packet_version_string; - size_t full_pcap_version_string_len; + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A pcap_open_dead pcap_t has no event handle"); + return (INVALID_HANDLE_VALUE); +} - if (full_pcap_version_string == NULL) { - /* - * Generate the version string. - */ - packet_version_string = PacketGetVersion(); - if (strcmp(wpcap_version_string, packet_version_string) == 0) { - /* - * WinPcap version string and packet.dll version - * string are the same; just report the WinPcap - * version. - */ - full_pcap_version_string_len = - (sizeof pcap_version_string_fmt - 4) + - strlen(wpcap_version_string) + - strlen(pcap_version_string); - full_pcap_version_string = - malloc(full_pcap_version_string_len); - if (full_pcap_version_string == NULL) - return (NULL); - pcap_snprintf(full_pcap_version_string, - full_pcap_version_string_len, - pcap_version_string_fmt, - wpcap_version_string, - pcap_version_string); - } else { - /* - * WinPcap version string and packet.dll version - * string are different; that shouldn't be the - * case (the two libraries should come from the - * same version of WinPcap), so we report both - * versions. - */ - full_pcap_version_string_len = - (sizeof pcap_version_string_packet_dll_fmt - 6) + - strlen(wpcap_version_string) + - strlen(packet_version_string) + - strlen(pcap_version_string); - full_pcap_version_string = malloc(full_pcap_version_string_len); - if (full_pcap_version_string == NULL) - return (NULL); - pcap_snprintf(full_pcap_version_string, - full_pcap_version_string_len, - pcap_version_string_packet_dll_fmt, - wpcap_version_string, - packet_version_string, - pcap_version_string); - } - } - return (full_pcap_version_string); +static int +pcap_oid_get_request_dead(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, + size_t *lenp _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID get request cannot be performed on a pcap_open_dead pcap_t"); + return (PCAP_ERROR); } -#else /* HAVE_VERSION_H */ +static int +pcap_oid_set_request_dead(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, + size_t *lenp _U_) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID set request cannot be performed on a pcap_open_dead pcap_t"); + return (PCAP_ERROR); +} -/* - * libpcap being built for Windows, not as part of a WinPcap/Npcap source - * tree. - */ -static const char pcap_version_string_packet_dll_fmt[] = - "%s (packet.dll version %s)"; -const char * -pcap_lib_version(void) +static u_int +pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue, int sync) { - char *packet_version_string; - size_t full_pcap_version_string_len; + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Packets cannot be transmitted on a pcap_open_dead pcap_t"); + return (0); +} - if (full_pcap_version_string == NULL) { - /* - * Generate the version string. Report the packet.dll - * version. - */ - packet_version_string = PacketGetVersion(); - full_pcap_version_string_len = - (sizeof pcap_version_string_packet_dll_fmt - 4) + - strlen(pcap_version_string) + - strlen(packet_version_string); - full_pcap_version_string = malloc(full_pcap_version_string_len); - if (full_pcap_version_string == NULL) - return (NULL); - pcap_snprintf(full_pcap_version_string, - full_pcap_version_string_len, - pcap_version_string_packet_dll_fmt, - pcap_version_string, - packet_version_string); - } - return (full_pcap_version_string); +static int +pcap_setuserbuffer_dead(pcap_t *p, int size) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The user buffer cannot be set on a pcap_open_dead pcap_t"); + return (-1); } -#endif /* HAVE_VERSION_H */ +static int +pcap_live_dump_dead(pcap_t *p, char *filename, int maxsize, int maxpacks) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); + return (-1); +} -#elif defined(MSDOS) +static int +pcap_live_dump_ended_dead(pcap_t *p, int sync) +{ + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); + return (-1); +} + +static PAirpcapHandle +pcap_get_airpcap_handle_dead(pcap_t *p) +{ + return (NULL); +} +#endif /* _WIN32 */ -static char *full_pcap_version_string; +static void +pcap_cleanup_dead(pcap_t *p _U_) +{ + /* Nothing to do. */ +} -const char * -pcap_lib_version (void) +pcap_t * +pcap_open_dead_with_tstamp_precision(int linktype, int snaplen, u_int precision) { - char *packet_version_string; - size_t full_pcap_version_string_len; - static char dospfx[] = "DOS-"; + pcap_t *p; + + switch (precision) { + + case PCAP_TSTAMP_PRECISION_MICRO: + case PCAP_TSTAMP_PRECISION_NANO: + break; - if (full_pcap_version_string == NULL) { + default: /* - * Generate the version string. + * This doesn't really matter, but we don't have any way + * to report particular errors, so the only failure we + * should have is a memory allocation failure. Just + * pick microsecond precision. */ - full_pcap_version_string_len = - sizeof dospfx + strlen(pcap_version_string); - full_pcap_version_string = - malloc(full_pcap_version_string_len); - if (full_pcap_version_string == NULL) - return (NULL); - strcpy(full_pcap_version_string, dospfx); - strcat(full_pcap_version_string, pcap_version_string); + precision = PCAP_TSTAMP_PRECISION_MICRO; + break; } - return (full_pcap_version_string); -} + p = malloc(sizeof(*p)); + if (p == NULL) + return NULL; + memset (p, 0, sizeof(*p)); + p->snapshot = snaplen; + p->linktype = linktype; + p->opt.tstamp_precision = precision; + p->can_set_rfmon_op = pcap_can_set_rfmon_dead; + p->read_op = pcap_read_dead; + p->inject_op = pcap_inject_dead; + p->setfilter_op = pcap_setfilter_dead; + p->setdirection_op = pcap_setdirection_dead; + p->set_datalink_op = pcap_set_datalink_dead; + p->getnonblock_op = pcap_getnonblock_dead; + p->setnonblock_op = pcap_setnonblock_dead; + p->stats_op = pcap_stats_dead; +#ifdef _WIN32 + p->stats_ex_op = pcap_stats_ex_dead; + p->setbuff_op = pcap_setbuff_dead; + p->setmode_op = pcap_setmode_dead; + p->setmintocopy_op = pcap_setmintocopy_dead; + p->getevent_op = pcap_getevent_dead; + p->oid_get_request_op = pcap_oid_get_request_dead; + p->oid_set_request_op = pcap_oid_set_request_dead; + p->sendqueue_transmit_op = pcap_sendqueue_transmit_dead; + p->setuserbuffer_op = pcap_setuserbuffer_dead; + p->live_dump_op = pcap_live_dump_dead; + p->live_dump_ended_op = pcap_live_dump_ended_dead; + p->get_airpcap_handle_op = pcap_get_airpcap_handle_dead; +#endif + p->cleanup_op = pcap_cleanup_dead; -#else /* UN*X */ + /* + * A "dead" pcap_t never requires special BPF code generation. + */ + p->bpf_codegen_flags = 0; -const char * -pcap_lib_version(void) + p->activated = 1; + return (p); +} + +pcap_t * +pcap_open_dead(int linktype, int snaplen) { - return (pcap_version_string); + return (pcap_open_dead_with_tstamp_precision(linktype, snaplen, + PCAP_TSTAMP_PRECISION_MICRO)); } -#endif #ifdef YYDEBUG /* @@ -2296,32 +3950,6 @@ PCAP_API void pcap_set_parser_debug(int value); PCAP_API_DEF void pcap_set_parser_debug(int value) { - extern int pcap_debug; - pcap_debug = value; } #endif - -#ifdef BDEBUG -/* - * Set the internal "debug printout" flag for the filter expression optimizer. - * The code to print that stuff is present only if BDEBUG is defined, so - * the flag, and the routine to set it, are defined only if BDEBUG is - * defined. - * - * This is intended for libpcap developers, not for general use. - * If you want to set these in a program, you'll have to declare this - * routine yourself, with the appropriate DLL import attribute on Windows; - * it's not declared in any header file, and won't be declared in any - * header file provided by libpcap. - */ -PCAP_API void pcap_set_optimizer_debug(int value); - -PCAP_API_DEF void -pcap_set_optimizer_debug(int value) -{ - extern int pcap_optimizer_debug; - - pcap_optimizer_debug = value; -} -#endif |