X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/3329c692d4884dfaa7b29393f9c3c28517133256..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap.c diff --git a/pcap.c b/pcap.c index 24f9c432..ef1bbb71 100644 --- a/pcap.c +++ b/pcap.c @@ -53,7 +53,6 @@ struct rtentry; /* declarations in */ #include #endif /* _WIN32 */ -#include #include #include #include @@ -64,6 +63,8 @@ struct rtentry; /* declarations in */ #include #include +#include "diag-control.h" + #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif @@ -92,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,55 +125,187 @@ struct rtentry; /* declarations in */ #include "pcap-dpdk.h" #endif +#ifdef HAVE_AIRPCAP_API +#include "pcap-airpcap.h" +#endif + #ifdef _WIN32 /* - * DllMain(), required when built as a Windows DLL. + * 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.) */ -BOOL WINAPI DllMain( - HANDLE hinstDLL, - DWORD dwReason, - LPVOID lpvReserved -) -{ - return (TRUE); -} /* - * Start WinSock. - * Exported in case some applications using WinPcap called it, - * even though it wasn't exported. + * Start Winsock. + * Internal routine. */ -int -wsockinit(void) +static int +internal_wsockinit(char *errbuf) { WORD wVersionRequested; WSADATA wsaData; static int err = -1; static int done = 0; + int status; if (done) return (err); - wVersionRequested = MAKEWORD( 1, 1); - err = WSAStartup( wVersionRequested, &wsaData ); - atexit ((void(*)(void))WSACleanup); + /* + * 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 ( err != 0 ) - err = -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 (wsockinit()); + 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 @@ -191,12 +324,12 @@ pcap_set_not_initialized_message(pcap_t *pcap) { if (pcap->activated) { /* A module probably forgot to set the function pointer */ - (void)pcap_snprintf(pcap->errbuf, sizeof(pcap->errbuf), + (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"); } @@ -258,7 +391,7 @@ pcap_stats_not_initialized(pcap_t *pcap, struct pcap_stat *ps _U_) } #ifdef _WIN32 -struct pcap_stat * +static struct pcap_stat * pcap_stats_ex_not_initialized(pcap_t *pcap, int *pcap_stat_size _U_) { pcap_set_not_initialized_message(pcap); @@ -313,7 +446,8 @@ pcap_oid_set_request_not_initialized(pcap_t *pcap, bpf_u_int32 oid _U_, } 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_) { pcap_set_not_initialized_message(pcap); return (0); @@ -385,8 +519,17 @@ 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); @@ -397,8 +540,8 @@ pcap_list_tstamp_types(pcap_t *p, int **tstamp_typesp) } (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); } /* @@ -470,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 @@ -489,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". */ @@ -525,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 @@ -542,6 +689,9 @@ static struct capture_source_type { #endif #ifdef PCAP_SUPPORT_DPDK { pcap_dpdk_findalldevs, pcap_dpdk_create }, +#endif +#ifdef HAVE_AIRPCAP_API + { airpcap_findalldevs, airpcap_create }, #endif { NULL, NULL } }; @@ -622,39 +772,26 @@ 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)) @@ -681,6 +818,13 @@ get_figure_of_merit(pcap_if_t *dev) 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); } @@ -802,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. */ @@ -881,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 @@ -1362,6 +1506,22 @@ pcap_lookupdev(char *errbuf) static char device[IF_NAMESIZE + 1]; char *ret; + /* + * 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 (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); @@ -1425,7 +1585,7 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, #ifdef PCAP_SUPPORT_BT || strstr(device, "bluetooth") != NULL #endif -#ifdef PCAP_SUPPORT_USB +#ifdef PCAP_SUPPORT_LINUX_USBMON || strstr(device, "usbmon") != NULL #endif #ifdef HAVE_SNF_API @@ -1457,7 +1617,7 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, (void)pcap_strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name)); if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) < 0) { if (errno == EADDRNOTAVAIL) { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: no IPv4 address assigned", device); } else { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, @@ -1490,7 +1650,7 @@ pcap_lookupnet(const char *device, bpf_u_int32 *netp, bpf_u_int32 *maskp, else if (IN_CLASSC(*netp)) *maskp = IN_CLASSC_NET; else { - (void)pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + (void)snprintf(errbuf, PCAP_ERRBUF_SIZE, "inet class for 0x%x unknown", *netp); return (-1); } @@ -1786,7 +1946,7 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, /* * There's no closing square bracket. */ - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "IP-literal in URL doesn't end with ]"); free(userinfo); free(authority); @@ -1799,7 +1959,7 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, * There's extra crud after the * closing square bracketn. */ - pcap_snprintf(ebuf, PCAP_ERRBUF_SIZE, + snprintf(ebuf, PCAP_ERRBUF_SIZE, "Extra text after IP-literal in URL"); free(userinfo); free(authority); @@ -1905,7 +2065,7 @@ pcap_createsrcstr_ex(char *source, int type, const char *host, const char *port, pcap_strlcat(source, name, PCAP_BUF_SIZE); return (0); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The file name cannot be NULL."); return (-1); } @@ -1934,7 +2094,7 @@ pcap_createsrcstr_ex(char *source, int type, const char *host, const char *port, pcap_strlcat(source, "/", PCAP_BUF_SIZE); } else { - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The host name cannot be NULL."); return (-1); } @@ -1953,7 +2113,7 @@ pcap_createsrcstr_ex(char *source, int type, const char *host, const char *port, return (0); default: - pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + snprintf(errbuf, PCAP_ERRBUF_SIZE, "The interface type is not valid."); return (-1); } @@ -2024,7 +2184,7 @@ pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, */ if (host && tmphost) { if (tmpuserinfo) - pcap_snprintf(host, PCAP_BUF_SIZE, "%s@%s", + snprintf(host, PCAP_BUF_SIZE, "%s@%s", tmpuserinfo, tmphost); else pcap_strlcpy(host, tmphost, PCAP_BUF_SIZE); @@ -2103,13 +2263,42 @@ pcap_create(const char *device, char *errbuf) else { #ifdef _WIN32 /* - * If the string appears to be little-endian UCS-2/UTF-16, - * convert it to ASCII. + * 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. * - * XXX - to UTF-8 instead? Or report an error if any - * character isn't 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 (device[0] != '\0' && device[1] == '\0') { + if (!pcap_new_api && device[0] != '\0' && device[1] == '\0') { size_t length; length = wcslen((wchar_t *)device); @@ -2121,7 +2310,7 @@ pcap_create(const char *device, char *errbuf) return (NULL); } - pcap_snprintf(device_str, length + 1, "%ws", + snprintf(device_str, length + 1, "%ws", (const wchar_t *)device); } else #endif @@ -2234,42 +2423,30 @@ initialize_ops(pcap_t *p) */ 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; + /* + * 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. - * - * The structure following it must be aligned on - * the appropriate alignment boundary for this platform. - * We align on an 8-byte boundary as that's probably what - * at least some platforms do, even with 32-bit integers, - * and because we can't be sure that some values won't - * require 8-byte alignment even on platforms with 32-bit - * integers. + * total_size is the size of a structure containing a pcap_t + * followed by a private structure. */ -#define PCAP_T_ALIGNED_SIZE ((sizeof(pcap_t) + 7) & ~0x7) - chunk = malloc(PCAP_T_ALIGNED_SIZE + size); + chunk = calloc(total_size, 1); if (chunk == NULL) { pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc"); return (NULL); } - memset(chunk, 0, PCAP_T_ALIGNED_SIZE + size); /* * Get a pointer to the pcap_t at the beginning. @@ -2286,26 +2463,24 @@ pcap_alloc_pcap_t(char *ebuf, size_t size) #endif /* MSDOS */ #endif /* _WIN32 */ - 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 + PCAP_T_ALIGNED_SIZE); - } + /* + * 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 (p); } 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); @@ -2357,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); } @@ -2565,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)); } @@ -2623,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) { @@ -2669,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, - PCAP_ERRBUF_SIZE - 3, 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), PCAP_ERRBUF_SIZE - 6, 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); @@ -2863,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); } @@ -2980,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"), @@ -3111,10 +3310,16 @@ static struct dlt_choice dlt_choices[] = { 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 }; @@ -3154,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; @@ -3166,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 } }; @@ -3251,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->handle != INVALID_HANDLE_VALUE) - return ((int)(DWORD)p->handle); - 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 @@ -3271,7 +3507,7 @@ pcap_get_selectable_fd(pcap_t *p) return (p->selectable_fd); } -struct timeval * +const struct timeval * pcap_get_required_select_timeout(pcap_t *p) { return (p->required_select_timeout); @@ -3427,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"); @@ -3441,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); } @@ -3469,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 } @@ -3490,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 @@ -3626,7 +3880,7 @@ 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); @@ -3663,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 @@ -3731,6 +4005,10 @@ pcap_breakloop_common(pcap_t *p) 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; @@ -3809,12 +4087,85 @@ pcap_inject(pcap_t *p, const void *buf, size_t 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. @@ -3836,7 +4187,7 @@ pcap_offline_filter(const struct bpf_program *fp, const struct pcap_pkthdr *h, static int pcap_can_set_rfmon_dead(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Rfmon mode doesn't apply on a pcap_open_dead pcap_t"); return (PCAP_ERROR); } @@ -3845,15 +4196,29 @@ static int pcap_read_dead(pcap_t *p, int cnt _U_, pcap_handler callback _U_, u_char *user _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + 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_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packets can't be sent on a pcap_open_dead pcap_t"); return (-1); } @@ -3861,7 +4226,7 @@ pcap_inject_dead(pcap_t *p, const void *buf _U_, int size _U_) static int pcap_setfilter_dead(pcap_t *p, struct bpf_program *fp _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A filter cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3869,7 +4234,7 @@ pcap_setfilter_dead(pcap_t *p, struct bpf_program *fp _U_) static int pcap_setdirection_dead(pcap_t *p, pcap_direction_t d _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The packet direction cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3877,7 +4242,7 @@ pcap_setdirection_dead(pcap_t *p, pcap_direction_t d _U_) static int pcap_set_datalink_dead(pcap_t *p, int dlt _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The link-layer header type cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3885,7 +4250,7 @@ pcap_set_datalink_dead(pcap_t *p, int dlt _U_) static int pcap_getnonblock_dead(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); return (-1); } @@ -3893,7 +4258,7 @@ pcap_getnonblock_dead(pcap_t *p) static int pcap_setnonblock_dead(pcap_t *p, int nonblock _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A pcap_open_dead pcap_t does not have a non-blocking mode setting"); return (-1); } @@ -3901,40 +4266,40 @@ pcap_setnonblock_dead(pcap_t *p, int nonblock _U_) static int pcap_stats_dead(pcap_t *p, struct pcap_stat *ps _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from a pcap_open_dead pcap_t"); return (-1); } #ifdef _WIN32 -struct pcap_stat * +static struct pcap_stat * pcap_stats_ex_dead(pcap_t *p, int *pcap_stat_size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Statistics aren't available from a pcap_open_dead pcap_t"); return (NULL); } static int -pcap_setbuff_dead(pcap_t *p, int dim) +pcap_setbuff_dead(pcap_t *p, int dim _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The kernel buffer size cannot be set on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_setmode_dead(pcap_t *p, int mode) +pcap_setmode_dead(pcap_t *p, int mode _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "impossible to set mode on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_setmintocopy_dead(pcap_t *p, int size) +pcap_setmintocopy_dead(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The mintocopy parameter cannot be set on a pcap_open_dead pcap_t"); return (-1); } @@ -3942,7 +4307,7 @@ pcap_setmintocopy_dead(pcap_t *p, int size) static HANDLE pcap_getevent_dead(pcap_t *p) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "A pcap_open_dead pcap_t has no event handle"); return (INVALID_HANDLE_VALUE); } @@ -3951,7 +4316,7 @@ 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, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID get request cannot be performed on a pcap_open_dead pcap_t"); return (PCAP_ERROR); } @@ -3960,45 +4325,47 @@ 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, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "An OID set request cannot be performed on a pcap_open_dead pcap_t"); return (PCAP_ERROR); } static u_int -pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue, int sync) +pcap_sendqueue_transmit_dead(pcap_t *p, pcap_send_queue *queue _U_, + int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Packets cannot be transmitted on a pcap_open_dead pcap_t"); return (0); } static int -pcap_setuserbuffer_dead(pcap_t *p, int size) +pcap_setuserbuffer_dead(pcap_t *p, int size _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "The user buffer cannot be set on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_live_dump_dead(pcap_t *p, char *filename, int maxsize, int maxpacks) +pcap_live_dump_dead(pcap_t *p, char *filename _U_, int maxsize _U_, + int maxpacks _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); return (-1); } static int -pcap_live_dump_ended_dead(pcap_t *p, int sync) +pcap_live_dump_ended_dead(pcap_t *p, int sync _U_) { - pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Live packet dumping cannot be performed on a pcap_open_dead pcap_t"); return (-1); } static PAirpcapHandle -pcap_get_airpcap_handle_dead(pcap_t *p) +pcap_get_airpcap_handle_dead(pcap_t *p _U_) { return (NULL); } @@ -4061,6 +4428,7 @@ pcap_open_dead_with_tstamp_precision(int linktype, int snaplen, u_int precision) 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; /*