diff --git a/Makefile b/Makefile index 5743050..a031fb5 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,7 @@ $(BUILDDIR): $(TARGET): $(OBJECTS) - $(CC) $^ -o $(BUILDDIR)/$@ -lboost_filesystem -lboost_system -lpthread - + $(CC) $^ -o $(BUILDDIR)/$@ -lboost_filesystem -lboost_system -lreadline -lboost_program_options $(OBJECTS): $(BUILDDIR)/%.o : $(SOURCEDIR)/%.cpp $(CC) $(CFLAGS) $< -o $@ diff --git a/README.md b/README.md index 36f6447..cd00218 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,10 @@ Command-Line Tool which might in the future be able to configure TP-Link Easy Sm The usage is loosely based on the swconfig utility. ### dependencys - libboost-filesystem-dev - libboost-system-dev + libboost-filesystem + libboost-system + libboost-program-options + libreadline ### compatible devices + TL-SG105E diff --git a/src/Interactive.cpp b/src/Interactive.cpp index 6875dc2..19ab930 100644 --- a/src/Interactive.cpp +++ b/src/Interactive.cpp @@ -7,27 +7,40 @@ #include #include +#include +#include +#include +#include +#include + +#include "Program.h" #include "Interactive.h" using namespace std; -Interactive::Interactive() { - // TODO Auto-generated constructor stub - -} - -Interactive::~Interactive() { - // TODO Auto-generated destructor stub -} - -int Interactive::run() { +int Interactive::loop() { string cmd; - while(cmd.compare("quit")){ - cout<< "smrtlink>" << flush; - cin >> cmd; - cout << cmd < v; + Program p = Program(); + p.init(); + while (1) { + cmd = readline("smrtlink> "); + if (!cmd.compare("quit") || !cmd.compare("q")) + return 0; + if (!cmd.empty()) { + add_history(cmd.c_str()); + v = boost::program_options::split_unix(cmd); + p.run(v); + } } return 0; } +int Interactive::single(vector v) { + Program p = Program(); + p.init(); + p.run(v); + return 0; +} + diff --git a/src/Interactive.h b/src/Interactive.h index 2bf41be..d1d974a 100644 --- a/src/Interactive.h +++ b/src/Interactive.h @@ -10,9 +10,12 @@ class Interactive { public: - Interactive(); - virtual ~Interactive(); - int run(); + Interactive() { + } + virtual ~Interactive() { + } + int loop(); + int single(std::vector v); }; #endif /* INTERACTIVE_H_ */ diff --git a/src/Packet.cpp b/src/Packet.cpp index 0e61bf7..9bcf06c 100644 --- a/src/Packet.cpp +++ b/src/Packet.cpp @@ -158,8 +158,8 @@ std::string Packet::opCodeToString() { return "SET"; case CONFIRM: return "CONFIRM"; - case RETURN: - return "RETURN"; + case REPLY: + return "REPLY"; default: return "NONE"; } diff --git a/src/Packet.h b/src/Packet.h index 2e04a2d..a34e1c9 100644 --- a/src/Packet.h +++ b/src/Packet.h @@ -13,12 +13,13 @@ #include "Types.h" -static short sequenceId=0; +static short sequenceId = 0; -class Packet -{ +class Packet { public: - enum OpCode {DISCOVERY, GET, RETURN, SET, CONFIRM, NONE}; + enum OpCode { + DISCOVERY, GET, REPLY, SET, CONFIRM, NONE + }; Packet(OpCode); static void encode(bytes&); bytes getBytes(); @@ -37,7 +38,7 @@ public: void setHostMac(macAddr); void setSwitchMac(macAddr); void setCheckSum(int); - void setSequenceId(short ); + void setSequenceId(short); void setPayload(datasets payload); short getTokenId() const; void setTokenId(short tokenId = 0); @@ -50,9 +51,9 @@ private: datasets payload; byte version = 1; byte opCode; - macAddr switchMac { {0, 0, 0, 0, 0, 0}}; - macAddr hostMac { {0, 0, 0, 0, 0, 0}}; - macAddr broadcastMac { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; + macAddr switchMac { { 0, 0, 0, 0, 0, 0 } }; + macAddr hostMac { { 0, 0, 0, 0, 0, 0 } }; + macAddr broadcastMac { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } }; short tokenId = 0; short fragmentOffset = 0; int errorCode = 0; @@ -60,7 +61,7 @@ private: short flag = 0; void buildHead(); void buildBody(); - void push(bytes&, int&, short ); + void push(bytes&, int&, short); void push(bytes&, int&, int); void push(bytes&, int&, byte); void push(bytes&, int&, bytes); @@ -70,7 +71,7 @@ private: void pull(bytes&, int&, short &); void pull(bytes&, int&, int&); void pull(bytes&, int&, byte&); - void pull(bytes&, int&, bytes&, unsigned ); + void pull(bytes&, int&, bytes&, unsigned); void pull(bytes&, int&, ipAddr&); void pull(bytes&, int&, macAddr&); void pull(bytes&, int&, dataset&); diff --git a/src/Program.cpp b/src/Program.cpp index 33f27d7..d14c7d1 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -6,6 +6,9 @@ */ #include #include +#include +#include +#include #include "Constant.h" #include "Program.h" @@ -14,11 +17,110 @@ #include "Socket.h" #include "Switch.h" #include "Packet.h" +#include "Filter.h" +#include "Types.h" #include "lookup.h" #include "table.h" +using namespace smrtlink; using namespace std; +int Program::run(vector arg) { + int optind = 0; + std::vector vect; + std::map ll; + std::cmatch sm; + std::string cmd = arg[optind++]; + + io_service->reset(); + sock->clear(); + switch (caseArg(cmd.c_str())) { + case caseArg("reboot"): + if (reboot()) + return 1; + break; + case caseArg("reset"): + if (!reset()) + return 0; + break; + case caseArg("save"): + if (save()) + return 1; + break; + case caseArg("restore"): + if (restore()) + return 1; + break; + case caseArg("flash"): + if (flash()) + return 1; + break; + + case caseArg("list"): + if (list()) + return 1; + break; + + case caseArg("sniff"): + if (sniff()) + return 1; + break; + + case caseArg("encode"): + if (optind < arg.size()) { + std::string s(arg[optind]); + optind++; + if (!encode(s)) + return 1; + } else { + fprintf(stderr, "Argument expected after encode\n"); + return 1; + } + break; + case caseArg("set"): + while (optind < arg.size()) { + if (regex_match(arg[optind].c_str(), sm, + std::regex("^([a-z]+)=(.*)$"))) { + if (!snd_lookup.exists(sm[1]) && !rcv_lookup.exists(sm[1])) { + cerr << "Unknown argument " << arg[optind] << endl; + return 1; + } + ll.insert(std::pair(sm[1], sm[2])); + } else { + cerr << "Invalid Syntax " << arg[optind] << endl; + return 1; + } + optind++; + } + if (setProperty(ll)) + return 1; + break; + case caseArg("get"): + while (optind < arg.size()) { + if (regex_match(arg[optind].c_str(), sm, + std::regex("^([a-z]+)$"))) { + if (!snd_lookup.exists(sm[1]) && !rcv_lookup.exists(sm[1])) { + cerr << "Unknown argument " << arg[optind] << endl; + return 1; + } + vect.push_back(sm[1]); + } else { + cerr << "Invalid argument " << arg[optind] << endl; + return 1; + } + optind++; + } + if (getProperty(vect)) + return 1; + break; + default: + printf("Unknown command: %s\n", cmd.c_str()); + return 1; + } + io_service->run(); + return 0; +} + int printHeader(Packet p) { if (options.flags.HEADER) { if (options.flags.HEX) { @@ -93,15 +195,18 @@ int Program::list() { cout <<"Received Payload:\n"<(b,Switch())); + devices[a.getSwitchMac().hash()].parse(d); + devices[a.getSwitchMac().hash()].print(); + File f; - f.write(sw.toString()); - sw.print(); + f.write(devices[a.getSwitchMac().hash()].toString()); } return 0; }); - io_service->run(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } @@ -111,22 +216,15 @@ int Program::list() { int Program::sniff() { printf("Listening:\n"); try { - boost::asio::io_service io_service; - Socket s(io_service); - s.setHostIp(host.getIp()); - s.init(DST_PORT, SRC_PORT); - s.callback = [](Packet p) { + sock->listen([](Packet p) { cout << p.opCodeToString() << "\n"; printHeader(p); printPacket(p); return 0; - }; - s.listen(); - io_service.run(); + }); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } - return 0; } @@ -179,8 +277,6 @@ int Program::setProperty(map prop) { }); return 0; }); - - io_service->run(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } @@ -210,7 +306,6 @@ int Program::getProperty(vector prop) { return 0; }); - io_service->run(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } @@ -240,27 +335,27 @@ int Program::flash() { int Program::reboot() { try { - discover([this](Packet a) { - datasets d =a.getPayload(); - Switch sw = Switch(); - sw.parse(d); - cout <run(); + discover( + [this](Packet a) { + datasets d =a.getPayload(); + int b = a.getSwitchMac().hash(); + if (devices.empty()||devices.find(b) == devices.end()) + devices.insert(pair(b,Switch())); + devices[a.getSwitchMac().hash()].parse(d); + datasets t = { {SND_PING, 0, {}}}; + get(a, t, [this](Packet a) { + datasets d =a.getPayload(); + cout <run(); } catch (exception& e) { cerr << "Exception: " << e.what() << "\n"; } return 0; } -int Program::discover(function c) { +int Program::discover(Listener c) { Packet p = Packet(Packet::DISCOVERY); p.setHostMac(host.getMac()); p.setPayload( { }); - sock->callback = c; + sock->listen(c, Filter(Packet::REPLY)); sock->send(p); return 0; } -int Program::get(Packet l, datasets t, function c) { +int Program::get(Packet l, datasets t, Listener c) { Packet p = Packet(Packet::GET); p.setSwitchMac(l.getSwitchMac()); p.setHostMac(host.getMac()); p.setPayload(t); - sock->callback = c; + sock->listen(c, Filter(Packet::REPLY).mac(l.getSwitchMac())); sock->send(p); return 0; } -int Program::set(Packet l, datasets t, function c) { +int Program::set(Packet l, datasets t, Listener c) { Packet p = Packet(Packet::SET); p.setSwitchMac(l.getSwitchMac()); p.setTokenId(l.getTokenId()); @@ -325,12 +419,14 @@ int Program::set(Packet l, datasets t, function c) { datasets ld = { { LOGIN_USER, (short) (n.size()), n }, { LOGIN_PASSWORD, (short) (w.size()), w } }; p.setPayload(ld + t); - sock->callback = c; + sock->listen(c, Filter(Packet::CONFIRM).mac(l.getSwitchMac())); sock->send(p); return 0; } void Program::init() { + io_service = std::make_shared(); + sock = std::make_shared < Socket > (*io_service); if (options.interface.compare("") == 0) options.interface = host.getIface(); diff --git a/src/Program.h b/src/Program.h index 614047c..b039623 100644 --- a/src/Program.h +++ b/src/Program.h @@ -9,36 +9,42 @@ #define PROGRAM_H_ #include +#include #include "Types.h" #include "Host.h" +#include "Switch.h" #include "Socket.h" class Program { private: - std::shared_ptr io_service; - std::shared_ptr sock; - Host host = Host(); - int get(Packet,datasets,std::function); - int set(Packet,datasets,std::function); + std::shared_ptr io_service; + std::shared_ptr sock; + Host host = Host(); + std::map devices; + int get(Packet, datasets, std::function); + int set(Packet, datasets, std::function); int discover(std::function); public: - Program() { - io_service = std::make_shared(); - sock = std::make_shared(*io_service); - } - void init(); - int list(); - int sniff(); - int encode(std::string); - int getProperty(std::vector); - int setProperty(std::map); - int save(); - int restore(); - int flash(); - int reboot(); - int reset(); - std::string input; + Program() { + } + void init(); + int run(std::vector); + std::function callback = []() { + return 0; + }; + + int list(); + int sniff(); + int encode(std::string); + int getProperty(std::vector); + int setProperty(std::map); + int save(); + int restore(); + int flash(); + int reboot(); + int reset(); + std::string input; }; #endif /* PROGRAM_H_ */ diff --git a/src/Socket.cpp b/src/Socket.cpp index 2d0e363..f6d6282 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -5,29 +5,33 @@ * Author: jdi */ #include -#include +#include #include #include "Socket.h" #include "Packet.h" #include "Constant.h" +#include "Filter.h" #include "Host.h" #include "Types.h" +using namespace std; + Socket::Socket(boost::asio::io_service& io_service) : send_socket_(io_service), receive_socket_(io_service), timer(io_service) { } //, resolver( io_service) -void Socket::init(short dst_port, short src_port) { - if (initialized) - return; +Socket::~Socket() { +} + +void Socket::init(short dst_port, short src_port) { if (options.flags.REVERSE) { short p = dst_port; dst_port = src_port; src_port = p; } - if (options.debug_level>=1) + if (options.debug_level >= 1) std::cout << "Local IP:\t" << local_ip << "\n"; wildcard_endpoint_ = boost::asio::ip::udp::endpoint( @@ -47,26 +51,25 @@ void Socket::init(short dst_port, short src_port) { receive_socket_.set_option(boost::asio::socket_base::broadcast(true)); receive_socket_.set_option(boost::asio::socket_base::reuse_address(true)); receive_socket_.bind(wildcard_endpoint_); - - if (options.timeout != 0) { - timer.expires_from_now( - boost::posix_time::milliseconds(options.timeout)); - timer.async_wait([this](const boost::system::error_code& error) - { - if (!error) - { - receive_socket_.close(); - } - }); - } - - initialized = 1; } void Socket::setHostIp(ipAddr ip) { local_ip = ip; } +void Socket::clear(){ + callback.clear(); +} + +void Socket::listen(Listener l, Filter f) { + if (callback.find(f) == callback.end()) { + callback.insert(ListenerPair(f, l)); + } else { + callback[f] = l; + } + receive(); +} + void Socket::send(Packet p) { bytes data = p.getBytes(); p.encode(data); @@ -75,11 +78,12 @@ void Socket::send(Packet p) { broadcast_endpoint_, [this](boost::system::error_code ec, std::size_t bytes_sent) { - listen(); + receive(); + settimeout(); }); } -void Socket::listen() { +void Socket::receive() { data.resize(MAX_LENGTH); receive_socket_.async_receive_from(boost::asio::buffer(data, MAX_LENGTH), remote_endpoint_, @@ -92,13 +96,37 @@ void Socket::listen() { data.resize(bytes_recvd); Packet p = Packet(Packet::NONE); p.encode(data); - // std::cout << "err" << p.getErrorCode() < #include #include "Packet.h" +#include "Filter.h" #include "Types.h" #define SRC_PORT 29809 @@ -17,30 +19,32 @@ #define MAX_LENGTH 1024 +typedef std::function Listener; +typedef std::pair ListenerPair; + class Socket { public: - Socket(boost::asio::io_service&); - virtual ~Socket() { - } - void init(short, short); - void send(Packet); - void listen(); - void setHostIp(ipAddr); - std::function callback = [](Packet a) { - return 0; - }; + Socket(boost::asio::io_service&); + virtual ~Socket(); + void init(short, short); + void clear(); + void send(Packet); + void setHostIp(ipAddr); + void listen(Listener l, Filter f = Filter()); private: - boost::asio::ip::udp::socket send_socket_; - boost::asio::ip::udp::socket receive_socket_; - boost::asio::ip::udp::endpoint broadcast_endpoint_; - boost::asio::ip::udp::endpoint remote_endpoint_; - boost::asio::ip::udp::endpoint wildcard_endpoint_; - boost::asio::ip::udp::endpoint local_endpoint_; - boost::asio::deadline_timer timer; - bytes data = bytes(MAX_LENGTH); - ipAddr local_ip; - int initialized = 0; + void receive(); + void settimeout(); + boost::asio::ip::udp::socket send_socket_; + boost::asio::ip::udp::socket receive_socket_; + boost::asio::ip::udp::endpoint broadcast_endpoint_; + boost::asio::ip::udp::endpoint remote_endpoint_; + boost::asio::ip::udp::endpoint wildcard_endpoint_; + boost::asio::ip::udp::endpoint local_endpoint_; + boost::asio::deadline_timer timer; + bytes data = bytes(MAX_LENGTH); + ipAddr local_ip; + std::map callback = { }; }; diff --git a/src/Switch.h b/src/Switch.h index 1b40071..cb962ab 100644 --- a/src/Switch.h +++ b/src/Switch.h @@ -65,9 +65,13 @@ public: int parse(datasets); int parse(dataset); int parse(std::string); - int print(); + int set(std::pair); + + std::string get(std::string); std::string toString(); + int print(); + struct { std::string type; std::string hardware_version; diff --git a/src/Types.h b/src/Types.h index 489529a..b27657d 100644 --- a/src/Types.h +++ b/src/Types.h @@ -50,6 +50,28 @@ public: else break; } } + + int hash() { + int ret=0; + for (unsigned i = 0; i < 6; i++) { + ret = (ret*33) ^ (*this)[i]; + } + return ret; + } + + bool operator==(const macAddr &A) { + for (unsigned i = 0; i < 6; i++) { + if(A[i]!=(*this)[i])return false; + } + return true; + } + + bool operator!=(const macAddr &A) { + for (unsigned i = 0; i < 6; i++) { + if(A[i]!=(*this)[i])return true; + } + return false; + } }; /* @@ -95,6 +117,14 @@ public: } }; +namespace smrtlink { + +constexpr unsigned int caseArg(const char* str, int h = 0) { + return !str[h] ? 5381 : (caseArg(str, h + 1) * 33) ^ str[h]; +} + +} + template std::vector operator+(const std::vector &A, const std::vector &B) { std::vector AB; @@ -117,6 +147,7 @@ struct Options { bool JSON; bool PLAIN; bool REVERSE; + bool HEADER; bool PERMANENT; bool WAIT; @@ -128,7 +159,7 @@ struct Options { std::string file; int debug_level = 0; int verbosity = 0; - long timeout = 180U; + long timeout = 250U; }; #endif /* TYPES_H_ */ diff --git a/src/smrtlink.cpp b/src/smrtlink.cpp index 7a76c1e..735587c 100644 --- a/src/smrtlink.cpp +++ b/src/smrtlink.cpp @@ -1,14 +1,13 @@ //============================================================================ // Name : smrtlink.cpp // Author : jdi -// Version : +// Version : 1.2 // Copyright : GPL v2 // Description : SmrtLink in C++, Ansi-style //============================================================================ #include #include -#include #include #include @@ -21,6 +20,7 @@ #include "Interactive.h" #include "Host.h" #include "Program.h" +#include "Types.h" #include "Switch.h" #include "lookup.h" @@ -32,12 +32,8 @@ using namespace std; Options options; -constexpr unsigned int caseArg(const char* str, int h = 0) { - return !str[h] ? 5381 : (caseArg(str, h + 1) * 33) ^ str[h]; -} - int main(int argc, char *argv[]) { - int index,opt; + int index, opt; options.user = DEFAULT_USER; options.password = DEFAULT_PASS; @@ -56,7 +52,7 @@ int main(int argc, char *argv[]) { - while ((opt = getopt_long(argc, argv, "bhrVsxP:U:i:t::", longopts, + while ((opt = getopt_long(argc, argv, "bhrVXsxP:U:i:t::", longopts, &index)) != -1) { switch (opt) { @@ -149,118 +145,20 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + Interactive p = Interactive(); if (options.flags.INTERACTIVE) { if (optind < argc) { - cerr << "Command is ignored in interactive mode\n"; - } - Interactive p = Interactive(); - if (!p.run()) + cerr << "Command is ignored in interactive mode\n"; + } + if (!p.loop()) exit(EXIT_SUCCESS); fprintf(stderr, "Not yet implemented.\n"); exit(EXIT_FAILURE); - } - else if (optind < argc) { - Program p = Program(); - p.init(); - std::vector vect; - std::map list; - std::cmatch sm; - std::string cmd = std::string(argv[optind++]); - switch (caseArg(cmd.c_str())) { - case caseArg("reboot"): - if (!p.reboot()) - exit(EXIT_SUCCESS); - fprintf(stderr, "Not yet implemented.\n"); - exit(EXIT_FAILURE); - break; - case caseArg("reset"): - if (!p.reset()) - exit(EXIT_SUCCESS); - fprintf(stderr, "Not yet implemented.\n"); - exit(EXIT_FAILURE); - break; - case caseArg("save"): - if (!p.save()) - exit(EXIT_SUCCESS); - fprintf(stderr, "Not yet implemented.\n"); - exit(EXIT_FAILURE); - break; - case caseArg("restore"): - if (!p.restore()) - exit(EXIT_SUCCESS); - fprintf(stderr, "Not yet implemented.\n"); - exit(EXIT_FAILURE); - break; - case caseArg("flash"): - if (!p.flash()) - exit(EXIT_SUCCESS); - fprintf(stderr, "Not yet implemented.\n"); - exit(EXIT_FAILURE); - break; - - case caseArg("list"): - if (!p.list()) - exit(EXIT_SUCCESS); - break; - - case caseArg("sniff"): - if (!p.sniff()) - exit(EXIT_SUCCESS); - break; - - case caseArg("encode"): - if (optind < argc) { - std::string s(argv[optind]); - optind++; - if (p.encode(s)) - exit(EXIT_SUCCESS); - } else { - fprintf(stderr, "Argument expected after encode\n"); - exit(EXIT_FAILURE); - } - break; - case caseArg("set"): - while (optind < argc) { - if (regex_match(argv[optind], sm, - std::regex("^([a-z]+)=(.*)$"))) { - if (!snd_lookup.exists(sm[1])&&!rcv_lookup.exists(sm[1])) { - cerr << "Unknown argument " << argv[optind] << endl; - exit(EXIT_FAILURE); - } - list.insert( - std::pair(sm[1], sm[2])); - } else { - cerr << "Invalid Syntax " << argv[optind] << endl; - exit(EXIT_FAILURE); - } - optind++; - } - if (!p.setProperty(list)) - exit(EXIT_SUCCESS); - fprintf(stderr, "Not yet implemented.\n"); - exit(EXIT_FAILURE); - break; - case caseArg("get"): - while (optind < argc) { - if (regex_match(argv[optind], sm, std::regex("^([a-z]+)$"))) { - if (!snd_lookup.exists(sm[1])&&!rcv_lookup.exists(sm[1])) { - cerr << "Unknown argument " << argv[optind] << endl; - exit(EXIT_FAILURE); - } - vect.push_back(sm[1]); - } else { - cerr << "Invalid argument " << argv[optind] << endl; - exit(EXIT_FAILURE); - } - optind++; - } - if (!p.getProperty(vect)) - exit(EXIT_SUCCESS); - break; - default: - printf("Unknown command: %s\n", cmd.c_str()); - exit(EXIT_FAILURE); - } + } else if (optind < argc) { + vector v; + while (optind < argc) + v.push_back(argv[optind++]); + p.single(v); } exit(EXIT_FAILURE); }