Politician 1.0.0
WiFi Auditing Library for ESP32
Loading...
Searching...
No Matches
PoliticianStorage.h
Go to the documentation of this file.
1#pragma once
2
3#ifndef ARDUINO
4#error "PoliticianStorage.h requires the Arduino framework. Use ESP-IDF VFS and nvs_flash APIs directly."
5#endif
6
7#include <Arduino.h>
8#include <FS.h>
9#include <Preferences.h>
10#include <string>
11#ifndef POLITICIAN_NO_NETWORK_LOGGER
12#ifdef ARDUINO
13#include <WiFiClient.h>
14#if __has_include(<WiFiUdp.h>)
15#include <WiFiUdp.h>
16#elif __has_include(<WiFiUDP.h>)
17#include <WiFiUDP.h>
18#endif
19#endif
20#endif
21#include "Politician.h"
22#include "PoliticianFormat.h"
23
24namespace politician {
25namespace storage {
26
27namespace detail {
28#ifndef POLITICIAN_NO_STD_FUNCTION
29using TimestampCb = std::function<const char *()>;
30#else
31using TimestampCb = const char *(*)();
32#endif
33
35 static TimestampCb cb;
36 return cb;
37}
38inline const char *_timestamp() {
39 auto &cb = _timestampCb();
40 const char *ts = cb ? cb() : nullptr;
41 return ts ? ts : "1970-01-01 00:00:00";
42}
43
44/**
45 * @brief Writes a CSV-safe quoted field into output (RFC 4180).
46 * Wraps in double-quotes and escapes embedded double-quotes by doubling them.
47 */
48inline void escapeCsvField(const char *input, char *output, size_t maxLen) {
49 size_t out = 0;
50 if (out < maxLen - 1) output[out++] = '"';
51 for (size_t i = 0; input[i] && out < maxLen - 2; i++) {
52 if (input[i] == '"' && out < maxLen - 2) output[out++] = '"';
53 output[out++] = input[i];
54 }
55 if (out < maxLen - 1) output[out++] = '"';
56 output[out] = '\0';
57}
58} // namespace detail
59
60/**
61 * @brief Sets a global timestamp provider called by all CSV loggers when no
62 * explicit timestamp string is supplied.
63 *
64 * The callback should return a pointer to a static or long-lived buffer with a
65 * datetime string in Wigle CSV format: "YYYY-MM-DD HH:MM:SS". It is called
66 * once per log entry.
67 *
68 * @code
69 * // NTP example
70 * storage::setTimestampProvider([]() -> const char* {
71 * static char buf[20];
72 * time_t now = time(nullptr);
73 * strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
74 * return buf;
75 * });
76 * @endcode
77 */
81
82/**
83 * @brief Helper for writing HandshakeRecords and raw packets to a standard PCAPNG file.
84 *
85 * Two usage modes:
86 * - **Streaming (recommended for continuous capture):** call open() once, write()/writePacket()
87 * repeatedly, then close(). The file handle stays open across writes, avoiding repeated
88 * open/close overhead and SD wear.
89 * - **One-shot (legacy):** use the static append() / appendPacket() helpers which open and
90 * close the file on every call.
91 */
93public:
94 PcapngFileLogger() : _fs(nullptr), _open(false) {}
95
97
98 /**
99 * @brief Opens the PCAPNG file for streaming writes.
100 * Writes the global Section Header Block if the file is new or empty.
101 *
102 * @param fs The filesystem (e.g., SD, LittleFS)
103 * @param path The file path (e.g., "/captures.pcapng")
104 * @return true if the file was opened successfully
105 */
106 bool open(fs::FS &fs, const char *path, uint32_t maxBytes = 0) {
107 if (_open) close();
108 _fs = &fs;
109 _maxBytes = maxBytes;
110 _fileIdx = 0;
111 if (path) {
112 strncpy(_basePath, path, sizeof(_basePath) - 1);
113 _basePath[sizeof(_basePath) - 1] = '\0';
114 } else {
115 _basePath[0] = '\0';
116 }
117
118 bool isNew = !fs.exists(path);
119 if (!isNew) {
120 fs::File check = fs.open(path, FILE_READ);
121 if (check) { isNew = (check.size() == 0); check.close(); }
122 }
123
124 _file = fs.open(path, FILE_APPEND);
125 if (!_file) { _fs = nullptr; return false; }
126
127 if (isNew) {
128 uint8_t hdr[48];
129 size_t hl = format::writePcapngGlobalHeader(hdr);
130 _file.write(hdr, hl);
131 }
132 _open = true;
133 return true;
134 }
135
136 /**
137 * @brief Writes a HandshakeRecord to the open file.
138 * @return true if data was written, false if the logger is not open or serialization failed
139 */
140 bool write(const HandshakeRecord &rec) {
141 if (!_open) return false;
142 _checkRotate();
143 if (!_open) return false;
144 uint8_t buf[512];
145 size_t len = format::writePcapngRecord(rec, buf, sizeof(buf));
146 if (len > 0) { _file.write(buf, len); _file.flush(); }
147 return len > 0;
148 }
149
150 /**
151 * @brief Writes a raw 802.11 sniffer frame to the open file.
152 * @return true if data was written, false if the logger is not open or serialization failed
153 */
154 bool writePacket(const uint8_t *payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts_usec) {
155 if (!_open) return false;
156 _checkRotate();
157 if (!_open) return false;
158 uint8_t buf[2500]; // Max 802.11 frame is 2346 bytes
159 size_t wlen = format::writePcapngPacket(payload, len, rssi, channel, ts_usec, buf, sizeof(buf));
160 if (wlen > 0) { _file.write(buf, wlen); _file.flush(); }
161 return wlen > 0;
162 }
163
164 /** @brief Closes the underlying file handle. Safe to call multiple times. */
165 void close() {
166 if (_open) { _file.close(); _open = false; _fs = nullptr; }
167 }
168
169 /** @brief Returns true if the file is currently open for streaming. */
170 bool isOpen() const { return _open; }
171
172 // ── Static one-shot helpers (backward compatible) ──────────────────────────
173
174 /**
175 * @brief Appends a HandshakeRecord to a file as PCAPNG (opens and closes per call).
176 * Prefer the streaming API (open/write/close) for continuous capture.
177 */
178 static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec) {
179 PcapngFileLogger logger;
180 return logger.open(fs, path) && logger.write(rec);
181 }
182
183 /**
184 * @brief Appends a raw 802.11 sniffer frame to a PCAPNG file (opens and closes per call).
185 * Prefer the streaming API (open/writePacket/close) for continuous capture.
186 */
187 static bool appendPacket(fs::FS &fs, const char *path, const uint8_t *payload, uint16_t len,
188 int8_t rssi, uint8_t channel, uint32_t ts_usec) {
189 PcapngFileLogger logger;
190 return logger.open(fs, path) && logger.writePacket(payload, len, rssi, channel, ts_usec);
191 }
192
193private:
194 void _checkRotate() {
195 if (!_open || _maxBytes == 0) return;
196 if ((uint32_t)_file.size() < _maxBytes) return;
197 _file.close();
198 _fileIdx++;
199 char rotPath[80];
200 const char *dot = strrchr(_basePath, '.');
201 if (dot) {
202 size_t base_len = (size_t)(dot - _basePath);
203 snprintf(rotPath, sizeof(rotPath), "%.*s_%02u%s",
204 (int)base_len, _basePath, _fileIdx, dot);
205 } else {
206 snprintf(rotPath, sizeof(rotPath), "%s_%02u", _basePath, _fileIdx);
207 }
208 _file = _fs->open(rotPath, FILE_WRITE);
209 if (_file) {
210 uint8_t hdr[48];
211 size_t hl = format::writePcapngGlobalHeader(hdr);
212 _file.write(hdr, hl);
213 } else {
214 _open = false;
215 }
216 }
217
218 fs::FS *_fs;
219 fs::File _file;
220 bool _open;
221 uint32_t _maxBytes = 0;
222 char _basePath[64] = {};
223 uint8_t _fileIdx = 0;
224};
225
226/**
227 * @brief Helper for writing HandshakeRecords to an HC22000 text file.
228 *
229 * Supports both streaming (open/write/close) and one-shot (static append()) usage.
230 */
232public:
233 Hc22000FileLogger() : _open(false) {}
234
236
237 /**
238 * @brief Opens the HC22000 file for streaming writes.
239 * @return true if successful
240 */
241 bool open(fs::FS &fs, const char *path) {
242 if (_open) close();
243 _file = fs.open(path, FILE_APPEND);
244 if (!_file) return false;
245 _open = true;
246 return true;
247 }
248
249 /**
250 * @brief Writes a HandshakeRecord to the open file.
251 * @return true if data was written
252 */
253 bool write(const HandshakeRecord &rec) {
254 if (!_open) return false;
255 std::string str = format::toHC22000(rec);
256 if (!str.empty()) { _file.println(str.c_str()); _file.flush(); }
257 return !str.empty();
258 }
259
260 /** @brief Closes the underlying file handle. Safe to call multiple times. */
261 void close() {
262 if (_open) { _file.close(); _open = false; }
263 }
264
265 /** @brief Returns true if the file is currently open for streaming. */
266 bool isOpen() const { return _open; }
267
268 /**
269 * @brief Appends a HandshakeRecord to a file as an HC22000 string (opens and closes per call).
270 * Prefer the streaming API (open/write/close) for continuous capture.
271 */
272 static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec) {
273 Hc22000FileLogger logger;
274 return logger.open(fs, path) && logger.write(rec);
275 }
276
277private:
278 fs::File _file;
279 bool _open;
280};
281
282/**
283 * @brief Helper for writing precise GPS location coordinates to a Wigle.net compatible CSV file.
284 *
285 * Wigle.net has a strict CSV format starting with a specific header:
286 * MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type
287 */
289public:
290 /**
291 * @brief Appends a HandshakeRecord's details alongside GPS coordinates to a Wigle CSV.
292 *
293 * @param fs The filesystem (e.g., SD, LittleFS)
294 * @param path The path to the file (e.g., "/wardrive.csv")
295 * @param rec The captured HandshakeRecord
296 * @param lat Current GPS Latitude
297 * @param lon Current GPS Longitude
298 * @param alt (Optional) Current GPS Altitude in meters
299 * @param acc (Optional) GPS Accuracy radius in meters
300 * @return true if successful, false if file could not be opened
301 */
302 static bool append(fs::FS &fs, const char* path, const HandshakeRecord& rec,
303 float lat, float lon, float alt = 0.0, float acc = 10.0,
304 const char* timestamp = nullptr) {
305 fs::File file = _openWithHeader(fs, path);
306 if (!file) return false;
307
308 char ssidEscaped[72]; // 32 chars worst-case doubled + 2 quotes + NUL
309 detail::escapeCsvField(rec.ssid, ssidEscaped, sizeof(ssidEscaped));
310
311 char line[256];
312 snprintf(line, sizeof(line), "%02X:%02X:%02X:%02X:%02X:%02X,%s,%s,%s,%d,%d,%.6f,%.6f,%.1f,%.1f,WIFI",
313 rec.bssid[0], rec.bssid[1], rec.bssid[2], rec.bssid[3], rec.bssid[4], rec.bssid[5],
314 ssidEscaped, _authStr(rec.enc), timestamp ? timestamp : detail::_timestamp(),
315 rec.channel, rec.rssi, lat, lon, alt, acc);
316
317 file.println(line);
318 file.flush();
319 file.close();
320 return true;
321 }
322
323 /**
324 * @brief Appends any discovered ApRecord alongside GPS coordinates to a Wigle CSV.
325 * Use this with setApFoundCallback() to log all networks, not just captured ones.
326 *
327 * @param fs The filesystem (e.g., SD, LittleFS)
328 * @param path The path to the file (e.g., "/wardrive.csv")
329 * @param ap The discovered ApRecord
330 * @param lat Current GPS Latitude
331 * @param lon Current GPS Longitude
332 * @param alt (Optional) Current GPS Altitude in meters
333 * @param acc (Optional) GPS Accuracy radius in meters
334 * @return true if successful, false if file could not be opened
335 */
336 static bool appendAp(fs::FS &fs, const char* path, const ApRecord& ap,
337 float lat, float lon, float alt = 0.0, float acc = 10.0,
338 const char* timestamp = nullptr) {
339 fs::File file = _openWithHeader(fs, path);
340 if (!file) return false;
341
342 char ssidEscaped[72];
343 detail::escapeCsvField(ap.ssid, ssidEscaped, sizeof(ssidEscaped));
344
345 char line[256];
346 snprintf(line, sizeof(line), "%02X:%02X:%02X:%02X:%02X:%02X,%s,%s,%s,%d,%d,%.6f,%.6f,%.1f,%.1f,WIFI",
347 ap.bssid[0], ap.bssid[1], ap.bssid[2], ap.bssid[3], ap.bssid[4], ap.bssid[5],
348 ssidEscaped, _authStr(ap.enc), timestamp ? timestamp : detail::_timestamp(),
349 ap.channel, ap.rssi, lat, lon, alt, acc);
350
351 file.println(line);
352 file.flush();
353 file.close();
354 return true;
355 }
356
357private:
358 static const char* _authStr(uint8_t enc) {
359 switch (enc) {
360 case 1: return "[WEP][ESS]";
361 case 2: return "[WPA-PSK-TKIP][ESS]";
362 case 3: return "[WPA2-PSK-CCMP][ESS]";
363 case 4: return "[WPA2-EAP-CCMP][ESS]";
364 default: return "[ESS]";
365 }
366 }
367
368 static fs::File _openWithHeader(fs::FS &fs, const char* path) {
369 bool isNew = !fs.exists(path);
370 if (!isNew) {
371 fs::File check = fs.open(path, FILE_READ);
372 if (check) { isNew = (check.size() == 0); check.close(); }
373 }
374 fs::File file = fs.open(path, FILE_APPEND);
375 if (!file) return file;
376 if (isNew) {
377 file.println("WigleWifi-1.4,appRelease=1.0,model=Politician,release=1.0,device=ESP32,display=1.0,board=ESP32,brand=Espressif");
378 file.println("MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type");
379 }
380 return file;
381 }
382};
383
384/**
385 * @brief Helper for logging harvested 802.1X Enterprise Credentials.
386 * It writes a Clean CSV file containing BSSID, Client, and Plaintext Identity.
387 */
389public:
390 static bool append(fs::FS &fs, const char *path, const EapIdentityRecord &rec,
391 const char *timestamp = nullptr) {
392 bool isNew = !fs.exists(path);
393
394 fs::File file = fs.open(path, FILE_APPEND);
395 if (!file) return false;
396
397 if (isNew) {
398 file.println("Enterprise BSSID,Client MAC,Plaintext Identity,EAP Method,FirstSeen,Channel,RSSI");
399 }
400
401 char identityEscaped[136]; // 64 chars worst-case doubled + 2 quotes + NUL
402 detail::escapeCsvField(rec.identity, identityEscaped, sizeof(identityEscaped));
403
404 char line[256];
405 snprintf(line, sizeof(line), "%02X:%02X:%02X:%02X:%02X:%02X,%02X:%02X:%02X:%02X:%02X:%02X,%s,%u,%s,%d,%d",
406 rec.bssid[0], rec.bssid[1], rec.bssid[2], rec.bssid[3], rec.bssid[4], rec.bssid[5],
407 rec.client[0], rec.client[1], rec.client[2], rec.client[3], rec.client[4], rec.client[5],
408 identityEscaped, rec.eap_method, timestamp ? timestamp : detail::_timestamp(),
409 rec.channel, rec.rssi);
410
411 file.println(line);
412 file.flush();
413 file.close();
414 return true;
415 }
416};
417
418/**
419 * @brief Helper for persistently storing captured BSSIDs in NVS memory.
420 * This ensures that previously captured networks aren't attacked again after a reboot.
421 */
423private:
424 Preferences _prefs;
425 char _ns[16];
426 static const int MAX_STORED = 128;
427 uint8_t _cache[MAX_STORED][6];
428 size_t _count;
429 bool _dirty;
430
431public:
432 NvsBssidCache(const char* ns = "wardrive") : _count(0), _dirty(false) {
433 if (strlen(ns) > 15)
434 Serial.println("[NvsBssidCache] WARNING: namespace name exceeds 15 chars and will be truncated");
435 strncpy(_ns, ns, sizeof(_ns) - 1);
436 _ns[sizeof(_ns) - 1] = '\0';
437 memset(_cache, 0, sizeof(_cache));
438 }
439
440 /**
441 * @brief Initializes the NVS memory and loads the cached BSSIDs into RAM.
442 */
443 void begin() {
444 _prefs.begin(_ns, false);
445 size_t bytes = _prefs.getBytes("bssids", _cache, sizeof(_cache));
446 _count = bytes / 6;
447 if (_count > MAX_STORED) _count = MAX_STORED; // Safety parameter
448 _dirty = false;
449 }
450
451 /**
452 * @brief Feeds the loaded BSSIDs into the Politician engine so it knows to ignore them.
453 * @param engine Reference to your active Politician instance
454 */
456 for (size_t i = 0; i < _count; i++) {
457 engine.markCaptured(_cache[i]);
458 }
459 }
460
461 /**
462 * @brief Adds a newly captured BSSID to the in-RAM cache.
463 * The change is not written to NVS until flush() is called.
464 * @param bssid The 6-byte BSSID to save.
465 * @return true if added, false if it already exists or the cache is full.
466 */
467 bool add(const uint8_t* bssid) {
468 for (size_t i = 0; i < _count; i++) {
469 if (memcmp(_cache[i], bssid, 6) == 0) return false; // Already cached
470 }
471 if (_count >= MAX_STORED) return false; // Cache full
472
473 memcpy(_cache[_count], bssid, 6);
474 _count++;
475 _dirty = true;
476 return true;
477 }
478
479 /**
480 * @brief Writes any pending in-RAM changes to NVS.
481 * Call this periodically (e.g., in end() or on a timer) to batch flash writes.
482 * @return true if data was written, false if there were no pending changes.
483 */
484 bool flush() {
485 if (!_dirty) return false;
486 _prefs.putBytes("bssids", _cache, _count * 6);
487 _dirty = false;
488 return true;
489 }
490
491 /** @brief Returns true if there are in-RAM changes not yet written to NVS. */
492 bool isDirty() const { return _dirty; }
493
494 /**
495 * @brief Returns the number of BSSIDs currently stored.
496 */
497 size_t count() const { return _count; }
498
499 /**
500 * @brief Clears the entire cache from both RAM and NVS immediately.
501 */
502 void clear() {
503 _count = 0;
504 _dirty = false;
505 _prefs.remove("bssids");
506 }
507};
508
509// Gate the network loggers — they require WiFi.h which may not be available in all environments
510#ifndef POLITICIAN_NO_NETWORK_LOGGER
511#ifdef ARDUINO
512
513/**
514 * @brief Streams HandshakeRecords as raw PCAPNG data over an existing TCP connection.
515 *
516 * Designed for wardrive deployments where SD is unavailable but a laptop on the
517 * same network can run Wireshark with `-i TCP@<ip>:<port>` or a raw receiver.
518 */
519class TcpStreamLogger {
520public:
521 TcpStreamLogger() : _client(nullptr), _open(false) {}
522
523 bool connect(WiFiClient &client) {
524 if (!client.connected()) return false;
525 _client = &client;
526 uint8_t hdr[48];
527 size_t hl = format::writePcapngGlobalHeader(hdr);
528 _client->write(hdr, hl);
529 _open = true;
530 return true;
531 }
532
533 bool write(const HandshakeRecord &rec) {
534 if (!_open || !_client || !_client->connected()) { _open = false; return false; }
535 uint8_t buf[512];
536 size_t len = format::writePcapngRecord(rec, buf, sizeof(buf));
537 if (len > 0) { _client->write(buf, len); return true; }
538 return false;
539 }
540
541 void close() {
542 if (_open && _client) { _client->stop(); }
543 _open = false;
544 _client = nullptr;
545 }
546
547 bool isConnected() const { return _open && _client && _client->connected(); }
548
549private:
550 WiFiClient *_client;
551 bool _open;
552};
553
554/**
555 * @brief Sends HandshakeRecords as PCAPNG Enhanced Packet Blocks over UDP.
556 *
557 * UDP datagrams have a 1472-byte payload limit on typical networks. Records that
558 * exceed this limit are silently dropped. Suitable for LAN-local collection servers
559 * where packet loss is acceptable and TCP session management is undesirable.
560 */
561class UdpStreamLogger {
562public:
563 UdpStreamLogger() : _udp(nullptr), _host(nullptr), _port(0), _open(false) {}
564
565 bool begin(WiFiUDP &udp, const char *host, uint16_t port) {
566 _udp = &udp;
567 _host = host;
568 _port = port;
569 _open = true;
570 return true;
571 }
572
573 bool write(const HandshakeRecord &rec) {
574 if (!_open || !_udp) return false;
575 uint8_t buf[512];
576 size_t len = format::writePcapngRecord(rec, buf, sizeof(buf));
577 if (len == 0 || len > 1472) return false;
578 _udp->beginPacket(_host, _port);
579 _udp->write(buf, len);
580 return _udp->endPacket() != 0;
581 }
582
583 void close() {
584 _open = false;
585 _udp = nullptr;
586 }
587
588 bool isOpen() const { return _open; }
589
590private:
591 WiFiUDP *_udp;
592 const char *_host;
593 uint16_t _port;
594 bool _open;
595};
596
597#endif // ARDUINO
598#endif // POLITICIAN_NO_NETWORK_LOGGER
599
600} // namespace storage
601} // namespace politician
The core WiFi handshake capturing engine.
Definition Politician.h:103
Helper for logging harvested 802.1X Enterprise Credentials.
static bool append(fs::FS &fs, const char *path, const EapIdentityRecord &rec, const char *timestamp=nullptr)
Helper for writing HandshakeRecords to an HC22000 text file.
bool open(fs::FS &fs, const char *path)
Opens the HC22000 file for streaming writes.
void close()
Closes the underlying file handle.
bool isOpen() const
Returns true if the file is currently open for streaming.
bool write(const HandshakeRecord &rec)
Writes a HandshakeRecord to the open file.
static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec)
Appends a HandshakeRecord to a file as an HC22000 string (opens and closes per call).
Helper for persistently storing captured BSSIDs in NVS memory.
NvsBssidCache(const char *ns="wardrive")
bool isDirty() const
Returns true if there are in-RAM changes not yet written to NVS.
bool add(const uint8_t *bssid)
Adds a newly captured BSSID to the in-RAM cache.
bool flush()
Writes any pending in-RAM changes to NVS.
size_t count() const
Returns the number of BSSIDs currently stored.
void loadInto(Politician &engine)
Feeds the loaded BSSIDs into the Politician engine so it knows to ignore them.
void begin()
Initializes the NVS memory and loads the cached BSSIDs into RAM.
void clear()
Clears the entire cache from both RAM and NVS immediately.
Helper for writing HandshakeRecords and raw packets to a standard PCAPNG file.
static bool appendPacket(fs::FS &fs, const char *path, const uint8_t *payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts_usec)
Appends a raw 802.11 sniffer frame to a PCAPNG file (opens and closes per call).
bool isOpen() const
Returns true if the file is currently open for streaming.
void close()
Closes the underlying file handle.
bool open(fs::FS &fs, const char *path, uint32_t maxBytes=0)
Opens the PCAPNG file for streaming writes.
static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec)
Appends a HandshakeRecord to a file as PCAPNG (opens and closes per call).
bool write(const HandshakeRecord &rec)
Writes a HandshakeRecord to the open file.
bool writePacket(const uint8_t *payload, uint16_t len, int8_t rssi, uint8_t channel, uint32_t ts_usec)
Writes a raw 802.11 sniffer frame to the open file.
Helper for writing precise GPS location coordinates to a Wigle.net compatible CSV file.
static bool append(fs::FS &fs, const char *path, const HandshakeRecord &rec, float lat, float lon, float alt=0.0, float acc=10.0, const char *timestamp=nullptr)
Appends a HandshakeRecord's details alongside GPS coordinates to a Wigle CSV.
static bool appendAp(fs::FS &fs, const char *path, const ApRecord &ap, float lat, float lon, float alt=0.0, float acc=10.0, const char *timestamp=nullptr)
Appends any discovered ApRecord alongside GPS coordinates to a Wigle CSV.
Politician engine
Definition main.cpp:6
size_t writePcapngPacket(const uint8_t *payload, size_t payload_len, int8_t rssi, uint8_t channel, uint64_t ts_usec, uint8_t *buffer, size_t max_len)
Serializes a Raw 802.11 Frame into a PCAPNG Enhanced Packet Block.
std::string toHC22000(const HandshakeRecord &rec)
Converts a captured HandshakeRecord into the Hashcat 22000 (hc22000) text format.
size_t writePcapngRecord(const HandshakeRecord &rec, uint8_t *buffer, size_t max_len)
Serializes a HandshakeRecord into PCAPNG Enhanced Packet Blocks.
size_t writePcapngGlobalHeader(uint8_t *buffer)
Writes a PCAPNG Global Header (SHB + IDB).
std::function< const char *()> TimestampCb
void escapeCsvField(const char *input, char *output, size_t maxLen)
Writes a CSV-safe quoted field into output (RFC 4180).
void setTimestampProvider(detail::TimestampCb cb)
Sets a global timestamp provider called by all CSV loggers when no explicit timestamp string is suppl...
uint8_t eap_method
EAP method negotiated by the AP (EAP_METHOD_* constant); 0 if not yet observed.
Snapshot of a discovered Access Point from the internal cache.
A harvested 802.1X Enterprise plaintext identity, delivered to the IdentityCb callback.
A captured handshake or PMKID record delivered to the EapolCb callback.