+/*
+ * Get a list of time stamp types.
+ */
+#ifdef HAVE_PACKET_GET_TIMESTAMP_MODES
+static int
+get_ts_types(const char *device, pcap_t *p, char *ebuf)
+{
+ char *device_copy = NULL;
+ ADAPTER *adapter = NULL;
+ ULONG num_ts_modes;
+ BOOL ret;
+ DWORD error = ERROR_SUCCESS;
+ ULONG *modes = NULL;
+ int status = 0;
+
+ do {
+ /*
+ * First, find out how many time stamp modes we have.
+ * To do that, we have to open the adapter.
+ *
+ * XXX - PacketOpenAdapter() takes a non-const pointer
+ * as an argument, so we make a copy of the argument and
+ * pass that to it.
+ */
+ device_copy = strdup(device);
+ if (device_copy == NULL) {
+ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc");
+ status = -1;
+ break;
+ }
+
+ adapter = PacketOpenAdapter(device_copy);
+ if (adapter == NULL)
+ {
+ error = GetLastError();
+ /*
+ * If we can't open the device now, we won't be
+ * able to later, either.
+ *
+ * If the error is something that indicates
+ * that the device doesn't exist, or that they
+ * don't have permission to open the device - or
+ * perhaps that they don't have permission to get
+ * a list of devices, if PacketOpenAdapter() does
+ * that - the user will find that out when they try
+ * to activate the device; just return an empty
+ * list of time stamp types.
+ *
+ * Treating either of those as errors will, for
+ * example, cause "tcpdump -i <number>" to fail,
+ * because it first tries to pass the interface
+ * name to pcap_create() and pcap_activate(),
+ * in order to handle OSes where interfaces can
+ * have names that are just numbers (stand up
+ * and say hello, Linux!), and, if pcap_activate()
+ * fails with a "no such device" error, checks
+ * whether the interface name is a valid number
+ * and, if so, tries to use it as an index in
+ * the list of interfaces.
+ *
+ * That means pcap_create() must succeed even
+ * for interfaces that don't exist, with the
+ * failure occurring at pcap_activate() time.
+ */
+ if (error == ERROR_BAD_UNIT ||
+ error == ERROR_ACCESS_DENIED) {
+ p->tstamp_type_count = 0;
+ p->tstamp_type_list = NULL;
+ status = 0;
+ } else {
+ pcap_fmt_errmsg_for_win32_err(ebuf,
+ PCAP_ERRBUF_SIZE, error,
+ "Error opening adapter");
+ status = -1;
+ }
+ break;
+ }
+
+ /*
+ * Get the total number of time stamp modes.
+ *
+ * The buffer for PacketGetTimestampModes() is
+ * a sequence of 1 or more ULONGs. What's
+ * passed to PacketGetTimestampModes() should have
+ * the total number of ULONGs in the first ULONG;
+ * what's returned *from* PacketGetTimestampModes()
+ * has the total number of time stamp modes in
+ * the first ULONG.
+ *
+ * Yes, that means if there are N time stamp
+ * modes, the first ULONG should be set to N+1
+ * on input, and will be set to N on output.
+ *
+ * We first make a call to PacketGetTimestampModes()
+ * with a pointer to a single ULONG set to 1; the
+ * call should fail with ERROR_MORE_DATA (unless
+ * there are *no* modes, but that should never
+ * happen), and that ULONG should be set to the
+ * number of modes.
+ */
+ num_ts_modes = 1;
+ ret = PacketGetTimestampModes(adapter, &num_ts_modes);
+ if (!ret) {
+ /*
+ * OK, it failed. Did it fail with
+ * ERROR_MORE_DATA?
+ */
+ error = GetLastError();
+ if (error != ERROR_MORE_DATA) {
+ /*
+ * No, did it fail with ERROR_INVALID_FUNCTION?
+ */
+ if (error == ERROR_INVALID_FUNCTION) {
+ /*
+ * This is probably due to
+ * the driver with which Packet.dll
+ * communicates being older, or
+ * being a WinPcap driver, so
+ * that it doesn't support
+ * BIOCGTIMESTAMPMODES.
+ *
+ * Tell the user to try uninstalling
+ * Npcap - and WinPcap if installed -
+ * and re-installing it, to flush
+ * out all older drivers.
+ */
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "PacketGetTimestampModes() failed with ERROR_INVALID_FUNCTION; try uninstalling Npcap, and WinPcap if installed, and re-installing it from npcap.com");
+ status = -1;
+ break;
+ }
+
+ /*
+ * No, some other error. Fail.
+ */
+ pcap_fmt_errmsg_for_win32_err(ebuf,
+ PCAP_ERRBUF_SIZE, error,
+ "Error calling PacketGetTimestampModes");
+ status = -1;
+ break;
+ }
+ }
+ /* else (ret == TRUE)
+ * Unexpected success. Let's act like we got ERROR_MORE_DATA.
+ * If it doesn't work, we'll hit some other error condition farther on.
+ */
+
+ /* If the driver reports no modes supported *and*
+ * ERROR_MORE_DATA, something is seriously wrong.
+ * We *could* ignore the error and continue without supporting
+ * settable timestamp modes, but that would hide a bug.
+ */
+ if (num_ts_modes == 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "PacketGetTimestampModes() reports 0 modes supported.");
+ status = -1;
+ break;
+ }
+
+ /*
+ * Yes, so we now know how many types to fetch.
+ *
+ * The buffer needs to have one ULONG for the
+ * count and num_ts_modes ULONGs for the
+ * num_ts_modes time stamp types.
+ */
+ modes = (ULONG *)malloc((1 + num_ts_modes) * sizeof(ULONG));
+ if (modes == NULL) {
+ /* Out of memory. */
+ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc");
+ status = -1;
+ break;
+ }
+ modes[0] = 1 + num_ts_modes;
+ if (!PacketGetTimestampModes(adapter, modes)) {
+ pcap_fmt_errmsg_for_win32_err(ebuf,
+ PCAP_ERRBUF_SIZE, GetLastError(),
+ "Error calling PacketGetTimestampModes");
+ status = -1;
+ break;
+ }
+ if (modes[0] != num_ts_modes) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "First PacketGetTimestampModes() call gives %lu modes, second call gives %lu modes",
+ num_ts_modes, modes[0]);
+ status = -1;
+ break;
+ }
+
+ /*
+ * Allocate a buffer big enough for
+ * PCAP_TSTAMP_HOST (default) plus
+ * the explicitly specified modes.
+ */
+ p->tstamp_type_list = malloc((1 + num_ts_modes) * sizeof(u_int));
+ if (p->tstamp_type_list == NULL) {
+ pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "malloc");
+ status = -1;
+ break;
+ }
+ u_int num_ts_types = 0;
+ p->tstamp_type_list[num_ts_types] =
+ PCAP_TSTAMP_HOST;
+ num_ts_types++;
+ for (ULONG i = 0; i < num_ts_modes; i++) {
+ switch (modes[i + 1]) {
+
+ case TIMESTAMPMODE_SINGLE_SYNCHRONIZATION:
+ /*
+ * Better than low-res,
+ * but *not* synchronized
+ * with the OS clock.
+ */
+ p->tstamp_type_list[num_ts_types] =
+ PCAP_TSTAMP_HOST_HIPREC_UNSYNCED;
+ num_ts_types++;
+ break;
+
+ case TIMESTAMPMODE_QUERYSYSTEMTIME:
+ /*
+ * Low-res, but synchronized
+ * with the OS clock.
+ */
+ p->tstamp_type_list[num_ts_types] =
+ PCAP_TSTAMP_HOST_LOWPREC;
+ num_ts_types++;
+ break;
+
+ case TIMESTAMPMODE_QUERYSYSTEMTIME_PRECISE:
+ /*
+ * High-res, and synchronized
+ * with the OS clock.
+ */
+ p->tstamp_type_list[num_ts_types] =
+ PCAP_TSTAMP_HOST_HIPREC;
+ num_ts_types++;
+ break;
+
+ default:
+ /*
+ * Unknown, so we can't
+ * report it.
+ */
+ break;
+ }
+ }
+ p->tstamp_type_count = num_ts_types;
+ } while (0);
+
+ /* Clean up temporary allocations */
+ if (device_copy != NULL) {
+ free(device_copy);
+ }
+ if (modes != NULL) {
+ free(modes);
+ }
+ if (adapter != NULL) {
+ PacketCloseAdapter(adapter);
+ }
+
+ return status;
+}
+#else /* HAVE_PACKET_GET_TIMESTAMP_MODES */
+static int
+get_ts_types(const char *device _U_, pcap_t *p _U_, char *ebuf _U_)
+{
+ /*
+ * Nothing to fetch, so it always "succeeds".
+ */
+ return 0;
+}
+#endif /* HAVE_PACKET_GET_TIMESTAMP_MODES */
+