/* ** Copyright (C) 2004-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@ */ /* ** rwbagbuild can read an IP set and generate a bag with a default ** count for each IP address, or it can read a pipe-separated text ** file representing a bag. */ #include "silk.h" RCSIDENT("$SiLK: rwbagbuild.c 7315 2007-05-29 13:53:33Z mthomas $"); #include "bagtree.h" #include "iptree.h" #include "utils.h" #include "sksite.h" /* LOCAL DEFINES AND TYPEDEFS */ /* where to write --help output */ #define USAGE_FH stdout /* LOCAL FUNCTIONS */ static void appUsageLong(void); /* never returns */ static void appTeardown(void); static void appSetup(int argc, char **argv); /* never returns when error */ static int appOptionsHandler(clientData, int, char *); static int rwbb_parseBagLine(char *line, skBag_header_t *bag); static int createBagFromTextBag(skBag_header_t *bag, skstream_t *stream); static int createBagFromSet(skBag_header_t *bag, skstream_t *stream); /* LOCAL VARIABLES */ /* output stream */ static skstream_t *s_out = NULL; /* the compression method to use when writing the file. * sksiteCompmethodOptionsRegister() will set this to the default or * to the value the user specifies. */ static sk_compmethod_t comp_method; /* input streams (for reading a textual bag or an ip set */ static skstream_t *bag_input = NULL; static skstream_t *set_input = NULL; /* counter */ static int f_use_default_count = 0; static skBag_counter_t default_count = (skBag_counter_t)1; /* delimiter for text input */ static char delimiter = '|'; /* OPTIONS SETUP */ typedef enum _appOptionsEnum { OPT_SET_INPUT, OPT_BAG_INPUT, OPT_DELIMITER, OPT_DEFAULT_COUNT, OPT_OUTPUT } appOptionsEnum; static struct option appOptions[] = { {"set-input", REQUIRED_ARG, 0, OPT_SET_INPUT}, {"bag-input", REQUIRED_ARG, 0, OPT_BAG_INPUT}, {"delimiter", REQUIRED_ARG, 0, OPT_DELIMITER}, {"default-count", REQUIRED_ARG, 0, OPT_DEFAULT_COUNT}, {"output", REQUIRED_ARG, 0, OPT_OUTPUT}, {0,0,0,0} /* sentinel entry */ }; static const char *appHelp[] = { "Create a bag from the specified IP set.", "Create a bag from a delimiter-separated text file.", ("Specify the delimiter separating the key and value\n" "\tfor the --bag-input switch. Def. '|'"), ("Set default count for each key in the new bag.\n" "\tDef. 1"), ("Specify destination for the new bag (stdout|pipe|filename).\n" "\tDef. stdout"), (char *)NULL }; /* FUNCTION DEFINITIONS */ /* * appUsageLong(); * * Print complete usage information to USAGE_FH. Pass this * function to skOptionsSetUsageCallback(); optionsParse() will * call this funciton and then exit the program when the --help * option is given. */ static void appUsageLong(void) { #define USAGE_MSG \ ("{--set-input=FILE | --bag-input=FILE} [SWITCHES]\n" \ "\tCreate a binary Bag file from either a binary IPset file or from\n" \ "\ta textual input file. Use \"stdin\" as the file name to read\n" \ "\tfrom the standard input. The Bag is written to the standard\n" \ "\toutput or the location specified with the --output switch.\n") FILE *fh = USAGE_FH; skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp); sksiteCompmethodOptionsUsage(fh); fprintf(fh, "\nNOTE: Either --set-input or --bag-input is required.\n"); } /* * appTeardown() * * Teardown all modules, close all files, and tidy up all * application state. * * This function is idempotent. */ static void appTeardown(void) { static int teardownFlag = 0; int rv; if (teardownFlag) { return; } teardownFlag = 1; /* * close pipes/files */ if (s_out) { rv = skStreamClose(s_out); if (rv) { skStreamPrintLastErr(s_out, rv, &skAppPrintErr); } skStreamDestroy(&s_out); } skStreamDestroy(&bag_input); skStreamDestroy(&set_input); skAppUnregister(); } /* * appSetup(argc, argv); * * Perform all the setup for this application include setting up * required modules, parsing options, etc. This function should be * passed the same arguments that were passed into main(). * * Returns to the caller if all setup succeeds. If anything fails, * this function will cause the application to exit with a FAILURE * exit status. */ static void appSetup(int argc, char **argv) { int arg_index; int rv; /* verify same number of options and help strings */ assert((sizeof(appHelp)/sizeof(char *)) == (sizeof(appOptions)/sizeof(struct option))); /* register the application */ skAppRegister(argv[0]); skOptionsSetUsageCallback(&appUsageLong); /* create output stream */ if (skStreamCreate(&s_out, SK_IO_WRITE, SK_CONTENT_SILK)) { skAppPrintErr("Cannot create output stream"); exit(EXIT_FAILURE); } /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL) || sksiteCompmethodOptionsRegister(&comp_method)) { skAppPrintErr("unable to register options"); exit(EXIT_FAILURE); } /* parse options */ arg_index = optionsParse(argc, argv); if (arg_index < 0) { /* options parsing should print error */ skAppUsage(); /* never returns */ } /* check for input */ if ( !set_input && !bag_input) { skAppPrintErr("either --%s or --%s switch is required\n", appOptions[OPT_SET_INPUT].name, appOptions[OPT_BAG_INPUT].name); skAppUsage(); /* never returns */ } /* Complain about extra args on command line */ if (arg_index != argc) { skAppPrintErr("too many or unrecognized argument specified: '%s'", argv[arg_index]); exit(EXIT_FAILURE); } /* If output was never set, bind it to stdout */ if (NULL == skStreamGetPathname(s_out)) { rv = skStreamBind(s_out, "stdout"); if (rv) { skStreamPrintLastErr(s_out, rv, &skAppPrintErr); appTeardown(); exit(EXIT_FAILURE); } } /* Open output */ if ((rv = skStreamSetCompressionMethod(s_out, comp_method)) || (rv = skStreamOpen(s_out))) { skStreamPrintLastErr(s_out, rv, &skAppPrintErr); appTeardown(); exit(EXIT_FAILURE); } if (atexit(appTeardown) < 0) { skAppPrintErr("unable to register appTeardown() with atexit()"); appTeardown(); exit(EXIT_FAILURE); } return; /* OK */ } /* * status = appOptionsHandler(cData, opt_index, opt_arg); * * Called by optionsParse(), this handles a user-specified switch * that the application has registered, typically by setting global * variables. Returns 1 if the switch processing failed or 0 if it * succeeded. Returning a non-zero from from the handler causes * optionsParse() to return a negative value. * * The clientData in 'cData' is typically ignored; 'opt_index' is * the index number that was specified as the last value for each * struct option in appOptions[]; 'opt_arg' is the user's argument * to the switch for options that have a REQUIRED_ARG or an * OPTIONAL_ARG. */ static int appOptionsHandler( clientData UNUSED(cData), int opt_index, char *opt_arg) { int rv; switch ((appOptionsEnum)opt_index) { case OPT_SET_INPUT: if (set_input) { skAppPrintErr("May only specify --%s one time", appOptions[opt_index].name); return 1; } if (bag_input) { skAppPrintErr("May only specify one of --%s or --%s", appOptions[OPT_SET_INPUT].name, appOptions[OPT_BAG_INPUT].name); return 1; } if ((rv = skStreamCreate(&set_input, SK_IO_READ, SK_CONTENT_SILK)) || (rv = skStreamBind(set_input, opt_arg)) || (rv = skStreamOpen(set_input))) { skStreamPrintLastErr(set_input, rv, &skAppPrintErr); skStreamDestroy(&set_input); return 1; } break; case OPT_BAG_INPUT: if (bag_input) { skAppPrintErr("May only specify --%s one time", appOptions[opt_index].name); return 1; } if (set_input) { skAppPrintErr("May only specify one of --%s or --%s", appOptions[OPT_SET_INPUT].name, appOptions[OPT_BAG_INPUT].name); return 1; } if ((rv = skStreamCreate(&bag_input, SK_IO_READ, SK_CONTENT_TEXT)) || (rv = skStreamBind(bag_input, opt_arg)) || (rv = skStreamOpen(bag_input))) { skStreamPrintLastErr(bag_input, rv, &skAppPrintErr); skStreamDestroy(&bag_input); return 1; } break; case OPT_OUTPUT: rv = skStreamBind(s_out, opt_arg); if (rv) { skStreamPrintLastErr(s_out, rv, &skAppPrintErr); return 1; } break; case OPT_DEFAULT_COUNT: if (skStringParseUint64((uint64_t*)&default_count, opt_arg, 0, 0)) { skAppPrintErr("default count '%s' is not parsable", opt_arg); return 1; } f_use_default_count = 1; break; case OPT_DELIMITER: delimiter = opt_arg[0]; if (delimiter == '#') { skAppPrintErr("The character '#' is the comment character;\n" "\tit cannot be used as the delimiter."); return 1; } if (delimiter == '\n') { skAppPrintErr("Newline is not a valid delimiter."); return 1; } break; } return 0; /* OK */ } static int rwbb_parseBagLine( char *line, skBag_header_t *bag) { skBag_key_t key; skBag_counter_t counter; char *sz_key; char *sz_counter; skOctetMapIterator_t iter; skOctetMap_t octetmap; int rv; /* ** search for pipe, and break line into key and counter */ sz_key = line; sz_counter = strchr(line, delimiter); if (sz_counter) { /* change pipe into NULL char, to terminate first string */ *sz_counter = '\0'; } if (f_use_default_count == 1) { counter = default_count; } else if (sz_counter == NULL) { /* not a pipe delimited line; use default count */ counter = default_count; } else { /* move counter to point to next char (start of counter) */ sz_counter++; rv = skStringParseUint64(&counter, sz_counter, 0, 0); if (rv < 0) { /* parse error */ return 1; } if ((rv > 0) && (sz_counter[rv - 1] != delimiter)) { /* unrecognized stuff after count */ return 1; } /* ignore trailing delimiter and everything after it */ } /* ** parse key section of bag line */ /* Fill in octet bitmap */ rv = skStringParseWildcardIP(&octetmap, sz_key); if (rv != 0) { /* not parsable */ return 1; } skOctetMapIteratorInitialize(&iter, &octetmap); while (skOctetMapIteratorNext(&key, &iter) == SK_ITERATOR_OK) { if (skBag_addToCounter(bag, &key, &counter) != SKBAG_OK) { return 1; } } return 0; } static int createBagFromTextBag( skBag_header_t *bag, skstream_t *stream) { char buffer[128]; int lc = 0; int rv; if (skStreamSetCommentStart(stream, "#")) { return 1; } /* read until end of file */ while ((rv = skStreamGetLine(stream, buffer, sizeof(buffer), &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 %d too long. ignored", lc); continue; default: /* unexpected error */ skStreamPrintLastErr(stream, rv, &skAppPrintErr); return 1; } /* parse the line */ if (rwbb_parseBagLine(buffer, bag)) { skAppPrintErr("error parsing line %d", lc); return 1; } } return 0; } static int createBagFromSet( skBag_header_t *bag, skstream_t *stream) { skIPTree_t *set = NULL; skBag_key_t ip; skIPTreeIterator_t iter; /* Read macroTree from file */ if (skIPTreeRead(stream, &set) != 0) { return 1; } if (skIPTreeIteratorBind(&iter, set) != 0) { return 1; } while (skIPTreeIteratorNext((uint32_t*)&ip, &iter) == SK_ITERATOR_OK) { if (f_use_default_count == 0) { if (skBag_incrCounter(bag, &ip) != SKBAG_OK) { return 1; } } else if (skBag_setCounter(bag, &ip, &default_count)) { return 1; } } skIPTreeDelete(&set); return 0; } int main(int argc, char **argv) { skBag_header_t *bag = NULL; int rv = EXIT_FAILURE; appSetup(argc, argv); /* never returns on error */ /* Create new bag */ if (skBag_create(&bag) != SKBAG_OK) { skAppPrintErr("error allocating bag"); goto END; } /* Process input */ if (set_input) { /* Handle set-file input */ if (createBagFromSet(bag, set_input)) { skAppPrintErr("error creating bag from set"); goto END; } } else if (bag_input) { /* Handle bag-file input */ if (createBagFromTextBag(bag, bag_input)) { skAppPrintErr("error creating bag from text bag"); goto END; } } else { assert(0); abort(); } /* write output */ if (skBag_writeBinary(bag, s_out) != SKBAG_OK) { skAppPrintErr("Error writing bag to %s", skStreamGetPathname(s_out)); goto END; } rv = EXIT_SUCCESS; END: if (bag) { skBag_free(bag); } appTeardown(); exit(rv); return rv; } /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */