#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet6/in6_var.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <err.h>
SLIST_HEAD(wifidat_head, wifidat) interfaces;
struct wifidat {
SLIST_ENTRY(wifidat) elems;
const char* interface;
struct ieee80211_nodereq_all na;
struct ieee80211_nodereq nr[512];
struct ieee80211_nwid nwid;
struct ieee80211_bssid bssid;
};
typedef struct {
const char *interface;
int connected;
char *ssid;
char *bssid;
int rssi;
int channel;
uint nr_capinfo;
uint nr_rsnprotos;
uint nr_rsnakms;
} lswifi_result;
int network_name_is_sane(const u_char* name, int len)
{
if (len > IEEE80211_NWID_LEN)
return 0;
for (int i = 0; i < len; i++)
if (name[i] & 0x80 || !isprint(name[i]))
return 0;
return 1;
}
void format_interface_data(struct wifidat* data, lswifi_result **networks, int *networks_idx)
{
int i, len, connected;
struct ieee80211_nodereq* network;
for (i = 0; i < data->na.na_nodes; i++) {
network = &data->nr[i];
len = network->nr_nwid_len;
if (!network_name_is_sane(network->nr_nwid, len))
continue;
connected = (len == data->nwid.i_len
&& memcmp(network->nr_nwid, data->nwid.i_nwid, len) == 0
&& memcmp(network->nr_bssid, data->bssid.i_bssid, IEEE80211_ADDR_LEN) == 0
);
const char *interface = data->interface;
char *bssid = strdup(ether_ntoa((struct ether_addr *)network->nr_bssid));
int rssi = network->nr_max_rssi
? -IEEE80211_NODEREQ_RSSI(network)
: network->nr_rssi;
char *ssid;
if (asprintf(&ssid, "%.*s", len, network->nr_nwid) == -1) {
perror("asprintf");
free(bssid);
continue;
}
lswifi_result *result = malloc(sizeof(lswifi_result));
if (result == NULL) {
free(ssid);
free(bssid);
} else {
*result = (lswifi_result){
.interface = interface,
.connected = connected,
.ssid = ssid,
.bssid = bssid,
.rssi = rssi,
.channel = network->nr_channel,
.nr_capinfo = network->nr_capinfo & ~IEEE80211_CAPINFO_ESS,
.nr_rsnprotos = network->nr_rsnprotos,
.nr_rsnakms = network->nr_rsnakms
};
}
networks[*networks_idx] = result;
(*networks_idx)++;
}
}
int query_interface(const char* if_name, struct wifidat* data)
{
int sock;
struct ifreq ifr;
int inwid, ibssid;
sock = socket(AF_INET, SOCK_DGRAM, 0);
data->interface = if_name;
bzero(&ifr, sizeof(ifr));
ifr.ifr_data = (caddr_t)&data->nwid;
strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
inwid = ioctl(sock, SIOCG80211NWID, (caddr_t)&ifr);
bzero(&data->bssid, sizeof(data->bssid));
strlcpy(data->bssid.i_name, if_name, sizeof(data->bssid.i_name));
ibssid = ioctl(sock, SIOCG80211BSSID, &data->bssid);
if (inwid != 0 && ibssid != 0) {
close(sock);
return -1;
}
bzero(&ifr, sizeof(ifr));
strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCS80211SCAN, (caddr_t)&ifr) != 0) {
close(sock);
return -1;
}
bzero(&data->na, sizeof(data->na));
bzero(&data->nr, sizeof(data->nr));
data->na.na_node = data->nr;
data->na.na_size = sizeof(data->nr);
strlcpy(data->na.na_ifname, if_name, sizeof(data->na.na_ifname));
if (ioctl(sock, SIOCG80211ALLNODES, &data->na) != 0) {
warn("SIOCG80211ALLNODES");
close(sock);
return -1;
}
if (!data->na.na_nodes) {
close(sock);
return -1;
}
close(sock);
return 0;
}
lswifi_result **get_networks()
{
struct ifaddrs *ifap;
struct ifaddrs *ifa;
struct wifidat* data;
SLIST_INIT(&interfaces);
if (getifaddrs(&ifap) != 0) {
perror("getifaddrs");
return NULL;
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
data = malloc(sizeof(struct wifidat));
if(query_interface(ifa->ifa_name, data) == -1)
{
free(data);
continue;
}
else
{
SLIST_INSERT_HEAD(&interfaces, data, elems);
}
}
int total_networks = 0;
SLIST_FOREACH(data, &interfaces, elems)
total_networks += data->na.na_nodes;
lswifi_result **networks = malloc(sizeof(lswifi_result *) * (total_networks + 1));
if (!networks)
goto cleanup;
int networks_idx = 0;
SLIST_FOREACH(data, &interfaces, elems)
format_interface_data(data, networks, &networks_idx);
networks[networks_idx] = NULL;
cleanup:
freeifaddrs(ifap);
while (!SLIST_EMPTY(&interfaces)) {
data = SLIST_FIRST(&interfaces);
SLIST_REMOVE_HEAD(&interfaces, elems);
free(data);
}
return networks;
}
void free_networks(lswifi_result **networks)
{
for (int i = 0; networks[i] != NULL; i++) {
free(networks[i]->bssid);
free(networks[i]->ssid);
free(networks[i]);
}
free(networks);
}