/* * Copyright (C) 1999 Andrea Marangoni * Universita' di Macerata * Italy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "ntop.h" #include "globals-report.h" /* Forward */ static void NotesURL(char *addr, char *ip_addr); static void addNotes(char *addr, char *PostNotes); static void deletelastSeenURL( char *addr ); static void setPluginStatus(char * status); static void termLsFunct(u_char); static void handleLsPacket(u_char *_deviceId, const struct pcap_pkthdr *h _UNUSED_, const u_char *p); static void handleLsHTTPrequest(char* url); /* ****************************** * Plugin data block * ****************************** */ static PluginInfo LsPluginInfo[] = { { VERSION, /* current ntop version */ "Host Last Seen", "This plugin produces a report about the last time packets were seen from " "each specific host.
A note card database is available for recording " "additional information.", "2.3a", /* version */ "A.Marangoni", "LastSeen", /* http://:/plugins/Ls */ 0, /* Active by default */ ViewOnly, 0, /* Inactive setup */ NULL, /* no special startup after init */ termLsFunct, /* TermFunc */ handleLsPacket, /* PluginFunc */ handleLsHTTPrequest, NULL, /* no host creation/deletion handle */ "ip or (vlan and ip)", /* BPF filter: filter all the IP packets */ NULL, /* no status */ NULL /* no extra pages */ } }; /* ****************************** */ static GDBM_FILE LsDB = NULL; static int disabled = 0; typedef struct LsHostInfo { HostAddr HostIpAddress; time_t LastUpdated; } LsHostInfo; typedef struct LsHostNote { char note[50]; } LsHostNote; static void handleLsPacket(u_char *_deviceId, const struct pcap_pkthdr *h _UNUSED_, const u_char *p) { struct ip ip; struct ether_header *ep; datum key_data, data_data; char tmpStr[32]; LsHostInfo HostI; unsigned short rc; u_int deviceId; if ( disabled ) return; #ifdef WIN32 deviceId = 0; #else deviceId = (u_int)(*_deviceId); #endif ep = (struct ether_header *)p; if(ntohs(ep->ether_type) == ETHERTYPE_802_1Q) /* VLAN - skip 802.1q header too */ memcpy(&ip, (p+sizeof(struct ether_header)+4), sizeof(struct ip)); else memcpy(&ip, (p+sizeof(struct ether_header)), sizeof(struct ip)); NTOHL(ip.ip_src.s_addr); NTOHL(ip.ip_dst.s_addr); #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "%s [%x]", intoa(ip.ip_src), ip.ip_src.s_addr); traceEvent(CONST_TRACE_INFO, "->%s [%x]", intoa(ip.ip_dst), ip.ip_dst.s_addr); #endif rc = in_isPseudoLocalAddress(&ip.ip_src, deviceId, NULL, NULL); if(rc == 0) return; #ifdef DEBUG traceEvent(CONST_TRACE_INFO, "-->>>>%s [%d]", intoa(ip.ip_src), rc); #endif addrinit(&HostI.HostIpAddress); HostI.HostIpAddress.addr._hostIp4Address = ip.ip_src; HostI.LastUpdated = myGlobals.actTime; safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "%u", (unsigned) ip.ip_src.s_addr); key_data.dptr = tmpStr; key_data.dsize = (int)strlen(key_data.dptr)+1; data_data.dptr = (char *)&HostI; data_data.dsize = sizeof(HostI)+1; /* Test for disabled inside the protection of the mutex, also, so that if * we disabled the plugin since the test above, we don't seg fault */ if (!disabled) gdbm_store(LsDB, key_data, data_data, GDBM_REPLACE); } /* Record sort */ static int SortLS(const void *_a, const void *_b) { LsHostInfo *a = (LsHostInfo *)_a; LsHostInfo *b = (LsHostInfo *)_b; unsigned long n1, n2; if(((a) == NULL) && ((b) != NULL)) { traceEvent(CONST_TRACE_WARNING, "SortLS() (1)"); return(1); } else if(((a) != NULL) && ((b) == NULL)) { traceEvent(CONST_TRACE_WARNING, "SortLS() (2)"); return(-1); } else if(((a) == NULL) && ((b) == NULL)) { traceEvent(CONST_TRACE_WARNING, "SortLS() (3)"); return(0); } n1 = (*a).HostIpAddress.Ip4Address.s_addr; n2 = (*b).HostIpAddress.Ip4Address.s_addr; if (n1 == n2) return(0); else if(n1 > n2) return(-1); else return(1); } /* ============================================================== */ static void processHTMLrequest(char* url) { char tmpStr[LEN_GENERAL_WORK_BUFFER], hostLinkBuf[LEN_GENERAL_WORK_BUFFER]; char tmpTime[LEN_TIMEFORMAT_BUFFER], postData[128]; char *no_info = "-NO INFO-",*tmp, *no_note ="-"; datum ret_data,key_data, content; LsHostInfo tablehost[MAX_LASTSEEN_TABLE_SIZE]; LsHostNote HostN; HostTraffic *HostT; struct tm loctime; struct in_addr char_ip; int entry = 0, num_hosts; if ( url && strncmp(url,"N",1)==0 ) { char_ip.s_addr = strtoul(url+1,NULL,10); NotesURL(url+1, intoa(char_ip)); return; } if ( url && strncmp(url,"P",1)==0 ) { entry = recv(myGlobals.newSock, &postData[0],127,0); postData[entry] = '\0'; addNotes( url+1, &postData[6]); char_ip.s_addr = strtoul(url+1, NULL, 10); safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "

OK! Added comments for %s.

\n", intoa(char_ip)); sendString(tmpStr); return; } if ( url && strncmp(url,"D",1)==0 ) deletelastSeenURL(url+1); /* Finding hosts... */ ret_data = gdbm_firstkey(LsDB); while ( ret_data.dptr !=NULL ) { key_data = ret_data; content = gdbm_fetch(LsDB,key_data); if ( (key_data.dptr[1]!='_') && (entry < MAX_LASTSEEN_TABLE_SIZE) ) { memcpy(&tablehost[entry],(struct LsHostInfo *)content.dptr,sizeof(struct LsHostInfo)); entry++; } free(content.dptr); ret_data = gdbm_nextkey(LsDB,key_data); free(key_data.dptr); } /* ========================================================================= */ qsort(( void *)&tablehost[0],entry,sizeof(LsHostInfo),SortLS); num_hosts=entry; entry--; if (entry >= MAX_LASTSEEN_TABLE_SIZE - 1) { sendString("

NOTE: Table size at/exceeds limit, some data may not be displayed.

\n"); } sendString("
\n"); sendString("\n"); while ( entry >= 0 ) { HostAddr addr; /* Getting notes from the DN */ safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "N_%u", (unsigned)tablehost[entry].HostIpAddress.Ip4Address.s_addr); key_data.dptr = tmpStr; key_data.dsize = (int)strlen(key_data.dptr)+1; content = gdbm_fetch(LsDB,key_data); strncpy(HostN.note, no_note, sizeof(HostN.note)); if ( content.dptr ) { memcpy(&HostN,(struct LsHostNote *)content.dptr,sizeof(struct LsHostNote)); free(content.dptr); } /* ================================================================== */ addrcpy(&addr, &tablehost[entry].HostIpAddress); HostT = findHostByNumIP(addr, 0, myGlobals.actualReportDeviceId); if(HostT) tmp = makeHostLink(HostT, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)); else tmp = no_info; localtime_r(&tablehost[entry].LastUpdated, &loctime); strftime(tmpTime, sizeof(tmpTime), CONST_LOCALE_TIMESPEC, &loctime); safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "%s" "" "\n", getRowColor(), tmp, addrtostr(&tablehost[entry].HostIpAddress), tmpTime, HostN.note, (unsigned) tablehost[entry].HostIpAddress.Ip4Address.s_addr, (unsigned) tablehost[entry].HostIpAddress.Ip4Address.s_addr); sendString(tmpStr); entry--; } sendString("
HostAddressLastSeenCommentsOptions
  %s    %s  %s" "Del   " "Notes

\n"); safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "

%u host displayed

", num_hosts); sendString(tmpStr); } /* ============================================================== */ static void handleLsHTTPrequest(char* url) { sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1); printHTMLheader("Last Seen Statistics", NULL, 0); if(disabled) { printFlagedWarning("This plugin is disabled."); if(LsPluginInfo->pluginStatusMessage != NULL) { sendString("

"); sendString(LsPluginInfo->pluginStatusMessage); sendString("

\n"); } } else { processHTMLrequest(url); } printPluginTrailer(LsPluginInfo->pluginURLname, NULL); printHTMLtrailer(); } /* ============================================================== */ /* Adding notes changing the key */ static void addNotes(char *addr, char *PostNotes) { datum key_data, data_data; char tmpStr[32]; LsHostNote HostN; int idx; if(disabled) return; for ( idx =0; PostNotes[idx]; idx++) { if ( PostNotes[idx]=='+') PostNotes[idx]=' '; } strncpy(HostN.note,PostNotes, sizeof(HostN.note)); safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "N_%s",addr); key_data.dptr = tmpStr; key_data.dsize = (int)strlen(key_data.dptr)+1; data_data.dptr = (char *)&HostN; data_data.dsize = sizeof(HostN)+1; if (strlen(PostNotes) > 2) gdbm_store(LsDB, key_data, data_data, GDBM_REPLACE); else gdbm_delete(LsDB,key_data); } /* Preparing the page */ static void NotesURL(char *addr, char *ip_addr) { datum key_data, content; char tmpStr[32]; char tmp[64]; if(disabled) { printFlagedWarning("This plugin is disabled."); return; } safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "N_%s",addr); key_data.dptr = tmpStr; key_data.dsize = (int)strlen(key_data.dptr)+1; content = gdbm_fetch(LsDB,key_data); // sendString("


\n"); //sendString("Manage Notes\n"); //sendString("\n"); safe_snprintf(__FILE__, __LINE__, tmp, sizeof(tmp), "

Notes for %s

\n

\n",ip_addr); sendString(tmp); safe_snprintf(__FILE__, __LINE__, tmp, sizeof(tmp), "

\n",addr); sendString(tmp); if (content.dptr) { safe_snprintf(__FILE__, __LINE__, tmp, sizeof(tmp), "\n", content.dptr); sendString(tmp); free(content.dptr); } else { sendString("\n"); } sendString("

\n"); sendString("

\n"); // sendString("
\n"); } static void deletelastSeenURL( char *addr ) { datum key_data; char tmpStr[32]; if(disabled) return; safe_snprintf(__FILE__, __LINE__, tmpStr, sizeof(tmpStr), "N_%s",addr); key_data.dptr = addr; key_data.dsize = (int)strlen(key_data.dptr)+1; gdbm_delete(LsDB,key_data); /* Record */ key_data.dptr = tmpStr; key_data.dsize = (int)strlen(key_data.dptr)+1; gdbm_delete(LsDB,key_data); /* Notes */ } static void termLsFunct(u_char termNtop /* 0=term plugin, 1=term ntop */) { traceEvent(CONST_TRACE_INFO, "LASTSEEN: Thanks for using LsWatch"); fflush(stdout); if(LsDB != NULL) { disabled = 1; sleep(1); /* Wait if there's an ongoing request being processed (otherwise we get an lseek_error if we close the DB while somebody is using it */ gdbm_close(LsDB); LsDB = NULL; } traceEvent(CONST_TRACE_INFO, "LASTSEEN: Done"); fflush(stdout); } /* ====================================================================== */ /* Plugin entry fctn */ #ifdef MAKE_STATIC_PLUGIN PluginInfo* lsPluginEntryFctn(void) { #else PluginInfo* PluginEntryFctn(void) { #endif char tmpBuf[200]; traceEvent(CONST_TRACE_ALWAYSDISPLAY, "LASTSEEN: Welcome to %s. (C) 1999 by Andrea Marangoni", LsPluginInfo->pluginName); /* Fix courtesy of Ralf Amandi */ safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "%s/LsWatch.db", myGlobals.dbPath); LsDB = gdbm_open (tmpBuf, 0, GDBM_WRCREAT, 00640, NULL); if(LsDB == NULL) { traceEvent(CONST_TRACE_ERROR, "LASTSEEN: Unable to open LsWatch database (%s/LsWatch.db)- the plugin will be disabled", myGlobals.dbPath); setPluginStatus("Disabled - unable to open LsWatch database."); disabled = 1; } else { setPluginStatus(NULL); } return(LsPluginInfo); } static void setPluginStatus(char * status) { if (LsPluginInfo->pluginStatusMessage != NULL) free(LsPluginInfo->pluginStatusMessage); if (status == NULL) { LsPluginInfo->pluginStatusMessage = NULL; } else { LsPluginInfo->pluginStatusMessage = strdup(status); } }