/* ** Copyright (C) 2001-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@ */ /* ** sku-string.c ** ** A collection of utility routines to manipulate strings ** */ #include "silk.h" RCSIDENT("$SiLK: sku-string.c 7800 2007-07-06 15:32:43Z mthomas $"); #include "utils.h" #include /* for HUGE_VAL */ #define STRING_PARSE_MIN_YEAR 1970 #define STRING_PARSE_MAX_YEAR 2039 /* Structure used when parsing a comma-separated list of numbers and ranges */ typedef struct { const char *sp; uint32_t min; uint32_t max; } sk_number_parser_t; typedef enum { /* Indicates _skNumberListParserInit() initialized successfully */ SK_NUM_PARSER_OK = 0, /* Indicates _skNumberListParserNext() parsed a single number and * an optional trailing comma, e.g. "3" or "5," */ SK_NUM_PARSER_NUMBER, /* Indicates _skNumberListParserNext() parsed a range and an * optional trailing comma, e.g., "3-4" or "5-6," */ SK_NUM_PARSER_RANGE, /* Indicates _skNumberListParserNext() parsed an open-ended range * and an optional trailing comma, e.g., "3-" or "5-,". Ranges * with open-beginnings are not allowed. */ SK_NUM_PARSER_RANGE_OPENMAX, /* Indicates _skNumberListParserNext() reached the end of the input */ SK_NUM_PARSER_END_OF_STRING, /* Indicates _skNumberListParserInit() was given input that did * not contain a digit, or that _skNumberListParserNext() was * unable to parse a number */ SK_NUM_PARSER_NOT_A_NUMBER = -126, /* Indicates _skNumberListParserNext() attempted to parse a number * too large for the parser to handle. */ SK_NUM_PARSER_OVERFLOW, /* Indicates _skNumberListParserNext() parsed a number smaller * than the caller specified minimum. */ SK_NUM_PARSER_MINIMUM, /* Indicates _skNumberListParserNext() parsed a number larger than * the caller specified maximum. */ SK_NUM_PARSER_MAXIMUM, /* Indicates _skNumberListParserInit() was given a minimum larger * than the maximum, or that _skNumberListParserNext() was unable * to parse a range: e.g., "3-2", "2-3-", "4-x", "5--", etc. */ SK_NUM_PARSER_BAD_RANGE, /* Indicates _skNumberListParserNext() found a hyphen when it was * not expecting one.*/ SK_NUM_PARSER_MISPLACED_HYPHEN, /* Indicates _skNumberListParserNext() encounted embedded * whitespace. Leading and trailing whitespace will be * ignored. */ SK_NUM_PARSER_EMBEDDED_WHITESPACE, /* Indicates _skNumberListParserNext() encounted a character it * did not expect. */ SK_NUM_PARSER_UNEXPECTED_CHAR } sk_number_parser_result_t; /* * pos = _skNumberParserCurrentChar(p); * * Returns the beginning of the token that the parser was parsing * when it encountered the error. */ #define _skNumberParserCurrentChar(parser) ((parser)->sp) uint32_t dot2num(const char *ip) { struct in_addr inp; #ifdef HAVE_INET_PTON if (inet_pton(AF_INET, ip, &inp) <= 0) #else if (inet_aton(ip, &inp) == 0) #endif /* HAVE_INET_PTON */ { return 0xFFFFFFFF; } return ntohl(inp.s_addr); } /* Convert integer 0 to string "0.0.0.0"; uses static buffer */ char *num2dot(uint32_t ip) { #ifdef HAVE_INET_PTON # if SK_NUM2DOT_STRLEN < INET_ADDRSTRLEN # error "SK_NUM2DOT_STRLEN is not big enough" # endif static char dotted[SK_NUM2DOT_STRLEN]; ip = htonl(ip); return (char*)inet_ntop(AF_INET, &ip, dotted, SK_NUM2DOT_STRLEN); #else struct in_addr inp; inp.s_addr = htonl(ip); return inet_ntoa(inp); #endif /* HAVE_INET_PTON */ } /* Convert integer 0 to string "0.0.0.0"; uses caller's buffer */ char *num2dot_r(uint32_t ip, char *outbuf) { #ifdef HAVE_INET_PTON ip = htonl(ip); return (char*)inet_ntop(AF_INET, &ip, outbuf, SK_NUM2DOT_STRLEN); #else struct in_addr inp; char *cp; inp.s_addr = htonl(ip); if ( !(cp = inet_ntoa(inp))) { return NULL; } strncpy(outbuf, cp, SK_NUM2DOT_STRLEN-1); outbuf[SK_NUM2DOT_STRLEN-1] = '\0'; return outbuf; #endif /* HAVE_INET_PTON */ } /* Convert integer 0 to string "000.000.000.000"; uses static buffer */ char *num2dot0(uint32_t ip) { static char dotted[SK_NUM2DOT_STRLEN]; return num2dot0_r(ip, dotted); } /* Convert integer 0 to string "000.000.000.000"; uses caller's buffer */ char *num2dot0_r(uint32_t ip, char *outbuf) { snprintf(outbuf, SK_NUM2DOT_STRLEN, "%03u.%03u.%03u.%03u", ((ip >> 24) & 0xFF), ((ip >> 16) & 0xFF), ((ip >> 8) & 0xFF), (ip & 0xFF)); outbuf[SK_NUM2DOT_STRLEN-1] = '\0'; return outbuf; } /* Convert a DNS name to an in_addr_t. */ const char *skNameToAddr(const char *name, struct in_addr *addr) { struct addrinfo *addrinfo; struct addrinfo hints; struct sockaddr_in *sockaddr; int rv; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; rv = getaddrinfo(name, NULL, &hints, &addrinfo); if (rv != 0) { return gai_strerror(rv); } sockaddr = (struct sockaddr_in *)addrinfo->ai_addr; memcpy(&addr->s_addr, &sockaddr->sin_addr, sizeof(*addr)); freeaddrinfo(addrinfo); return NULL; } /* Convert integer to FSRPAUEC string; space (' ') represents unset flags. */ char *tcpflags_string(uint8_t flags) { static char flag_string[SK_TCPFLAGS_STRLEN]; return tcpflags_string_r(flags, flag_string); } /* Convert integer to FSRPAUEC string; space (' ') represents unset * flags. Users caller's buffer. */ char *tcpflags_string_r(uint8_t flags, char *outbuf) { outbuf[0] = (char)((flags & FIN_FLAG) ? 'F' : ' '); outbuf[1] = (char)((flags & SYN_FLAG) ? 'S' : ' '); outbuf[2] = (char)((flags & RST_FLAG) ? 'R' : ' '); outbuf[3] = (char)((flags & PSH_FLAG) ? 'P' : ' '); outbuf[4] = (char)((flags & ACK_FLAG) ? 'A' : ' '); outbuf[5] = (char)((flags & URG_FLAG) ? 'U' : ' '); outbuf[6] = (char)((flags & ECE_FLAG) ? 'E' : ' '); outbuf[7] = (char)((flags & CWR_FLAG) ? 'C' : ' '); outbuf[SK_TCPFLAGS_STRLEN-1] = '\0'; return outbuf; } /* strip whitespace of line in-place; return length */ int strip(char *line) { char *sp, *ep; int len; sp = line; while ( *sp && isspace((int)*sp) ) { sp++; } /* at first non-space char OR at end of line */ if (*sp == '\0') { /* line full of white space. nail at beginning and return with 0 */ line[0] = '\0'; return 0; } /* figure out where to stop the line */ ep = sp + strlen(sp) - 1; while ( isspace((int)*ep) && (ep > sp) ) { ep--; } /* ep at last non-space char. Nail after */ ep++; *ep = '\0'; len = (int)(ep - sp); if (sp == line) { /* no shifting required */ return(len); } memmove(line, sp, len+1); return(len); } /* Down-case 'cp' in place */ void lower(char *cp) { while (*cp) { if (isupper((int)*cp)) { *cp = *cp + 32; } cp++; } return; } /* Up-case 'cp' in place */ void upper(char *cp) { while (*cp) { if (islower((int)*cp)) { *cp = *cp - 32; } cp++; } return; } /* * result = _skNumberListParserInit(&p, *input, minimum, maximum); * * Fills in the sk_number_parser_t data structure---which should * have been declared and allocated by the caller---pointed at by * 'p' with state necessary to call _skNumberListParserNext(). * * The caller must not modify 'input' while the * _skNumberListParserNext() function is in use. * * On success, SK_NUM_PARSER_OK is returned. On error, this * function returns SK_NUM_PARSER_BAD_RANGE if minimum is greater * than maximum; SK_NUM_PARSER_NOT_A_NUMBER if 'input' does not * contain at least one digit. */ static sk_number_parser_result_t _skNumberListParserInit( sk_number_parser_t *parser, const char *input, uint32_t minimum, uint32_t maximum) { const char *sp; /* check input */ assert(parser); assert(input); if (maximum == 0) { maximum = UINT32_MAX; } else if (minimum > maximum) { return SK_NUM_PARSER_BAD_RANGE; } sp = input; while (*sp && isspace((int)*sp)) { ++sp; } if (!isdigit((int)*sp)) { return SK_NUM_PARSER_NOT_A_NUMBER; } parser->min = minimum; parser->max = maximum; parser->sp = sp; return SK_NUM_PARSER_OK; } /* * result = _skNumberListParserNext(&out_val, &out_length, p); * * Parse the next number or range in the 'input' that was used to * initialize the sk_number_parser_t 'p'. * * If the next token is a single number ("3,"), its value is stored * in 'out_val', 1 is stored in 'out_length', and * SK_NUM_PARSER_NUMBER is returned. * * If the next token is a range ("2-4,"), the starting value (2) is * stored in 'out_val', the number of elements in the range (3) is * stored in 'out_length', and SK_NUM_PARSER_RANGE is returned. * * When there are no more tokens in 'input', * SK_NUM_PARSER_END_OF_STRING is returned. * * Any other return value indicates an error. */ static sk_number_parser_result_t _skNumberListParserNext( uint64_t *range_length, uint32_t *value, sk_number_parser_t *parser) { unsigned long n = 0; const char *sp; char *ep; int is_space; sk_number_parser_result_t rv; /* check input */ assert(parser); assert(value); assert(range_length); /* initialize vars */ *value = 0; *range_length = 0; sp = parser->sp; /* are we at end of string? */ if (*sp == '\0') { return SK_NUM_PARSER_END_OF_STRING; } while (*sp) { /* parse the number */ errno = 0; n = strtoul(sp, &ep, 10); if (sp == ep) { rv = SK_NUM_PARSER_NOT_A_NUMBER; goto END; } if (n == ULONG_MAX && errno == ERANGE) { rv = SK_NUM_PARSER_OVERFLOW; goto END; } if (n < parser->min) { rv = SK_NUM_PARSER_MINIMUM; goto END; } if (n > parser->max) { rv = SK_NUM_PARSER_MAXIMUM; goto END; } /* parsed the number; move pointer to next token */ sp = ep; /* see if we are parsing a range; if so, store the first * number (lower limit) and loop through the while again in * order to parse the second number (upper limit). */ if (*sp != '-') { break; } else if (*range_length != 0) { /* second pass through the while loop yet we're looking at * another hyphen. We've got something like "1-2-". An * error */ rv = SK_NUM_PARSER_BAD_RANGE; goto END; } else { /* first pass, we just parsed lower limit */ ++sp; if (isdigit((int)*sp)) { /* looks like a good range; store the value we just * parsed, set range_length so we know we're in a * range, and loop again */ *value = n; *range_length = 1; continue; } else if (*sp == '\0' || *sp == ',') { /* open-ended range, store current value, use * range_length to denote open-ended range, set n to * the max */ *value = n; *range_length = 2; n = parser->max; break; } else { rv = SK_NUM_PARSER_BAD_RANGE; goto END; } } } /* we've parsed a number or a range */ if (*range_length == 0) { /* single number */ *value = n; *range_length = 1; rv = SK_NUM_PARSER_NUMBER; } else if (*range_length == 2) { *range_length = (n - *value + 1); rv = SK_NUM_PARSER_RANGE_OPENMAX; } else if (n == *value) { /* range of 3-3; treat as single number */ rv = SK_NUM_PARSER_NUMBER; } else if (n < *value) { rv = SK_NUM_PARSER_BAD_RANGE; goto END; } else { *range_length = (n - *value + 1); rv = SK_NUM_PARSER_RANGE; } /* move forward to the start of the next number. Use is_space to * check for embedded whitespace in the input. */ is_space = 0; while (*sp) { if (is_space && !isspace((int)*sp)) { /* error: embedded whitespace */ rv = SK_NUM_PARSER_EMBEDDED_WHITESPACE; goto END; } if (isspace((int)*sp)) { /* white is fine but only at end of string */ is_space = 1; ++sp; continue; } if (isdigit((int)*sp)) { /* this is what we expect*/ break; } if (*sp == ',') { /* duplicate comma is allowed */ ++sp; continue; } if (*sp == '-') { /* error */ rv = SK_NUM_PARSER_MISPLACED_HYPHEN; goto END; } /* error */ rv = SK_NUM_PARSER_UNEXPECTED_CHAR; goto END; } /* we've successfully parsed the token; we can store 'sp' on the * parser object */ parser->sp = sp; END: return rv; } /* parse string "4,3,2-6" to array {4,3,2,3,4,5,6}. See header for details */ int skStringParseNumberList( uint32_t **number_list, uint32_t *number_count, const char *input, uint32_t min_value, uint32_t max_value, uint32_t max_number_count) { uint64_t range_length; uint64_t i; uint32_t range_start; uint32_t *out_array_list = NULL; /* returned array */ uint32_t out_count = 0; /* returned count */ uint32_t array_size; sk_number_parser_t parser; sk_number_parser_result_t rv; /* check input */ assert(number_list); assert(number_count); if ((input == NULL) || (*input == '\0')) { skAppPrintErr("Number list is empty"); return -1; } rv = _skNumberListParserInit(&parser, input, min_value, max_value); if (rv != SK_NUM_PARSER_OK) { if (rv == SK_NUM_PARSER_BAD_RANGE) { skAppPrintErr(("Range maximum is less than range minimum\n" "\tmin=%u max=%u"), min_value, max_value); } else if (rv == SK_NUM_PARSER_NOT_A_NUMBER) { skAppPrintErr("Number list does not begin with a digit: '%s'", input); } else { skAppPrintErr("Error initializing number list"); } return -1; } /* If no max count was given, assume the user is only allowed to * choose each item one time, and set the max_number_count to the * number of items. */ if (max_number_count == 0) { if (max_value != 0) { max_number_count = 1 + max_value - min_value; } else { /* something big */ max_number_count = (1 << 24); } } /* Create the array to hold the list of values. If * max_number_count is greater than 256, use half of it as the * initial size; otherwise, create an array of max_number_count * entries. */ if (max_number_count <= 256) { array_size = max_number_count; } else { array_size = max_number_count / 2; } out_array_list = calloc(array_size, sizeof(uint32_t)); if (!out_array_list) { skAppPrintErr("Out of memory! out_array_list=calloc()"); goto ERROR; } while ((rv = _skNumberListParserNext(&range_length, &range_start, &parser)) != SK_NUM_PARSER_END_OF_STRING) { if (rv == SK_NUM_PARSER_NUMBER || rv == SK_NUM_PARSER_RANGE) { /* check number of fields user gave */ if ((out_count + range_length) > max_number_count) { skAppPrintErr(("Too many fields (%lld) provided." " Only %u fields allowed"), (long long)(out_count + range_length), max_number_count); goto ERROR; } /* check if we need to grow array? If so, realloc the array * to double its size and memset the new section to 0. */ while ((out_count + range_length) > array_size) { size_t old_size = array_size; uint32_t *old_array = out_array_list; array_size *= 2; if (array_size > max_number_count) { array_size = max_number_count; } out_array_list = realloc(out_array_list, array_size * sizeof(uint32_t)); if (!out_array_list) { out_array_list = old_array; skAppPrintErr("Out of memory! out_array_list=realloc()"); goto ERROR; } memset((out_array_list+old_size), 0, (array_size-old_size) * sizeof(uint32_t)); } /* add the entries */ for (i = 0; i < range_length; ++i, ++range_start, ++out_count) { out_array_list[out_count] = range_start; } } else { skAppPrintErr("parse error at position %u in '%s'", (unsigned int)(_skNumberParserCurrentChar(&parser) - input + 1u), input); switch (rv) { case SK_NUM_PARSER_NOT_A_NUMBER: case SK_NUM_PARSER_UNEXPECTED_CHAR: fprintf(stderr, "\t Unexpected character.\n"); break; case SK_NUM_PARSER_EMBEDDED_WHITESPACE: fprintf(stderr, "\t Embedded whitespace is not allowed.\n"); break; case SK_NUM_PARSER_OVERFLOW: fprintf(stderr, "\t Number causes parser to overflow.\n"); break; case SK_NUM_PARSER_MINIMUM: fprintf(stderr, "\t Number is less than minimum of %u.\n", min_value); break; case SK_NUM_PARSER_MAXIMUM: fprintf(stderr, "\t Number is greater than maximum of %u.\n", max_value); break; case SK_NUM_PARSER_RANGE_OPENMAX: fprintf(stderr, "\t Range is missing its upper limit.\n"); break; case SK_NUM_PARSER_BAD_RANGE: case SK_NUM_PARSER_MISPLACED_HYPHEN: fprintf(stderr, "\t Error parsing range.\n"); break; default: fprintf(stderr, "\t Unexpected error.\n"); break; } goto ERROR; } } *number_count = out_count; *number_list = out_array_list; return 0; ERROR: if (out_array_list) { free(out_array_list); } *number_count = 0; return -1; } /* parse string "4,3,2-6" to bitfield Array. See header for details */ int skStringParseNumberListToBitArray( uint32_t *out_bitmap, const char *input, uint32_t bitmap_size) { uint64_t range_length; uint64_t i; uint32_t value; BITMAP_DECLARE(bmap, bitmap_size); sk_number_parser_t parser; sk_number_parser_result_t rv; /* check input */ assert(out_bitmap); if (input == NULL) { return -1; } rv = _skNumberListParserInit(&parser, input, 0, bitmap_size-1); if (rv != SK_NUM_PARSER_OK) { return -1; } BITMAP_INIT(bmap); while ((rv = _skNumberListParserNext(&range_length, &value, &parser)) != SK_NUM_PARSER_END_OF_STRING) { switch (rv) { case SK_NUM_PARSER_NUMBER: case SK_NUM_PARSER_RANGE: case SK_NUM_PARSER_RANGE_OPENMAX: /* add the entries */ for (i = 0; i < range_length; ++i, ++value) { BITMAP_SETBIT(bmap, value); } break; default: return -1; } } memcpy(out_bitmap, bmap, sizeof(bmap)); return 0; } /* parse string "4,3,2-6" to skbitmap. See header for details */ int skStringParseNumberListToBitmap( sk_bitmap_t *out_bitmap, const char *input) { uint64_t range_length; uint64_t i; uint32_t value; uint32_t bitmap_size; sk_number_parser_t parser; sk_number_parser_result_t rv; /* check input */ assert(out_bitmap); if (input == NULL) { return -1; } bitmap_size = skBitmapGetSize(out_bitmap); if (bitmap_size < 1) { /* bitmap too small */ return -1; } rv = _skNumberListParserInit(&parser, input, 0, bitmap_size-1); if (rv != SK_NUM_PARSER_OK) { return -1; } while ((rv = _skNumberListParserNext(&range_length, &value, &parser)) != SK_NUM_PARSER_END_OF_STRING) { switch (rv) { case SK_NUM_PARSER_NUMBER: case SK_NUM_PARSER_RANGE: case SK_NUM_PARSER_RANGE_OPENMAX: /* add the entries */ for (i = 0; i < range_length; ++i, ++value) { skBitmapSetBit(out_bitmap, value); } break; default: return -1; } } return 0; } uint8_t *skParseNumberList( const char *input, uint8_t minValue, uint8_t maxValue, uint8_t *returnValueCount) { unsigned long n, rangeStart, i; const char *sp; char *ep; /* max number of fields user can give */ const uint32_t countUpperLimit = 255; uint8_t *numberListArray = NULL; /* returned array */ uint8_t count = 0; /* returned count */ uint32_t arraySize; uint32_t numEntries; /* the last thing we successfully parsed. NUMBER is a single number * or start of a range; RANGE is the upper limit of a range. */ enum {COMMA, HYPHEN, NUMBER, RANGE} parseState; /* check input */ assert(input); assert(maxValue); assert(returnValueCount); assert(minValue < maxValue); /* Set initial length of numberListArray as the number of possible * values and malloc something that size. Duplicate values allow * arraySize to be larger that the possible values, and we will grow * the array if necessary. */ arraySize = 1 + maxValue - minValue; numberListArray = calloc(arraySize, sizeof(*numberListArray)); if (!numberListArray) { skAppPrintErr("Out of memory! numberListArray=calloc()"); goto ERROR; } /* this is non-zero when the number we are about to parse is the * upper half of the range m-n */ rangeStart = 0; parseState = COMMA; sp = input; /* parse user input */ while (*sp) { if (!isdigit((int)*sp)) { if (parseState == HYPHEN) { /* digit must follow a hyphen */ skAppPrintErr(("parse error: expecting digit after hyphen; " "saw '%c' at position %u"), *sp, (unsigned int)(1u+(sp - input))); goto ERROR; } if (*sp == ',') { /* Comma is legal anywhere except after a hyphen, * which we handled above. (we allow double comma) */ parseState = COMMA; ++sp; continue; } if (parseState == RANGE) { /* range must end with a comma */ skAppPrintErr(("parse error: expecting comma after range; " "saw '%c' at position %u"), *sp, (unsigned int)(1u+(sp - input))); goto ERROR; } if (parseState == COMMA) { /* a digit or a comma(handled above) must follow a comma */ skAppPrintErr(("parse error: expecting digit after comma; " "saw '%c' at position %u"), *sp, (unsigned int)(1u+(sp - input))); goto ERROR; } /* if we are here, parseState must be NUMBER */ assert(parseState == NUMBER); if (*sp == '-') { parseState = HYPHEN; ++sp; continue; } skAppPrintErr(("parse error: expecting digit, comma, or hyphen; " "saw '%c' at position %u"), *sp, (unsigned int)(1u+(sp - input))); goto ERROR; } /* We are looking at a digit; parseState must be either COMMA * or HYPHEN */ assert(parseState == COMMA || parseState == HYPHEN); n = strtoul(sp, &ep, 10); if (sp == ep) { skAppPrintErr("cannot parse '%s' as an integer", sp); goto ERROR; } if ( !(minValue <= n && n <= maxValue) ) { skAppPrintErr("value '%lu' out of range. use %u <= n <= %u", n, (unsigned int)minValue, (unsigned int)maxValue); goto ERROR; } sp = ep; if (parseState == COMMA) { /* we were not parsing the upper half of a range */ parseState = NUMBER; numEntries = 1; rangeStart = n; } else { /* parseState must be HYPHEN: we were parsing a range */ parseState = RANGE; numEntries = n - rangeStart; if (n < rangeStart) { /* user entered 3-2 */ skAppPrintErr("bad range: lower > upper: %lu-%lu", rangeStart, n); goto ERROR; } if (rangeStart == n) { /* user entered 3-3, ignore second '3' */ continue; } /* we added rangeStart on the previous iteration, so add * rangeStart+1 through n now; */ rangeStart++; } /* check number of fields user gave */ if ((count + numEntries) > countUpperLimit) { skAppPrintErr("Too many fields provided. Only %u fields allowed", countUpperLimit); goto ERROR; } /* check if we need to grow array? If so, realloc the array * to double its size and memset the new section to 0. */ while ((count + numEntries) > arraySize) { numberListArray = realloc(numberListArray, 2*arraySize); if (!numberListArray) { skAppPrintErr("Out of memory! numberListArray=realloc()"); goto ERROR; } memset((numberListArray+arraySize), 0, arraySize); arraySize *= 2; } /* add the entries */ for (i = rangeStart; i <= n; ++i) { numberListArray[count] = (uint8_t)i; ++count; } } /* while(*sp) */ *returnValueCount = count; return numberListArray; ERROR: if (numberListArray) { free(numberListArray); } *returnValueCount = 0; return NULL; } /* Convert string "0" or "0.0.0.0" to integer 0 */ int skStringParseIP(uint32_t *out_val, const char *ip_string) { const char *sp; char *ep; unsigned long val; unsigned long final = 0; int i; /* verify input */ if (!ip_string) { return -1; } /* skip leading whitespace */ sp = ip_string; while (isspace((int)*sp)) { ++sp; } if ('\0' == *sp) { /* whitespace only or empty string */ return -2; } /* number that begins with '-' is not unsigned */ if ('-' == *sp) { return -3; } for (i = 3; i >= 0; --i) { /* parse the string */ errno = 0; val = strtoul(sp, &ep, 10); if (sp == ep) { /* parse error */ return -3; } if (val == ULONG_MAX && errno == ERANGE) { /* overflow */ return -4; } sp = ep; if (*sp != '.') { if (i == 3) { /* treat as a single integer */ final = val; break; } else if (i != 0) { /* need a '.' between octets */ return -3; } /* else i == 0 and we've finished parsing */ } else if (i > 0) { /* move to start of next octet */ ++sp; if (!isdigit((int)*sp)) { /* error: only '.' and digits are allowed */ return -3; } } if (val > 255) { /* value too big for octet */ return -4; } final |= val << (8 * i); } /* all done */ *out_val = (uint32_t)final; /* move sp forward over any whitespace */ while (isspace((int)*sp)) { ++sp; } if ('\0' != *sp) { /* text after IP */ return (1 + sp - ip_string); } return 0; } /* * converts an C-string IP into an array of 4 bitmaps, each containing * 256 bits for the octet. See header for details. */ int skIPStringToBitmap( uint32_t bitmap[4][8], const char *ip_string) { skOctetMap_t octetmap; int rv; rv = skStringParseWildcardIP(&octetmap, ip_string); if (rv == 0) { memcpy(bitmap, octetmap.m_octets, sizeof(octetmap.m_octets)); } return rv; } int skStringParseWildcardIP( skOctetMap_t *octetmap, const char *ip_string) { const unsigned int octet_max_value = 255; const char *sp; const char *final; char *ep; size_t pos; unsigned long n; unsigned long i; unsigned long range_start = 0; unsigned long range_end = 0; unsigned int octet = 0; unsigned long cidr = 0; enum {DOT, COMMA, HYPHEN, NUMBER, WILDCARD, RANGE} parse_state; /* * parse_state values: DOT: last char parsed was an octet * separator '.'. Also initial state of the parser. COMMA: last * character parsed was the inter-octet list operator ','. * HYPHEN: last char parsed was the inter-octet range operator * '-'. NUMBER: last char parsed was a stand-alone number or the * first part of a range---we won't know which until we parse the * next char. WILDCARD: last char parsed was the octet-wildcard * char 'x' or 'X'---this translates to 0-255. RANGE: last char * parsed was the number at the upper half of a range. */ /* make certain we get input */ if (!ip_string || !octetmap) { return -1; } /* clean out the octetmap */ skOctetMapClear(octetmap); /* handle case of empty string */ if (*ip_string == '\0') { return 1; } /* point 'final' to the final character in the input */ final = &(ip_string[strlen(ip_string)]); /* move 'final' toward front of string over any whitespace */ while ((final > ip_string) && isspace((int) *(final-1))) { --final; } /* check to see if input was whitespace only */ if (final == ip_string) { return 2; } /* * Determine if we are using CIDR notation by looking for a * slash. if found, read in the number of significant bits and * treat the '/' as the end of the string by adjusting 'final'. * Don't use the parse_state here. */ sp = strchr(ip_string, '/'); if (NULL != sp) { const char *cp; /* make certain no commas, hyphens, or wildcards (x, X) */ if ((NULL != (cp = strchr(ip_string, ','))) || (NULL != (cp = strchr(ip_string, '-'))) || (NULL != (cp = strchr(ip_string, 'x'))) || (NULL != (cp = strchr(ip_string, 'X')))) { skAppPrintErr("IP parse error: cannot have char '%c' in input\n" "\t when using CIDR notation", *cp); goto ERROR; } /* character offset in string---for error reporting */ pos = 1u + sp - ip_string; /* goto char after slash; must be a digit */ ++sp; if (!isdigit((int)*sp)) { skAppPrintErr("IP parse error: unexpected '%c' at pos %u;\n" "\t expected digits after '/'", *sp, (unsigned int)pos); goto ERROR; } /* parse the number */ errno = 0; cidr = strtoul(sp, &ep, 10); if (ep == sp) { skAppPrintErr("IP parse error: cannot parse number at pos %u\n" "\t attempting to parse '%s'", (unsigned int)pos, sp); goto ERROR; } if (cidr == ULONG_MAX || errno == ERANGE) { skAppPrintErr("IP parse error: integer overflow at pos %u\n" "\t attempting to parse '%s'", (unsigned int)pos, (ip_string+pos)); goto ERROR; } if (ep != final) { skAppPrintErr("IP parse error: extra text '%s'\n" "\t after CIDR block", ep); goto ERROR; } if (cidr == 0) { skAppPrintErr("IP parse error: CIDR prefix of 0 is nonsensical"); goto ERROR; } if (cidr > 32) { skAppPrintErr("IP parse error: CIDR mask of %lu larger than 32", cidr); goto ERROR; } /* nail 'final' at the slash */ final = sp - 1; } /* Use DOT as our initial parse_state */ parse_state = DOT; /* Parse the input ip from the beginning */ sp = ip_string; /* ignore leading whitespace */ while (isspace((int)*sp)) { ++sp; } /* parse until we reach 'final' */ while (sp < final) { /* character offset in string---for error reporting */ pos = 1u + sp - ip_string; if (isdigit((int)*sp)) { /* number can follow a comma, hyphen, or dot */ /* parse number */ errno = 0; n = strtoul(sp, &ep, 10); if (sp == ep) { skAppPrintErr("IP parse error: cannot parse number at pos %u\n" "\t attempting to parse '%s'", (unsigned int)pos, sp); goto ERROR; } if (n == ULONG_MAX || errno == ERANGE) { skAppPrintErr("IP parse error: integer overflow at pos %u\n" "\t attempting to parse '%s'", (unsigned int)pos, sp); goto ERROR; } /* move pointer to end of number */ sp = ep; /* If this is the first octet, see if the caller gave us a * single integer value. */ if ((octet == 0) && (ep == final)) { #if (ULONG_MAX > UINT32_MAX) /* See if number is too large on 64bit platforms */ if (n > UINT32_MAX) { /* too big */ skAppPrintErr(("IP parse error: integer value %" PRIu64 " too large for IPv4 address."), n); goto ERROR; } #endif /* We have a single value. Break it into octets, * apply CIDR if given, and set the octetmap. */ for (octet = 0; octet < 4; ++octet) { if ((cidr == 0) || (cidr >= (8 * (1 + octet)))) { /* no CIDR mask or no effect this octet; store * single octet */ i = ((n >> (8 * (3 - octet))) & 0xFF); octetmap->m_octets[octet][_BMAP_INDEX(i)] |= _BMAP_OFFSET(i); } else if (cidr <= (8 * octet)) { /* all ones */ memset(octetmap->m_octets[octet], 0xFF, sizeof(uint32_t) * 8); } else { range_start = (0xFF & ((n & ~(0xFFFFFFFF >> cidr)) >> (8 * (3 - octet)))); range_end = (0xFF & ((n | (0xFFFFFFFF >> cidr)) >> (8 * (3 - octet)))); for (i = range_start; i <= range_end; ++i) { octetmap->m_octets[octet][_BMAP_INDEX(i)] |= _BMAP_OFFSET(i); } } } /* Done. Set octet to 3, and loop again---which will * break out of while() */ parse_state = NUMBER; octet = 3; continue; } /* single value check */ if (n > octet_max_value) { skAppPrintErr("IP parse error: octet value %lu larger than %u", n, octet_max_value); goto ERROR; } switch (parse_state) { case NUMBER: case RANGE: /* programmer error since strtoul would have eaten it */ skAppPrintErr("programmer error. number after number"); abort(); case WILDCARD: /* number after an 'x'. Show the entire octet as invalid */ skAppPrintErr("IP parse error: invalid octet at pos %lu\n" "\t numbers follow octet wildcard: '%s'", (unsigned long)pos-1u, (ip_string+pos-2u)); goto ERROR; case COMMA: case DOT: /* parsing a single number or start of a range. Treat * it as a single number for now. */ parse_state = NUMBER; range_start = range_end = n; /* If user specified CIDR block, apply mask if required */ if (cidr != 0) { /* number of masking bits for this octet */ long bits; bits = cidr - (8 * octet); if (bits >= 8) { /* no effect on this octet */ } else if (bits <= 0) { /* all ones */ if (n != 0) { /* user entered a number that is ignored. * Should we warn them?? */ } range_start = 0; range_end = octet_max_value; } else { range_start = 0xff & (n & (0xff << (8 - bits))); range_end = n | (0xff >> bits); } } break; case HYPHEN: /* this number is range's upper limit */ parse_state = RANGE; range_end = n; if (range_end < range_start) { /* user entered 3-2 */ skAppPrintErr("IP parse_state: bad range %lu-%lu\n" "\t start larger than end", range_start, range_end); goto ERROR; } if (range_start == range_end) { /* user entered 3-3, ignore second '3' */ continue; } /* technically we added range_start on the previous * iteration, but there is no harm to add it again */ break; } /* switch(parse_state)*/ } else { /* *sp is not a digit */ /* must have a digit after a hyphen to make a range */ if (HYPHEN == parse_state) { skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t expecting digit after hyphen", *sp, (unsigned int)pos); goto ERROR; } switch (*sp) { case '-': /* hyphen can only follow a number */ assert(cidr == 0); if (NUMBER != parse_state) { skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t hyphen only permitted after a number.", *sp, (unsigned int)pos); goto ERROR; } parse_state = HYPHEN; ++sp; continue; break; /*NOTREACHED*/ case ',': /* comma must follow a number or range; also permit after * a comma in case user is sloppy */ switch (parse_state) { case NUMBER: case RANGE: case COMMA: parse_state = COMMA; ++sp; continue; default: skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t comma only permitted after a number.", *sp, (unsigned int)pos); goto ERROR; } break; /*NOTREACHED*/ case '.': /* dot must follow a number, range, wildcard */ switch (parse_state) { case NUMBER: case RANGE: case WILDCARD: if (octet >= 3) { skAppPrintErr("IP parse error: More than four" " octets detected at pos %u", (unsigned int)pos); goto ERROR; } parse_state = DOT; ++sp; ++octet; continue; default: skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t dot only permitted after a number.", *sp, (unsigned int)pos); goto ERROR; } break; /*NOTREACHED*/ case '/': /* slash must follow a number, range, wildcard; this * code-block should never be seen since 'final' should * point to the slash. */ assert(cidr > 0); switch (parse_state) { case NUMBER: case RANGE: case WILDCARD: /* force-stop all processing by setting sp to final */ sp = final; continue; default: skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t slash only permitted after a number.", *sp, (unsigned int)pos); goto ERROR; } break; /*NOTREACHED*/ case 'X': case 'x': /* wildcard 'x' must follow a dot */ assert(cidr == 0); if (DOT != parse_state) { skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t octet wildcard only permitted after dot" " or start of IP.", *sp, (unsigned int)pos); goto ERROR; } parse_state = WILDCARD; ++sp; range_start = 0; range_end = octet_max_value; /* set the bits below */ break; default: /* any unhandled character *sp */ skAppPrintErr("IP parse error: unexpected '%c' at pos %u\n" "\t expected digit, hyphen, comma, dot, or x", *sp, (unsigned int)pos); goto ERROR; break; /*NOTREACHED*/ } /* switch(*sp) */ } /* else block of if(isdigit(*sp)) */ /* add entries */ for (i = range_start; i <= range_end; ++i) { octetmap->m_octets[octet][_BMAP_INDEX(i)] |= _BMAP_OFFSET(i); } } /* while(*sp) */ /* make certain we saw four octets; make certain last octet had data */ if ((octet != 3) || (parse_state == DOT)) { skAppPrintErr("IP parse error: too few octets given"); goto ERROR; } /* we should be at end of text */ if (sp != final) { skAppPrintErr("IP parse error: extra text after CIDR block"); goto ERROR; } return 0; ERROR: return -1; } /* time string to struct timeval; see utils.h for details */ int skStringParseDatetime( struct timeval *date_val, const char *date_string, int *resulting_precision) { const int min_precision = 3; /* at least day */ struct tm ts; time_t t; size_t pos = 0; const char *sp; char *ep; const char delim[] = {'\0', '/', '/', ':', ':', ':', '.'}; long val; long usec; int i; int j; /* check inputs */ assert(date_val); if (NULL == date_string) { skAppPrintErr("data parser error: input is NULL"); return -1; } /* initialize */ memset(&ts, 0, sizeof(struct tm)); usec = 0; sp = date_string; /* eat leading whitespace */ while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' == *sp) { /* whitespace only or empty string */ pos = sp - date_string; skAppPrintErr(("date parser error at pos %u in '%s'\n" "\t reached unexpected end of string"), (unsigned int)pos, date_string); return -1; } /* 'i' is the part of the date we have successfully parsed; * 1=year, 2=month, 3=day, 4=hour, 5=min, 6=sec, 7=usec */ i = 0; while (*sp && (i < (int)(sizeof(delim)/sizeof(char)))) { /* character offset in string---for error reporting */ pos = 1u + sp - date_string; /* this will catch things like double ':' in the string */ if ( !isdigit((int)*sp)) { skAppPrintErr(("date parser error at pos %u in '%s'\n" "\t expecting digit but found '%c'"), (unsigned int)pos, date_string, *sp); return -1; } /* parse the digit */ errno = 0; val = strtol(sp, &ep, 10); if (sp == ep) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t attempting to parse '%s'"), (unsigned int)pos, date_string, sp); return -1; } if (val == LONG_MAX && errno == ERANGE) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t integer overflow while parsing '%s'"), (unsigned int)pos, date_string, sp); return -1; } if (val < 0) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t negative value found while parsing '%s'"), (unsigned int)pos, date_string, sp); return -1; } /* check that value is valid given what we are parsing */ switch (i) { case 0: /* year */ if (val < STRING_PARSE_MIN_YEAR || val > STRING_PARSE_MAX_YEAR) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid year %ld; use %d <= y <= %d"), (unsigned int)pos, date_string, val, STRING_PARSE_MIN_YEAR, STRING_PARSE_MAX_YEAR); return -1; } ts.tm_year = val - 1900; break; case 1: /* month */ if (val < 1 || val > 12) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid month %ld; use %d <= mon <= %d"), (unsigned int)pos, date_string, val, 1, 12); return -1; } ts.tm_mon = val - 1; break; case 2: /* day */ if (val < 1 || val > 31) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid day %ld; use %d <= day <= %d"), (unsigned int)pos, date_string, val, 1, 31); return -1; } if (val > maxDayInMonth((1900 + ts.tm_year), (1 + ts.tm_mon))) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid day %ld for month %d"), (unsigned int)pos, date_string, val, (1 + ts.tm_mon)); return -1; } ts.tm_mday = val; break; case 3: /* hour */ if (val > 23) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid hour %ld; use 0 <= hr <= 23"), (unsigned int)pos, date_string, val); return -1; } ts.tm_hour = val; break; case 4: /* minute */ if (val > 59) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid minute %ld; use 0 <= min <= 59"), (unsigned int)pos, date_string, val); return -1; } ts.tm_min = val; break; case 5: /* second */ if (val > 59) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t invalid second %ld; use 0 <= sec <= 59"), (unsigned int)pos, date_string, val); return -1; } ts.tm_sec = val; break; case 6: /* fractional seconds */ if ((ep - sp) < 6) { /* multiply to get to microseconds */ for (j = (ep - sp); j < 6; ++j) { val *= 10; } } else if ((ep - sp) > 6) { /* divide to get to microseconds; round final digit */ ldiv_t q; for (j = 7; j < (ep - sp); ++j) { val /= 10; } q = ldiv(val, 10); if (q.rem < 5) { val = q.quot; } else { val = q.quot + 1; } } usec = val; break; default: /* should never get here */ abort(); } /* we parsed the number; move to the delimiter */ ++i; sp = ep; pos = 1u + sp - date_string; /* check for whitespace or end of string */ if (('\0' == *sp) || isspace((int)*sp)) { break; } /* check that delimiter is what we expect; if so move over it * to next character */ if (delim[i]) { if (*sp == delim[i]) { ++sp; } else if (i == 3 && (*sp == 'T')) { /* allow a ':' or a 'T' to separate the day and hour */ ++sp; } else { skAppPrintErr(("date parser error at pos %u in '%s'\n" "\t expecting %c but found '%c'"), (unsigned int)pos, date_string, delim[i], *sp); return -1; } } } /* if the caller wants to know the last state parsed, we update * their variable. */ if ( resulting_precision != NULL ) { *resulting_precision = i; } /* need at least year/month/day */ if (i < min_precision) { skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t date string ends early"), (unsigned int)pos, date_string); return -1; } /* space is allowed at end of string; eat it */ while (*sp && isspace((int)*sp)) { ++sp; } /* check for junk at end of string */ if ('\0' != *sp) { /* use 'ep' in this error so any previous whitespace will be * printed as well */ skAppPrintErr(("date parse error at pos %u in '%s'\n" "\t unexpected text '%s' at end of string"), (unsigned int)pos, date_string, ep); return -1; } /* convert the time */ #ifdef SK_ENABLE_LOCALTIME t = mktime(&ts); #else t = timegm(&ts); #endif if (t == (time_t)-1) { return -1; } date_val->tv_sec = t; date_val->tv_usec = usec; return 0; } /* parses string of the form DATETIME[-DATETIME], where DATETIME is * parsed by skStringParseDatetime(). see header for details. */ int skStringParseDatetimeRange( struct timeval *start, struct timeval *end, const char *s_datetime, int *start_precision, int *end_precision) { char *s_start_time; char *s_end_time; int rv = 0; /* check inputs */ assert(start); assert(end); if (s_datetime == NULL) { skAppPrintErr("date range input is null"); return -1; } /* copy the string, since we are going to modify it */ s_start_time = strdup(s_datetime); if (!s_start_time) { skAppPrintErr("strdup failed!"); return -1; } /* search for dash. if dash does not exist, parse entire string * as start date. if dash does exist, parse string on left as * start date and string on right as end date. */ s_end_time = strchr(s_start_time, '-'); if (s_end_time != NULL) { /* change the dash to a NUL to create two seperate strings */ *s_end_time = '\0'; /* set the s_end_time pointer to be the char after the dash */ ++s_end_time; /* treat missing end-time as infinity */ if (*s_end_time == '\0') { s_end_time = NULL; } } /* parse start */ rv = skStringParseDatetime(start, s_start_time, start_precision); if (s_end_time == NULL) { end->tv_sec = LONG_MAX; end->tv_usec = 0; } else if (rv == 0) { rv = skStringParseDatetime(end, s_end_time, end_precision); } free(s_start_time); /* check error conditions and return error if necessary */ if (rv) { return -1; } if ((end->tv_sec < start->tv_sec) || ((end->tv_sec == start->tv_sec) && (end->tv_usec < start->tv_usec))) { skAppPrintErr("The ending time is earlier than the starting time."); return 1; } return 0; } /* set 'ceiling_time' to the greatest value that does not change the * 'precision' of 'time_val'. see header for details. */ int skDatetimeCeiling( struct timeval *ceiling_time, const struct timeval *time_val, int precision) { struct tm ts; time_t t; /* if we're already at max precision, just return what we were given */ if (precision == 7) { if (time_val != ceiling_time) { memcpy(ceiling_time, time_val, sizeof(struct timeval)); } return 0; } /* we must have at least parsed a year */ if (precision < 1) { return -1; } #ifdef SK_ENABLE_LOCALTIME if ( !localtime_r(&time_val->tv_sec, &ts)) { return -1; } #else if ( !gmtime_r(&time_val->tv_sec, &ts)) { return -1; } #endif /* 'precision' is what we know; we need to set everything that is * "finer" than that value. */ switch (precision) { case 1: /* know year, set month */ ts.tm_mon = 11; /* FALLTHROUGH */ case 2: /* know month, set month-day */ ts.tm_mday = maxDayInMonth((1900 + ts.tm_year), (1 + ts.tm_mon)); /* FALLTHROUGH */ case 3: /* know month-day, set hour */ ts.tm_hour = 23; /* FALLTHROUGH */ case 4: /* know hour, set min */ ts.tm_min = 59; /* FALLTHROUGH */ case 5: /* know min, set sec */ ts.tm_sec = 59; /* FALLTHROUGH */ case 6: /* know sec, set fractional-sec (below) */ break; default: assert(0); abort(); } ts.tm_wday = -1; ts.tm_yday = -1; ts.tm_isdst = -1; /* convert the time */ #ifdef SK_ENABLE_LOCALTIME t = mktime(&ts); #else t = timegm(&ts); #endif if (t == (time_t)-1) { return -1; } ceiling_time->tv_sec = t; ceiling_time->tv_usec = (((long)1e6)-1); return 0; } /* parse string as TCP flags. see header for details. */ int skStringParseTCPFlags( uint8_t *result, const char *flag_string) { const char *cur_char = flag_string; /* check inputs */ assert(result); if (flag_string == NULL) { return -1; } /* parse each character, unless it is a terminating NULL or an * illegal character. */ *result = 0; while (*cur_char) { switch (*cur_char) { case 'f': case 'F': TCP_FLAG_SET_FLAG(*result, FIN_FLAG); break; case 's': case 'S': TCP_FLAG_SET_FLAG(*result, SYN_FLAG); break; case 'r': case 'R': TCP_FLAG_SET_FLAG(*result, RST_FLAG); break; case 'p': case 'P': TCP_FLAG_SET_FLAG(*result, PSH_FLAG); break; case 'a': case 'A': TCP_FLAG_SET_FLAG(*result, ACK_FLAG); break; case 'u': case 'U': TCP_FLAG_SET_FLAG(*result, URG_FLAG); break; case 'e': case 'E': TCP_FLAG_SET_FLAG(*result, ECE_FLAG); break; case 'c': case 'C': TCP_FLAG_SET_FLAG(*result, CWR_FLAG); break; default: if (!isspace((int)*cur_char)) { /* reached illegal, non-space character */ return (1 + cur_char - flag_string); } } ++cur_char; } /* at end of string. did we see any flags? */ /* * COMMENT THIS OUT FOR NOW SINCE CURRENT TCP PARSER ALLOWS EMPTY STRING * if (0 == *result) { * return -2; * } */ return 0; } /* parse flag definition in the form high/mask. see header for * details. */ int skStringParseTCPFlagsHighMask( uint8_t *high, uint8_t *mask, const char *flag_string) { int err_high = 0; int err_mask = 0; /* check inputs */ assert(high); assert(mask); if (flag_string == NULL) { return -1; } *high = 0; *mask = 0; /* get flags up to first illegal character; which should be a '/' */ err_high = skStringParseTCPFlags(high, flag_string); if (err_high < 0) { return -1; } /* reached end of string before '/' character was found */ if (err_high == 0) { return -1; } /* did we get a '/' */ if (flag_string[err_high - 1] != '/') { return -1; } /* get flags after the '/'. This must go to the end of the * string, and mask must have a value. */ err_mask = skStringParseTCPFlags(mask, &flag_string[err_high]); if (err_mask != 0) { return -1; } if (*mask == 0) { return -1; } /* make sure high is a proper subset of mask */ if ((*high & *mask) != *high) { return -1; } /* success */ return 0; } /* parse string as uint32_t. see header for details */ int skStringParseUint32( uint32_t *result_val, const char *int_string, uint32_t min_val, uint32_t max_val) { const char *sp; char *ep; unsigned long val; /* check inputs */ assert(result_val); assert(max_val == 0 || min_val <= max_val); if (!int_string) { return -1; } sp = int_string; /* eat leading whitespace */ while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' == *sp) { /* whitespace only or empty string */ return -2; } /* number that begins with '-' is not unsigned */ if ('-' == *sp) { return -3; } /* parse the string */ errno = 0; val = strtoul(sp, &ep, 10); if (sp == ep) { /* parse error */ return -3; } if (val == ULONG_MAX && errno == ERANGE) { /* overflow */ return -4; } #if (ULONG_MAX > UINT32_MAX) if (val > UINT32_MAX) { /* too big */ return -4; } #endif *result_val = (uint32_t)val; if (*result_val < min_val) { /* too small */ return -11; } if (max_val > 0 && *result_val > max_val) { /* too big */ return -12; } /* eat trailing whitespace */ sp = ep; while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' != *sp) { /* junk at end */ return 1 + sp - int_string; } return 0; } /* parse string as uint64_t. see header for details */ int skStringParseUint64( uint64_t *result_val, const char *int_string, uint64_t min_val, uint64_t max_val) { const char *sp; char *ep; #if (SIZEOF_LONG >= 8) #define U64_OVERFLOW ULONG_MAX unsigned long val; #else #define U64_OVERFLOW ULLONG_MAX unsigned long long val; #endif /* check inputs */ assert(result_val); assert(max_val == 0 || min_val <= max_val); if (!int_string) { return -1; } sp = int_string; /* eat leading whitespace */ while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' == *sp) { /* whitespace only or empty string */ return -2; } /* number that begins with '-' is not unsigned */ if ('-' == *sp) { return -3; } /* parse the string */ errno = 0; #if (SIZEOF_LONG >= 8) val = strtoul(sp, &ep, 10); #else val = strtoull(sp, &ep, 10); #endif if (sp == ep) { /* parse error */ return -3; } if (val == U64_OVERFLOW && errno == ERANGE) { /* overflow */ return -4; } #if (U64_OVERFLOW > UINT64_MAX) if (val > UINT64_MAX) { /* too big */ return -4; } #endif *result_val = (uint64_t)val; if (*result_val < min_val) { /* too small */ return -11; } if (max_val > 0 && *result_val > max_val) { /* too big */ return -12; } /* eat trailing whitespace */ sp = ep; while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' != *sp) { /* junk at end */ return 1 + sp - int_string; } return 0; } int skStringParseHumanUint64( uint64_t *result_val, const char *int_string, skHumanFlags_t parse_flags) { const char *sp; /* Current parse position */ const char *tp; /* Temporary pointer */ const char *hv; /* Index into sk_human_value_list */ char *ep; /* End of parsed number */ int val_index; double tmp_val; struct { const char c; double si; double trad; } sk_human_values[] = { {'k', 1.0e3, 1024.0}, {'m', 1.0e6, 1048576.0}, {'g', 1.0e9, 1073741824.0}, {'t', 1.0e12, 1099511627776.0} }; const char sk_human_value_list[] = "kmgt"; /* check inputs */ assert(result_val); if (!int_string) { return -1; } sp = int_string; /* Eat leading whitespace */ while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' == *sp) { /* whitespace only or empty string */ return -2; } /* parse the string */ errno = 0; tmp_val = strtod(sp, &ep); if (sp == ep) { /* parse error */ return -3; } if (tmp_val == HUGE_VAL && errno == ERANGE) { /* overflow */ return -4; } if (tmp_val < 0) { /* underflow */ return -5; } if (isnan(tmp_val)) { /* NAN */ return -6; } tp = sp = ep; /* Possibly eat trailing whitespace (Parse with tp because it might be space at the end (no suffix) when we are not supposed to parse that.) */ if ((parse_flags & SK_HUMAN_MID_WS) || !(parse_flags & SK_HUMAN_END_NO_WS)) { while (*tp && isspace((int)*tp)) { ++tp; } } /* No suffix? */ if ('\0' == *tp) { if (!(parse_flags & SK_HUMAN_END_NO_WS)) { /* If there was no suffix, and we are supposed to parse trailing whitespace, set pointer to the end. */ sp = tp; } goto parsed; } /* Spaces before suffix allowed? */ if (!(parse_flags & SK_HUMAN_MID_WS)) { /* If there is a suffix, and white space in the middle is illegal, reset to temp pointer. */ tp = sp; } /* Possible suffix */ hv = strchr(sk_human_value_list, tolower((int)*tp)); if (hv != NULL) { /* Valid suffix found, set the parse pointer to the end of it */ sp = tp + 1; /* Find suffix information */ val_index = hv - sk_human_value_list; assert((int)sk_human_values[val_index].c == tolower((int)*tp)); /* Use suffix value */ if (((parse_flags & SK_HUMAN_LOWER_SI) && islower((int)*tp)) || ((parse_flags & SK_HUMAN_UPPER_SI) && isupper((int)*tp))) { tmp_val *= sk_human_values[val_index].si; } else { tmp_val *= sk_human_values[val_index].trad; } } else if (!(parse_flags & SK_HUMAN_END_NO_WS)) { /* No valid suffix, but we allow trailing spaces. */ sp = tp; } parsed: if (tmp_val > UINT64_MAX) { /* overflow */ return -4; } *result_val = tmp_val; if ('\0' != *sp) { /* junk at end */ return 1 + sp - int_string; } return 0; } /* parse string as a double. see header for details */ int skStringParseDouble( double *result_val, const char *dbl_string, double min_val, double max_val) { const char *sp; char *ep; double val; /* check inputs */ assert(result_val); assert(max_val == 0 || min_val <= max_val); if (!dbl_string) { return -1; } sp = dbl_string; /* eat leading whitespace */ while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' == *sp) { /* whitespace only or empty string */ return -2; } /* parse the string */ errno = 0; val = strtod(sp, &ep); if (sp == ep) { /* parse error */ return -3; } if (errno == ERANGE) { if (val == 0) { /* underflow */ } /* overflow */ assert(val == HUGE_VAL || val == -HUGE_VAL); return -4; } *result_val = val; if (*result_val < min_val) { /* too small */ return -11; } if (max_val > 0.0 && *result_val > max_val) { /* too big */ return -12; } /* eat trailing whitespace */ sp = ep; while (*sp && isspace((int)*sp)) { ++sp; } if ('\0' != *sp) { /* junk at end */ return 1 + sp - dbl_string; } return 0; } /* ** Local variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */