X-Git-Url: https://git.tcpdump.org/libpcap/blobdiff_plain/3a0096146960ae89f8407858491c3f6c50260743..09b51d326c38ea8e10ce4da09c09d50e08c5aeb8:/pcap.c diff --git a/pcap.c b/pcap.c index 0c79d95e..ef1bbb71 100644 --- a/pcap.c +++ b/pcap.c @@ -63,6 +63,8 @@ struct rtentry; /* declarations in */ #include #include +#include "diag-control.h" + #ifdef HAVE_OS_PROTO_H #include "os-proto.h" #endif @@ -123,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 _U_, - DWORD dwReason _U_, - LPVOID lpvReserved _U_ -) -{ - return (TRUE); -} /* - * Start WinSock. - * Exported in case some applications using WinPcap/Npcap 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 @@ -479,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 @@ -498,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". */ @@ -551,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 } }; @@ -884,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' && - (PCAP_ISDIGIT(name[2]) || name[2] == '\0') + (PCAP_ISDIGIT(name[2]) || name[2] == '\0')) pcap_flags |= PCAP_IF_LOOPBACK; #endif #ifdef IFF_UP @@ -1365,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); @@ -2121,15 +2278,27 @@ pcap_create(const char *device, char *errbuf) * so, convert it back to the local code page's * extended ASCII. * - * XXX - 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. This particular - * version of this heuristic dates back to WinPcap - * 4.1.1; PacketOpenAdapter() does uses the same - * heuristic, with the exact same vulnerability. + * 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); @@ -2263,33 +2432,21 @@ initialize_ops(pcap_t *p) } 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) + 7U) & ~0x7U) - 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. @@ -2306,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); @@ -2689,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) + 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, p->errbuf); - else if (status == PCAP_ERROR_NO_SUCH_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) - snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s (%.*s)", device, - pcap_statustostr(status), PCAP_ERRBUF_SIZE - 6, p->errbuf); - else + 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); @@ -3000,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"), @@ -3140,6 +3319,7 @@ static struct dlt_choice dlt_choices[] = { 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 }; @@ -3189,7 +3369,7 @@ pcap_datalink_val_to_description_or_dlt(int dlt) if (description != NULL) { return description; } else { - (void)snprintf(unkbuf, sizeof(unkbuf), "DLT %u", dlt); + (void)snprintf(unkbuf, sizeof(unkbuf), "DLT %d", dlt); return unkbuf; } } @@ -3206,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 } }; @@ -3291,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 @@ -3311,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); @@ -3467,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"); @@ -3531,10 +3727,28 @@ pcap_setdirection(pcap_t *p, pcap_direction_t d) { if (p->setdirection_op == NULL) { snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "Setting direction is not implemented on this platform"); + "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 @@ -3791,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; @@ -3869,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. @@ -3910,6 +4201,20 @@ pcap_read_dead(pcap_t *p, int cnt _U_, pcap_handler callback _U_, 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_) { @@ -4123,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; /*