X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/c94b4178db74a2baf1e970f10c89faaf379f0436..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap.c diff --git a/pcap.c b/pcap.c index ccf82f15..ef1bbb71 100644 --- a/pcap.c +++ b/pcap.c @@ -36,9 +36,7 @@ #endif #include -#ifdef _WIN32 -#include -#else /* _WIN32 */ +#ifndef _WIN32 #include #ifndef MSDOS #include @@ -55,7 +53,6 @@ struct rtentry; /* declarations in */ #include #endif /* _WIN32 */ -#include #include #include #include @@ -64,11 +61,9 @@ struct rtentry; /* declarations in */ #endif #include #include -#ifdef HAVE_LIMITS_H #include -#else -#define INT_MAX 2147483647 -#endif + +#include "diag-control.h" #ifdef HAVE_OS_PROTO_H #include "os-proto.h" @@ -80,6 +75,8 @@ struct rtentry; /* declarations in */ #include "pcap-int.h" +#include "optimize.h" + #ifdef HAVE_DAG_API #include "pcap-dag.h" #endif /* HAVE_DAG_API */ @@ -96,7 +93,7 @@ struct rtentry; /* declarations in */ #include "pcap-tc.h" #endif /* HAVE_TC_API */ -#ifdef PCAP_SUPPORT_USB +#ifdef PCAP_SUPPORT_LINUX_USBMON #include "pcap-usb-linux.h" #endif @@ -124,46 +121,364 @@ struct rtentry; /* declarations in */ #include "pcap-rdmasniff.h" #endif +#ifdef PCAP_SUPPORT_DPDK +#include "pcap-dpdk.h" +#endif + +#ifdef HAVE_AIRPCAP_API +#include "pcap-airpcap.h" +#endif + +#ifdef _WIN32 +/* + * To quote the WSAStartup() documentation: + * + * The WSAStartup function typically leads to protocol-specific helper + * DLLs being loaded. As a result, the WSAStartup function should not + * be called from the DllMain function in a application DLL. This can + * potentially cause deadlocks. + * + * and the WSACleanup() documentation: + * + * The WSACleanup function typically leads to protocol-specific helper + * DLLs being unloaded. As a result, the WSACleanup function should not + * be called from the DllMain function in a application DLL. This can + * potentially cause deadlocks. + * + * So we don't initialize Winsock in a DllMain() routine. + * + * pcap_init() should be called to initialize pcap on both UN*X and + * Windows; it will initialize Winsock on Windows. (It will also be + * initialized as needed if pcap_init() hasn't been called.) + */ + +/* + * Start Winsock. + * Internal routine. + */ static int -pcap_not_initialized(pcap_t *pcap) +internal_wsockinit(char *errbuf) +{ + WORD wVersionRequested; + WSADATA wsaData; + static int err = -1; + static int done = 0; + int status; + + if (done) + return (err); + + /* + * Versions of Windows that don't support Winsock 2.2 are + * too old for us. + */ + wVersionRequested = MAKEWORD(2, 2); + status = WSAStartup(wVersionRequested, &wsaData); + done = 1; + if (status != 0) { + if (errbuf != NULL) { + pcap_fmt_errmsg_for_win32_err(errbuf, PCAP_ERRBUF_SIZE, + status, "WSAStartup() failed"); + } + return (err); + } + atexit ((void(*)(void))WSACleanup); + err = 0; + return (err); +} + +/* + * Exported in case some applications using WinPcap/Npcap called it, + * even though it wasn't exported. + */ +int +wsockinit(void) +{ + return (internal_wsockinit(NULL)); +} + +/* + * This is the exported function; new programs should call this. + * *Newer* programs should call pcap_init(). + */ +int +pcap_wsockinit(void) +{ + return (internal_wsockinit(NULL)); +} +#endif /* _WIN32 */ + +/* + * Do whatever initialization is needed for libpcap. + * + * The argument specifies whether we use the local code page or UTF-8 + * for strings; on UN*X, we just assume UTF-8 in places where the encoding + * would matter, whereas, on Windows, we use the local code page for + * PCAP_CHAR_ENC_LOCAL and UTF-8 for PCAP_CHAR_ENC_UTF_8. + * + * On Windows, we also disable the hack in pcap_create() to deal with + * being handed UTF-16 strings, because if the user calls this they're + * explicitly declaring that they will either be passing local code + * page strings or UTF-8 strings, so we don't need to allow UTF-16LE + * strings to be passed. For good measure, on Windows *and* UN*X, + * we disable pcap_lookupdev(), to prevent anybody from even + * *trying* to pass the result of pcap_lookupdev() - which might be + * UTF-16LE on Windows, for ugly compatibility reasons - to pcap_create() + * or pcap_open_live() or pcap_open(). + * + * Returns 0 on success, -1 on error. + */ +int pcap_new_api; /* pcap_lookupdev() always fails */ +int pcap_utf_8_mode; /* Strings should be in UTF-8. */ + +int +pcap_init(unsigned int opts, char *errbuf) +{ + static int initialized; + + /* + * Don't allow multiple calls that set different modes; that + * may mean a library is initializing pcap in one mode and + * a program using that library, or another library used by + * that program, is initializing it in another mode. + */ + switch (opts) { + + case PCAP_CHAR_ENC_LOCAL: + /* Leave "UTF-8 mode" off. */ + if (initialized) { + if (pcap_utf_8_mode) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Multiple pcap_init calls with different character encodings"); + return (PCAP_ERROR); + } + } + break; + + case PCAP_CHAR_ENC_UTF_8: + /* Turn on "UTF-8 mode". */ + if (initialized) { + if (!pcap_utf_8_mode) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "Multiple pcap_init calls with different character encodings"); + return (PCAP_ERROR); + } + } + pcap_utf_8_mode = 1; + break; + + default: + snprintf(errbuf, PCAP_ERRBUF_SIZE, "Unknown options specified"); + return (PCAP_ERROR); + } + + /* + * Turn the appropriate mode on for error messages; those routines + * are also used in rpcapd, which has no access to pcap's internal + * UTF-8 mode flag, so we have to call a routine to set its + * UTF-8 mode flag. + */ + pcap_fmt_set_encoding(opts); + + if (initialized) { + /* + * Nothing more to do; for example, on Windows, we've + * already initialized Winsock. + */ + return (0); + } + +#ifdef _WIN32 + /* + * Now set up Winsock. + */ + if (internal_wsockinit(errbuf) == -1) { + /* Failed. */ + return (PCAP_ERROR); + } +#endif + + /* + * We're done. + */ + initialized = 1; + pcap_new_api = 1; + return (0); +} + +/* + * 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 void +pcap_set_not_initialized_message(pcap_t *pcap) { + if (pcap->activated) { + /* A module probably forgot to set the function pointer */ + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), + "This operation isn't properly handled by that device"); + return; + } /* in case the caller doesn't check for PCAP_ERROR_NOT_ACTIVATED */ - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + (void)snprintf(pcap->errbuf, sizeof(pcap->errbuf), "This handle hasn't been activated yet"); +} + +static int +pcap_read_not_initialized(pcap_t *pcap, int cnt _U_, pcap_handler callback _U_, + u_char *user _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_inject_not_initialized(pcap_t *pcap, const void * buf _U_, int size _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_setfilter_not_initialized(pcap_t *pcap, struct bpf_program *fp _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_setdirection_not_initialized(pcap_t *pcap, pcap_direction_t d _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_set_datalink_not_initialized(pcap_t *pcap, int dlt _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_getnonblock_not_initialized(pcap_t *pcap) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_stats_not_initialized(pcap_t *pcap, struct pcap_stat *ps _U_) +{ + pcap_set_not_initialized_message(pcap); /* this means 'not initialized' */ return (PCAP_ERROR_NOT_ACTIVATED); } #ifdef _WIN32 -static void * -pcap_not_initialized_ptr(pcap_t *pcap) +static struct pcap_stat * +pcap_stats_ex_not_initialized(pcap_t *pcap, int *pcap_stat_size _U_) { - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), - "This handle hasn't been activated yet"); + pcap_set_not_initialized_message(pcap); return (NULL); } +static int +pcap_setbuff_not_initialized(pcap_t *pcap, int dim _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_setmode_not_initialized(pcap_t *pcap, int mode _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_setmintocopy_not_initialized(pcap_t *pcap, int size _U_) +{ + pcap_set_not_initialized_message(pcap); + /* this means 'not initialized' */ + return (PCAP_ERROR_NOT_ACTIVATED); +} + static HANDLE pcap_getevent_not_initialized(pcap_t *pcap) { - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), - "This handle hasn't been activated yet"); + pcap_set_not_initialized_message(pcap); return (INVALID_HANDLE_VALUE); } +static int +pcap_oid_get_request_not_initialized(pcap_t *pcap, bpf_u_int32 oid _U_, + void *data _U_, size_t *lenp _U_) +{ + pcap_set_not_initialized_message(pcap); + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_oid_set_request_not_initialized(pcap_t *pcap, bpf_u_int32 oid _U_, + const void *data _U_, size_t *lenp _U_) +{ + pcap_set_not_initialized_message(pcap); + return (PCAP_ERROR_NOT_ACTIVATED); +} + static u_int -pcap_sendqueue_transmit_not_initialized(pcap_t *pcap, pcap_send_queue* queue, int sync) +pcap_sendqueue_transmit_not_initialized(pcap_t *pcap, pcap_send_queue* queue _U_, + int sync _U_) { - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), - "This handle hasn't been activated yet"); + pcap_set_not_initialized_message(pcap); return (0); } +static int +pcap_setuserbuffer_not_initialized(pcap_t *pcap, int size _U_) +{ + pcap_set_not_initialized_message(pcap); + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_live_dump_not_initialized(pcap_t *pcap, char *filename _U_, int maxsize _U_, + int maxpacks _U_) +{ + pcap_set_not_initialized_message(pcap); + return (PCAP_ERROR_NOT_ACTIVATED); +} + +static int +pcap_live_dump_ended_not_initialized(pcap_t *pcap, int sync _U_) +{ + pcap_set_not_initialized_message(pcap); + return (PCAP_ERROR_NOT_ACTIVATED); +} + static PAirpcapHandle pcap_get_airpcap_handle_not_initialized(pcap_t *pcap) { - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), - "This handle hasn't been activated yet"); + pcap_set_not_initialized_message(pcap); return (NULL); } #endif @@ -204,20 +519,29 @@ pcap_list_tstamp_types(pcap_t *p, int **tstamp_typesp) if (p->tstamp_type_count == 0) { /* * We don't support multiple time stamp types. + * That means the only type we support is PCAP_TSTAMP_HOST; + * set up a list containing only that type. */ - *tstamp_typesp = NULL; + *tstamp_typesp = (int*)malloc(sizeof(**tstamp_typesp)); + if (*tstamp_typesp == NULL) { + pcap_fmt_errmsg_for_errno(p->errbuf, sizeof(p->errbuf), + errno, "malloc"); + return (PCAP_ERROR); + } + **tstamp_typesp = PCAP_TSTAMP_HOST; + return (1); } else { *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, sizeof(**tstamp_typesp) * p->tstamp_type_count); + return (p->tstamp_type_count); } - return (p->tstamp_type_count); } /* @@ -289,7 +613,9 @@ pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, * Return codes for pcap_offline_read() are: * - 0: EOF * - -1: error - * - >1: OK + * - >0: OK - result is number of packets read, so + * it will be 1 in this case, as we've passed + * a maximum packet count of 1 * The first one ('0') conflicts with the return code of * 0 from pcap_read() meaning "no packets arrived before * the timeout expired", so we map it to -2 so you can @@ -308,7 +634,9 @@ pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, * - 0: timeout * - -1: error * - -2: loop was broken out of with pcap_breakloop() - * - >1: OK + * - >0: OK, result is number of packets captured, so + * it will be 1 in this case, as we've passed + * a maximum packet count of 1 * The first one ('0') conflicts with the return code of 0 from * pcap_offline_read() meaning "end of file". */ @@ -344,7 +672,7 @@ static struct capture_source_type { #ifdef PCAP_SUPPORT_BT_MONITOR { bt_monitor_findalldevs, bt_monitor_create }, #endif -#ifdef PCAP_SUPPORT_USB +#ifdef PCAP_SUPPORT_LINUX_USBMON { usb_findalldevs, usb_create }, #endif #ifdef PCAP_SUPPORT_NETFILTER @@ -358,6 +686,12 @@ static struct capture_source_type { #endif #ifdef PCAP_SUPPORT_RDMASNIFF { rdmasniff_findalldevs, rdmasniff_create }, +#endif +#ifdef PCAP_SUPPORT_DPDK + { pcap_dpdk_findalldevs, pcap_dpdk_create }, +#endif +#ifdef HAVE_AIRPCAP_API + { airpcap_findalldevs, airpcap_create }, #endif { NULL, NULL } }; @@ -438,45 +772,59 @@ dup_sockaddr(struct sockaddr *sa, size_t sa_length) * * 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.) + * below that set if the interface isn't up, the bit below that + * set if the interface is a loopback interface, and the bit below + * that set if it's the "any" interface. + * + * Note: we don't sort by unit number because 1) not all interfaces have + * a unit number (systemd, for example, might assign interface names + * based on the interface's MAC address or on the physical location of + * the adapter's connector), and 2) if the name does end with a simple + * unit number, it's not a global property of the interface, it's only + * useful as a sort key for device names with the same prefix, so xyz0 + * shouldn't necessarily sort before abc2. This means that interfaces + * with the same figure of merit will be sorted by the order in which + * the mechanism from which we're getting the interfaces supplies them. */ 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; - } + n = 0; if (!(dev->flags & PCAP_IF_RUNNING)) n |= 0x80000000; if (!(dev->flags & PCAP_IF_UP)) n |= 0x40000000; - if (dev->flags & PCAP_IF_LOOPBACK) + + /* + * 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; + + /* + * Sort the "any" device before loopback and disconnected devices, + * but after all other devices. + */ + if (strcmp(dev->name, "any") == 0) + n |= 0x08000000; + return (n); } @@ -501,7 +849,7 @@ get_figure_of_merit(pcap_if_t *dev) * description available, it still might be nice to get some description * string based on the device type or something such as that. * - * In OS X, the System Configuration framework can apparently return + * 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" @@ -531,9 +879,9 @@ get_figure_of_merit(pcap_if_t *dev) * description? */ static char * +#ifdef SIOCGIFDESCR get_if_description(const char *name) { -#ifdef SIOCGIFDESCR char *description = NULL; int s; struct ifreq ifrdesc; @@ -547,7 +895,7 @@ get_if_description(const char *name) * Get the description for the interface. */ memset(&ifrdesc, 0, sizeof ifrdesc); - strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name); + pcap_strlcpy(ifrdesc.ifr_name, name, sizeof ifrdesc.ifr_name); s = socket(AF_INET, SOCK_DGRAM, 0); if (s >= 0) { #ifdef __FreeBSD__ @@ -598,7 +946,7 @@ get_if_description(const char *name) } #endif /* __FreeBSD__ */ close(s); - if (description != NULL && strlen(description) == 0) { + if (description != NULL && description[0] == '\0') { /* * Description is empty, so discard it. */ @@ -629,20 +977,13 @@ get_if_description(const char *name) * OK, it's a valid number that's not * bigger than INT_MAX. Construct * a description from it. + * (If that fails, we don't worry about + * it, we just return NULL.) */ - 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); + if (pcap_asprintf(&description, + "USB bus number %ld", busnum) == -1) { + /* Failed. */ + description = NULL; } } } @@ -650,6 +991,8 @@ get_if_description(const char *name) #endif return (description); #else /* SIOCGIFDESCR */ +get_if_description(const char *name _U_) +{ return (NULL); #endif /* SIOCGIFDESCR */ } @@ -665,7 +1008,7 @@ get_if_description(const char *name) */ pcap_if_t * find_or_add_if(pcap_if_list_t *devlistp, const char *name, - bpf_u_int32 if_flags, char *errbuf) + bpf_u_int32 if_flags, get_if_flags_func get_flags_func, char *errbuf) { bpf_u_int32 pcap_flags; @@ -682,7 +1025,7 @@ find_or_add_if(pcap_if_list_t *devlistp, const char *name, * see if it looks like a loopback device. */ if (name[0] == 'l' && name[1] == 'o' && - (isdigit((unsigned char)(name[2])) || name[2] == '\0') + (PCAP_ISDIGIT(name[2]) || name[2] == '\0')) pcap_flags |= PCAP_IF_LOOPBACK; #endif #ifdef IFF_UP @@ -699,7 +1042,7 @@ find_or_add_if(pcap_if_list_t *devlistp, const char *name, * attempt to add one. */ return (find_or_add_dev(devlistp, name, pcap_flags, - get_if_description(name), errbuf)); + get_flags_func, get_if_description(name), errbuf)); } /* @@ -722,7 +1065,7 @@ find_or_add_if(pcap_if_list_t *devlistp, const char *name, */ int add_addr_to_if(pcap_if_list_t *devlistp, const char *name, - bpf_u_int32 if_flags, + 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, @@ -734,7 +1077,8 @@ add_addr_to_if(pcap_if_list_t *devlistp, const char *name, /* * Check whether the device exists and, if not, add it. */ - curdev = find_or_add_if(devlistp, name, if_flags, errbuf); + curdev = find_or_add_if(devlistp, name, if_flags, get_flags_func, + errbuf); if (curdev == NULL) { /* * Error - give up. @@ -780,8 +1124,8 @@ add_addr_to_dev(pcap_if_t *curdev, */ curaddr = (pcap_addr_t *)malloc(sizeof(pcap_addr_t)); if (curaddr == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); return (-1); } @@ -789,8 +1133,8 @@ add_addr_to_dev(pcap_if_t *curdev, if (addr != NULL && addr_size != 0) { curaddr->addr = (struct sockaddr *)dup_sockaddr(addr, addr_size); if (curaddr->addr == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); free(curaddr); return (-1); } @@ -800,8 +1144,8 @@ add_addr_to_dev(pcap_if_t *curdev, if (netmask != NULL && netmask_size != 0) { curaddr->netmask = (struct sockaddr *)dup_sockaddr(netmask, netmask_size); if (curaddr->netmask == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); if (curaddr->addr != NULL) free(curaddr->addr); free(curaddr); @@ -813,8 +1157,8 @@ add_addr_to_dev(pcap_if_t *curdev, if (broadaddr != NULL && broadaddr_size != 0) { curaddr->broadaddr = (struct sockaddr *)dup_sockaddr(broadaddr, broadaddr_size); if (curaddr->broadaddr == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); if (curaddr->netmask != NULL) free(curaddr->netmask); if (curaddr->addr != NULL) @@ -828,8 +1172,8 @@ add_addr_to_dev(pcap_if_t *curdev, if (dstaddr != NULL && dstaddr_size != 0) { curaddr->dstaddr = (struct sockaddr *)dup_sockaddr(dstaddr, dstaddr_size); if (curaddr->dstaddr == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); if (curaddr->broadaddr != NULL) free(curaddr->broadaddr); if (curaddr->netmask != NULL) @@ -882,7 +1226,7 @@ add_addr_to_dev(pcap_if_t *curdev, */ pcap_if_t * find_or_add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, - const char *description, char *errbuf) + get_if_flags_func get_flags_func, const char *description, char *errbuf) { pcap_if_t *curdev; @@ -898,7 +1242,21 @@ find_or_add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, } /* - * No, we didn't find it. Try to add it to the list of devices. + * 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)); } @@ -950,8 +1308,8 @@ add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, curdev = malloc(sizeof(pcap_if_t)); if (curdev == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); return (NULL); } @@ -961,8 +1319,8 @@ add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, curdev->next = NULL; curdev->name = strdup(name); if (curdev->name == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); free(curdev); return (NULL); } @@ -977,8 +1335,8 @@ add_dev(pcap_if_list_t *devlistp, const char *name, bpf_u_int32 flags, */ curdev->description = strdup(description); if (curdev->description == NULL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, - "malloc: %s", pcap_strerror(errno)); + pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); free(curdev->name); free(curdev); return (NULL); @@ -1107,56 +1465,860 @@ pcap_freealldevs(pcap_if_t *alldevs) } } -#ifdef HAVE_REMOTE -#include "pcap-rpcap.h" -#endif - -pcap_t * -pcap_create(const char *device, char *errbuf) +/* + * 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) { - size_t i; - int is_theirs; - pcap_t *p; - char *device_str; + 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; /* - * A null device name is equivalent to the "any" device - - * which might not be supported on this platform, but - * this means that you'll get a "not supported" error - * rather than, say, a crash when we try to dereference - * the null pointer. + * We disable this in "new API" mode, because 1) in WinPcap/Npcap, + * it may return UTF-16 strings, for backwards-compatibility + * reasons, and we're also disabling the hack to make that work, + * for not-going-past-the-end-of-a-string reasons, and 2) we + * want its behavior to be consistent. + * + * In addition, it's not thread-safe, so we've marked it as + * deprecated. */ - if (device == NULL) - device_str = strdup("any"); - else { -#ifdef _WIN32 - /* - * If the string appears to be little-endian UCS-2/UTF-16, - * convert it to ASCII. + if (pcap_new_api) { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "pcap_lookupdev() is deprecated and is not supported in programs calling pcap_init()"); + return (NULL); + } + + 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)pcap_strlcpy(errbuf, "no suitable device found", + PCAP_ERRBUF_SIZE); + ret = NULL; + } else { + /* + * Return the name of the first device on the list. + */ + (void)pcap_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_LINUX_USBMON + || 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 +#ifdef PCAP_SUPPORT_DPDK + || strncmp(device, "dpdk:", 5) == 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)pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); + if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { + if (errno == EADDRNOTAVAIL) { + (void)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)pcap_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)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) { + *pathp = strdup(colonp + 3); + if (*pathp == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + free(scheme); + return (-1); + } + *schemep = scheme; + 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 || + pcap_strcasecmp(scheme, "rpcaps") == 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 (or an IPv4address, for that + * matter, just in case we get handed a + * URL with an IPv4 IP-Literal, of the sort + * that pcap_createsrcstr() used to generate, + * and that pcap_parsesrcstr(), in the original + * WinPcap code, accepted). + */ + bracketp = strchr(parsep, ']'); + if (bracketp == NULL) { + /* + * There's no closing square bracket. + */ + 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. + */ + 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_ex(char *source, int type, const char *host, const char *port, + const char *name, unsigned char uses_ssl, char *errbuf) +{ + switch (type) { + + case PCAP_SRC_FILE: + pcap_strlcpy(source, PCAP_SRC_FILE_STRING, PCAP_BUF_SIZE); + if (name != NULL && *name != '\0') { + pcap_strlcat(source, name, PCAP_BUF_SIZE); + return (0); + } else { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The file name cannot be NULL."); + return (-1); + } + + case PCAP_SRC_IFREMOTE: + pcap_strlcpy(source, + (uses_ssl ? "rpcaps://" : 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. + */ + pcap_strlcat(source, "[", PCAP_BUF_SIZE); + pcap_strlcat(source, host, PCAP_BUF_SIZE); + pcap_strlcat(source, "]", PCAP_BUF_SIZE); + } else + pcap_strlcat(source, host, PCAP_BUF_SIZE); + + if (port != NULL && *port != '\0') { + pcap_strlcat(source, ":", PCAP_BUF_SIZE); + pcap_strlcat(source, port, PCAP_BUF_SIZE); + } + + pcap_strlcat(source, "/", PCAP_BUF_SIZE); + } else { + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The host name cannot be NULL."); + return (-1); + } + + if (name != NULL && *name != '\0') + pcap_strlcat(source, name, PCAP_BUF_SIZE); + + return (0); + + case PCAP_SRC_IFLOCAL: + pcap_strlcpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); + + if (name != NULL && *name != '\0') + pcap_strlcat(source, name, PCAP_BUF_SIZE); + + return (0); + + default: + snprintf(errbuf, PCAP_ERRBUF_SIZE, + "The interface type is not valid."); + return (-1); + } +} + + +int +pcap_createsrcstr(char *source, int type, const char *host, const char *port, + const char *name, char *errbuf) +{ + return (pcap_createsrcstr_ex(source, type, host, port, name, 0, errbuf)); +} + +int +pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, + char *name, unsigned char *uses_ssl, char *errbuf) +{ + char *scheme, *tmpuserinfo, *tmphost, *tmpport, *tmppath; + + /* Initialization stuff */ + if (host) + *host = '\0'; + if (port) + *port = '\0'; + if (name) + *name = '\0'; + if (uses_ssl) + *uses_ssl = 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) + pcap_strlcpy(name, tmppath, PCAP_BUF_SIZE); + if (type) + *type = PCAP_SRC_IFLOCAL; + free(tmppath); + free(tmpport); + free(tmphost); + free(tmpuserinfo); + return (0); + } + + int is_rpcap = 0; + if (strcmp(scheme, "rpcaps") == 0) { + is_rpcap = 1; + if (uses_ssl) *uses_ssl = 1; + } else if (strcmp(scheme, "rpcap") == 0) { + is_rpcap = 1; + } + + if (is_rpcap) { + /* + * rpcap[s]:// * - * XXX - to UTF-8 instead? Or report an error if any - * character isn't ASCII? + * pcap_parse_source() has already handled the case of + * rpcap[s]://device */ - if (device[0] != '\0' && device[1] == '\0') { + if (host && tmphost) { + if (tmpuserinfo) + snprintf(host, PCAP_BUF_SIZE, "%s@%s", + tmpuserinfo, tmphost); + else + pcap_strlcpy(host, tmphost, PCAP_BUF_SIZE); + } + if (port && tmpport) + pcap_strlcpy(port, tmpport, PCAP_BUF_SIZE); + if (name && tmppath) + pcap_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) + pcap_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) + pcap_strlcpy(name, source, PCAP_BUF_SIZE); + if (type) + *type = PCAP_SRC_IFLOCAL; + free(tmppath); + free(tmpport); + free(tmphost); + free(tmpuserinfo); + free(scheme); + return (0); +} + +int +pcap_parsesrcstr(const char *source, int *type, char *host, char *port, + char *name, char *errbuf) +{ + return (pcap_parsesrcstr_ex(source, type, host, port, name, NULL, errbuf)); +} +#endif + +pcap_t * +pcap_create(const char *device, char *errbuf) +{ + size_t i; + int is_theirs; + pcap_t *p; + char *device_str; + + /* + * A null device name is equivalent to the "any" device - + * which might not be supported on this platform, but + * this means that you'll get a "not supported" error + * rather than, say, a crash when we try to dereference + * the null pointer. + */ + if (device == NULL) + device_str = strdup("any"); + else { +#ifdef _WIN32 + /* + * On Windows, for backwards compatibility reasons, + * pcap_lookupdev() returns a pointer to a sequence of + * pairs of UTF-16LE device names and local code page + * description strings. + * + * This means that if a program uses pcap_lookupdev() + * to get a default device, and hands that to an API + * that opens devices, we'll get handed a UTF-16LE + * string, not a string in the local code page. + * + * To work around that, we check whether the string + * looks as if it might be a UTF-16LE string and, if + * so, convert it back to the local code page's + * extended ASCII. + * + * We disable that check in "new API" mode, because: + * + * 1) You *cannot* reliably detect whether a + * string is UTF-16LE or not; "a" could either + * be a one-character ASCII string or the first + * character of a UTF-16LE string. + * + * 2) Doing that test can run past the end of + * the string, if it's a 1-character ASCII + * string + * + * This particular version of this heuristic dates + * back to WinPcap 4.1.1; PacketOpenAdapter() does + * uses the same heuristic, with the exact same + * vulnerability. + * + * That's why we disable this in "new API" mode. + * We keep it around in legacy mode for backwards + * compatibility. + */ + if (!pcap_new_api && device[0] != '\0' && device[1] == '\0') { size_t length; 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); } - pcap_snprintf(device_str, length + 1, "%ws", + snprintf(device_str, length + 1, "%ws", (const wchar_t *)device); } else #endif 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); } @@ -1226,25 +2388,25 @@ initialize_ops(pcap_t *p) * an activated pcap_t to point to a routine that returns * a "this isn't activated" error. */ - p->read_op = (read_op_t)pcap_not_initialized; - p->inject_op = (inject_op_t)pcap_not_initialized; - p->setfilter_op = (setfilter_op_t)pcap_not_initialized; - 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->stats_op = (stats_op_t)pcap_not_initialized; + p->read_op = pcap_read_not_initialized; + p->inject_op = pcap_inject_not_initialized; + p->setfilter_op = pcap_setfilter_not_initialized; + p->setdirection_op = pcap_setdirection_not_initialized; + p->set_datalink_op = pcap_set_datalink_not_initialized; + p->getnonblock_op = pcap_getnonblock_not_initialized; + p->stats_op = pcap_stats_not_initialized; #ifdef _WIN32 - p->stats_ex_op = (stats_ex_op_t)pcap_not_initialized_ptr; - p->setbuff_op = (setbuff_op_t)pcap_not_initialized; - p->setmode_op = (setmode_op_t)pcap_not_initialized; - p->setmintocopy_op = (setmintocopy_op_t)pcap_not_initialized; + p->stats_ex_op = pcap_stats_ex_not_initialized; + p->setbuff_op = pcap_setbuff_not_initialized; + p->setmode_op = pcap_setmode_not_initialized; + p->setmintocopy_op = pcap_setmintocopy_not_initialized; p->getevent_op = pcap_getevent_not_initialized; - p->oid_get_request_op = (oid_get_request_op_t)pcap_not_initialized; - p->oid_set_request_op = (oid_set_request_op_t)pcap_not_initialized; + p->oid_get_request_op = pcap_oid_get_request_not_initialized; + p->oid_set_request_op = pcap_oid_set_request_not_initialized; p->sendqueue_transmit_op = pcap_sendqueue_transmit_not_initialized; - p->setuserbuffer_op = (setuserbuffer_op_t)pcap_not_initialized; - p->live_dump_op = (live_dump_op_t)pcap_not_initialized; - p->live_dump_ended_op = (live_dump_ended_op_t)pcap_not_initialized; + p->setuserbuffer_op = pcap_setuserbuffer_not_initialized; + p->live_dump_op = pcap_live_dump_not_initialized; + p->live_dump_ended_op = pcap_live_dump_ended_not_initialized; p->get_airpcap_handle_op = pcap_get_airpcap_handle_not_initialized; #endif @@ -1260,287 +2422,65 @@ initialize_ops(pcap_t *p) * be used for pcap_next()/pcap_next_ex(). */ p->oneshot_callback = pcap_oneshot; + + /* + * Default breakloop operation - implementations can override + * this, but should call pcap_breakloop_common() before doing + * their own logic. + */ + p->breakloop_op = pcap_breakloop_common; } static pcap_t * -pcap_alloc_pcap_t(char *ebuf, size_t size) +pcap_alloc_pcap_t(char *ebuf, size_t total_size, size_t private_offset) { char *chunk; pcap_t *p; /* - * Allocate a chunk of memory big enough for a pcap_t - * 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. - */ - chunk = malloc(sizeof (pcap_t) + size); - if (chunk == NULL) { - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s", - pcap_strerror(errno)); - return (NULL); - } - memset(chunk, 0, sizeof (pcap_t) + size); - - /* - * Get a pointer to the pcap_t at the beginning. + * total_size is the size of a structure containing a pcap_t + * followed by a private structure. */ - p = (pcap_t *)chunk; - -#ifndef _WIN32 - p->fd = -1; /* not opened yet */ - p->selectable_fd = -1; -#endif - - if (size == 0) { - /* No private data was requested. */ - p->priv = NULL; - } else { - /* - * 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)); - } - - return (p); -} - -#ifdef HAVE_REMOTE -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) && (*name)) - { - 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) && (*host)) - { - if ((strcspn(host, "aAbBcCdDeEfFgGhHjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ")) == strlen(host)) - { - /* the host name does not contains alphabetic chars. So, it is a numeric address */ - /* In this case we have to include it between 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) && (*port)) - { - 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) && (*name)) - strlcat(source, name, PCAP_BUF_SIZE); - - return 0; - } - - case PCAP_SRC_IFLOCAL: - { - strlcpy(source, PCAP_SRC_IF_STRING, PCAP_BUF_SIZE); - - if ((name) && (*name)) - 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 *ptr; - int ntoken; - char tmpname[PCAP_BUF_SIZE]; - char tmphost[PCAP_BUF_SIZE]; - char tmpport[PCAP_BUF_SIZE]; - int tmptype; - - /* Initialization stuff */ - tmpname[0] = 0; - tmphost[0] = 0; - tmpport[0] = 0; - - if (host) - *host = 0; - if (port) - *port = 0; - if (name) - *name = 0; - - /* Look for a 'rpcap://' identifier */ - if ((ptr = strstr(source, PCAP_SRC_IF_STRING)) != NULL) - { - if (strlen(PCAP_SRC_IF_STRING) == strlen(source)) - { - /* The source identifier contains only the 'rpcap://' string. */ - /* So, this is a local capture. */ - *type = PCAP_SRC_IFLOCAL; - return 0; - } - - ptr += strlen(PCAP_SRC_IF_STRING); - - if (strchr(ptr, '[')) /* This is probably a numeric address */ - { - ntoken = sscanf(ptr, "[%[1234567890:.]]:%[^/]/%s", tmphost, tmpport, tmpname); - - if (ntoken == 1) /* probably the port is missing */ - ntoken = sscanf(ptr, "[%[1234567890:.]]/%s", tmphost, tmpname); - - tmptype = PCAP_SRC_IFREMOTE; - } - else - { - ntoken = sscanf(ptr, "%[^/:]:%[^/]/%s", tmphost, tmpport, tmpname); - - if (ntoken == 1) - { - /* - * This can be due to two reasons: - * - we want a remote capture, but the network port is missing - * - we want to do a local capture - * To distinguish between the two, we look for the '/' char - */ - if (strchr(ptr, '/')) - { - /* We're on a remote capture */ - sscanf(ptr, "%[^/]/%s", tmphost, tmpname); - tmptype = PCAP_SRC_IFREMOTE; - } - else - { - /* We're on a local capture */ - if (*ptr) - strlcpy(tmpname, ptr, PCAP_BUF_SIZE); - - /* Clean the host name, since it is a remote capture */ - /* NOTE: the host name has been assigned in the previous "ntoken= sscanf(...)" line */ - tmphost[0] = 0; - - tmptype = PCAP_SRC_IFLOCAL; - } - } - else - tmptype = PCAP_SRC_IFREMOTE; - } - - if (host) - strlcpy(host, tmphost, PCAP_BUF_SIZE); - if (port) - strlcpy(port, tmpport, PCAP_BUF_SIZE); - if (type) - *type = tmptype; - - if (name) - { - /* - * If the user wants the host name, but it cannot be located into the source string, return error - * However, if the user is not interested in the interface name (e.g. if we're called by - * pcap_findalldevs_ex(), which does not have interface name, do not return error - */ - if (tmpname[0]) - { - strlcpy(name, tmpname, PCAP_BUF_SIZE); - } - else - { - if (errbuf) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface name has not been specified in the source string."); - - return -1; - } - } - - return 0; - } - - /* Look for a 'file://' identifier */ - if ((ptr = strstr(source, PCAP_SRC_FILE_STRING)) != NULL) - { - ptr += strlen(PCAP_SRC_FILE_STRING); - if (*ptr) - { - if (name) - strlcpy(name, ptr, PCAP_BUF_SIZE); - - if (type) - *type = PCAP_SRC_FILE; - - return 0; - } - else - { - if (errbuf) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The file name has not been specified in the source string."); - - return -1; - } - + chunk = calloc(total_size, 1); + if (chunk == NULL) { + pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, + errno, "malloc"); + return (NULL); } - /* Backward compatibility; the user didn't use the 'rpcap://, file://' specifiers */ - if ((source) && (*source)) - { - if (name) - strlcpy(name, source, PCAP_BUF_SIZE); + /* + * Get a pointer to the pcap_t at the beginning. + */ + p = (pcap_t *)chunk; - if (type) - *type = PCAP_SRC_IFLOCAL; +#ifdef _WIN32 + p->handle = INVALID_HANDLE_VALUE; /* not opened yet */ +#else /* _WIN32 */ + p->fd = -1; /* not opened yet */ +#ifndef MSDOS + p->selectable_fd = -1; + p->required_select_timeout = NULL; +#endif /* MSDOS */ +#endif /* _WIN32 */ - return 0; - } - else - { - if (errbuf) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface name has not been specified in the source string."); + /* + * private_offset is the offset, in bytes, of the private + * data from the beginning of the structure. + * + * Set the pointer to the private data; that's private_offset + * bytes past the pcap_t. + */ + p->priv = (void *)(chunk + private_offset); - return -1; - } + return (p); } -#endif pcap_t * -pcap_create_common(char *ebuf, size_t size) +pcap_create_common(char *ebuf, size_t total_size, size_t private_offset) { pcap_t *p; - p = pcap_alloc_pcap_t(ebuf, size); + p = pcap_alloc_pcap_t(ebuf, total_size, private_offset); if (p == NULL) return (NULL); @@ -1566,13 +2506,19 @@ pcap_create_common(char *ebuf, size_t size) p->opt.timeout = 0; /* no timeout specified */ p->opt.buffer_size = 0; /* use the platform's default */ p->opt.promisc = 0; -#ifdef __linux__ - p->opt.protocol = 0; -#endif p->opt.rfmon = 0; 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. @@ -1586,7 +2532,7 @@ int pcap_check_activated(pcap_t *p) { if (p->activated) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't perform " + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "can't perform " " operation on activated capture"); return (-1); } @@ -1794,7 +2740,7 @@ pcap_activate(pcap_t *p) * handle errors other than PCAP_ERROR, return the * error message corresponding to the status. */ - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s", pcap_statustostr(status)); } @@ -1812,12 +2758,22 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er { pcap_t *p; int status; -#ifdef HAVE_REMOTE +#ifdef ENABLE_REMOTE char host[PCAP_BUF_SIZE + 1]; char port[PCAP_BUF_SIZE + 1]; char name[PCAP_BUF_SIZE + 1]; int srctype; + /* + * A null device name is equivalent to the "any" device - + * which might not be supported on this platform, but + * this means that you'll get a "not supported" error + * rather than, say, a crash when we try to dereference + * the null pointer. + */ + if (device == NULL) + device = "any"; + /* * Retrofit - we have to make older applications compatible with * remote capture. @@ -1842,7 +2798,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er NULL, errbuf)); } if (srctype == PCAP_SRC_FILE) { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown URL scheme \"file\""); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "unknown URL scheme \"file\""); return (NULL); } if (srctype == PCAP_SRC_IFLOCAL) { @@ -1858,7 +2814,7 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er device += strlen(PCAP_SRC_IF_STRING); } } -#endif /* HAVE_REMOTE */ +#endif /* ENABLE_REMOTE */ p = pcap_create(device, errbuf); if (p == NULL) @@ -1888,27 +2844,51 @@ pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *er goto fail; return (p); fail: - if (status == PCAP_ERROR) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device, - p->errbuf); - else if (status == PCAP_ERROR_NO_SUCH_DEVICE || + if (status == PCAP_ERROR) { + /* + * Another buffer is a bit cumbersome, but it avoids + * -Wformat-truncation. + */ + char trimbuf[PCAP_ERRBUF_SIZE - 5]; /* 2 bytes shorter */ + + pcap_strlcpy(trimbuf, p->errbuf, sizeof(trimbuf)); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %.*s", device, + PCAP_ERRBUF_SIZE - 3, trimbuf); + } else if (status == PCAP_ERROR_NO_SUCH_DEVICE || status == PCAP_ERROR_PERM_DENIED || - status == PCAP_ERROR_PROMISC_PERM_DENIED) - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%s)", device, - pcap_statustostr(status), p->errbuf); - else - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device, + status == PCAP_ERROR_PROMISC_PERM_DENIED) { + /* + * Only show the additional message if it's not + * empty. + */ + if (p->errbuf[0] != '\0') { + /* + * Idem. + */ + char trimbuf[PCAP_ERRBUF_SIZE - 8]; /* 2 bytes shorter */ + + pcap_strlcpy(trimbuf, p->errbuf, sizeof(trimbuf)); + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%.*s)", + device, pcap_statustostr(status), + PCAP_ERRBUF_SIZE - 6, trimbuf); + } else { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", + device, pcap_statustostr(status)); + } + } else { + snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", device, pcap_statustostr(status)); + } pcap_close(p); return (NULL); } pcap_t * -pcap_open_offline_common(char *ebuf, size_t size) +pcap_open_offline_common(char *ebuf, size_t total_size, size_t private_offset) { pcap_t *p; - p = pcap_alloc_pcap_t(ebuf, size); + p = pcap_alloc_pcap_t(ebuf, total_size, private_offset); if (p == NULL) return (NULL); @@ -1959,7 +2939,7 @@ pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user) void pcap_breakloop(pcap_t *p) { - p->break_loop = 1; + p->breakloop_op(p); } int @@ -1992,8 +2972,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; @@ -2001,8 +2981,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, @@ -2082,11 +3062,11 @@ pcap_set_datalink(pcap_t *p, int dlt) unsupported: dlt_name = pcap_datalink_val_to_name(dlt); if (dlt_name != NULL) { - (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf), + (void) snprintf(p->errbuf, sizeof(p->errbuf), "%s is not one of the DLTs supported by this device", dlt_name); } else { - (void) pcap_snprintf(p->errbuf, sizeof(p->errbuf), + (void) snprintf(p->errbuf, sizeof(p->errbuf), "DLT %d is not one of the DLTs supported by this device", dlt); } @@ -2199,7 +3179,7 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(RAW, "Raw IP"), DLT_CHOICE(SLIP_BSDOS, "BSD/OS SLIP"), DLT_CHOICE(PPP_BSDOS, "BSD/OS PPP"), - DLT_CHOICE(ATM_CLIP, "Linux Classical IP-over-ATM"), + DLT_CHOICE(ATM_CLIP, "Linux Classical IP over ATM"), DLT_CHOICE(PPP_SERIAL, "PPP over serial"), DLT_CHOICE(PPP_ETHER, "PPPoE"), DLT_CHOICE(SYMANTEC_FIREWALL, "Symantec Firewall"), @@ -2208,7 +3188,7 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(FRELAY, "Frame Relay"), DLT_CHOICE(LOOP, "OpenBSD loopback"), DLT_CHOICE(ENC, "OpenBSD encapsulated IP"), - DLT_CHOICE(LINUX_SLL, "Linux cooked"), + DLT_CHOICE(LINUX_SLL, "Linux cooked v1"), DLT_CHOICE(LTALK, "Localtalk"), DLT_CHOICE(PFLOG, "OpenBSD pflog file"), DLT_CHOICE(PFSYNC, "Packet filter state syncing"), @@ -2242,7 +3222,7 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(GPF_T, "GPF-T"), DLT_CHOICE(GPF_F, "GPF-F"), DLT_CHOICE(JUNIPER_PIC_PEER, "Juniper PIC Peer"), - DLT_CHOICE(ERF_ETH, "Ethernet with Endace ERF header"), + DLT_CHOICE(ERF_ETH, "Ethernet with Endace ERF header"), DLT_CHOICE(ERF_POS, "Packet-over-SONET with Endace ERF header"), DLT_CHOICE(LINUX_LAPD, "Linux vISDN LAPD"), DLT_CHOICE(JUNIPER_ETHER, "Juniper Ethernet"), @@ -2266,10 +3246,11 @@ static struct dlt_choice dlt_choices[] = { DLT_CHOICE(SITA, "SITA pseudo-header"), DLT_CHOICE(ERF, "Endace ERF header"), DLT_CHOICE(RAIF1, "Ethernet with u10 Networks pseudo-header"), - DLT_CHOICE(IPMB, "IPMB"), + DLT_CHOICE(IPMB_KONTRON, "IPMB with Kontron pseudo-header"), DLT_CHOICE(JUNIPER_ST, "Juniper Secure Tunnel"), DLT_CHOICE(BLUETOOTH_HCI_H4_WITH_PHDR, "Bluetooth HCI UART transport layer plus pseudo-header"), DLT_CHOICE(AX25_KISS, "AX.25 with KISS header"), + DLT_CHOICE(IPMB_LINUX, "IPMB with Linux/Pigeon Point pseudo-header"), DLT_CHOICE(IEEE802_15_4_NONASK_PHY, "IEEE 802.15.4 with non-ASK PHY data"), DLT_CHOICE(MPLS, "MPLS with label as link-layer header"), DLT_CHOICE(LINUX_EVDEV, "Linux evdev events"), @@ -2323,6 +3304,22 @@ static struct dlt_choice dlt_choices[] = { 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(LINUX_SLL2, "Linux cooked v2"), + DLT_CHOICE(OPENVIZSLA, "OpenVizsla USB"), + DLT_CHOICE(EBHSCR, "Elektrobit High Speed Capture and Replay (EBHSCR)"), + DLT_CHOICE(VPP_DISPATCH, "VPP graph dispatch tracer"), + DLT_CHOICE(DSA_TAG_BRCM, "Broadcom tag"), + DLT_CHOICE(DSA_TAG_BRCM_PREPEND, "Broadcom tag (prepended)"), + DLT_CHOICE(IEEE802_15_4_TAP, "IEEE 802.15.4 with pseudo-header"), + DLT_CHOICE(DSA_TAG_DSA, "Marvell DSA"), + DLT_CHOICE(DSA_TAG_EDSA, "Marvell EDSA"), + DLT_CHOICE(ELEE, "ELEE lawful intercept packets"), + DLT_CHOICE(Z_WAVE_SERIAL, "Z-Wave serial frames between host and chip"), + DLT_CHOICE(USB_2_0, "USB 2.0/1.1/1.0 as transmitted over the cable"), + DLT_CHOICE(ATSC_ALP, "ATSC Link-Layer Protocol packets"), DLT_CHOICE_SENTINEL }; @@ -2362,6 +3359,21 @@ pcap_datalink_val_to_description(int dlt) return (NULL); } +const char * +pcap_datalink_val_to_description_or_dlt(int dlt) +{ + static char unkbuf[40]; + const char *description; + + description = pcap_datalink_val_to_description(dlt); + if (description != NULL) { + return description; + } else { + (void)snprintf(unkbuf, sizeof(unkbuf), "DLT %d", dlt); + return unkbuf; + } +} + struct tstamp_type_choice { const char *name; const char *description; @@ -2374,6 +3386,7 @@ static struct tstamp_type_choice tstamp_type_choices[] = { { "host_hiprec", "Host, high precision", PCAP_TSTAMP_HOST_HIPREC }, { "adapter", "Adapter", PCAP_TSTAMP_ADAPTER }, { "adapter_unsynced", "Adapter, not synced with system time", PCAP_TSTAMP_ADAPTER_UNSYNCED }, + { "host_hiprec_unsynced", "Host, high precision, not synced with system time", PCAP_TSTAMP_HOST_HIPREC_UNSYNCED }, { NULL, NULL, 0 } }; @@ -2459,18 +3472,33 @@ pcap_file(pcap_t *p) return (p->rfile); } +#ifdef _WIN32 int pcap_fileno(pcap_t *p) { -#ifndef _WIN32 - return (p->fd); -#else - if (p->adapter != NULL) - return ((int)(DWORD)p->adapter->hFile); - else + if (p->handle != INVALID_HANDLE_VALUE) { + /* + * This is a bogus and now-deprecated API; we + * squelch the narrowing warning for the cast + * from HANDLE to intptr_t. If Windows programmmers + * need to get at the HANDLE for a pcap_t, *if* + * there is one, they should request such a + * routine (and be prepared for it to return + * INVALID_HANDLE_VALUE). + */ +DIAG_OFF_NARROWING + return ((int)(intptr_t)p->handle); +DIAG_ON_NARROWING + } else return (PCAP_ERROR); -#endif } +#else /* _WIN32 */ +int +pcap_fileno(pcap_t *p) +{ + return (p->fd); +} +#endif /* _WIN32 */ #if !defined(_WIN32) && !defined(MSDOS) int @@ -2478,6 +3506,12 @@ pcap_get_selectable_fd(pcap_t *p) { return (p->selectable_fd); } + +const struct timeval * +pcap_get_required_select_timeout(pcap_t *p) +{ + return (p->required_select_timeout); +} #endif void @@ -2508,7 +3542,7 @@ pcap_getnonblock(pcap_t *p, char *errbuf) * We copy the error message to errbuf, so callers * can find it in either place. */ - strlcpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); + pcap_strlcpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); } return (ret); } @@ -2525,8 +3559,8 @@ pcap_getnonblock_fd(pcap_t *p) fdflags = fcntl(p->fd, F_GETFL, 0); if (fdflags == -1) { - pcap_snprintf(p->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) @@ -2552,7 +3586,7 @@ pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf) * We copy the error message to errbuf, so callers * can find it in either place. */ - strlcpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); + pcap_strlcpy(errbuf, p->errbuf, PCAP_ERRBUF_SIZE); } return (ret); } @@ -2571,8 +3605,8 @@ pcap_setnonblock_fd(pcap_t *p, int nonblock) fdflags = fcntl(p->fd, F_GETFL, 0); if (fdflags == -1) { - pcap_snprintf(p->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) @@ -2580,43 +3614,14 @@ pcap_setnonblock_fd(pcap_t *p, int nonblock) else fdflags &= ~O_NONBLOCK; if (fcntl(p->fd, F_SETFL, fdflags) == -1) { - pcap_snprintf(p->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); } #endif -#ifdef _WIN32 -/* - * Generate a string for a Win32-specific error (i.e. an error generated when - * calling a Win32 API). - * For errors occurred during standard C calls, we still use pcap_strerror() - */ -void -pcap_win32_err_to_str(DWORD error, char *errbuf) -{ - size_t errlen; - char *p; - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, errbuf, - PCAP_ERRBUF_SIZE, NULL); - - /* - * "FormatMessage()" "helpfully" sticks CR/LF at the end of the - * message. Get rid of it. - */ - errlen = strlen(errbuf); - if (errlen >= 2) { - errbuf[errlen - 1] = '\0'; - errbuf[errlen - 2] = '\0'; - } - p = strchr(errbuf, '\0'); - pcap_snprintf (p, PCAP_ERRBUF_SIZE+1-(p-errbuf), " (%lu)", error); -} -#endif - /* * Generate error strings for PCAP_ERROR_ and PCAP_WARNING_ values. */ @@ -2658,7 +3663,7 @@ pcap_statustostr(int errnum) return ("That operation is supported only in monitor mode"); case PCAP_ERROR_PERM_DENIED: - return ("You don't have permission to capture on that device"); + return ("You don't have permission to perform this capture on that device"); case PCAP_ERROR_IFACE_NOT_UP: return ("That device is not up"); @@ -2672,7 +3677,7 @@ pcap_statustostr(int errnum) case PCAP_ERROR_TSTAMP_PRECISION_NOTSUP: return ("That device doesn't support that time stamp precision"); } - (void)pcap_snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); + (void)snprintf(ebuf, sizeof ebuf, "Unknown error: %d", errnum); return(ebuf); } @@ -2688,7 +3693,7 @@ pcap_strerror(int errnum) 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); + pcap_strlcpy(errbuf, "strerror_s() error", PCAP_ERRBUF_SIZE); return (errbuf); #else return (strerror(errnum)); @@ -2700,7 +3705,7 @@ pcap_strerror(int errnum) if ((unsigned int)errnum < sys_nerr) return ((char *)sys_errlist[errnum]); - (void)pcap_snprintf(errbuf, sizeof errbuf, "Unknown error: %d", errnum); + (void)snprintf(errbuf, sizeof errbuf, "Unknown error: %d", errnum); return (errbuf); #endif } @@ -2721,11 +3726,29 @@ int pcap_setdirection(pcap_t *p, pcap_direction_t d) { if (p->setdirection_op == NULL) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Setting direction is not implemented on this platform"); + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Setting direction is not supported on this device"); return (-1); - } else - return (p->setdirection_op(p, d)); + } else { + switch (d) { + + case PCAP_D_IN: + case PCAP_D_OUT: + case PCAP_D_INOUT: + /* + * Valid direction. + */ + return (p->setdirection_op(p, d)); + + default: + /* + * Invalid direction. + */ + snprintf(p->errbuf, sizeof(p->errbuf), + "Invalid direction"); + return (-1); + } + } } int @@ -2734,14 +3757,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) @@ -2755,86 +3770,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) { @@ -2890,56 +3855,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) { @@ -2947,17 +3880,11 @@ pcap_get_airpcap_handle(pcap_t *p) handle = p->get_airpcap_handle_op(p); if (handle == NULL) { - (void)pcap_snprintf(p->errbuf, sizeof(p->errbuf), + (void)snprintf(p->errbuf, sizeof(p->errbuf), "This isn't an AirPcap device"); } return (handle); } - -static PAirpcapHandle -pcap_get_airpcap_handle_dead(pcap_t *p) -{ - return (NULL); -} #endif /* @@ -2990,8 +3917,28 @@ pcap_close_all(void) { struct pcap *handle; - while ((handle = pcaps_to_close) != NULL) + while ((handle = pcaps_to_close) != NULL) { pcap_close(handle); + + /* + * If a pcap module adds a pcap_t to the "close all" + * list by calling pcap_add_to_pcaps_to_close(), it + * must have a cleanup routine that removes it from the + * list, by calling pcap_remove_from_pcaps_to_close(), + * and must make that cleanup routine the cleanup_op + * for the pcap_t. + * + * That means that, after pcap_close() - which calls + * the cleanup_op for the pcap_t - the pcap_t must + * have been removed from the list, so pcaps_to_close + * must not be equal to handle. + * + * We check for that, and abort if handle is still + * at the head of the list, to prevent infinite loops. + */ + if (pcaps_to_close == handle) + abort(); + } } int @@ -3006,7 +3953,7 @@ pcap_do_addexit(pcap_t *p) /* * "atexit()" failed; let our caller know. */ - strlcpy(p->errbuf, "atexit failed", PCAP_ERRBUF_SIZE); + pcap_strlcpy(p->errbuf, "atexit failed", PCAP_ERRBUF_SIZE); return (0); } did_atexit = 1; @@ -3048,9 +3995,20 @@ pcap_remove_from_pcaps_to_close(pcap_t *p) } } +void +pcap_breakloop_common(pcap_t *p) +{ + p->break_loop = 1; +} + + void pcap_cleanup_live_common(pcap_t *p) { + if (p->opt.device != NULL) { + free(p->opt.device); + p->opt.device = NULL; + } if (p->buffer != NULL) { free(p->buffer); p->buffer = NULL; @@ -3077,67 +4035,7 @@ pcap_cleanup_live_common(pcap_t *p) p->fd = -1; } p->selectable_fd = -1; -#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)); +#endif } /* @@ -3149,6 +4047,12 @@ pcap_open_dead(int linktype, int snaplen) int pcap_sendpacket(pcap_t *p, const u_char *buf, int size) { + if (size <= 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "The number of bytes to be sent must be positive"); + return (PCAP_ERROR); + } + if (p->inject_op(p, buf, size) == -1) return (-1); return (0); @@ -3161,18 +4065,107 @@ pcap_sendpacket(pcap_t *p, const u_char *buf, int size) int pcap_inject(pcap_t *p, const void *buf, size_t size) { - return (p->inject_op(p, buf, size)); + /* + * We return the number of bytes written, so the number of + * bytes to write must fit in an int. + */ + if (size > INT_MAX) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "More than %d bytes cannot be injected", INT_MAX); + return (PCAP_ERROR); + } + + if (size == 0) { + pcap_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE, + errno, "The number of bytes to be injected must not be zero"); + return (PCAP_ERROR); + } + + return (p->inject_op(p, buf, (int)size)); } void pcap_close(pcap_t *p) { - if (p->opt.device != NULL) - free(p->opt.device); p->cleanup_op(p); free(p); } +/* + * Helpers for safely loading code at run time. + * Currently Windows-only. + */ +#ifdef _WIN32 +// +// This wrapper around loadlibrary appends the system folder (usually +// C:\Windows\System32) to the relative path of the DLL, so that the DLL +// is always loaded from an absolute path (it's no longer possible to +// load modules from the application folder). +// This solves the DLL Hijacking issue discovered in August 2010: +// +// https://blog.rapid7.com/2010/08/23/exploiting-dll-hijacking-flaws/ +// https://blog.rapid7.com/2010/08/23/application-dll-load-hijacking/ +// (the purported Rapid7 blog post link in the first of those two links +// is broken; the second of those links works.) +// +// If any links there are broken from all the content shuffling Rapid& +// did, see archived versions of the posts at their original homes, at +// +// https://web.archive.org/web/20110122175058/http://blog.metasploit.com/2010/08/exploiting-dll-hijacking-flaws.html +// https://web.archive.org/web/20100828112111/http://blog.rapid7.com/?p=5325 +// +pcap_code_handle_t +pcap_load_code(const char *name) +{ + /* + * XXX - should this work in UTF-16LE rather than in the local + * ANSI code page? + */ + CHAR path[MAX_PATH]; + CHAR fullFileName[MAX_PATH]; + UINT res; + HMODULE hModule = NULL; + + do + { + res = GetSystemDirectoryA(path, MAX_PATH); + + if (res == 0) { + // + // some bad failure occurred; + // + break; + } + + if (res > MAX_PATH) { + // + // the buffer was not big enough + // + SetLastError(ERROR_INSUFFICIENT_BUFFER); + break; + } + + if (res + 1 + strlen(name) + 1 < MAX_PATH) { + memcpy(fullFileName, path, res * sizeof(TCHAR)); + fullFileName[res] = '\\'; + memcpy(&fullFileName[res + 1], name, (strlen(name) + 1) * sizeof(TCHAR)); + + hModule = LoadLibraryA(fullFileName); + } else + SetLastError(ERROR_INSUFFICIENT_BUFFER); + + } while(FALSE); + + return hModule; +} + +pcap_funcptr_t +pcap_find_function(pcap_code_handle_t code, const char *func) +{ + return (GetProcAddress(code, func)); +} +#endif + /* * Given a BPF program, a pcap_pkthdr structure for a packet, and the raw * data for the packet, check whether the packet passes the filter. @@ -3186,205 +4179,273 @@ pcap_offline_filter(const struct bpf_program *fp, const struct pcap_pkthdr *h, const struct bpf_insn *fcode = fp->bf_insns; if (fcode != NULL) - return (bpf_filter(fcode, pkt, h->len, h->caplen)); + return (pcap_filter(fcode, pkt, h->len, h->caplen)); else return (0); } -#include "pcap_version.h" +static int +pcap_can_set_rfmon_dead(pcap_t *p) +{ + 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_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Packets aren't available from a pcap_open_dead pcap_t"); + return (-1); +} + +static void +pcap_breakloop_dead(pcap_t *p _U_) +{ + /* + * A "dead" pcap_t is just a placeholder to use in order to + * compile a filter to BPF code or to open a savefile for + * writing. It doesn't support any operations, including + * capturing or reading packets, so there will never be a + * get-packets loop in progress to break out *of*. + * + * As such, this routine doesn't need to do anything. + */ +} + +static int +pcap_inject_dead(pcap_t *p, const void *buf _U_, int size _U_) +{ + 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_) +{ + 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_) +{ + 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_) +{ + 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) +{ + 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_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); + return (-1); +} -static const char *pcap_lib_version_string; +static int +pcap_stats_dead(pcap_t *p, struct pcap_stat *ps _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Statistics aren't available from a pcap_open_dead pcap_t"); + return (-1); +} #ifdef _WIN32 +static struct pcap_stat * +pcap_stats_ex_dead(pcap_t *p, int *pcap_stat_size _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Statistics aren't available from a pcap_open_dead pcap_t"); + return (NULL); +} -#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_setbuff_dead(pcap_t *p, int dim _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The kernel buffer size cannot be set 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_setmode_dead(pcap_t *p, int mode _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "impossible to set mode on a pcap_open_dead pcap_t"); + return (-1); +} -const char * -pcap_lib_version(void) +static int +pcap_setmintocopy_dead(pcap_t *p, int size _U_) { - char *packet_version_string; - size_t full_pcap_version_string_len; - char *full_pcap_version_string; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The mintocopy parameter cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} - if (pcap_lib_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); - } - pcap_lib_version_string = full_pcap_version_string; - } - return (pcap_lib_version_string); +static HANDLE +pcap_getevent_dead(pcap_t *p) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "A pcap_open_dead pcap_t has no event handle"); + return (INVALID_HANDLE_VALUE); } -#else /* HAVE_VERSION_H */ +static int +pcap_oid_get_request_dead(pcap_t *p, bpf_u_int32 oid _U_, void *data _U_, + size_t *lenp _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID get 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 int +pcap_oid_set_request_dead(pcap_t *p, bpf_u_int32 oid _U_, const void *data _U_, + size_t *lenp _U_) { - char *packet_version_string; - size_t full_pcap_version_string_len; - char *full_pcap_version_string; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "An OID set request cannot be performed on a pcap_open_dead pcap_t"); + return (PCAP_ERROR); +} - if (pcap_lib_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); - pcap_lib_version_string = full_pcap_version_string; - } - return (pcap_lib_version_string); +static u_int +pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue _U_, + int sync _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Packets cannot be transmitted on a pcap_open_dead pcap_t"); + return (0); } -#endif /* HAVE_VERSION_H */ +static int +pcap_setuserbuffer_dead(pcap_t *p, int size _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "The user buffer cannot be set on a pcap_open_dead pcap_t"); + return (-1); +} -#elif defined(MSDOS) +static int +pcap_live_dump_dead(pcap_t *p, char *filename _U_, int maxsize _U_, + int maxpacks _U_) +{ + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); + return (-1); +} -const char * -pcap_lib_version(void) +static int +pcap_live_dump_ended_dead(pcap_t *p, int sync _U_) { - char *packet_version_string; - size_t full_pcap_version_string_len; - char *full_pcap_version_string; - static char dospfx[] = "DOS-"; + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); + return (-1); +} - if (pcap_lib_version_string == NULL) { - /* - * Generate the version string. - */ - 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); - pcap_lib_version_string = full_pcap_version_string; - } - return (pcap_lib_version_string); +static PAirpcapHandle +pcap_get_airpcap_handle_dead(pcap_t *p _U_) +{ + return (NULL); } +#endif /* _WIN32 */ -#else /* UN*X */ +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) { - const char *platform_version_string; - size_t full_pcap_version_string_len; - char *full_pcap_version_string; + pcap_t *p; + + switch (precision) { + + case PCAP_TSTAMP_PRECISION_MICRO: + case PCAP_TSTAMP_PRECISION_NANO: + break; - if (pcap_lib_version_string == NULL) { + default: /* - * Generate the version string. - * Get any platform-specific information. - * - * XXX - what about all the local capture modules other - * that the "native interface" one? That could make - * the version string really long. + * 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. */ - platform_version_string = pcap_platform_lib_version(); - if (platform_version_string == NULL) { - /* - * No platform-specific information. - */ - pcap_lib_version_string = pcap_version_string; - } else { - /* - * Add on the platform-specific information. - */ - full_pcap_version_string_len = - strlen(pcap_version_string) + 2 + strlen(platform_version_string) + 1 + 1; - 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, - "%s (%s)", pcap_version_string, - platform_version_string); - pcap_lib_version_string = full_pcap_version_string; - } + precision = PCAP_TSTAMP_PRECISION_MICRO; + break; } - return (pcap_lib_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->breakloop_op = pcap_breakloop_dead; + 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)); +} #ifdef YYDEBUG /* @@ -3404,32 +4465,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