wirelessgateway/wifi/wpa_client.cpp

556 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "wpa_client.h"
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
namespace wifi {
size_t strlcpy (char *dst, const char *src, size_t dst_sz)
{
size_t n;
for (n = 0; n < dst_sz; n++) {
if ((*dst++ = *src++) == '\0')
break;
}
if (n < dst_sz)
return n;
if (n > 0)
*(dst - 1) = '\0';
return n + strlen (src);
}
static std::string to_string(int val) {
char *buf = NULL;
int size;
int temp;
if (val < 0)
{
temp = -val;
size = 2;
}
else
{
temp = val;
size = 1;
}
for(; temp > 0; temp = temp / 10, size++);
size++;
buf = (char *)malloc(size);
if (buf == NULL)
{
return "";
}
memset(buf, 0, size);
sprintf(buf, "%d", val);
std::string re(buf);
free(buf);
return re;
}
MXDHCP::MXDHCP()
{
pstream = NULL;
}
MXDHCP::~MXDHCP()
{
if(pstream!=NULL)
{
pclose(pstream);
pstream = NULL;
}
}
bool MXDHCP::Start(const std::string & net_interface)
{
if(pstream!=NULL)
{
pclose(pstream);
pstream = NULL;
}
std::string cmd = "udhcpc -b -i " + net_interface + " &";
system("killall -9 udhcpc &");
usleep(1000*100);
pstream = popen(cmd.data(),"r");
if(pstream == NULL)
{
return false;
}
return true;
}
/*
* dhcp 这个类的主要原理是通过读取udhcpc 的输出来判断是否拿到了ip地址实际情况中可以使用别的方法
*/
bool MXDHCP::GetDHCPStatus()
{
if(pstream == NULL)
{
return false;
}
int len = 1024;
char *buff = (char *)malloc(sizeof(char)*len);
if(buff==NULL)
{
return false;
}
int res = fread(buff,sizeof(char),len,pstream);
if(res<=0)
{
free(buff);
return false;
}
if(!CheckString(buff,res))
{
free(buff);
return false;
}
pclose(pstream);
pstream = NULL;
free(buff);
return true;
}
bool MXDHCP::CheckString(char *buf ,int len)
{
if(strstr(buf,"adding dns")==NULL)
{
return false;
}
return true;
}
WPAClient::WPAClient(const std::string & wpa_control_path)
{
wpa_context_ = NULL;
wpa_control_path_ = wpa_control_path;
SetConnStatus(STEP_SCAN);
Init();
}
WPAClient::~WPAClient()
{
Close(wpa_context_);
}
bool WPAClient::Init()
{
wpa_context_ = Open(wpa_control_path_);
if (wpa_context_ == NULL)
{
print_error("open wpa failed\n");
return false;
}
SetConnStatus(STEP_SCAN);
return true;
}
std::string WPAClient::GetCurrentSSID()
{
std::string cmd = "STATUS";
std::string ssid_key = "\nssid=";
std::string recv;
std::string ssid = "";
if (!Request(wpa_context_, cmd, recv))
{
return "";
}
char temp[1024] = {0};
strcpy(temp, recv.data());
char *key = NULL;
key = strstr(temp, ssid_key.data());
if (key == NULL)
{
return "";
}
key += ssid_key.length();
for(; (*key != '\0') && (*key != '\n') && (*key != '\r'); key++)
{
ssid += *key;
}
return ssid;
}
std::string WPAClient::GetNetSsid()
{
std::string cmd = "SCAN";
std::string recv;
if (!Request(wpa_context_, cmd, recv))
{
return "";
}
recv.clear();
cmd = "SCAN_RESULTS";
if (!Request(wpa_context_, cmd, recv))
{
return "";
}
return recv;
}
int WPAClient::GetWiFiRssi()
{
std::string cmd = "SIGNAL_POLL";
std::string rssi_key = "RSSI";
std::string recv;
if (!Request(wpa_context_, cmd, recv))
{
return 0;
}
char temp[1024] = {0};
strcpy(temp, recv.data());
print_info("recv = %s\n",recv.c_str());
char *key = NULL;
key = strstr(temp, rssi_key.data());
if (key == NULL)
{
return 0;
}
for(; (*key != '\0') && (*key != '\n') && (*key != '\r'); key++)
{
if ((*key >= '0') && (*key <= '9'))
{
return atoi(key);
}
}
return 0;
}
bool WPAClient::ConnectWiFi(const std::string & ssid, const std::string & password) {
int net_id;
SetConnStatus(STEP_SCAN);
if (!CleanAllWiFi())
{
return false;
}
print_info("CleanAllWiFi \n");
if (!AddWiFi(net_id))
{
return false;
}
print_info("AddWiFi \n");
if (!SetSSID(ssid, net_id))
{
return false;
}
print_info("SetSSID \n");
if (!SetPassword(password, 0))
{
return false;
}
if (!SetProtocol(net_id, 1))
{
return false;
}
print_info("SetProtocol\n");
SetScanSSID(net_id);
if (!EnableWiFi(net_id))
{
return false;
}
print_info("EnableWiFi\n");
return CheckCommandWithOk("SAVE_CONFIG");
//return true;
}
bool WPAClient::ConnectWiFiWithNoPassword(const std::string & ssid)
{
int net_id;
SetConnStatus(STEP_SCAN);
if (!CleanAllWiFi())
{
return false;
}
print_info("CleanAllWiFi\n");
if (!AddWiFi(net_id))
{
return false;
}
print_info("AddWiFi\n");
if (!SetSSID(ssid, net_id))
{
return false;
}
print_info("SetSSID\n");
if (!SetProtocol(net_id, 0))
{
return false;
}
print_info("SetProtocol\n");
SetScanSSID(net_id);
if (!EnableWiFi(net_id))
{
return false;
}
print_info("EnableWiFi\n");
return CheckCommandWithOk("SAVE_CONFIG");
}
bool WPAClient::ConnectWiFiWithLast()
{
SetConnStatus(STEP_SCAN);
if (!CheckCommandWithOk("ENABLE_NETWORK all"))
{
return false;
}
return true;
}
bool WPAClient::GetConnectStatus() {
std::string cmd = "STATUS";
std::string recv;
int addr;
switch (step_) {
case STEP_SCAN:
if (!Request(wpa_context_, cmd, recv)) {
return false;
}
addr = recv.find("COMPLETED");
if (addr == -1) {
return false;
}
SetConnStatus(STEP_CONNECT_AP_OK);
case STEP_CONNECT_AP_OK:
if (!dhcp_.Start("wlan0")) {
return false;
}
SetConnStatus(STEP_DHCP_IP);
case STEP_DHCP_IP:
if (!dhcp_.GetDHCPStatus()) {
return false;
}
SetConnStatus(STEP_SUCCESS);
case STEP_SUCCESS:
return true;
default:
return false;
}
return false;
}
void WPAClient::SetConnStatus(ConnectStatus status) {
step_ = status;
}
void WPAClient::wifiup()
{
system("ifconfig mlan0 up");
system("ifconfig mlan0 up");
system("ifconfig mlan0 up");
}
bool WPAClient::CleanWifi()
{
bool flag = CleanAllWiFi();
CheckCommandWithOk("SAVE_CONFIG");
return flag;
}
int WPAClient::GetConnStatus() {
return step_;
}
WPAContext * WPAClient::Open(const std::string & path) {
struct WPAContext *ctrl;
ctrl = (struct WPAContext*)malloc(sizeof(struct WPAContext));
if (ctrl == NULL) {
print_error("malloc failed\n");
return NULL;
}
memset(ctrl, 0, sizeof(struct WPAContext));
static int counter = 0;
int ret;
int tries = 0;
size_t res;
ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
if (ctrl->s < 0) {
print_error("socket failed\n");
free(ctrl);
return NULL;
}
ctrl->local.sun_family = AF_UNIX;
counter++;
try_again:
ret = snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
"/tmp" "/"
"wpa_ctrl_" "%d-%d",
(int)getpid(), counter);
if (ret < 0 || (size_t)ret >= sizeof(ctrl->local.sun_path)) {
print_error("snprintf failed\n");
close(ctrl->s);
free(ctrl);
return NULL;
}
tries++;
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
if (errno == EADDRINUSE && tries < 2) {
/*
* getpid() returns unique identifier for this instance
* of wpa_ctrl, so the existing socket file must have
* been left by unclean termination of an earlier run.
* Remove the file and try again.
*/
unlink(ctrl->local.sun_path);
goto try_again;
}
print_error("bind failed\n");
close(ctrl->s);
free(ctrl);
return NULL;
}
ctrl->dest.sun_family = AF_UNIX;
res = strlcpy(ctrl->dest.sun_path, wpa_control_path_.data(),
sizeof(ctrl->dest.sun_path));
if (res >= sizeof(ctrl->dest.sun_path)) {
close(ctrl->s);
free(ctrl);
return NULL;
}
if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
sizeof(ctrl->dest)) < 0) {
print_error("connect failed\n");
close(ctrl->s);
unlink(ctrl->local.sun_path);
free(ctrl);
return NULL;
}
return ctrl;
}
void WPAClient::Close(WPAContext * context) {
if (context == NULL)
return;
unlink(context->local.sun_path);
if (context->s >= 0)
close(context->s);
free(context);
}
bool WPAClient::Request(WPAContext * context, const std::string & cmd, std::string& reply) {
int res;
fd_set rfds;
struct timeval tv;
const char *_cmd;
char *cmd_buf = NULL;
size_t _cmd_len;
_cmd = cmd.data();
_cmd_len = cmd.length();
if (wpa_context_ == NULL) {
print_error("wpa_context_ is null\n");
return false;
}
if (send(wpa_context_->s, _cmd, _cmd_len, 0) < 0) {
free(cmd_buf);
return -1;
}
free(cmd_buf);
for (;;) {
tv.tv_sec = 10;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(wpa_context_->s, &rfds);
res = select(wpa_context_->s + 1, &rfds, NULL, NULL, &tv);
if (res < 0)
return false;
if (FD_ISSET(wpa_context_->s, &rfds)) {
char temp[1024] = {0};
int temp_len = 1024;
res = recv(wpa_context_->s, temp, temp_len, 0);
if (res < 0)
return false;
if (res > 0 && temp[0] == '<') {
continue;
}
reply = temp;
break;
} else {
return false;
}
}
return true;
}
bool WPAClient::CheckCommandWithOk(const std::string cmd) {
std::string recv;
if (!Request(wpa_context_, cmd, recv)) {
print_error("send cmd falied\n");
return false;
}
print_error("recv cmd %s\n",recv.data());
if (strstr(recv.data(), "OK") == NULL) {
return false;
}
return true;
}
bool WPAClient::AddWiFi(int & id) {
std::string add_cmd = "ADD_NETWORK";
std::string recv;
if (!Request(wpa_context_, add_cmd, recv)) {
return false;
}
id = atoi(recv.data());
return true;
}
bool WPAClient::SetScanSSID(int id) {
std::string cmd = "SET_NETWORK " + to_string(id) + " scan_ssid 1";
return CheckCommandWithOk(cmd);
}
bool WPAClient::SetSSID(const std::string & ssid, int id) {
std::string cmd = "SET_NETWORK " + to_string(id) + " ssid " + "\"" + ssid + "\"";
return CheckCommandWithOk(cmd);
}
bool WPAClient::SetPassword(const std::string & password, int id) {
std::string cmd = "SET_NETWORK " + to_string(id) + " psk " + "\"" + password + "\"";
return CheckCommandWithOk(cmd);
}
bool WPAClient::SetProtocol(int id, int en_crypt) {
std::string cmd = "SET_NETWORK " + to_string(id);
if (en_crypt) {
cmd += " key_mgmt WPA-PSK";
return CheckCommandWithOk(cmd);
} else {
cmd += " key_mgmt NONE";
return CheckCommandWithOk(cmd);
}
}
bool WPAClient::CleanAllWiFi() {
CheckCommandWithOk("REMOVE_NETWORK all");
CheckCommandWithOk("DISABLE_NETWORK all");
return true;
}
bool WPAClient::EnableWiFi(int id) {
std::string cmd = "ENABLE_NETWORK " + to_string(id);
return CheckCommandWithOk(cmd);
}
bool WPAClient::ReconnectWiFi() {
std::string cmd = "RECONNECT";
return CheckCommandWithOk(cmd);
}
}