diff options
Diffstat (limited to '')
-rw-r--r-- | parse.c | 567 |
1 files changed, 567 insertions, 0 deletions
@@ -0,0 +1,567 @@ +/* parsing routines for the counted string class. for generic + * informaton see parse.h. + * + * begun 2005-09-15 rgerhards + * + * Copyright 2005-2017 Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include "rsyslog.h" +#include "net.h" /* struct NetAddr */ +#include "parse.h" +#include "debug.h" + +/* ################################################################# * + * private members * + * ################################################################# */ + + + +/* ################################################################# * + * public members * + * ################################################################# */ + + +/** + * Destruct a rsPars object and its associated string. + * rgerhards, 2005-09-26 + */ +rsRetVal rsParsDestruct(rsParsObj *pThis) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + if(pThis->pCStr != NULL) + rsCStrDestruct(&pThis->pCStr); + RSFREEOBJ(pThis); + return RS_RET_OK; +} + + +/** + * Construct a rsPars object. + */ +rsRetVal rsParsConstruct(rsParsObj **ppThis) +{ + rsParsObj *pThis; + + assert(ppThis != NULL); + + if((pThis = (rsParsObj*) calloc(1, sizeof(rsParsObj))) == NULL) + return RS_RET_OUT_OF_MEMORY; + + rsSETOBJTYPE(pThis, OIDrsPars); + + *ppThis = pThis; + return RS_RET_OK; +} + +/** + * Construct a rsPars object and populate it with a + * classical zero-terinated C-String. + * rgerhards, 2005-09-27 + */ +rsRetVal rsParsConstructFromSz(rsParsObj **ppThis, unsigned char *psz) +{ + DEFiRet; + rsParsObj *pThis; + cstr_t *pCS; + + assert(ppThis != NULL); + assert(psz != NULL); + + /* create string for parser */ + CHKiRet(rsCStrConstructFromszStr(&pCS, psz)); + + /* create parser */ + if((iRet = rsParsConstruct(&pThis)) != RS_RET_OK) { + rsCStrDestruct(&pCS); + FINALIZE; + } + + /* assign string to parser */ + if((iRet = rsParsAssignString(pThis, pCS)) != RS_RET_OK) { + rsParsDestruct(pThis); + FINALIZE; + } + *ppThis = pThis; + +finalize_it: + RETiRet; +} + +/** + * Assign the to-be-parsed string. + */ +rsRetVal rsParsAssignString(rsParsObj *pThis, cstr_t *pCStr) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + rsCHECKVALIDOBJECT(pCStr, OIDrsCStr); + + pThis->pCStr = pCStr; + pThis->iCurrPos = 0; + + return RS_RET_OK; +} + +/* parse an integer. The parse pointer is advanced to the + * position directly after the last digit. If no digit is + * found at all, an error is returned and the parse pointer + * is NOT advanced. + * PORTABILITY WARNING: this function depends on the + * continues representation of digits inside the character + * set (as in ASCII). + * rgerhards 2005-09-27 + */ +rsRetVal parsInt(rsParsObj *pThis, int* pInt) +{ + unsigned char *pC; + int iVal; + + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + assert(pInt != NULL); + + iVal = 0; + pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; + + /* order of checks is important, else we might do + * mis-addressing! (off by one) + */ + if(pThis->iCurrPos >= rsCStrLen(pThis->pCStr)) + return RS_RET_NO_MORE_DATA; + if(!isdigit((int)*pC)) + return RS_RET_NO_DIGIT; + + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && isdigit((int)*pC)) { + iVal = iVal * 10 + *pC - '0'; + ++pThis->iCurrPos; + ++pC; + } + + *pInt = iVal; + + return RS_RET_OK; +} + +/* Skip everything up to a specified character. + * Returns with ParsePointer set BEHIND this character. + * Returns RS_RET_OK if found, RS_RET_NOT_FOUND if not + * found. In that case, the ParsePointer is moved to the + * last character of the string. + * 2005-09-19 rgerhards + */ +rsRetVal parsSkipAfterChar(rsParsObj *pThis, char c) +{ + register unsigned char *pC; + DEFiRet; + + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + pC = rsCStrGetBufBeg(pThis->pCStr); + + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) { + if(pC[pThis->iCurrPos] == c) + break; + ++pThis->iCurrPos; + } + + /* delimiter found? */ + if(pC[pThis->iCurrPos] == c) { + if(pThis->iCurrPos+1 < rsCStrLen(pThis->pCStr)) { + iRet = RS_RET_OK; + pThis->iCurrPos++; /* 'eat' delimiter */ + } else { + iRet = RS_RET_FOUND_AT_STRING_END; + } + } else { + iRet = RS_RET_NOT_FOUND; + } + + RETiRet; +} + +/* Skip whitespace. Often used to trim parsable entries. + * Returns with ParsePointer set to first non-whitespace + * character (or at end of string). + * If bRequireOne is set to true, at least one whitespace + * must exist, else an error is returned. + */ +rsRetVal parsSkipWhitespace(rsParsObj *pThis) +{ + register unsigned char *pC; + int numSkipped; + DEFiRet; + + + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + pC = rsCStrGetBufBeg(pThis->pCStr); + + numSkipped = 0; + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) { + if(!isspace((int)*(pC+pThis->iCurrPos))) + break; + ++pThis->iCurrPos; + ++numSkipped; + } + + RETiRet; +} + +/* Parse string up to a delimiter. + * + * Input: + * cDelim - the delimiter. Note that SP within a value always is a delimiter, + * so cDelim is actually an *additional* delimiter. + * The following two are for whitespace stripping, + * 0 means "no", 1 "yes" + * - bTrimLeading + * - bTrimTrailing + * - bConvLower - convert string to lower case? + * + * Output: + * ppCStr Pointer to the parsed string - must be freed by caller! + */ +rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrimLeading, int bTrimTrailing, + int bConvLower) +{ + DEFiRet; + register unsigned char *pC; + cstr_t *pCStr = NULL; + + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + CHKiRet(rsCStrConstruct(&pCStr)); + + if(bTrimLeading) + parsSkipWhitespace(pThis); + + pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; + + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && *pC != cDelim) { + CHKiRet(cstrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC)); + ++pThis->iCurrPos; + ++pC; + } + + if(pThis->iCurrPos < cstrLen(pThis->pCStr)) { //BUGFIX!! + ++pThis->iCurrPos; /* eat delimiter */ + } + + /* We got the string, now take it and see if we need to + * remove anything at its end. + */ + cstrFinalize(pCStr); + + if(bTrimTrailing) { + cstrTrimTrailingWhiteSpace(pCStr); + } + + /* done! */ + *ppCStr = pCStr; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pCStr != NULL) + rsCStrDestruct(&pCStr); + } + + RETiRet; +} + +/* Parse a quoted string ("-some-data") from the given position. + * Leading whitespace before the first quote is skipped. During + * parsing, escape sequences are detected and converted: + * \\ - backslash character + * \" - quote character + * any other value \<somechar> is reserved for future use. + * + * After return, the parse pointer is paced after the trailing + * quote. + * + * Output: + * ppCStr Pointer to the parsed string - must be freed by caller and + * does NOT include the quotes. + * rgerhards, 2005-09-19 + */ +rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr) +{ + register unsigned char *pC; + cstr_t *pCStr = NULL; + DEFiRet; + + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + CHKiRet(parsSkipAfterChar(pThis, '"')); + pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; + + /* OK, we most probably can obtain a value... */ + CHKiRet(cstrConstruct(&pCStr)); + + while(pThis->iCurrPos < cstrLen(pThis->pCStr)) { + if(*pC == '"') { + break; /* we are done! */ + } else if(*pC == '\\') { + ++pThis->iCurrPos; + ++pC; + if(pThis->iCurrPos < cstrLen(pThis->pCStr)) { + /* in this case, we copy the escaped character + * to the output buffer (but do not rely on this, + * we might later introduce other things, like \007! + */ + CHKiRet(cstrAppendChar(pCStr, *pC)); + } + } else { /* regular character */ + CHKiRet(cstrAppendChar(pCStr, *pC)); + } + ++pThis->iCurrPos; + ++pC; + } + + if(*pC == '"') { + ++pThis->iCurrPos; /* 'eat' trailing quote */ + } else { + /* error - improperly quoted string! */ + cstrDestruct(&pCStr); + ABORT_FINALIZE(RS_RET_MISSING_TRAIL_QUOTE); + } + + cstrFinalize(pCStr); + + /* done! */ + *ppCStr = pCStr; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pCStr != NULL) + cstrDestruct(&pCStr); + } + + RETiRet; +} + +/* + * Parsing routine for IPv4, IPv6 and domain name wildcards. + * + * Parses string in the format <addr>[/bits] where + * addr can be a IPv4 address (e.g.: 127.0.0.1), IPv6 address (e.g.: [::1]), + * full hostname (e.g.: localhost.localdomain) or hostname wildcard + * (e.g.: *.localdomain). + */ +rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits) +{ + register uchar *pC; + uchar *pszIP = NULL; + uchar *pszTmp; + struct addrinfo hints, *res = NULL; + cstr_t *pCStr; + DEFiRet; + + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + assert(pIP != NULL); + assert(pBits != NULL); + + CHKiRet(cstrConstruct(&pCStr)); + + parsSkipWhitespace(pThis); + pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; + + /* we parse everything until either '/', ',' or + * whitespace. Validity will be checked down below. + */ + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) + && *pC != '/' && *pC != ',' && !isspace((int)*pC)) { + if((iRet = cstrAppendChar(pCStr, *pC)) != RS_RET_OK) { + cstrDestruct (&pCStr); + FINALIZE; + } + ++pThis->iCurrPos; + ++pC; + } + + cstrFinalize(pCStr); + + /* now we have the string and must check/convert it to + * an NetAddr structure. + */ + CHKiRet(cstrConvSzStrAndDestruct(&pCStr, &pszIP, 0)); + + if((*pIP = calloc(1, sizeof(struct NetAddr))) == NULL) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + + if (*((char*)pszIP) == '[') { + pszTmp = (uchar*)strchr ((char*)pszIP, ']'); + if (pszTmp == NULL) { + free (*pIP); + ABORT_FINALIZE(RS_RET_INVALID_IP); + } + *pszTmp = '\0'; + + memset (&hints, 0, sizeof (struct addrinfo)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + + switch(getaddrinfo ((char*)pszIP+1, NULL, &hints, &res)) { + case 0: + (*pIP)->addr.NetAddr = malloc (res->ai_addrlen); + memcpy ((*pIP)->addr.NetAddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo (res); + break; + case EAI_NONAME: + /* The "address" is not an IP prefix but a wildcard */ + F_SET((*pIP)->flags, ADDR_NAME|ADDR_PRI6); + (*pIP)->addr.HostWildcard = strdup ((const char*)pszIP+1); + break; + default: + free (*pIP); + ABORT_FINALIZE(RS_RET_ERR); + } + + if(*pC == '/') { + /* mask bits follow, let's parse them! */ + ++pThis->iCurrPos; /* eat slash */ + if((iRet = parsInt(pThis, pBits)) != RS_RET_OK) { + free((*pIP)->addr.NetAddr); + free((*pIP)->addr.HostWildcard); + free (*pIP); + FINALIZE; + } + /* we need to refresh pointer (changed by parsInt()) */ + pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; + } else { + /* no slash, so we assume a single host (/128) */ + *pBits = 128; + } + } else { /* now parse IPv4 */ + memset (&hints, 0, sizeof (struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_flags = AI_NUMERICHOST; + + switch(getaddrinfo ((char*)pszIP, NULL, &hints, &res)) { + case 0: + (*pIP)->addr.NetAddr = malloc (res->ai_addrlen); + memcpy ((*pIP)->addr.NetAddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo (res); + break; + case EAI_NONAME: + /* The "address" is not an IP prefix but a wildcard */ + F_SET((*pIP)->flags, ADDR_NAME); + (*pIP)->addr.HostWildcard = strdup ((const char*)pszIP); + break; + default: + free (*pIP); + ABORT_FINALIZE(RS_RET_ERR); + } + + if(*pC == '/') { + /* mask bits follow, let's parse them! */ + ++pThis->iCurrPos; /* eat slash */ + if((iRet = parsInt(pThis, pBits)) != RS_RET_OK) { + free((*pIP)->addr.NetAddr); + free((*pIP)->addr.HostWildcard); + free (*pIP); + FINALIZE; + } + /* we need to refresh pointer (changed by parsInt()) */ + pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; + } else { + /* no slash, so we assume a single host (/32) */ + *pBits = 32; + } + } + + /* skip to next processable character */ + while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) + && (*pC == ',' || isspace((int)*pC))) { + ++pThis->iCurrPos; + ++pC; + } + + iRet = RS_RET_OK; + +finalize_it: + free(pszIP); + RETiRet; +} + + +/* tell if the parsepointer is at the end of the + * to-be-parsed string. Returns 1, if so, 0 + * otherwise. rgerhards, 2005-09-27 + */ +int parsIsAtEndOfParseString(rsParsObj *pThis) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + return (pThis->iCurrPos < rsCStrLen(pThis->pCStr)) ? 0 : 1; +} + + +/* return the position of the parse pointer + */ +int rsParsGetParsePointer(rsParsObj *pThis) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + + if(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) + return pThis->iCurrPos; + else + return rsCStrLen(pThis->pCStr) - 1; +} + +/* peek at the character at the parse pointer + * the caller must ensure that the parse pointer is not + * at the end of the parse buffer (e.g. by first calling + * parsIsAtEndOfParseString). + * rgerhards, 2005-09-27 + */ +char parsPeekAtCharAtParsPtr(rsParsObj *pThis) +{ + rsCHECKVALIDOBJECT(pThis, OIDrsPars); + assert(pThis->iCurrPos < rsCStrLen(pThis->pCStr)); + + return(*(pThis->pCStr->pBuf + pThis->iCurrPos)); +} + +/* return the current position inside the parse object. + * rgerhards, 2007-07-04 + */ +int parsGetCurrentPosition(rsParsObj *pThis) +{ + return pThis->iCurrPos; +} + + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + * vi:set ai: + */ |