在任意 OpenWrt 设备上运行 EasyDrcom
一块心病的解决 ❤️
“如何在任意刷写了 OpenWrt 的设备上运行 EasyDrcom” 一直是我的一个心病,因为大学期间搞了好多次,搞了好久,时间跨度将近两年也没解决这个问题。不过最近有时间,用了两天时间,终于研究得差不多了。
设备以 HG255D 为例
开发前要搭建 OpenWrt 交叉编译环境,按照网上教程就可以了,就是在 Ubuntu 上安装各种包。然后开始开发:
1.下载对应芯片的 SDK,里面包含了开发一个 OpenWrt 软件包所需要的所有东西。 比如 HG255D 是 rampis 厂家 rt3052 芯片,那么最新版 OpenWrt 对应的 SDK 链接就是 https://archive.openwrt.org/releases/21.02.0/targets/ramips/rt305x/openwrt-sdk-21.02.0-ramips-rt305x_gcc-8.4.0_musl.Linux-x86_64.tar.xz
OpenWrt 前几个版本差异有点大,而且还出了 LEDE 这样一个 fork,所以你路由里用的什么版本的固件,就要使用什么版本的 SDK,不然兼容性可能有问题。比如 21 的 SDK 编译的软件包在 14 的系统下很有可能是跑不了的。
2.解压 SDK,进入文件夹,首先要获取必要的包,运行 ./script/feeds update -a
,之后运行 ./script/feeds install -af
这样就安装了所有的依赖。如果发生问题,要不是时区的问题,要么是梯子的问题,自行解决吧。
其实有 docker 的话还能再简单一点:docker pull openwrtorg/sdk:ramips-rt305x-21.02.1
3.在 package 目录下建立我们自己的包,首先建空文件夹命名为 easydrcom,然后创建一个 Makefile,内容如下:
###############################################
#OpenWrt Makefile for easydrcom program
##############################################
include $(TOPDIR)/rules.mk
# Name and release number of this package
PKG_NAME:=easydrcom
PKG_RELEASE:=1
# This specifies the directory where we're going to build the program.
# The root build directory, $(BUILD_DIR), is by default the build_mipsel
# directory in your OpenWrt SDK directory
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
# Specify package information for this program.
# The variables defined here should be self explanatory.
# If you are running Kamikaze, delete the DESCRIPTION
# variable below and uncomment the Kamikaze define
# directive for the description below
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
DEPENDS:=+libpcap +libpthread +libstdcpp
TITLE:=$(PKG_NAME) -- drcom fro hitwh
endef
# Uncomment portion below for Kamikaze and delete DESCRIPTION variable above
define Package/$(PKG_NAME)/description
free wireless at hitwh.
endef
# Specify what needs to be done to prepare for building the package.
# In our case, we need to copy the source files to the build directory.
# This is NOT the default. The default uses the PKG_SOURCE_URL and the
# PKG_SOURCE which is not defined here to download the source from the web.
# In order to just build a simple program that we have just written, it is
# much easier to do it this way.
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
# We do not need to define Build/Configure or Build/Compile directives
# The defaults are appropriate for compiling a simple program such as this one
# Specify where and how to install the program. Since we only have one file,
# the easydrcom executable, install it by copying it to the /bin directory on
# the router. The $(1) variable represents the root directory on the router running
# OpenWrt. The $(INSTALL_DIR) variable contains a command to prepare the install
# directory if it does not already exist. Likewise $(INSTALL_BIN) contains the
# command to copy the binary file from its current location (in our case the build
# directory) to the install directory.
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/$(PKG_NAME) $(1)/bin/
endef
# This line executes the necessary commands to compile our program.
# The above define directives specify all the information needed, but this
# line calls BuildPackage which in turn actually uses this information to build a package.
$(eval $(call BuildPackage,$(PKG_NAME)))
4.建立src文件夹,将 EasyDrcom 的代码复制进去,链接:https://github.com/coverxit/EasyDrcom/tree/master/EasyDrcom 这里需要修改一下,我们不用 boost,一是太老了,二是编译时候还需要额外引入 boost,没必要这样。
我们使用这个头文件,用一个文件就能解析 init 配置文件:https://github.com/benhoyt/inih
复制 https://github.com/benhoyt/inih/tree/master/cpp 下 INIReader.h 到 src 文件夹里
5.修改 main.cpp 为
/**
* Copyright (C) 2014 Shindo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <functional>
#include <ctime>
#include <fstream>
#include <sstream>
#include <cctype>
#if defined(__APPLE__) || defined(LINUX) || defined(linux)
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#endif
#include "INIReader.h"
#include "easy_drcom_exception.hpp"
struct easy_drcom_config {
struct config_general {
int mode;
std::string username;
std::string password;
bool auto_online;
bool auto_redial;
} general;
struct config_remote {
std::string ip;
uint32_t port;
bool use_broadcast;
std::vector<uint8_t> mac;
} remote;
struct config_local {
std::string nic;
std::string hostname;
std::string kernel_version;
std::string ip;
std::vector<uint8_t> mac;
uint32_t eap_timeout;
uint32_t udp_timeout;
} local;
struct config_fake {
bool enable;
std::vector<uint8_t> mac;
std::string username;
std::string password;
} fake;
} conf;
// Log Config
#define EASYDRCOM_DEBUG
//#define EASYDRCOM_PRINT_DBG_ON_SCREEN
#include "log.hpp"
#define MAX_RETRY_TIME 2
#include "utils.hpp"
#include "drcom_dealer.hpp"
#include "eap_dealer.hpp"
#define VERSION "v0.9"
#if defined (WIN32)
#define VERSION (MAJOR_VERSION " for Windows")
#elif defined __APPLE__
#define VERSION (MAJOR_VERSION " for Mac OSX")
#elif defined (OPENWRT)
#define VERSION (MAJOR_VERSION " for OpenWrt (mips AR7xxx/9xxx)")
#elif defined (LINUX)
#define VERSION (MAJOR_VERSION " for Linux")
#endif
int read_config(std::string path)
{
INIReader pt(path);
try {
conf.general.mode = pt.GetInteger("General", "Mode", 0);
conf.general.username = pt.Get("General", "UserName", "");
conf.general.password = pt.Get("General", "PassWord", "");
conf.local.nic = pt.Get("Local", "NIC", "");
}
catch (std::exception& e) {
SYS_LOG_ERR("Failed to read '" << path << "' - " << e.what() << std::endl);
return EBADF;
}
conf.general.auto_online = pt.GetBoolean("General", "AutoOnline", true);
conf.general.auto_redial = pt.GetBoolean("General", "AutoRedial", true);
conf.remote.ip = pt.Get("Remote", "IP", "172.25.8.4");
conf.remote.port = pt.GetBoolean("Remote", "Port", 61440);
conf.remote.use_broadcast = pt.GetBoolean("Remote", "UseBroadcast", true);
if (!conf.remote.use_broadcast)
conf.remote.mac = str_mac_to_vec(pt.Get("Remote","MAC", "00:1a:a9:c3:3a:59"));
conf.local.hostname = pt.Get("Local", "HostName", "EasyDrcom for HITwh");
conf.local.kernel_version = pt.Get("Local","KernelVersion", "0.7");
conf.local.eap_timeout = pt.GetInteger("Local", "EAPTimeout", 1000);
conf.local.udp_timeout = pt.GetInteger("Local", "UDPTimeout", 2000);
conf.fake.enable = pt.GetInteger("Fake", "Enable", 0);
SYS_LOG_DBG("General.UserName = " << conf.general.username << ", General.PassWord = " << conf.general.password << ", General.Mode = " << conf.general.mode << std::endl);
SYS_LOG_DBG("General.AutoOnline = " << (conf.general.auto_online ? "True" : "False") << ", General.AutoRedial = " << (conf.general.auto_redial ? "True" : "False" ) << std::endl);
SYS_LOG_DBG("Remote.IP:Port = " << conf.remote.ip << ":" << conf.remote.port << ", Remote.UseBroadcast = " << (conf.remote.use_broadcast ? "True" : "False" ) << std::endl);
if (!conf.remote.use_broadcast) SYS_LOG_DBG("Remote.MAC = " << hex_to_str(&conf.remote.mac[0], 6, ':') << std::endl);
SYS_LOG_DBG("Local.NIC = " << conf.local.nic << ", Local.HostName = " << conf.local.hostname << ", Local.KernelVersion = " << conf.local.kernel_version << std::endl);
SYS_LOG_DBG("Local.EAPTimeout = " << conf.local.eap_timeout << ", Local.UDPTimeout = " << conf.local.udp_timeout << std::endl);
try {
conf.local.ip = get_ip_address(conf.local.nic);
conf.local.mac = get_mac_address(conf.local.nic);
SYS_LOG_INFO("Fetch NIC IP & MAC successfully." << std::endl);
SYS_LOG_INFO("Local.IP = " << conf.local.ip << ", Local.MAC = " << hex_to_str(&conf.local.mac[0], 6, ':') << std::endl);
}
catch (std::exception& e) {
SYS_LOG_ERR("Failed to fetch NIC info - " << e.what() << std::endl);
return EBADF;
}
if (conf.fake.enable) // fake user
{
try {
conf.fake.mac = str_mac_to_vec(pt.Get("Fake", "MAC", ""));
conf.fake.username = pt.Get("Fake", "UserName", "");
conf.fake.password = pt.Get("Fake", "PassWord", "");
}
catch (std::exception& e) {
SYS_LOG_ERR("Failed to read fake settings - " << e.what() << std::endl);
return EBADF;
}
SYS_LOG_INFO("Fetch fake settings successfully." << std::endl);
SYS_LOG_INFO("Fake.MAC = " << hex_to_str(&conf.fake.mac[0], 6, ':') << ", Fake.UserName = " << conf.fake.username << ", Fake.PassWord = " << conf.fake.password << std::endl);
}
SYS_LOG_INFO("Loaded config successfully." << std::endl);
return 0;
}
std::shared_ptr<eap_dealer> eap;
std::shared_ptr<drcom_dealer_base> drcom;
enum ONLINE_STATE
{
OFFLINE_PROCESSING,
OFFLINE_NOTIFY,
OFFLINE,
ONLINE_PROCESSING,
ONLINE,
};
ONLINE_STATE state = OFFLINE;
std::mutex mtx;
std::condition_variable cv;
std::vector<uint8_t> broadcast_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
std::vector<uint8_t> nearest_mac = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
void online_func()
{
do
{
try
{
do
{
state = ONLINE_PROCESSING;
try
{
if (conf.general.mode != 1) // 宿舍区认证模式
{
if (conf.remote.use_broadcast)
{
eap->logoff(nearest_mac);
eap->logoff(nearest_mac);
if (eap->start(broadcast_mac)) break;
if (eap->response_identity(broadcast_mac)) break;
if (eap->response_md5_challenge(broadcast_mac)) break;
}
else
{
eap->logoff(conf.remote.mac);
eap->logoff(conf.remote.mac);
if (eap->start(conf.remote.mac)) break;
if (eap->response_identity(conf.remote.mac)) break;
if (eap->response_md5_challenge(conf.remote.mac)) break;
}
}
if (conf.general.mode <= 1) // U31.R0
{
std::shared_ptr<drcom_dealer_u31> dealer = std::dynamic_pointer_cast<drcom_dealer_u31>(drcom);
if (dealer->start_request()) break;
if (dealer->send_login_auth()) break;
}
else // U62.R0
{
std::shared_ptr<drcom_dealer_u62> dealer = std::dynamic_pointer_cast<drcom_dealer_u62>(drcom);
}
while (true && state != OFFLINE_PROCESSING) // Keep Alive
{
try
{
if (conf.general.mode <= 1) // U31.R0
{
std::shared_ptr<drcom_dealer_u31> dealer = std::dynamic_pointer_cast<drcom_dealer_u31>(drcom);
if (dealer->send_alive_request()) break;
if (dealer->send_alive_pkt1()) break;
if (dealer->send_alive_pkt2()) break;
}
else // U62.R0
{
std::shared_ptr<drcom_dealer_u62> dealer = std::dynamic_pointer_cast<drcom_dealer_u62>(drcom);
if (dealer->send_alive_pkt1()) break;
if (dealer->send_alive_pkt2()) break;
}
state = ONLINE;
std::unique_lock<std::mutex> lock(mtx);
cv.wait_for(lock, std::chrono::seconds(20));
}
catch (std::exception& e)
{
state = OFFLINE;
SYS_LOG_ERR("Keep Alive: " << e.what() << std::endl);
break;
}
}
}
catch (std::exception& e)
{
state = OFFLINE;
SYS_LOG_ERR("Go Online: " << e.what() << std::endl);
break;
}
if (state != OFFLINE_PROCESSING)
state = OFFLINE;
}
while (false); // run once
if (state != OFFLINE_PROCESSING)
{
SYS_LOG_INFO("Connection broken, try to redial after 5 seconds." << std::endl);
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
catch (std::exception& e)
{
SYS_LOG_ERR("Thread Online: " << e.what() << std::endl);
}
} while (conf.general.auto_redial && state != OFFLINE_PROCESSING); // auto redial
std::unique_lock<std::mutex> lock(mtx);
state = OFFLINE_NOTIFY;
cv.notify_one();
}
void offline_func()
{
try
{
state = OFFLINE_PROCESSING;
std::unique_lock<std::mutex> lock(mtx);
cv.notify_one();
while (state != OFFLINE_NOTIFY)
cv.wait(lock); // wait for signal
if (conf.general.mode <= 1) // U31.R0
{
std::shared_ptr<drcom_dealer_u31> dealer = std::dynamic_pointer_cast<drcom_dealer_u31>(drcom);
dealer->send_alive_request();
dealer->start_request();
dealer->send_logout_auth();
}
// U62.R0 needn't do anything
}
catch (std::exception& e)
{
SYS_LOG_ERR("Go Offline: " << e.what() << std::endl);
}
if (conf.general.mode == 0 || conf.general.mode == 2) // 宿舍区
{
if (conf.remote.use_broadcast)
{
eap->logoff(broadcast_mac);
eap->logoff(nearest_mac);
}
else
{
eap->logoff(conf.remote.mac);
}
}
state = OFFLINE;
SYS_LOG_INFO("Offline." << std::endl);
}
int main(int argc, const char * argv[])
{
int ret = 0;
bool background = false, redirect_to_null = false;
std::string config_path = "EasyDrcom.conf";
auto clog_def = std::clog.rdbuf();
auto cout_def = std::cout.rdbuf();
auto cerr_def = std::cerr.rdbuf();
#ifdef OPENWRT
std::string log_path = "/tmp/EasyDrcom.log";
#else
std::string log_path = "EasyDrcom.log";
#endif
for (int i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "-b"))
background = true;
else if (!strcmp(argv[i], "-r"))
redirect_to_null = true;
else if (!strcmp(argv[i], "-c"))
{
if (i + 1 < argc)
config_path = argv[i+1];
}
else if (!strcmp(argv[i], "-o"))
{
if (i + 1 < argc)
log_path = argv[i+1];
}
}
std::ofstream log(log_path);
if (!log.is_open())
{
std::cerr << "[Error] Failed to open log '" << log_path << "', quitting..." << std::endl;
return ENOENT;
}
std::clog.rdbuf(log.rdbuf());
std::ofstream null("/dev/null");
if (redirect_to_null)
{
std::cout.rdbuf(null.rdbuf());
std::cerr.rdbuf(null.rdbuf());
}
SYS_LOG_INFO("EasyDrcom " << VERSION << " (build on " << __DATE__ << " " << __TIME__ << ")" << std::endl);
SYS_LOG_INFO("Code by Shindo, Contributors: mylight, SwimmingTiger." << std::endl << std::endl);
SYS_LOG_INFO("Initializing..." << std::endl);
SYS_LOG_INFO("Loading config from '" << config_path << "'..." << std::endl);
// Initialization
if ((ret = read_config(config_path)) != 0)
goto end;
#if defined(WIN32)
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
try
{
eap = std::shared_ptr<eap_dealer>(new eap_dealer(conf.local.nic, conf.local.mac, conf.local.ip, conf.general.username, conf.general.password)); // the fucking "Segmentation fault", so we must have to use this line all the time!!!
if (!conf.fake.enable)
{
if (conf.general.mode <= 1) // U31.R0
drcom = std::shared_ptr<drcom_dealer_base>(new drcom_dealer_u31(conf.local.mac, conf.local.ip, conf.general.username, conf.general.password, conf.remote.ip, conf.remote.port, conf.local.hostname, conf.local.kernel_version));
else // U62.R0
drcom = std::shared_ptr<drcom_dealer_base>(new drcom_dealer_u62(conf.local.mac, conf.local.ip, conf.general.username, conf.general.password, conf.remote.ip, conf.remote.port, conf.local.hostname, conf.local.kernel_version));
}
else
{
if (conf.general.mode <= 1) // U31.R0
drcom = std::shared_ptr<drcom_dealer_base>(new drcom_dealer_u31(conf.fake.mac, conf.local.ip, conf.fake.username, conf.fake.password, conf.remote.ip, conf.remote.port, conf.local.hostname, conf.local.kernel_version));
else // U62.R0
drcom = std::shared_ptr<drcom_dealer_base>(new drcom_dealer_u62(conf.fake.mac, conf.local.ip, conf.fake.username, conf.fake.password, conf.remote.ip, conf.remote.port, conf.local.hostname, conf.local.kernel_version));
}
}
catch (std::exception& e)
{
SYS_LOG_ERR(e.what() << std::endl);
ret = ENETRESET;
goto end;
}
SYS_LOG_INFO("Initialization done!" << std::endl);
if (background)
{
SYS_LOG_INFO("Start in background, turn on Auto Online & Auto Redial." << std::endl);
conf.general.auto_online = true;
conf.general.auto_redial = true;
}
if (!background)
SYS_LOG_INFO("Enter 'help' to get help." << std::endl);
if (!conf.general.auto_online)
{
SYS_LOG_INFO("Enter 'online' to go online!" << std::endl);
}
else
{
SYS_LOG_INFO("Going online..." << std::endl);
std::thread(online_func).detach();
}
if (background)
{
std::thread(online_func).join();
}
else
{
// Command Loop
std::string cmd;
while (true)
{
std::cin >> cmd;
if (!cmd.compare("online"))
{
if (state == ONLINE)
{
SYS_LOG_INFO("Already online!" << std::endl);
}
else if (state == ONLINE_PROCESSING)
{
SYS_LOG_INFO("Online Processing!" << std::endl);
}
else if (state == OFFLINE_PROCESSING || state == OFFLINE_NOTIFY)
{
SYS_LOG_INFO("Offline Processing!" << std::endl);
}
else if (state == OFFLINE)
{
SYS_LOG_INFO("Going online..." << std::endl);
std::thread(online_func).detach();
}
}
else if (!cmd.compare("offline"))
{
if (state == OFFLINE)
{
SYS_LOG_INFO("Haven't been online!" << std::endl);
}
else if (state == ONLINE_PROCESSING)
{
SYS_LOG_INFO("Online Processing!" << std::endl);
}
else if (state == OFFLINE_PROCESSING)
{
SYS_LOG_INFO("Offline Processing!" << std::endl);
}
else if (state == ONLINE)
{
SYS_LOG_INFO("Going offline..." << std::endl);
std::thread(offline_func).detach();
}
}
else if (!cmd.compare("quit"))
{
if (state == ONLINE_PROCESSING)
{
SYS_LOG_INFO("Please wait for online processing finished." << std::endl);
continue;
}
if (state == OFFLINE_PROCESSING)
{
SYS_LOG_INFO("Please wait for offline processing finished." << std::endl);
continue;
}
if (state == ONLINE)
{
SYS_LOG_INFO("Going offline..." << std::endl);
offline_func();
}
SYS_LOG_INFO("Quitting..." << std::endl);
std::cout << "[EasyDrcom Info] Bye Bye!" << std::endl;
break;
}
else if (!cmd.compare("help"))
{
SYS_LOG_INFO("EasyDrcom " << VERSION << " (build on " << __DATE__ << " " << __TIME__ << ")" << std::endl);
SYS_LOG_INFO("Code by Shindo, Contributors: mylight, SwimmingTiger." << std::endl << std::endl);
SYS_LOG_INFO("Command list:" << std::endl);
SYS_LOG_INFO("online - go online." << std::endl);
SYS_LOG_INFO("offline - go offline." << std::endl);
SYS_LOG_INFO("quit - quit EasyDrcom." << std::endl);
}
else
{
SYS_LOG_INFO("Wrong command: " << cmd << std::endl);
}
}
}
end:
std::cout.rdbuf(cout_def);
std::cerr.rdbuf(cerr_def);
std::clog.rdbuf(clog_def);
log.close();
null.close();
#if defined (WIN32)
WSACleanup();
#endif
return ret;
}
6.最后在 src 目录下再建一个 Makefile:
LIBS += -lpthread -lpcap -lstdc++
LDFLAGS += -DLINUX -DOPENWRT
CFLAGS += -Os
easydrcom: main.o md5.o
$(CXX) $(LDFLAGS) main.o md5.o -o easydrcom $(LIBS)
md5.o: md5.c
$(CXX) $(CFLAGS) -c md5.c
main.o: main.cpp
$(CXX) $(CFLAGS) -c main.cpp
clean:
rm *.o
rm easydrcom
之后就开始编译,首先 make menuconfig
去掉不需要编译的包,然后 make package/easydrcom/compile -j8 V=s
八核编译,等一会就好了
附件: hg255d.zip