From 4fd24354a5dd4f35899e1b2b129bf98c0c7d261b Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Tue, 1 Apr 2014 23:00:18 +0100 Subject: [PATCH] Refactored ipxconfig, allow enabling Ethernet encapsulation. --- Makefile | 2 +- src/config.c | 3 + src/config.h | 1 + src/ipxconfig.cpp | 777 ++++++++++++++++++++++++++++------------------ src/ipxwrapper.c | 2 +- 5 files changed, 484 insertions(+), 301 deletions(-) diff --git a/Makefile b/Makefile index d8abdf2..e181a6d 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ endif INCLUDE := -I./include/ CFLAGS := -std=c99 -Wall -D_WIN32_WINNT=0x0500 -DHAVE_REMOTE $(DBG_OPT) $(INCLUDE) -CXXFLAGS := -Wall -DHAVE_REMOTE $(DBG_OPT) $(INCLUDE) +CXXFLAGS := -std=c++0x -Wall -DHAVE_REMOTE $(DBG_OPT) $(INCLUDE) # Used by mkdeps.pl # diff --git a/src/config.c b/src/config.c index 99aefbc..ff6b9b7 100644 --- a/src/config.c +++ b/src/config.c @@ -30,6 +30,7 @@ main_config_t get_main_config(void) config.udp_port = DEFAULT_PORT; config.w95_bug = true; config.fw_except = false; + config.use_pcap = false; config.log_level = LOG_INFO; HKEY reg = reg_open_main(false); @@ -51,6 +52,7 @@ main_config_t get_main_config(void) config.udp_port = reg_get_dword(reg, "port", config.udp_port); config.w95_bug = reg_get_dword(reg, "w95_bug", config.w95_bug); config.fw_except = reg_get_dword(reg, "fw_except", config.fw_except); + config.use_pcap = reg_get_dword(reg, "use_pcap", config.use_pcap); config.log_level = reg_get_dword(reg, "log_level", config.log_level); reg_close(reg); @@ -65,6 +67,7 @@ bool set_main_config(const main_config_t *config) bool ok = reg_set_dword(reg, "port", config->udp_port) && reg_set_dword(reg, "w95_bug", config->w95_bug) && reg_set_dword(reg, "fw_except", config->fw_except) + && reg_set_dword(reg, "use_pcap", config->use_pcap) && reg_set_dword(reg, "log_level", config->log_level); reg_close(reg); diff --git a/src/config.h b/src/config.h index 9c7195e..28e3eb1 100644 --- a/src/config.h +++ b/src/config.h @@ -31,6 +31,7 @@ typedef struct main_config { bool w95_bug; bool fw_except; + bool use_pcap; enum ipx_log_level log_level; } main_config_t; diff --git a/src/ipxconfig.cpp b/src/ipxconfig.cpp index 09de07e..ebab14d 100644 --- a/src/ipxconfig.cpp +++ b/src/ipxconfig.cpp @@ -1,5 +1,5 @@ /* IPXWrapper - Configuration tool - * Copyright (C) 2011-2013 Daniel Collins + * Copyright (C) 2011-2014 Daniel Collins * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -29,22 +29,25 @@ #include "interface.h" #include "addr.h" -#define ID_PRI_LIST 1 +const char *IPXCONFIG_WINDOW_CLASS = "ipxconfig_class"; -#define ID_NIC_LIST 11 -#define ID_NIC_ENABLED 12 -#define ID_NIC_NET 13 -#define ID_NIC_NODE 14 - -#define ID_OPT_PORT 21 -#define ID_OPT_W95 22 -#define ID_OPT_LOG_DEBUG 25 -#define ID_OPT_LOG_TRACE 26 -#define ID_OPT_FW_EXCEPT 27 - -#define ID_OK 31 -#define ID_CANCEL 32 -#define ID_APPLY 33 +enum { + ID_NIC_LIST = 11, + ID_NIC_ENABLED = 12, + ID_NIC_NET = 13, + ID_NIC_NODE = 14, + + ID_OPT_PORT = 21, + ID_OPT_W95 = 22, + ID_OPT_LOG_DEBUG = 25, + ID_OPT_LOG_TRACE = 26, + ID_OPT_FW_EXCEPT = 27, + ID_OPT_USE_PCAP = 28, + + ID_OK = 31, + ID_CANCEL = 32, + ID_APPLY = 33, +}; struct iface { /* C style string so it can be passed directly to the listview */ @@ -55,23 +58,31 @@ struct iface { iface_config_t config; }; -typedef std::vector iface_list; +static void reload_nics(); +static void reload_primary_nics(); +static bool stash_nic_config(); -static void get_nics(); static bool save_config(); -static bool store_nic(); -static void init_windows(); -static void update_nic_conf(); -static void init_pri_list(); -static HWND create_child(HWND parent, int x, int y, int w, int h, LPCTSTR class_name, LPCTSTR title, DWORD style = 0, DWORD ex_style = 0, unsigned int id = 0); -static HWND create_group(HWND parent, int x, int y, int w, int h, LPCTSTR title); +static void main_window_init(); +static void main_window_update(); +static void main_window_resize(int width, int height); + +static HWND create_child(HWND parent, LPCTSTR class_name, LPCTSTR title, DWORD style = 0, DWORD ex_style = 0, unsigned int id = 0); +static HWND create_GroupBox(HWND parent, LPCTSTR title); +static HWND create_STATIC(HWND parent, LPCTSTR text); +static HWND create_checkbox(HWND parent, LPCTSTR label, int id); +static bool get_checkbox(HWND checkbox); +static void set_checkbox(HWND checkbox, bool state); static int get_text_width(HWND hwnd, const char *txt); static int get_text_height(HWND hwnd); -static RECT get_window_rect(HWND hwnd); + static std::string w32_errmsg(DWORD errnum); static void die(std::string msg); +static bool _pcap_installed(); -static iface_list nics; +static const bool PCAP_INSTALLED = _pcap_installed(); + +static std::vector nics; static main_config_t main_config = get_main_config(); static addr48_t primary_iface = get_primary_iface(); @@ -85,10 +96,10 @@ static wproc_fptr default_groupbox_wproc = NULL; static struct { HWND main; - HWND primary_group; + HWND box_primary; HWND primary; - HWND nic_group; + HWND box_nic; HWND nic_list; HWND nic_enabled; HWND nic_net_lbl; @@ -96,7 +107,7 @@ static struct { HWND nic_node_lbl; HWND nic_node; - HWND opt_group; + HWND box_options; HWND opt_port_lbl; HWND opt_port; @@ -105,11 +116,12 @@ static struct { HWND opt_log_debug; HWND opt_log_trace; HWND opt_fw_except; + HWND opt_use_pcap; HWND ok_btn; HWND can_btn; HWND app_btn; -} windows; +} wh; /* Callback for the main window */ static LRESULT CALLBACK main_wproc(HWND window, UINT msg, WPARAM wp, LPARAM lp) { @@ -132,13 +144,13 @@ static LRESULT CALLBACK main_wproc(HWND window, UINT msg, WPARAM wp, LPARAM lp) NMLISTVIEW *lv = (NMLISTVIEW*)lp; if(lv->uNewState & LVIS_FOCUSED) { - update_nic_conf(); + main_window_update(); } }else if(nhdr->code == LVN_ITEMCHANGING) { NMLISTVIEW *lv = (NMLISTVIEW*)lp; if((lv->uOldState & LVIS_FOCUSED && !(lv->uNewState & LVIS_FOCUSED)) || (lv->uOldState & LVIS_SELECTED && !(lv->uNewState & LVIS_SELECTED))) { - if(!store_nic()) { + if(!stash_nic_config()) { return TRUE; } } @@ -152,30 +164,38 @@ static LRESULT CALLBACK main_wproc(HWND window, UINT msg, WPARAM wp, LPARAM lp) if(HIWORD(wp) == BN_CLICKED) { switch(LOWORD(wp)) { case ID_NIC_ENABLED: { - int nic = ListView_GetNextItem(windows.nic_list, (LPARAM)-1, LVNI_FOCUSED); - nics[nic].config.enabled = Button_GetCheck(windows.nic_enabled) == BST_CHECKED; + int nic = ListView_GetNextItem(wh.nic_list, (LPARAM)(-1), LVNI_FOCUSED); + nics[nic].config.enabled = get_checkbox(wh.nic_enabled); - init_pri_list(); - update_nic_conf(); + reload_primary_nics(); + main_window_update(); break; } case ID_OPT_LOG_DEBUG: { - EnableWindow(windows.opt_log_trace, Button_GetCheck(windows.opt_log_debug) == BST_CHECKED); + main_window_update(); + break; + } + + case ID_OPT_USE_PCAP: { + main_config.use_pcap = get_checkbox(wh.opt_use_pcap); + + reload_nics(); + break; } case ID_OK: { if(save_config()) { - PostMessage(windows.main, WM_CLOSE, 0, 0); + PostMessage(wh.main, WM_CLOSE, 0, 0); } break; } case ID_CANCEL: { - PostMessage(windows.main, WM_CLOSE, 0, 0); + PostMessage(wh.main, WM_CLOSE, 0, 0); break; } @@ -193,72 +213,7 @@ static LRESULT CALLBACK main_wproc(HWND window, UINT msg, WPARAM wp, LPARAM lp) } case WM_SIZE: { - int width = LOWORD(lp), height = HIWORD(lp); - - int edge = GetSystemMetrics(SM_CYEDGE); - int text_h = get_text_height(windows.primary_group); - int edit_h = text_h + 2 * edge; - int pri_h = edit_h + text_h + 10; - - MoveWindow(windows.primary_group, 0, 0, width, pri_h, TRUE); - MoveWindow(windows.primary, 10, text_h, width - 20, edit_h, TRUE); - - /* Buttons */ - - RECT btn_rect = get_window_rect(windows.ok_btn); - int btn_w = btn_rect.right - btn_rect.left; - int btn_h = btn_rect.bottom - btn_rect.top; - - MoveWindow(windows.app_btn, width - btn_w - 6, height - btn_h - 6, btn_w, btn_h, TRUE); - MoveWindow(windows.can_btn, width - btn_w * 2 - 12, height - btn_h - 6, btn_w, btn_h, TRUE); - MoveWindow(windows.ok_btn, width - btn_w * 3 - 18, height - btn_h - 6, btn_w, btn_h, TRUE); - - /* Options groupbox */ - - int lbl_w = get_text_width(windows.nic_net_lbl, "Network number"); - int edit_w = get_text_width(windows.nic_node, "000000"); - - int opt_h = 5 * text_h + edit_h + 18; - - MoveWindow(windows.opt_group, 0, height - opt_h - btn_h - 12, width, opt_h, TRUE); - - int y = text_h; - - MoveWindow(windows.opt_port_lbl, 10, y + edge, lbl_w, text_h, TRUE); - MoveWindow(windows.opt_port, 15 + lbl_w, y, edit_w, edit_h, TRUE); - - MoveWindow(windows.opt_w95, 10, y += edit_h + 4, width - 20, text_h, TRUE); - MoveWindow(windows.opt_log_debug, 10, y += text_h + 2, width - 20, text_h, TRUE); - MoveWindow(windows.opt_log_trace, 10, y += text_h + 2, width - 20, text_h, TRUE); - MoveWindow(windows.opt_fw_except, 10, y += text_h + 2, width - 20, text_h, TRUE); - - /* NIC groupbox */ - - // lbl_w = get_text_width(windows.nic_net_lbl, "Network number"); - edit_w = get_text_width(windows.nic_node, "00:00:00:00:00:00"); - - int net_h = height - pri_h - opt_h - btn_h - 12; - - MoveWindow(windows.nic_group, 0, pri_h, width, net_h, TRUE); - - y = net_h - (6 + edit_h); - - MoveWindow(windows.nic_node_lbl, 10, y + edge, lbl_w, text_h, TRUE); - MoveWindow(windows.nic_node, 15 + lbl_w, y, edit_w, edit_h, TRUE); - - y -= 2 + edit_h; - - MoveWindow(windows.nic_net_lbl, 10, y + edge, lbl_w, text_h, TRUE); - MoveWindow(windows.nic_net, 15 + lbl_w, y, edit_w, edit_h, TRUE); - - y -= 2 + edit_h; - - MoveWindow(windows.nic_enabled, 10, y, width - 20, text_h, TRUE); - - y -= 6; - - MoveWindow(windows.nic_list, 10, text_h, width - 20, y - text_h, TRUE); - + main_window_resize(LOWORD(lp), HIWORD(lp)); break; } @@ -274,7 +229,7 @@ static LRESULT CALLBACK groupbox_wproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp switch(msg) { case WM_COMMAND: case WM_NOTIFY: - return main_wproc(windows.main, msg, wp, lp); + return main_wproc(wh.main, msg, wp, lp); default: return default_groupbox_wproc(hwnd, msg, wp, lp); @@ -283,9 +238,42 @@ static LRESULT CALLBACK groupbox_wproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp int main() { - get_nics(); + INITCOMMONCONTROLSEX common_controls; + common_controls.dwSize = sizeof(common_controls); + common_controls.dwICC = ICC_LISTVIEW_CLASSES; - init_windows(); + if(!InitCommonControlsEx(&common_controls)) + { + die("Could not initialise common controls"); + } + + WNDCLASSEX wclass; + wclass.cbSize = sizeof(wclass); + wclass.style = CS_HREDRAW | CS_VREDRAW; + wclass.lpfnWndProc = &main_wproc; + wclass.cbClsExtra = 0; + wclass.cbWndExtra = 0; + wclass.hInstance = GetModuleHandle(NULL); + wclass.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(50), IMAGE_ICON, 32, 32, LR_SHARED); + wclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); + wclass.lpszMenuName = NULL; + wclass.lpszClassName = IPXCONFIG_WINDOW_CLASS; + wclass.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(50), IMAGE_ICON, 16, 16, LR_SHARED); + + if(!RegisterClassEx(&wclass)) + { + die("Could not register window class: " + w32_errmsg(GetLastError())); + } + + if(!PCAP_INSTALLED && main_config.use_pcap) + { + MessageBox(NULL, "IPXWrapper is currently configured to use WinPcap, but it doesn't seem to be installed.\n" + "Configuration has been reset to use IP encapsulation.", "WinPcap not found", MB_OK | MB_TASKMODAL | MB_ICONWARNING); + main_config.use_pcap = false; + } + + main_window_init(); MSG msg; BOOL mret; @@ -299,7 +287,7 @@ int main() DispatchMessage(&msg); if(inv_window && !PeekMessage(&msg, NULL, 0, 0, 0)) { - MessageBox(windows.main, inv_error.c_str(), "Error", MB_OK); + MessageBox(wh.main, inv_error.c_str(), "Error", MB_OK); SetFocus(inv_window); Edit_SetSel(inv_window, 0, Edit_GetTextLength(inv_window)); @@ -323,44 +311,170 @@ static void _add_nic(addr48_t hwaddr, const char *name) nics.push_back(iface); } -static void get_nics() +/* Repopulate the list of network interfaces. + * + * This is called during main window initialisation and when switching between + * IP/Ethernet encapsulation. +*/ +static void reload_nics() { - _add_nic(WILDCARD_IFACE_HWADDR, "Wildcard interface"); + nics.clear(); - IP_ADAPTER_INFO *ifroot = load_sys_interfaces(), *ifptr; - - for(ifptr = ifroot; ifptr; ifptr = ifptr->Next) + if(main_config.use_pcap) { - if(ifptr->AddressLength != 6) + /* Ethernet encapsulation is selected, so we base the NIC list + * on the list of usable WinPcap interfaces. + */ + + ipx_pcap_interface_t *pcap_interfaces = ipx_get_pcap_interfaces(), *i; + + DL_FOREACH(pcap_interfaces, i) { - continue; + _add_nic(i->mac_addr, i->desc); } - _add_nic(addr48_in(ifptr->Address), ifptr->Description); + ipx_free_pcap_interfaces(&pcap_interfaces); + } + else{ + /* IP encapsulation is selected, so we base the NIC list on the + * list of IP interfaces. + */ + + _add_nic(WILDCARD_IFACE_HWADDR, "Wildcard interface"); + + IP_ADAPTER_INFO *ifroot = load_sys_interfaces(), *ifptr; + + for(ifptr = ifroot; ifptr; ifptr = ifptr->Next) + { + if(ifptr->AddressLength != 6) + { + continue; + } + + _add_nic(addr48_in(ifptr->Address), ifptr->Description); + } + + free(ifroot); } - free(ifroot); + ListView_DeleteAllItems(wh.nic_list); + + LVITEM lvi; + lvi.mask = LVIF_TEXT | LVIF_STATE; + lvi.iItem = 0; + lvi.iSubItem = 0; + lvi.state = 0; + lvi.stateMask = 0; + + for(auto i = nics.begin(); i != nics.end(); ++i) + { + lvi.pszText = i->name; + + ListView_InsertItem(wh.nic_list, &lvi); + ++(lvi.iItem); + } + + ListView_SetColumnWidth(wh.nic_list, 0, LVSCW_AUTOSIZE); + + reload_primary_nics(); + main_window_update(); +} + +/* Repopulate the list of interfaces available to be used as the primary. + * + * This is called after reloading the list of NICs or when toggling the enabled + * state of an interface. +*/ +static void reload_primary_nics() +{ + ComboBox_ResetContent(wh.primary); + + ComboBox_AddString(wh.primary, "Default"); + ComboBox_SetCurSel(wh.primary, 0); + + for(auto i = nics.begin(); i != nics.end(); i++) + { + if(i->config.enabled) + { + int index = ComboBox_AddString(wh.primary, i->name); + + if(i->hwaddr == primary_iface) + { + ComboBox_SetCurSel(wh.primary, index); + } + } + } +} + +/* Fetch NIC settings from UI and stash them in the NIC list. + * + * Returns false if an address or other text value could not be parsed, true + * otherwise. +*/ +static bool stash_nic_config() +{ + int selected_nic = ListView_GetNextItem(wh.nic_list, (LPARAM)-1, LVNI_FOCUSED); + + if(selected_nic == -1) { + /* Return success if no NIC is selected */ + return true; + } + + char net[32], node[32]; + + GetWindowText(wh.nic_net, net, 32); + GetWindowText(wh.nic_node, node, 32); + + if(!addr32_from_string(&(nics[selected_nic].config.netnum), net)) + { + inv_error = "Network number is invalid.\n" + "Valid network numbers are in the format XX:XX:XX:XX"; + inv_window = wh.nic_net; + + return false; + } + + if(!main_config.use_pcap) + { + if(!addr48_from_string(&(nics[selected_nic].config.nodenum), node)) + { + inv_error = "Node number is invalid.\n" + "Valid numbers are in the format XX:XX:XX:XX:XX:XX"; + inv_window = wh.nic_node; + + return false; + } + } + + return true; } static bool save_config() { - if(!store_nic()) + if(!stash_nic_config()) { return false; } - int pri_index = ComboBox_GetCurSel(windows.primary); + int pri_index = ComboBox_GetCurSel(wh.primary); if(pri_index == 0) { primary_iface = addr48_in((unsigned char[]){0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}); } else{ - int this_nic = 1; + /* Iterate over the NIC list to find the selected primary + * interface. + * + * Can't just deref by index here as disabled interfaces don't + * increment the index. + */ - for(iface_list::iterator i = nics.begin(); i != nics.end(); i++) + int this_nic = 0; + + for(auto i = nics.begin(); i != nics.end(); i++) { - if(i->config.enabled && this_nic++ == pri_index) + if(i->config.enabled && ++this_nic == pri_index) { primary_iface = i->hwaddr; break; @@ -368,36 +482,41 @@ static bool save_config() } } - char port_s[32], *endptr; - - GetWindowText(windows.opt_port, port_s, 32); - int port = strtol(port_s, &endptr, 10); - - if(port < 1 || port > 65535 || *endptr) { - MessageBox(windows.main, "Invalid port number.\nPort number must be an integer in the range 1 - 65535", "Error", MB_OK); + if(!main_config.use_pcap) + { + char port_s[32], *endptr; - SetFocus(windows.opt_port); - Edit_SetSel(windows.opt_port, 0, Edit_GetTextLength(windows.opt_port)); + GetWindowText(wh.opt_port, port_s, 32); + int port = strtol(port_s, &endptr, 10); - return false; + if(port < 1 || port > 65535 || *endptr) { + MessageBox(wh.main, "Invalid port number.\n" + "Port number must be an integer in the range 1 - 65535", "Error", MB_OK); + + SetFocus(wh.opt_port); + Edit_SetSel(wh.opt_port, 0, Edit_GetTextLength(wh.opt_port)); + + return false; + } + + main_config.udp_port = port; } - main_config.udp_port = port; - main_config.w95_bug = Button_GetCheck(windows.opt_w95) == BST_CHECKED; - main_config.fw_except = Button_GetCheck(windows.opt_fw_except) == BST_CHECKED; + main_config.w95_bug = get_checkbox(wh.opt_w95); + main_config.fw_except = get_checkbox(wh.opt_fw_except); main_config.log_level = LOG_INFO; - if(Button_GetCheck(windows.opt_log_debug) == BST_CHECKED) + if(get_checkbox(wh.opt_log_debug)) { main_config.log_level = LOG_DEBUG; - if(Button_GetCheck(windows.opt_log_trace) == BST_CHECKED) + if(get_checkbox(wh.opt_log_trace)) { main_config.log_level = LOG_CALL; } } - for(iface_list::iterator i = nics.begin(); i != nics.end(); i++) + for(auto i = nics.begin(); i != nics.end(); i++) { if(!set_iface_config(i->hwaddr, &(i->config))) { @@ -408,68 +527,13 @@ static bool save_config() return set_main_config(&main_config) && set_primary_iface(primary_iface); } -/* Fetch NIC settings from UI and store in NIC list */ -static bool store_nic() { - int selected_nic = ListView_GetNextItem(windows.nic_list, (LPARAM)-1, LVNI_FOCUSED); - - if(selected_nic == -1) { - /* Return success if no NIC is selected */ - return true; - } - - char net[32], node[32]; - - GetWindowText(windows.nic_net, net, 32); - GetWindowText(windows.nic_node, node, 32); - - if(!addr32_from_string(&(nics[selected_nic].config.netnum), net)) - { - inv_error = "Network number is invalid.\nValid numbers are in the format XX:XX:XX:XX"; - inv_window = windows.nic_net; - - return false; - } - - if(!addr48_from_string(&(nics[selected_nic].config.nodenum), node)) - { - inv_error = "Node number is invalid.\nValid numbers are in the format XX:XX:XX:XX:XX:XX"; - inv_window = windows.nic_node; - - return false; - } - - return true; -} - -static void init_windows() { - INITCOMMONCONTROLSEX common_controls; - common_controls.dwSize = sizeof(common_controls); - common_controls.dwICC = ICC_LISTVIEW_CLASSES; - - if(!InitCommonControlsEx(&common_controls)) { - die("Failed to initialise common controls"); - } - - WNDCLASSEX wclass; - wclass.cbSize = sizeof(wclass); - wclass.style = 0; - wclass.lpfnWndProc = &main_wproc; - wclass.cbClsExtra = 0; - wclass.cbWndExtra = 0; - wclass.hInstance = GetModuleHandle(NULL); - wclass.hIcon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(50), IMAGE_ICON, 32, 32, LR_SHARED); - wclass.hCursor = LoadCursor(NULL, IDC_ARROW); - wclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); - wclass.lpszMenuName = NULL; - wclass.lpszClassName = "ipxconfig_class"; - wclass.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(50), IMAGE_ICON, 16, 16, LR_SHARED); - - if(!RegisterClassEx(&wclass)) { - die("Failed to register ipxconfig_class: " + w32_errmsg(GetLastError())); - } - - windows.main = CreateWindow( - "ipxconfig_class", +/* Create the main window and all the child windows, populate them with the + * current configuration and then display them. +*/ +static void main_window_init() +{ + wh.main = CreateWindow( + IPXCONFIG_WINDOW_CLASS, "IPXWrapper configuration", WS_TILEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, @@ -480,137 +544,225 @@ static void init_windows() { NULL ); - if(!windows.main) { - die("Failed to create main window: " + w32_errmsg(GetLastError())); + if(!wh.main) + { + die("Could not create main window: " + w32_errmsg(GetLastError())); } - windows.primary_group = create_group(windows.main, 0, 0, 400, 50, "Primary interface"); + /* +- Primary interface -------------------------------------+ + * | | Default |▼| | + * +---------------------------------------------------------+ + */ - int text_h = get_text_height(windows.primary_group); + wh.box_primary = create_GroupBox(wh.main, "Primary interface"); - windows.primary = create_child(windows.primary_group, 10, text_h, 380, 300, WC_COMBOBOX, NULL, CBS_DROPDOWNLIST | CBS_HASSTRINGS, 0, ID_PRI_LIST); - init_pri_list(); + wh.primary = create_child(wh.box_primary, WC_COMBOBOX, NULL, CBS_DROPDOWNLIST | CBS_HASSTRINGS, 0); + + /* +- Network adapters --------------------------------------+ + * | +-----------------------------------------------------+ | + * | | ... | | + * | | ... | | + * | | ... | | + * | +-----------------------------------------------------+ | + * | □ Enable interface | + * | Network number | AA:BB:CC:DD | | + * | Node number | AA:BB:CC:DD:EE:FF | | + * +---------------------------------------------------------+ + */ { - windows.nic_group = create_group(windows.main, 0, 0, 0, 0, "Network adapters"); + wh.box_nic = create_GroupBox(wh.main, "Network adapters"); - windows.nic_list = create_child(windows.nic_group, 0, 0, 0, 0, WC_LISTVIEW, NULL, LVS_SINGLESEL | LVS_REPORT | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, ID_NIC_LIST); - //ListView_SetExtendedListViewStyle(windows.nic_list, LVS_EX_FULLROWSELECT); + wh.nic_list = create_child(wh.box_nic, WC_LISTVIEW, NULL, LVS_SINGLESEL | LVS_REPORT | WS_TABSTOP | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, ID_NIC_LIST); LVCOLUMN lvc; lvc.mask = LVCF_FMT; - lvc.fmt = LVCFMT_LEFT; + lvc.fmt = LVCFMT_LEFT; - ListView_InsertColumn(windows.nic_list, 0, &lvc); + ListView_InsertColumn(wh.nic_list, 0, &lvc); - LVITEM lvi; + wh.nic_enabled = create_checkbox(wh.box_nic, "Enable interface", ID_NIC_ENABLED); - lvi.mask = LVIF_TEXT | LVIF_STATE; - lvi.iItem = 0; - lvi.iSubItem = 0; - lvi.state = 0; - lvi.stateMask = 0; + wh.nic_net_lbl = create_STATIC(wh.box_nic, "Network number"); + wh.nic_net = create_child(wh.box_nic, "EDIT", "", WS_TABSTOP, WS_EX_CLIENTEDGE, ID_NIC_NET); - for(iface_list::iterator i = nics.begin(); i != nics.end(); i++) { - lvi.pszText = i->name; - - ListView_InsertItem(windows.nic_list, &lvi); - lvi.iItem++; - } - - ListView_SetColumnWidth(windows.nic_list, 0, LVSCW_AUTOSIZE); - - windows.nic_enabled = create_child(windows.nic_group, 0, 0, 0, 0, "BUTTON", "Enable interface", BS_AUTOCHECKBOX | WS_TABSTOP, 0, ID_NIC_ENABLED); - - windows.nic_net_lbl = create_child(windows.nic_group, 0, 0, 0, 0, "STATIC", "Network number", SS_RIGHT); - windows.nic_net = create_child(windows.nic_group, 0, 0, 0, 0, "EDIT", "", WS_TABSTOP, WS_EX_CLIENTEDGE, ID_NIC_NET); - - windows.nic_node_lbl = create_child(windows.nic_group, 0, 0, 0, 0, "STATIC", "Node number", SS_RIGHT); - windows.nic_node = create_child(windows.nic_group, 0, 0, 0, 0, "EDIT", "", WS_TABSTOP, WS_EX_CLIENTEDGE, ID_NIC_NODE); - - update_nic_conf(); + wh.nic_node_lbl = create_STATIC(wh.box_nic, "Node number"); + wh.nic_node = create_child(wh.box_nic, "EDIT", "", WS_TABSTOP, WS_EX_CLIENTEDGE, ID_NIC_NODE); } + /* +- Options -----------------------------------------------+ + * | Broadcast port | 54792 | | + * | □ Enable Windows 95 SO_BROADCAST bug | + * | □ Log debugging messages | + * | □ Log WinSock API calls | + * | □ Automatically create Windows Firewall exceptions | + * | □ Send and receive real IPX packets | + * +---------------------------------------------------------+ + */ + { - windows.opt_group = create_group(windows.main, 0, 0, 0, 0, "Options"); + wh.box_options = create_GroupBox(wh.main, "Options"); - windows.opt_port_lbl = create_child(windows.opt_group, 0, 0, 0, 0, "STATIC", "Broadcast port", SS_RIGHT); - windows.opt_port = create_child(windows.opt_group, 0, 0, 0, 0, "EDIT", "", WS_TABSTOP, WS_EX_CLIENTEDGE, ID_OPT_PORT); + wh.opt_port_lbl = create_STATIC(wh.box_options, "Broadcast port"); + wh.opt_port = create_child(wh.box_options, "EDIT", "", WS_TABSTOP, WS_EX_CLIENTEDGE, ID_OPT_PORT); char port_s[8]; sprintf(port_s, "%hu", main_config.udp_port); - SetWindowText(windows.opt_port, port_s); + SetWindowText(wh.opt_port, port_s); - windows.opt_w95 = create_child(windows.opt_group, 0, 0, 0, 0, "BUTTON", "Enable Windows 95 SO_BROADCAST bug", BS_AUTOCHECKBOX | WS_TABSTOP, 0, ID_OPT_W95); - windows.opt_log_debug = create_child(windows.opt_group, 0, 0, 0, 0, "BUTTON", "Log debugging messages", BS_AUTOCHECKBOX | WS_TABSTOP, 0, ID_OPT_LOG_DEBUG); - windows.opt_log_trace = create_child(windows.opt_group, 0, 0, 0, 0, "BUTTON", "Log WinSock API calls", BS_AUTOCHECKBOX | WS_TABSTOP, 0, ID_OPT_LOG_TRACE); - windows.opt_fw_except = create_child(windows.opt_group, 0, 0, 0, 0, "BUTTON", "Automatically create Windows Firewall exceptions", BS_AUTOCHECKBOX | WS_TABSTOP, 0, ID_OPT_FW_EXCEPT); + wh.opt_w95 = create_checkbox(wh.box_options, "Enable Windows 95 SO_BROADCAST bug", ID_OPT_W95); + wh.opt_log_debug = create_checkbox(wh.box_options, "Log debugging messages", ID_OPT_LOG_DEBUG); + wh.opt_log_trace = create_checkbox(wh.box_options, "Log WinSock API calls", ID_OPT_LOG_TRACE); + wh.opt_fw_except = create_checkbox(wh.box_options, "Automatically create Windows Firewall exceptions", ID_OPT_FW_EXCEPT); + wh.opt_use_pcap = create_checkbox(wh.box_options, "Send and receive real IPX packets", ID_OPT_USE_PCAP); - Button_SetCheck(windows.opt_w95, main_config.w95_bug ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(windows.opt_log_debug, main_config.log_level <= LOG_DEBUG ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(windows.opt_log_trace, main_config.log_level <= LOG_CALL ? BST_CHECKED : BST_UNCHECKED); - Button_SetCheck(windows.opt_fw_except, main_config.fw_except ? BST_CHECKED : BST_UNCHECKED); + set_checkbox(wh.opt_w95, main_config.w95_bug); + set_checkbox(wh.opt_log_debug, main_config.log_level <= LOG_DEBUG); + set_checkbox(wh.opt_log_trace, main_config.log_level <= LOG_CALL); + set_checkbox(wh.opt_fw_except, main_config.fw_except); + set_checkbox(wh.opt_use_pcap, main_config.use_pcap); - EnableWindow(windows.opt_log_trace, Button_GetCheck(windows.opt_log_debug) == BST_CHECKED); + EnableWindow(wh.opt_use_pcap, PCAP_INSTALLED); } - /* TODO: Size buttons dynamically */ - int btn_w = 75; - int btn_h = 23; + wh.ok_btn = create_child(wh.main, "BUTTON", "OK", BS_PUSHBUTTON | WS_TABSTOP, 0, ID_OK); + wh.can_btn = create_child(wh.main, "BUTTON", "Cancel", BS_PUSHBUTTON | WS_TABSTOP, 0, ID_CANCEL); + wh.app_btn = create_child(wh.main, "BUTTON", "Apply", BS_PUSHBUTTON | WS_TABSTOP, 0, ID_APPLY); - windows.ok_btn = create_child(windows.main, 0, 0, btn_w, btn_h, "BUTTON", "OK", BS_PUSHBUTTON | WS_TABSTOP, 0, ID_OK); - windows.can_btn = create_child(windows.main, 0, 0, btn_w, btn_h, "BUTTON", "Cancel", BS_PUSHBUTTON | WS_TABSTOP, 0, ID_CANCEL); - windows.app_btn = create_child(windows.main, 0, 0, btn_w, btn_h, "BUTTON", "Apply", BS_PUSHBUTTON | WS_TABSTOP, 0, ID_APPLY); + reload_nics(); - ShowWindow(windows.main, SW_SHOW); - UpdateWindow(windows.main); + ShowWindow(wh.main, SW_SHOW); + UpdateWindow(wh.main); } -static void update_nic_conf() { - int selected_nic = ListView_GetNextItem(windows.nic_list, (LPARAM)-1, LVNI_FOCUSED); - bool enabled = selected_nic >= 0 ? nics[selected_nic].config.enabled : false; +/* Reset the NIC network/node numbers and toggle the enabled state of controls + * on the UI. Called when the selected NIC changes and when some options are + * toggled. +*/ +static void main_window_update() +{ + int selected_nic = ListView_GetNextItem(wh.nic_list, (LPARAM)(-1), LVNI_FOCUSED); - EnableWindow(windows.nic_net, enabled); - EnableWindow(windows.nic_node, enabled); - EnableWindow(windows.nic_enabled, selected_nic >= 0); + bool nic_enabled = selected_nic >= 0 + ? nics[selected_nic].config.enabled + : false; + + EnableWindow(wh.nic_enabled, selected_nic >= 0); + EnableWindow(wh.nic_net, nic_enabled); + EnableWindow(wh.nic_node, nic_enabled && !main_config.use_pcap); if(selected_nic >= 0) { - Button_SetCheck(windows.nic_enabled, enabled ? BST_CHECKED : BST_UNCHECKED); + set_checkbox(wh.nic_enabled, nic_enabled); char net_s[ADDR32_STRING_SIZE]; addr32_string(net_s, nics[selected_nic].config.netnum); char node_s[ADDR48_STRING_SIZE]; - addr48_string(node_s, nics[selected_nic].config.nodenum); + addr48_string(node_s, (main_config.use_pcap + ? nics[selected_nic].hwaddr + : nics[selected_nic].config.nodenum)); - SetWindowText(windows.nic_net, net_s); - SetWindowText(windows.nic_node, node_s); + SetWindowText(wh.nic_net, net_s); + SetWindowText(wh.nic_node, node_s); } + + EnableWindow(wh.opt_port, !main_config.use_pcap); + EnableWindow(wh.opt_log_trace, get_checkbox(wh.opt_log_debug)); } -static void init_pri_list() { - ComboBox_ResetContent(windows.primary); +/* Update the size and position of everything within the window. */ +static void main_window_resize(int width, int height) +{ + int edge = GetSystemMetrics(SM_CYEDGE); + int text_h = get_text_height(wh.box_primary); + int edit_h = text_h + 2 * edge; + int lbl_w = get_text_width(wh.nic_net_lbl, "Network number"); - ComboBox_AddString(windows.primary, "Default"); - ComboBox_SetCurSel(windows.primary, 0); + /* Primary interface + * Height is constant. Vertical position is at the very top. + */ - for(iface_list::iterator i = nics.begin(); i != nics.end(); i++) - { - if(i->config.enabled) - { - int index = ComboBox_AddString(windows.primary, i->name); - - if(i->hwaddr == primary_iface) - { - ComboBox_SetCurSel(windows.primary, index); - } - } - } + int pri_h = edit_h + text_h + 10; + + MoveWindow(wh.box_primary, 0, 0, width, pri_h, TRUE); + MoveWindow(wh.primary, 10, text_h, width - 20, height, TRUE); + + /* Buttons + * Height is constant. Vertical position is at the very bottom. + * + * TODO: Dynamic button sizing. + */ + + int btn_w = 75; + int btn_h = 23; + + MoveWindow(wh.app_btn, width - btn_w - 6, height - btn_h - 6, btn_w, btn_h, TRUE); + MoveWindow(wh.can_btn, width - btn_w * 2 - 12, height - btn_h - 6, btn_w, btn_h, TRUE); + MoveWindow(wh.ok_btn, width - btn_w * 3 - 18, height - btn_h - 6, btn_w, btn_h, TRUE); + + /* Options + * Height is constant. Vertical position is above buttons. + */ + + int box_options_y = text_h; + + int port_w = get_text_width(wh.nic_node, "000000"); + + MoveWindow(wh.opt_port_lbl, 10, box_options_y + edge, lbl_w, text_h, TRUE); + MoveWindow(wh.opt_port, 15 + lbl_w, box_options_y, port_w, edit_h, TRUE); + box_options_y += edit_h + 4; + + MoveWindow(wh.opt_w95, 10, box_options_y, width - 20, text_h, TRUE); + box_options_y += text_h + 2; + + MoveWindow(wh.opt_log_debug, 10, box_options_y, width - 20, text_h, TRUE); + box_options_y += text_h + 2; + + MoveWindow(wh.opt_log_trace, 10, box_options_y, width - 20, text_h, TRUE); + box_options_y += text_h + 2; + + MoveWindow(wh.opt_fw_except, 10, box_options_y, width - 20, text_h, TRUE); + box_options_y += text_h + 2; + + MoveWindow(wh.opt_use_pcap, 10, box_options_y, width - 20, text_h, TRUE); + box_options_y += text_h + 2; + + int box_options_h = box_options_y + 2; + + MoveWindow(wh.box_options, 0, height - box_options_y - btn_h - 12, width, box_options_h, TRUE); + + /* NICs + * Fills available space between primary interface and options. + */ + + int node_w = get_text_width(wh.nic_node, "00:00:00:00:00:00"); + int box_nic_h = height - pri_h - box_options_h - btn_h - 12; + + MoveWindow(wh.box_nic, 0, pri_h, width, box_nic_h, TRUE); + + int box_nic_y = box_nic_h - 4; + + box_nic_y -= edit_h + 2; + MoveWindow(wh.nic_node_lbl, 10, box_nic_y + edge, lbl_w, text_h, TRUE); + MoveWindow(wh.nic_node, 15 + lbl_w, box_nic_y, node_w, edit_h, TRUE); + + box_nic_y -= edit_h + 2; + MoveWindow(wh.nic_net_lbl, 10, box_nic_y + edge, lbl_w, text_h, TRUE); + MoveWindow(wh.nic_net, 15 + lbl_w, box_nic_y, node_w, edit_h, TRUE); + + box_nic_y -= edit_h + 2; + MoveWindow(wh.nic_enabled, 10, box_nic_y, width - 20, text_h, TRUE); + + box_nic_y -= 6; + MoveWindow(wh.nic_list, 10, text_h, width - 20, box_nic_y - text_h, TRUE); + + + UpdateWindow(wh.main); } -static HWND create_child(HWND parent, int x, int y, int w, int h, LPCTSTR class_name, LPCTSTR title, DWORD style, DWORD ex_style, unsigned int id) { +static HWND create_child(HWND parent, LPCTSTR class_name, LPCTSTR title, DWORD style, DWORD ex_style, unsigned int id) { static unsigned int idnum = 100; HWND hwnd = CreateWindowEx( @@ -618,15 +770,16 @@ static HWND create_child(HWND parent, int x, int y, int w, int h, LPCTSTR class_ class_name, title, WS_CHILD | WS_VISIBLE | style, - x, y, w, h, + 0, 0, 0, 0, parent, (HMENU)(id ? id : idnum++), NULL, NULL ); - if(!hwnd) { - die(std::string("Failed to create child window (") + class_name + "): " + w32_errmsg(GetLastError())); + if(!hwnd) + { + die(std::string("Could not create ") + class_name + " window: " + w32_errmsg(GetLastError())); } SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); @@ -634,13 +787,34 @@ static HWND create_child(HWND parent, int x, int y, int w, int h, LPCTSTR class_ return hwnd; } -static HWND create_group(HWND parent, int x, int y, int w, int h, LPCTSTR title) { - HWND groupbox = create_child(parent, x, y, w, h, "BUTTON", title, BS_GROUPBOX); +static HWND create_GroupBox(HWND parent, LPCTSTR title) +{ + HWND groupbox = create_child(parent, "BUTTON", title, BS_GROUPBOX); default_groupbox_wproc = (wproc_fptr)SetWindowLongPtr(groupbox, GWLP_WNDPROC, (LONG_PTR)&groupbox_wproc); return groupbox; } +static HWND create_STATIC(HWND parent, LPCTSTR text) +{ + return create_child(parent, "STATIC", text, SS_RIGHT); +} + +static HWND create_checkbox(HWND parent, LPCTSTR label, int id) +{ + return create_child(parent, "BUTTON", label, BS_AUTOCHECKBOX | WS_TABSTOP, 0, id); +} + +static bool get_checkbox(HWND checkbox) +{ + return Button_GetCheck(checkbox) == BST_CHECKED; +} + +static void set_checkbox(HWND checkbox, bool state) +{ + Button_SetCheck(checkbox, (state ? BST_CHECKED : BST_UNCHECKED)); +} + static int get_text_width(HWND hwnd, const char *txt) { HDC dc = GetDC(hwnd); if(!dc) { @@ -673,16 +847,6 @@ static int get_text_height(HWND hwnd) { return tm.tmHeight; } -static RECT get_window_rect(HWND hwnd) { - RECT rect; - - if(!GetWindowRect(hwnd, &rect)) { - die("GetWindowRect failed: " + w32_errmsg(GetLastError())); - } - - return rect; -} - /* Convert a win32 error number to a message */ static std::string w32_errmsg(DWORD errnum) { char buf[256] = {'\0'}; @@ -697,6 +861,21 @@ static void die(std::string msg) { exit(1); } +/* Check if WinPcap is installed. + * Returns true if wpcap.dll can be loaded. +*/ +static bool _pcap_installed() +{ + HMODULE wpcap = LoadLibrary("wpcap.dll"); + if(wpcap) + { + FreeLibrary(wpcap); + return true; + } + + return false; +} + /* Used to display errors from shared functions. */ extern "C" void log_printf(enum ipx_log_level level, const char *fmt, ...) { diff --git a/src/ipxwrapper.c b/src/ipxwrapper.c index c02e5f1..ab4533b 100644 --- a/src/ipxwrapper.c +++ b/src/ipxwrapper.c @@ -82,6 +82,7 @@ BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) main_config = get_main_config(); min_log_level = main_config.log_level; + ipx_use_pcap = main_config.use_pcap; if(main_config.fw_except) { @@ -89,7 +90,6 @@ BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) add_self_to_firewall(); } - ipx_use_pcap = FALSE; addr_cache_init(); ipx_interfaces_init();