Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ project(c5lab C)

add_library(c5lab
src/app.c
src/scan_csv.c
src/uart_comm.c
src/screen.c
src/screen_main_menu.c
Expand Down
1 change: 1 addition & 0 deletions application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ App(
"src/screen_nmap.c",
"src/screen_beacon_spam.c",
"src/screen_wardrive_upload.c",
"src/scan_csv.c",
"src/uart_comm.c",
],
)
12 changes: 8 additions & 4 deletions src/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
#include <furi/core/stream_buffer.h>
#include <stdint.h>
#include <stdbool.h>
#include "scan_csv.h"

typedef struct WiFiApp WiFiApp;

// WiFi network info from scan
// WiFi network info from scan_networks / show_scan_results.
// ssid and vendor are unescaped (a CSV "" is one ").
typedef struct {
char ssid[33];
char bssid[18];
char ssid[SCAN_CSV_SSID_MAX];
char bssid[SCAN_CSV_BSSID_MAX];
int8_t rssi;
int channel;
char auth[32];
char auth[SCAN_CSV_AUTH_MAX];
char vendor[SCAN_CSV_VENDOR_MAX];
char band[SCAN_CSV_BAND_MAX];
} WiFiNetwork;

#define MAX_SCAN_RESULTS 64
Expand Down
64 changes: 64 additions & 0 deletions src/scan_csv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include "scan_csv.h"

#include <stdlib.h>
#include <string.h>

bool csv_next_quoted_field(const char** p, char* out, size_t out_size) {
if(!p || !*p || !out || out_size == 0) return false;

out[0] = '\0';
const char* s = *p;
while(*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') s++;
if(*s != '"') return false;
s++; // opening quote

size_t i = 0;
while(*s) {
if(*s == '"') {
if(s[1] == '"') {
// RFC 4180: a doubled quote is one literal quote.
if(i + 1 < out_size) out[i++] = '"';
s += 2;
continue;
}
break; // closing quote
}
if(i + 1 < out_size) out[i++] = *s;
s++;
}

out[i] = '\0';
if(*s != '"') {
out[0] = '\0';
return false;
}

s++; // closing quote
if(*s == ',') s++;
*p = s;
return true;
}

bool parse_network_csv(const char* line, ScanCsvNetwork* net) {
if(!line || !net) return false;

memset(net, 0, sizeof(*net));

char index_str[8] = {0};
char channel_str[8] = {0};
char rssi_str[8] = {0};
const char* p = line;

if(!csv_next_quoted_field(&p, index_str, sizeof(index_str))) return false;
if(!csv_next_quoted_field(&p, net->ssid, sizeof(net->ssid))) return false;
if(!csv_next_quoted_field(&p, net->vendor, sizeof(net->vendor))) return false;
if(!csv_next_quoted_field(&p, net->bssid, sizeof(net->bssid))) return false;
if(!csv_next_quoted_field(&p, channel_str, sizeof(channel_str))) return false;
if(!csv_next_quoted_field(&p, net->auth, sizeof(net->auth))) return false;
if(!csv_next_quoted_field(&p, rssi_str, sizeof(rssi_str))) return false;
if(!csv_next_quoted_field(&p, net->band, sizeof(net->band))) return false;

if(channel_str[0] != '\0') net->channel = atoi(channel_str);
if(rssi_str[0] != '\0') net->rssi = atoi(rssi_str);
return true;
}
35 changes: 35 additions & 0 deletions src/scan_csv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <stddef.h>
#include <stdbool.h>

/* Field sizes include the terminating NUL.
* SSID is the 802.11 maximum (32 octets). Vendor uses the same bound.
* BSSID is "AA:BB:CC:DD:EE:FF". Band holds values such as "2.4GHz". */
#define SCAN_CSV_SSID_MAX 33
#define SCAN_CSV_VENDOR_MAX 33
#define SCAN_CSV_BSSID_MAX 18
#define SCAN_CSV_AUTH_MAX 32
#define SCAN_CSV_BAND_MAX 8

/* One scan_networks / show_scan_results row after RFC 4180 unescaping.
* A quote inside a field is stored as one ", not as the CSV "". */
typedef struct {
char ssid[SCAN_CSV_SSID_MAX];
char vendor[SCAN_CSV_VENDOR_MAX];
char bssid[SCAN_CSV_BSSID_MAX];
char auth[SCAN_CSV_AUTH_MAX];
char band[SCAN_CSV_BAND_MAX];
int channel;
int rssi;
} ScanCsvNetwork;

/* Read the next quoted CSV field. "" inside the field becomes one ".
* Stops at the closing quote even when out is too small: extra source
* bytes are discarded, never written past out[out_size - 1].
* On success, *p is advanced past the closing quote and a following comma.
* On failure, *p is left unchanged and out is an empty string. */
bool csv_next_quoted_field(const char** p, char* out, size_t out_size);

/* Parse "index","SSID","vendor","BSSID","channel","auth","RSSI","band". */
bool parse_network_csv(const char* line, ScanCsvNetwork* net);
36 changes: 20 additions & 16 deletions src/screen_wifi_scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,27 @@ static void network_info_draw(Canvas* canvas, void* model) {
screen_draw_title(canvas, "Network Info");

canvas_set_font(canvas, FontSecondary);
// SSID
char line[48];
snprintf(line, sizeof(line), "SSID: %s", net->ssid[0] ? net->ssid : "(hidden)");

// No "SSID:" prefix: a 21-character name fills one FontSecondary row.
char line[64];
snprintf(line, sizeof(line), "%s", net->ssid[0] ? net->ssid : "(hidden)");
canvas_draw_str(canvas, 2, 20, line);

// BSSID
snprintf(line, sizeof(line), "BSSID: %s", net->bssid);

snprintf(line, sizeof(line), "Vendor: %s", net->vendor[0] ? net->vendor : "-");
canvas_draw_str(canvas, 2, 30, line);

// Channel and RSSI
snprintf(line, sizeof(line), "Ch: %d Signal: %d dBm", net->channel, net->rssi);

snprintf(line, sizeof(line), "BSSID: %s", net->bssid);
canvas_draw_str(canvas, 2, 40, line);

// Auth/Encryption
snprintf(line, sizeof(line), "Auth: %s", net->auth[0] ? net->auth : "Open");

if(net->band[0]) {
snprintf(line, sizeof(line), "Ch: %d %s %d dBm", net->channel, net->band, (int)net->rssi);
} else {
snprintf(line, sizeof(line), "Ch: %d Signal: %d dBm", net->channel, (int)net->rssi);
}
canvas_draw_str(canvas, 2, 50, line);

snprintf(line, sizeof(line), "Auth: %s", net->auth[0] ? net->auth : "Open");
canvas_draw_str(canvas, 2, 60, line);
}

static bool network_info_input(InputEvent* event, void* context) {
Expand Down Expand Up @@ -171,12 +175,12 @@ static void wifi_scan_draw(Canvas* canvas, void* model) {
}

bool selected = is_network_selected(app, idx + 1);
char line[42];
char line[64];
if(net->ssid[0]) {
snprintf(line, sizeof(line), "%c %.18s [%d]",
snprintf(line, sizeof(line), "%c %s [%d]",
selected ? '*' : ' ',
net->ssid,
net->rssi);
(int)net->rssi);
} else {
snprintf(line, sizeof(line), "%c (%.17s)",
selected ? '*' : ' ',
Expand Down
80 changes: 17 additions & 63 deletions src/uart_comm.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,73 +24,27 @@ static void uart_serial_irq(FuriHalSerialHandle* handle, FuriHalSerialRxEvent ev

//=============================================================================
// CSV Parsing for network scan results
// Format: "index","ssid","vendor","BSSID","channel","auth","rssi","band"
// Quotes inside a field are RFC 4180 doubled quotes. See scan_csv.c.
//=============================================================================

bool csv_next_quoted_field(const char** p, char* out, size_t out_size) {
if(!p || !*p || !out || out_size == 0) return false;

const char* s = *p;
while(*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') s++;
if(*s != '"') return false;
s++; // skip opening quote

size_t i = 0;
while(*s && *s != '"') {
if(i + 1 < out_size) {
out[i++] = *s;
}
s++;
}
if(*s != '"') return false;
out[i] = '\0';
s++; // skip closing quote

if(*s == ',') s++; // skip comma if present

*p = s;
return true;
}

static bool parse_network_csv(const char* line, WiFiNetwork* net) {
static bool parse_network_line(const char* line, WiFiNetwork* net) {
if(!line || !net) return false;

// Format: "index","ssid","vendor","AA:BB:CC:DD:EE:FF","channel","auth","rssi","band"
char index_str[8] = {0};
char ssid[33] = {0};
char vendor[33] = {0};
char bssid[18] = {0};
char channel_str[8] = {0};
char auth[32] = {0};
char rssi_str[8] = {0};
char band[8] = {0};

const char* p = line;
bool ok = true;
ok &= csv_next_quoted_field(&p, index_str, sizeof(index_str));
ok &= csv_next_quoted_field(&p, ssid, sizeof(ssid));
ok &= csv_next_quoted_field(&p, vendor, sizeof(vendor));
ok &= csv_next_quoted_field(&p, bssid, sizeof(bssid));
ok &= csv_next_quoted_field(&p, channel_str, sizeof(channel_str));
ok &= csv_next_quoted_field(&p, auth, sizeof(auth));
ok &= csv_next_quoted_field(&p, rssi_str, sizeof(rssi_str));
ok &= csv_next_quoted_field(&p, band, sizeof(band));

if(!ok) return false;

int channel = 0;
int rssi = 0;
if(channel_str[0] != '\0') channel = atoi(channel_str);
if(rssi_str[0] != '\0') rssi = atoi(rssi_str);

strncpy(net->ssid, ssid, sizeof(net->ssid) - 1);
net->ssid[sizeof(net->ssid) - 1] = '\0';
strncpy(net->bssid, bssid, sizeof(net->bssid) - 1);
net->bssid[sizeof(net->bssid) - 1] = '\0';
strncpy(net->auth, auth, sizeof(net->auth) - 1);
net->auth[sizeof(net->auth) - 1] = '\0';
net->channel = channel;
ScanCsvNetwork parsed;
if(!parse_network_csv(line, &parsed)) return false;

memset(net, 0, sizeof(*net));
memcpy(net->ssid, parsed.ssid, sizeof(net->ssid));
memcpy(net->vendor, parsed.vendor, sizeof(net->vendor));
memcpy(net->bssid, parsed.bssid, sizeof(net->bssid));
memcpy(net->auth, parsed.auth, sizeof(net->auth));
memcpy(net->band, parsed.band, sizeof(net->band));
net->channel = parsed.channel;
int rssi = parsed.rssi;
if(rssi > 127) rssi = 127;
if(rssi < -128) rssi = -128;
net->rssi = (int8_t)rssi;

return true;
}

Expand Down Expand Up @@ -178,7 +132,7 @@ static int32_t uart_worker(void* context) {
} else if(line_str[0] == '"') {
// Parse network CSV line
if(app->scan_results && app->scan_result_count < app->scan_result_capacity) {
if(parse_network_csv(line_str, &app->scan_results[app->scan_result_count])) {
if(parse_network_line(line_str, &app->scan_results[app->scan_result_count])) {
app->scan_result_count++;
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/uart_comm.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,5 @@ void uart_clear_buffer(WiFiApp* app);
bool uart_check_board_connection(WiFiApp* app);
bool uart_check_sd_card(WiFiApp* app);

// CSV parsing
bool csv_next_quoted_field(const char** p, char* out, size_t out_size);

// Scanning
void uart_start_scan(WiFiApp* app);
Loading
Loading