/* ** Copyright (C) 2005-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@ */ /* ** ** rwresolve.c ** ** Mark Thomas ** December 2005 ** ** A pipe-line filter to convert an IP to a hostname. The ** default field delimiter is '|' in deference to our internal ** default. The default fields are 1,2 (numbering starts at 1). ** Changes can be provided via options --ip-fields= and ** --delimiter=. */ #include "silk.h" RCSIDENT("$SiLK: rwresolve.c 6081 2007-01-22 19:25:22Z mthomas $"); #include "utils.h" /* LOCAL DEFINES AND TYPEDEFS */ /* where to send usage output */ #define USAGE_FH stdout /* max fields (columns) we expect in each line of the output. */ #define MAX_FIELD_COUNT 100 /* set to 1 is use getaddrinfo(3), or to 0 for gethostbyaddr(3) */ #ifndef USE_GET_ADDR_INFO # define USE_GET_ADDR_INFO 0 #endif /* LOCAL FUNCTIONS */ static void appUsageLong(void); /* never returns */ static void appTeardown(void); static void appSetup(int argc, char **argv); /* never returns */ static int appOptionsHandler(clientData cData, int optindex, char *optarg); static int parseIPFields(const char *b); /* LOCAL VARIABLES */ /* a value for each field, where 1 means the field should be resolved; * 0 means it should not */ static uint8_t ipFields[MAX_FIELD_COUNT]; /* field delimiter */ static char delimChar; /* where to send output */ static FILE *outf; /* OPTIONS SETUP */ typedef enum { OPT_IP_FIELDS, OPT_DELIMITER } appOptionsEnum; static struct option appOptions[] = { {"ip-fields", REQUIRED_ARG, 0, OPT_IP_FIELDS}, {"delimiter", REQUIRED_ARG, 0, OPT_DELIMITER}, {0,0,0,0} /* sentinel entry */ }; static const char *appHelp[] = { "IP number fields to resolve (starting at 1). Def. 1,2", "delimiter to expect. Def. '|'", (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 \ ("[SWITCHES]\n" \ "\tReads delimited text (such as from rwcut) from the standard input\n" \ "\tand resolves the IP addresses in the specified columns. If the\n" \ "\t--ip-fields switch is not given, columns 1 and 2 are resolved.\n" \ "\tOutput is sent to the standard output. Beware, this is going\n" \ "\tto be slow.\n") FILE *fh = USAGE_FH; skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp); } /* * 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; if (teardownFlag) { return; } teardownFlag = 1; 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; /* 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); /* initialize globals */ memset(ipFields, sizeof(ipFields), 0); ipFields[0] = 1; ipFields[1] = 1; delimChar = '|'; outf = stdout; /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL)) { skAppPrintErr("unable to register options"); exit(EXIT_FAILURE); } /* parse options */ arg_index = optionsParse(argc, argv); if (arg_index < 0) { skAppUsage(); /* never returns */ } /* check for extra arguments */ if (arg_index != argc) { skAppPrintErr("unexpected argument '%s'", argv[arg_index]); skAppUsage(); /* never returns */ } if (atexit(appTeardown) < 0) { skAppPrintErr("registering appTeardown() failed"); 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) { switch ((appOptionsEnum)opt_index) { case OPT_IP_FIELDS: /* ip fields */ if (parseIPFields(opt_arg)) { return 1; } break; case OPT_DELIMITER: /* delimiter */ delimChar = *opt_arg; if ('\0' == delimChar) { skAppPrintErr("empty string not valid argument for --delimiter"); return 1; } break; } return 0; /* OK */ } /* * parseIPFields: * given a list (i.e., comma or - delimited set of numbers), set the * element in fields to 1 if the fields are wanted. * Input: * char * * Return: * 0 if OK. 1 else; * Side Effects: * fields[] */ static int parseIPFields(const char *arg) { uint32_t *list; uint32_t count; uint32_t i; if (skStringParseNumberList(&list, &count, arg, 1, MAX_FIELD_COUNT, 0)) { return 1; } memset(ipFields, 0, sizeof(ipFields)); for (i = 0; i < count; ++i) { ipFields[list[i] - 1] = 1; } return 0; /* OK */ } int main(int argc, char **argv) { char line[2048]; char *cp, *ep, *sp; char *eol; int field; FILE *handle = stdin; uint32_t val = 0; char *dotted; #if USE_GET_ADDR_INFO struct addrinfo hints; /* set up the hints structure */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_CANONNAME ; #endif appSetup(argc, argv); /* never returns */ /* read from handle until EOF */ while (!feof(handle)) { if (!fgets(line, sizeof(line), handle)) { continue; } eol = strchr(line, '\n'); if (eol == line) { /* empty line; ignore */ continue; } if (NULL != eol) { /* expected behavior: read an entire line */ *eol = '\0'; } else if (feof(handle)) { /* okay: last line did not end in newline */ } else { /* bad: line was longer than sizeof(line). read until newline * or eof, then throw away the line */ while (fgets(line, sizeof(line), handle) && !strchr(line,'\n')) ; /* empty */ continue; } cp = line; field = 0; while (*cp) { ep = strchr(cp, delimChar); if (ep) { *ep = '\0'; } if ( !ipFields[field]) { /* not an IP field. Print it. */ fprintf(outf, "%s", cp); goto NEXT_FIELD; } /* must convert this field */ sp = cp; /* eat leading whitespace */ while (*sp && isspace((int)*sp)) { ++sp; } dotted = strchr(sp, '.'); if (dotted == NULL) { unsigned long val_ul = strtoul(sp, NULL, 10); if (val_ul == 0 || val_ul >= UINT32_MAX) { fprintf(outf, "%s", cp); goto NEXT_FIELD; } val = (uint32_t)val_ul; } #if USE_GET_ADDR_INFO { struct addrinfo *addr; struct addrinfo *addresses; char buf[SK_NUM2DOT_STRLEN]; int err; if (dotted == NULL) { if (NULL == num2dot_r(val, buf)) { fprintf(outf, "%s", cp); goto NEXT_FIELD; } sp = buf; } err = getaddrinfo(sp, NULL, &hints, &addresses); if (err) { fprintf(outf, "%s", cp); goto NEXT_FIELD; } addr = addresses; while (addr) { if (addr->ai_canonname) { fprintf(outf, "%s", addr->ai_canonname); break; } addr = addr->ai_next; } if (addr == NULL) { /* didn't find a name */ fprintf(outf, "%s", cp); } freeaddrinfo(addresses); } #else { struct hostent *he; in_addr_t a; if (dotted) { val = dot2num(sp); if (val == UINT32_MAX) { fprintf(outf, "%s", cp); } } a = htonl(val); he = gethostbyaddr((char*)&a, sizeof(in_addr_t), AF_INET); if (he == NULL) { fprintf(outf, "%s", cp); goto NEXT_FIELD; } else { fprintf(outf, "%s", he->h_name); } } #endif /* USE_GET_ADDR_INFO */ NEXT_FIELD: if (ep) { /* we saw a delimiter earlier so print it */ fprintf(outf, "%c", delimChar); cp = ep + 1; } else { /* at end of line; break out of while() */ break; } ++field; } fprintf(outf, "%s", "\n"); } /* outer loop over lines */ appTeardown(); exit(EXIT_SUCCESS); } /* ** Local variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */