C строка парсер для++ в wpa_supplicant


Я создал наивные строки парсер для незапрашиваемых сообщений в wpa_supplicant. Я мог бы расширить это в будущем. Я не уверен, если это лучший способ для разбора этих видов сообщений. Я бы предпочел что-то напоминающее операторе switch, но я не уверен, что я мог бы сделать это без увеличения сложности кода. Хотелось бы более масштабируемое решение, хотя, в случае мне нужно искать много разных типов сообщений в будущем.

Код:

#include <stdio.h>
#include <string>
#include <map>

#define DEBUG_PRINT printf

class wpa_monitor
{
    public:
        void handle_wpa_message( const std::string& message );

    private:
        std::map< std::string, std::string > mac_ssid_association_map;
};

void wpa_monitor::handle_wpa_message( const std::string& message )
{
    DEBUG_PRINT( "WPA event: '%s'\n", message.c_str() );
    size_t pos;

    pos = message.find( "Trying to associate with " );

    if( pos != std::string::npos )
    {
        std::string mac_substring = message.substr( pos + sizeof( "Trying to associate with " ) - 1 );
        std::string mac_addr = mac_substring.substr( 0, mac_substring.find( " " ) );

        if( mac_addr.empty() )
        {
            return;
        }

        pos = message.find( "SSID='" );

        if( pos == std::string::npos )
        {
            return;
        }

        std::string ssid_substring = message.substr( pos + sizeof( "SSID='" ) - 1 );
        std::string ssid = ssid_substring.substr( 0, ssid_substring.find( "'" ) );

        DEBUG_PRINT( "Associating mac_address '%s' with ssid '%s'\n", mac_addr.c_str(), ssid.c_str() );
        mac_ssid_association_map.insert( std::make_pair< std::string, std::string >( mac_addr, ssid ) );

        return;
    }

    pos = message.find( "CTRL-EVENT-CONNECTED - Connection to " );

    if( pos != std::string::npos )
    {
        std::string mac_substring = message.substr( pos + sizeof( "CTRL-EVENT-CONNECTED - Connection to " ) - 1 );
        std::string mac_addr = mac_substring.substr( 0, mac_substring.find( " " ) );

        if( mac_addr.empty() )
        {
            return;
        }

        std::map< std::string, std::string >::iterator it = mac_ssid_association_map.find( mac_addr );

        if( it != mac_ssid_association_map.end() )
        {
            DEBUG_PRINT( "We are now connected to ssid '%s'\n", it->second.c_str() );
        }

        return;
    }

    pos = message.find( "CTRL-EVENT-SCAN-RESULTS" );

    if( pos != std::string::npos )
    {
        //scan results ready
        DEBUG_PRINT( "Scan results are ready\n" );
        return;
    }
}

Пример выходных данных (персональных запутывание данных):

WPA event: '<3>CTRL-EVENT-SCAN-STARTED '
WPA event: '<3>CTRL-EVENT-BSS-ADDED 0 00:00:00:00:00:00'
WPA event: '<3>CTRL-EVENT-BSS-ADDED 1 00:00:00:00:00:01'
WPA event: '<3>CTRL-EVENT-BSS-ADDED 2 00:00:00:00:00:02'
WPA event: '<3>CTRL-EVENT-SCAN-RESULTS '
Scan results are ready
WPA event: '<3>WPS-AP-AVAILABLE '
WPA event: '<3>Trying to associate with 00:00:00:00:00:00 (SSID='my_ssid' freq=5745 MHz)'
Associating mac_address '00:00:00:00:00:00' with ssid 'my_ssid'
WPA event: '<3>Associated with 00:00:00:00:00:00'
WPA event: '<3>CTRL-EVENT-SUBNET-STATUS-UPDATE status=0'
WPA event: '<3>WPA: Key negotiation completed with 00:00:00:00:00:00 [PTK=CCMP GTK=TKIP]'
WPA event: '<3>CTRL-EVENT-CONNECTED - Connection to 00:00:00:00:00:00 completed [id=1 id_str=]'
We are now connected to ssid 'my_ssid'


297
3
задан 8 февраля 2018 в 02:02 Источник Поделиться
Комментарии
2 ответа

Я не комментирую использовать C-стиля <stdio.h>, Так что, скорее всего, артефакт на окружающий код, а не то, что ты написал сам.

Отсутствует заголовок и опечатка/надзора

Заголовок необходимые для std::make_pair это <utility>. Ваша реализация может быть определена как побочный эффект, в том числе <map>но это не портативный, чтобы полагаться на это.

size_t pos должно быть std::size_t pos.

Тестовая программа

Мне удалось воссоздать некоторые из вашей пример вывода:

int main()
{
static auto const messages = {
"<3>CTRL-EVENT-SCAN-STARTED ",
"<3>CTRL-EVENT-BSS-ADDED 0 00:00:00:00:00:00",
"<3>CTRL-EVENT-BSS-ADDED 1 00:00:00:00:00:01",
"<3>CTRL-EVENT-BSS-ADDED 2 00:00:00:00:00:02",
"<3>CTRL-EVENT-SCAN-RESULTS ",
"<3>WPS-AP-AVAILABLE ",
"<3>Trying to associate with 00:00:00:00:00:00 (SSID='my_ssid' freq=5745 MHz)",
"<3>Associated with 00:00:00:00:00:00",
"<3>CTRL-EVENT-SUBNET-STATUS-UPDATE status=0",
"<3>WPA: Key negotiation completed with 00:00:00:00:00:00 [PTK=CCMP GTK=TKIP]",
"<3>CTRL-EVENT-CONNECTED - Connection to 00:00:00:00:00:00 completed [id=1 id_str=]",
};

wpa_monitor monitor;

for (auto m: messages)
monitor.handle_wpa_message(m);
}

Именования

Я избегаю имен, которые включают их тип, так как он может быть запутанным, если вы впоследствии измените этого типа. Так что я бы назвал карте mac_to_ssid вместо. Это также имеет преимущество быть короче, так легко набрать и почитать.

Растяжимость

Вы найдете, что один крупный способ становится все труднее ориентироваться, как он получает больше. Как говорилось в другой ответ, то имеет смысл делегировать кучу простых методов, каждая ручка в одном случае. Вы могли бы перебрать вектор, но я бы поставил на что-то простое, вроде этого:

class wpa_monitor
{
public:
bool handle_wpa_message(const std::string& message);

private:
std::map<std::string, std::string> mac_ssid_association_map = {};

bool handle_associate(const std::string& message);
bool handle_connected(const std::string& message);
bool handle_scanresult(const std::string& message);
};

bool wpa_monitor::handle_wpa_message(const std::string& message)
{
DEBUG_PRINT("WPA event: '%s'\n", message.c_str());

return handle_associate(message)
|| handle_connected(message)
|| handle_scanresult(message);
}

Теперь мы можем посмотреть на каждого совпадений, в свою очередь.

Рефакторинг некоторые общие код

Мы неоднократно искать строку и проявлять интерес к его концу, как это:

auto pos = message.find("Trying to associate with ");
if (pos == std::string::npos)
return false;

std::string mac_substring = message.substr(pos + sizeof "Trying to associate with " - 1);

Очевидно, существует вероятность ошибки, если мы не держать две копии строкой в договоре. Вместо этого мы можем написать небольшую функцию для этого:

static std::size_t find_end(const std::string& s, const std::string& substr)
{
auto pos = s.find(substr);
if (pos != std::string::npos)
pos += substr.size();
return pos;
}

Что делает абонент проще и надежнее:

auto pos = find_end(message, "Trying to associate with ");
if (pos == std::string::npos)
return false;

auto const mac_substring = message.substr(pos);

На самом деле, это не совсем МАК подстроки, как это продолжается до конца строки. Мы очень хотим еще одного фигуранта, принимая прекращении характер:

static std::string find_in_delimiters(const std::string& s,
const std::string& open, const char close)
{
auto start = s.find(open);
if (start == std::string::npos)
return {};
start += open.size();

auto end = s.find(close, start);
if (end == std::string::npos)
return {};

return s.substr(start, end-start);
}

И мы упростили абонентам еще больше:

    auto mac_addr = find_in_delimiters(message, "Trying to associate with ", ' ');
if (mac_addr.empty()) {
return false;
}

Больше make_pair()

Я получаю сбой компиляции, как gcc, кажется, хочет rvalue ссылки для make_pair<string,string>(). Я мог бы решить, что с std::move() как это:

    mac_ssid_association_map.insert( std::make_pair< std::string, std::string >( std::move(mac_addr), std::move(ssid) ) );

Однако, мы можем позволить дедукции тип шаблона делать свое дело:

    mac_ssid_association_map.insert(std::make_pair(std::move(mac_addr), std::move(ssid)));

Или даже просто:

    mac_ssid_association_map.insert({std::move(mac_addr), std::move(ssid)});

Использовать auto вместо точного типы iterator

Вот место, где меняется тип карты может быть головная боль содержание:

    std::map< std::string, std::string >::iterator it = mac_ssid_association_map.find( mac_addr );

Мы можем использовать auto, что также делает его более читабельным:

    auto it = mac_ssid_association_map.find(mac_addr);


Моя версия

