/* ** Copyright (C) 2006-2007 by Carnegie Mellon University. ** ** @OPENSOURCE_HEADER_START@ ** ** Use of the SILK system and related source code is subject to the terms ** of the following licenses: ** ** GNU Public License (GPL) Rights pursuant to Version 2, June 1991 ** Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013 ** ** NO WARRANTY ** ** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER ** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY ** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN ** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY ** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT ** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE ** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT, ** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY ** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF ** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. ** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF ** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON ** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE ** DELIVERABLES UNDER THIS LICENSE. ** ** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie ** Mellon University, its trustees, officers, employees, and agents from ** all claims or demands made against them (and any related losses, ** expenses, or attorney's fees) arising out of, or relating to Licensee's ** and/or its sub licensees' negligent use or willful misuse of or ** negligent conduct or willful misconduct regarding the Software, ** facilities, or other rights or assistance granted by Carnegie Mellon ** University under this License, including, but not limited to, any ** claims of product liability, personal injury, death, damage to ** property, or violation of any laws or regulations. ** ** Carnegie Mellon University Software Engineering Institute authored ** documents are sponsored by the U.S. Department of Defense under ** Contract F19628-00-C-0003. Carnegie Mellon University retains ** copyrights in all material produced under this contract. The U.S. ** Government retains a non-exclusive, royalty-free license to publish or ** reproduce these documents, or allow others to do so, for U.S. ** Government purposes only pursuant to the copyright license under the ** contract clause at 252.227.7013. ** ** @OPENSOURCE_HEADER_END@ */ #include "silk.h" RCSIDENT("$SiLK: ipport.c 7639 2007-06-25 19:23:14Z mthomas $"); #include "rwpack.h" #include "dynlib.h" #include "skvector.h" #include "skstream.h" #include "redblack.h" /* EXPORTED FUNCTIONS */ /* The dynamic library glue code calls these */ int dynlib_api_version(void); int setup(dynlibInfoStruct *dlISP, dynlibSymbolId appType); void teardown(dynlibSymbolId appType); void optionsUsage(dynlibSymbolId appType, FILE *fh); int check(rwRec *rwrec); /* DEFINES AND TYPEDEFS */ /* The delimiter that must appear between the IP and the Ports */ #define DELIM ' ' /* The types of filtering we support and their count */ #define IPPORT_INDEX_COUNT 1 typedef enum { IPP_ANY=0 } ipport_index_t; /* * We hand pointers the ip-port-nodes to redblack; we can't use * realloc() since that may make redblack's pointers invalid. * Instead, we'll use a vector of arrays, where each array contains * NODE_ARRAY_SIZE elements. */ #define NODE_ARRAY_SIZE 1024 /* an IP-port pair node */ typedef struct ppair { uint32_t ipnum; uint16_t port; } ipportpair_t; /* LOCAL VARIABLES */ /* the name of this plugin */ static const char *pluginName = "ipport"; /* the vector of ip-port arrays */ static sk_vector_t *array_vec; /* the current array we're working with and the number of elements it * contains */ static struct _cur_pair { ipportpair_t *array; int count; } cur_pairs; /* the redblack trees--one for each ipport index */ static struct rbtree *rb[IPPORT_INDEX_COUNT]; /* * Options that get added to rwfilter. */ static struct option filterOptions[] = { {"ipport-any", REQUIRED_ARG, 0, IPP_ANY}, {0, 0, 0, 0} /* sentinel */ }; static char *filterOptionsHelp[] = { ("Wanted file of IP addresses and ports.\n" "\tPass the record if its IP / port is in this list"), (char*)NULL }; /* LOCAL FUNCTION DECLARATIONS */ static int compare(const void *pa, const void *pb, const void *config); static int growMemory(void); static int initializeMemory(int ipp_idx); static int optionsHandler(clientData cData, int opt_index, char *opt_arg); static int parsefile(int ipp_idx, const char *path); /* FUNCTION DEFINITIONS */ /* * int dynlib_api_version(void); * * Return the dynlib API version that this plugin was compiled * against. */ int dynlib_api_version(void) { return DYNLIB_API_VERSION; } /* * setup * Called by dynlib interface code to setup this plugin. This * routine should set up options handling, if required. * Arguments: * --pointer to the dynamic library interface structure * --Application type that is using this plugin. * Returns: * DYNLIB_FAILED - if processing fails * DYNLIB_WONTPROCESS - if application should do normal output * DYNLIB_WILLPROCESS - if this plugin takes over output * Side Effects: */ int setup(dynlibInfoStruct *dlISP, dynlibSymbolId appType) { /* verify same number of options and help strings */ assert((sizeof(filterOptions)/sizeof(struct option)) == (sizeof(filterOptionsHelp)/sizeof(char*))); /* Make the plugin use the application's context, so we can add * options to main app. */ skAppContextSet(dynlibGetAppContext(dlISP)); /* initialize data */ memset(rb, 0, sizeof(rb)); array_vec = NULL; /* this plug-in only supports rwfilter */ switch (appType) { case DYNLIB_SHAR_FILTER: case DYNLIB_EXCL_FILTER: if (optionsRegister(filterOptions, &optionsHandler, (clientData)dlISP)) { skAppPrintErr("unable to register options for %s plugin", pluginName); return 1; } break; default: skAppPrintErr("Cannot use %s plug-in with %s application", pluginName, skAppName()); return 1; } /* we're only a pass/fail plug-in */ return DYNLIB_WONTPROCESS; } /* * teardown * Called by dynlib interface code to tear down this plugin. * Arguments: * --Application type that is using this plugin * Returns: * None. */ void teardown(dynlibSymbolId UNUSED(appType)) { static int teardownFlag = 0; size_t i; void *a; if (teardownFlag) { return; } teardownFlag = 1; /* destroy the redblack trees */ for (i = 0; i < IPPORT_INDEX_COUNT; ++i) { if (rb[i]) { rbdestroy(rb[i]); rb[i] = NULL; } } /* destroy the vector of array of nodes */ if (array_vec) { for (i = 0; i < skVectorGetCount(array_vec); ++i) { skVectorGetValue(&a, array_vec, i); free(a); } skVectorDestroy(array_vec); array_vec = NULL; } return; } /* * optionsUsage * Called by dynlib interface code to allow this plugin to print * the options it accepts. * Arguments: * --Application type that is using this plugin * Returns: * None. * Side Effects: * None. */ void optionsUsage(dynlibSymbolId UNUSED(appType), FILE *fh) { int i; for (i = 0; filterOptions[i].name != NULL; ++i) { fprintf(fh, "--%s %s. %s\n", filterOptions[i].name, SK_OPTION_HAS_ARG(filterOptions[i]), filterOptionsHelp[i]); } } /* * optionsHandler * Called by options parser to handle one user option * Arguments: * clientData cData: the dynamic lib interface struct pointer * int index: index into appOptions of the specific option * char *optarg: the argument; 0 if no argument was required/given. * Returns: * 0 if OK. 1 else. * Side Effects: * Relevant options are set. */ static int optionsHandler(clientData cData, int opt_index, char *opt_arg) { /* Make sure option in range; else a fatal programmer error */ if (opt_index < 0 || opt_index >= IPPORT_INDEX_COUNT) { skAppPrintErr("Option %d not handled at %s:%d", opt_index, __FILE__, __LINE__); exit(EXIT_FAILURE); } /* Verify that we have a file name */ if (opt_arg == NULL || opt_arg[0] == '\0') { skAppPrintErr("Expected name of %s file", filterOptions[opt_index].name); return 1; } if (initializeMemory(opt_index)) { return 1; } if (parsefile(opt_index, opt_arg)) { return 1; } /* Tell the dynlib glue-code that we are active. */ dynlibMakeActive((dynlibInfoStructPtr)cData); return 0; } /* * check * Called by dynlib interface code to filter records * Arguments: * a RW record * Returns: * 0 to accept the record, 1 to reject the record */ int check(rwRec *rwrec) { ipportpair_t node; /* look for source ip-port */ node.ipnum = rwrec->sIP.ipnum; node.port = rwrec->sPort; if (rbfind(&node, rb[IPP_ANY]) != NULL) { /* found it */ return 0; } /* look for destination ip-port */ node.ipnum = rwrec->dIP.ipnum; node.port = rwrec->dPort; if (rbfind(&node, rb[IPP_ANY]) != NULL) { /* found it */ return 0; } /* no match */ return 1; } /* * gt_lt_eq = compare(ipp_a, ipp_b, config); * * Comparison function for the redblack tree; compares the * ipportpair_t's ipp_a and ipp_b, returning -1 if ipp_a comes * before ipp_b. */ static int compare(const void *pa, const void *pb, const void UNUSED(*config)) { ipportpair_t *ppa = (ipportpair_t*)pa; ipportpair_t *ppb = (ipportpair_t*)pb; if (ppa->ipnum < ppb->ipnum) return -1; if (ppa->ipnum > ppb->ipnum) return 1; if (ppa->port > ppb->port) return -1; if (ppa->port < ppb->port) return 1; return 0; } /* * ok = growMemory(); * * Create a new array of ipportpair_t's and put a pointer to that * array into the global 'array_vec'. Return 0 on success, or -1 * for an allocation error. */ static int growMemory(void) { ipportpair_t *new_mem; assert(array_vec != NULL); new_mem = calloc(NODE_ARRAY_SIZE, sizeof(ipportpair_t)); if (new_mem == NULL) { return -1; } if (skVectorAppendValue(array_vec, &new_mem)) { free(new_mem); return -1; } cur_pairs.array = new_mem; cur_pairs.count = 0; return 0; } /* * ok = initializeMemory(ipp_idx); * * Create a redblack tree to hold information about the ipport * pairs that are index by 'ipp_idx', an ipport_index_t. * * In addition, if we haven't yet, create the global 'array_vec' * that is used to store pointers to arrays of ipportpair_t's, and * allocate an array and add it to the vector. * * Return 0 on success, or non-zero on allocation failure. */ static int initializeMemory(int ipp_idx) { /* initialize the binary tree for this ipport_index */ assert(ipp_idx >= 0 && ipp_idx < IPPORT_INDEX_COUNT); /* Create the main memory of nodes */ if (array_vec == NULL) { array_vec = skVectorNew(sizeof(ipportpair_t**)); if (array_vec == NULL) { skAppPrintErr("Insufficient memory to create vector"); return -1; } /* add an array to it */ if (growMemory()) { skVectorDestroy(array_vec); array_vec = NULL; return -1; } } /* Create the redblack tree */ if (rb[ipp_idx] == NULL) { rb[ipp_idx] = rbinit(&compare, NULL); if (rb[ipp_idx] == NULL) { skAppPrintErr("Insufficient memory to create redblack tree"); return -1; } } return 0; } /* * ok = parsefile(ipp_idx, path); * * Parse the file at 'path' that holds the data for the 'ipp_idx'ed * redblack tree. Return 0 on success, or non-zero otherwise. */ static int parsefile(int ipp_idx, const char *path) { char line_buf[1024]; char *ipp; char *ptp; int lc = 0; int err_count = 0; uint32_t ipnum; /* the parsed ip */ uint32_t *nll; /* the parsed list of ports */ uint32_t nlc; /* the number of elements in nll */ uint32_t i; skstream_t *stream; int rv; rv = dynlibOpenDataInputStream(&stream, SK_CONTENT_TEXT, path); if (rv == -1) { skAppPrintErr("Problem with input file %s", path); return 1; } if (rv == 1) { /* Ignore the file */ return 0; } if ((rv = skStreamSetCommentStart(stream, "#"))) { goto END; } /* read until end of file */ while ((rv = skStreamGetLine(stream, line_buf, sizeof(line_buf), &lc)) != SKSTREAM_ERR_EOF) { switch (rv) { case SKSTREAM_OK: /* good, we got our line */ break; case SKSTREAM_ERR_LONG_LINE: /* bad: line was longer than sizeof(line_buf) */ skAppPrintErr("Input line %s:%d too long. ignored", path, lc); continue; default: /* unexpected error */ skStreamPrintLastErr(stream, rv, &skAppPrintErr); goto END; } /* find first non-whitespace character */ ipp = line_buf; while (isspace((int)*ipp)) { ++ipp; } /* find the separator between the IP(s) and the port(s), then * move to the start of the ports */ ptp = strchr(ipp, DELIM); if (ptp == NULL) { skAppPrintErr("Bad ip-port pair at %s:%d", path, lc); ++err_count; continue; } *ptp = '\0'; ++ptp; while (isspace((int)*ptp)) { ++ptp; } /* parse the IP */ if (skStringParseIP(&ipnum, ipp)) { skAppPrintErr("Cannot parse IP '%s' at %s:%d", ipp, path, lc); ++err_count; continue; } /* parse the ports */ if (skStringParseNumberList(&nll, &nlc, ptp, 0, UINT16_MAX, 0)) { skAppPrintErr("Bad port list at %s:%d", path, lc); ++err_count; continue; } /* if we make it here we have one IP and one or more ports */ for (i = 0; i < nlc; ++i) { if (cur_pairs.count == NODE_ARRAY_SIZE) { if (growMemory()) { skAppPrintErr(("Ran out of memory adding ip-port pairs\n" "\tfrom %s. Edit the file and try again"), path); ++err_count; goto END; } } cur_pairs.array[cur_pairs.count].ipnum = ipnum; cur_pairs.array[cur_pairs.count].port = (uint16_t)nll[i]; (void)rbsearch(&cur_pairs.array[cur_pairs.count], rb[ipp_idx]); /* could be smarter here about only incrementing when * we've added a new node */ ++cur_pairs.count; } } END: skStreamDestroy(&stream); /* return error code if we did not get to the end of the stream */ if (rv != SKSTREAM_ERR_EOF) { return -1; } /* return error code if we encountered errors */ if (err_count) { return -1; } /* return error code if we do not have any entries */ if (array_vec == NULL || 0 == skVectorGetCount(array_vec) || 0 == cur_pairs.count) { skAppPrintErr("No valid IP/port pairs read from input file '%s'", path); return -1; } return 0; } /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */