/* For terms of usage/redistribution/modification see the LICENSE file */ /* For authors and contributors see the AUTHORS file */ /*** landesc.c - LAN host description management module Currently includes support for Ethernet, PLIP, and FDDI ***/ #include "iptraf-ng-compat.h" #include "tui/input.h" #include "tui/listbox.h" #include "tui/msgboxes.h" #include "tui/menurt.h" #include "landesc.h" #include "deskman.h" #include "attrs.h" #include "dirs.h" static int check_mac_addr(const char *mac) { if (strlen(mac) != 17) return 0; char a[3], b[3], c[3], d[3], e[3], f[3]; int success = sscanf(mac, "%02s:%02s:%02s:%02s:%02s:%02s", a, b, c, d, e, f); if (success != 6) return 0; char mac_hex[13]; sprintf(mac_hex, "%s%s%s%s%s%s", a, b, c, d, e, f); for (int ii = 0; ii < 12; ++ii) if (!isxdigit(mac_hex[ii])) return 0; return 1; } /* parse and insert unique eth description. * caller is responsible for freeing whole list */ static void parse_eth_desc(FILE * fp, struct eth_desc *hd) { char *l = NULL; size_t len = 0; ssize_t read; while ((read = getline(&l, &len, fp)) != -1) { if (l[0] == '\n' || l[0] == '#') continue; char *line = l; if (strchr(line, '\n')) strchr(line, '\n')[0] = '\0'; char mac[18] = { 0 }; strncpy(mac, line, 17); if (!check_mac_addr(mac)) { tui_error(ANYKEY_MSG, "Not a mac '%s' address, skipped", mac); continue; } /* skip mac address */ line += 17; /* mandatory space between mac and ip */ if (!isspace(*line)) { tui_error(ANYKEY_MSG, "Missing mandatory space between" "mac and host/ip address, skipped"); continue; } line = skip_whitespace(line); if (!*line) { tui_error(ANYKEY_MSG, "Missing description, skipped"); continue; } struct eth_desc *new = xmalloc(sizeof(struct eth_desc)); memcpy(new->hd_mac, mac, sizeof(mac)); new->hd_desc = xstrdup(line); struct eth_desc *desc = NULL; list_for_each_entry(desc, &hd->hd_list, hd_list) if ((strcmp(desc->hd_mac, mac) == 0) || (strcmp(desc->hd_desc, line) == 0)) goto dupe; list_add_tail(&new->hd_list, &hd->hd_list); dupe:; } free(l); } struct eth_desc *load_eth_desc(unsigned link_type) { /* why is usefull to have it two files with same content? * There is two options how to merge it. * 1) separate by comments * $ cat ETHFILE * # ethernet host description * MAC ip/hostname * * # fddi host description * MAC ip/hostname * 2) put it into groups * [ethernet] * MAC ip/hostname * * [fddi] * MAC ip/hostname */ char *filename = NULL; FILE *fp = NULL; if (link_type == ARPHRD_ETHER) filename = ETHFILE; else if (link_type == ARPHRD_FDDI) filename = FDDIFILE; struct eth_desc *hd = xmallocz(sizeof(struct eth_desc)); INIT_LIST_HEAD(&hd->hd_list); fp = fopen(filename, "r"); if (fp) { parse_eth_desc(fp, hd); fclose(fp); } /* merge with /etc/ethers */ fp = fopen("/etc/ethers", "r"); if (fp) { parse_eth_desc(fp, hd); fclose(fp); } return hd; } static void save_eth_desc(struct eth_desc *hd, unsigned linktype) { FILE *fd = NULL; if (linktype == ARPHRD_ETHER) fd = fopen(ETHFILE, "w"); else if (linktype == ARPHRD_FDDI) fd = fopen(FDDIFILE, "w"); if (!fd) { tui_error(ANYKEY_MSG, "Unable to save host description file"); return; } fprintf(fd, "# see man ethers for syntax\n\n"); struct eth_desc *desc = NULL; list_for_each_entry(desc, &hd->hd_list, hd_list) fprintf(fd, "%s %s\n", desc->hd_mac, desc->hd_desc); fclose(fd); } void free_eth_desc(struct eth_desc *hd) { struct eth_desc *entry = NULL; struct list_head *l, *n; list_for_each_safe(l, n, &hd->hd_list) { entry = list_entry(l, struct eth_desc, hd_list); free(entry->hd_desc); list_del(l); free(entry); } } static struct eth_desc *select_eth_desc(const struct eth_desc *hd) { int resp; struct scroll_list slist; char descline[80]; if (list_empty(&hd->hd_list)) { tui_error(ANYKEY_MSG, "No descriptions"); return NULL; } tx_init_listbox(&slist, COLS, 20, 0, (LINES - 20) / 2, STDATTR, BOXATTR, BARSTDATTR, HIGHATTR); tx_set_listbox_title(&slist, "Address", 1); tx_set_listbox_title(&slist, "Description", 19); struct eth_desc *entry = NULL; list_for_each_entry(entry, &hd->hd_list, hd_list) { snprintf(descline, 80, "%-18s%s", entry->hd_mac, entry->hd_desc); tx_add_list_entry(&slist, (char *) entry, descline); } tx_show_listbox(&slist); int aborted = 0; tx_operate_listbox(&slist, &resp, &aborted); if (!aborted) entry = (struct eth_desc *) slist.textptr->nodeptr; else entry = NULL; tx_close_listbox(&slist); tx_destroy_list(&slist); update_panels(); doupdate(); return entry; } static int dialog_eth_desc(struct FIELDLIST *fields, const char *initaddr, const char *initdesc) { /* TODO: move to tui */ WINDOW *win = newwin(8, 70, 8, (COLS - 70) / 2); PANEL *panel = new_panel(win); wattrset(win, DLGBOXATTR); tx_colorwin(win); tx_box(win, ACS_VLINE, ACS_HLINE); wmove(win, 6, 2 * COLS / 80); tabkeyhelp(win); wmove(win, 6, 20 * COLS / 80); stdkeyhelp(win); wattrset(win, DLGTEXTATTR); wmove(win, 2, 2 * COLS / 80); wprintw(win, "MAC Address:"); wmove(win, 4, 2 * COLS / 80); wprintw(win, "Description:"); tx_initfields(fields, 3, 52, 10, (COLS - 52) / 2 + 6 * COLS / 80, DLGTEXTATTR, FIELDATTR); tx_addfield(fields, 17, 0, 0, initaddr); tx_addfield(fields, 50, 2, 0, initdesc); int aborted = 0; tx_fillfields(fields, &aborted); del_panel(panel); delwin(win); return aborted; } static void add_eth_desc(struct eth_desc *list) { struct FIELDLIST fields; int aborted = dialog_eth_desc(&fields, "", ""); if (!aborted) { struct eth_desc *new = xmalloc(sizeof(struct eth_desc)); memcpy(new->hd_mac, fields.list->buf, sizeof(new->hd_mac)); new->hd_desc = xstrdup(fields.list->nextfield->buf); list_add_tail(&new->hd_list, &list->hd_list); } tx_destroyfields(&fields); update_panels(); doupdate(); } static void edit_eth_desc(struct eth_desc *list) { struct eth_desc *hd = select_eth_desc(list); if (!hd) return; struct FIELDLIST fields; int aborted = dialog_eth_desc(&fields, hd->hd_mac, hd->hd_desc); if (!aborted) { free(hd->hd_desc); memcpy(hd->hd_mac, fields.list->buf, sizeof(hd->hd_mac)); hd->hd_desc = xstrdup(fields.list->nextfield->buf); } tx_destroyfields(&fields); } static void del_eth_desc(struct eth_desc *list) { struct eth_desc *hd = select_eth_desc(list); if (hd) { free(hd->hd_desc); list_del(&hd->hd_list); free(hd); } } void manage_eth_desc(unsigned linktype) { struct MENU menu; int row = 1; int aborted = 0; tx_initmenu(&menu, 7, 31, (LINES - 6) / 2, (COLS - 31) / 2, BOXATTR, STDATTR, HIGHATTR, BARSTDATTR, BARHIGHATTR, DESCATTR); tx_additem(&menu, " ^A^dd description...", "Adds a description for a MAC address"); tx_additem(&menu, " ^E^dit description...", "Modifies an existing MAC address description"); tx_additem(&menu, " ^D^elete description...", "Deletes an existing MAC address description"); tx_additem(&menu, NULL, NULL); tx_additem(&menu, " E^x^it menu", "Returns to the main menu"); struct eth_desc *list = load_eth_desc(linktype /*, WITHOUTETCETHERS */ ); do { tx_showmenu(&menu); tx_operatemenu(&menu, &row, &aborted); switch (row) { case 1: add_eth_desc(list); break; case 2: edit_eth_desc(list); break; case 3: del_eth_desc(list); break; } } while (row != 5); tx_destroymenu(&menu); update_panels(); doupdate(); save_eth_desc(list, linktype); }