#include <cstdio>
#include <string>
#include <map>
#include <utility>

#define DEBUG_PRINT std::printf

class wpa_monitor
{
public:
bool handle_wpa_message(const std::string& message);

private:
std::map<std::string, std::string> mac_to_ssid = {};

bool handle_associate(const std::string& message);
bool handle_connected(const std::string& message);
bool handle_scanresult(const std::string& message);
};

// Helper method - returns substring of s contained between
// open and close, or empty if not matched
static std::string find_in_delimiters(const std::string& s,
const std::string& open, const char close)
{
auto start = s.find(open);
if (start == s.npos)
return {};
start += open.size();

auto end = s.find(close, start);
if (end == s.npos)
return {};

return s.substr(start, end-start);
}

// Handlers for specific message types

bool wpa_monitor::handle_associate(const std::string& message)
{
auto mac_addr = find_in_delimiters(message, "Trying to associate with ", ' ');
if (mac_addr.empty())
return false;

auto ssid = find_in_delimiters(message, "SSID='", '\'');
if (ssid.empty())
return false;

DEBUG_PRINT("Associating mac_address '%s' with ssid '%s'\n", mac_addr.c_str(), ssid.c_str());
mac_to_ssid.insert({std::move(mac_addr), std::move(ssid)});
return true;
}

bool wpa_monitor::handle_connected(const std::string& message)
{
auto mac_addr = find_in_delimiters(message, "CTRL-EVENT-CONNECTED - Connection to ", ' ');
if (mac_addr.empty())
return false;

auto it = mac_to_ssid.find(mac_addr);
if (it != mac_to_ssid.end()) {
DEBUG_PRINT("We are now connected to ssid '%s'\n", it->second.c_str());
}

return true;
}

bool wpa_monitor::handle_scanresult(const std::string& message)
{
auto pos = message.find("CTRL-EVENT-SCAN-RESULTS");
if (pos == message.npos)
return false;

//scan results ready
DEBUG_PRINT("Scan results are ready\n");
return true;
}

// Test program

int main()
{
static auto const messages = {
"<3>CTRL-EVENT-SCAN-STARTED ",
"<3>CTRL-EVENT-BSS-ADDED 0 00:00:00:00:00:00",
"<3>CTRL-EVENT-BSS-ADDED 1 00:00:00:00:00:01",
"<3>CTRL-EVENT-BSS-ADDED 2 00:00:00:00:00:02",
"<3>CTRL-EVENT-SCAN-RESULTS ",
"<3>WPS-AP-AVAILABLE ",
"<3>Trying to associate with 00:00:00:00:00:00 (SSID='my_ssid' freq=5745 MHz)",
"<3>Associated with 00:00:00:00:00:00",
"<3>CTRL-EVENT-SUBNET-STATUS-UPDATE status=0",
"<3>WPA: Key negotiation completed with 00:00:00:00:00:00 [PTK=CCMP GTK=TKIP]",
"<3>CTRL-EVENT-CONNECTED - Connection to 00:00:00:00:00:00 completed [id=1 id_str=]",
};

wpa_monitor monitor;

for (auto m: messages)
monitor.handle_wpa_message(m);
}

4
ответ дан 9 февраля 2018 в 10:02 Источник Поделиться

Вы правы искать более масштабируемую реализацию. С моей точки зрения, наиболее простым было бы связать в контейнере, строки искать, чтобы идентифицировать сообщение и функции, которые вы хотите применить к сообщению. Что-то вроде:

/* given: 
#include <string>
#include <map>
#include <algorithm>
#include <functional>
*/

void wpa_handler::handle_wpa_message( const std::string& message )
{
/* given, as class member:
std::map<std::string, std::function<void(const std::string&)>> message_dispatch;
// that you populate somewhere in the construction process:
message_dispatch["Trying to associate with "] = handle_function1;
message_dispatch["CTRL-EVENT-CONNECTED - Connection to "] = handle_function2;
// etc.
*/
auto handler = std::find_if(message_dispatch.begin(), message_dispatch.end(), [&message](auto&& pair) {
return message.find(pair.first) != std::string::npos;
});

if (handler == message_dispatch.end()) {
// handle unknown message recieved -> exception?
}
else {
handler->second(message);
}
}

Другой, может быть, еще проще вариант, это иметь вектор вашей функции обработки, и передать сообщение каждому из них. Это функция обработки, которая отвечает за обнаружение, если она обрабатывает это сообщение или нет. Проблема в том, что вы не можете знать, если сообщение было обработано, и по какой обработчик, если вы реализуете механизм обратной связи.

3
ответ дан 8 февраля 2018 в 09:02 Источник Поделиться