/* 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; DEFiRet; rsCHECKVALIDOBJECT(pThis, OIDrsPars); if((iRet = parsSkipAfterChar(pThis, '"')) != RS_RET_OK) FINALIZE; pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; /* OK, we most probably can obtain a value... */ CHKiRet(rsCStrConstruct(&pCStr)); while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) { if(*pC == '"') { break; /* we are done! */ } else if(*pC == '\\') { ++pThis->iCurrPos; ++pC; if(pThis->iCurrPos < rsCStrLen(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! */ if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { rsCStrDestruct(&pCStr); FINALIZE; } } } else { /* regular character */ if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { rsCStrDestruct (&pCStr); FINALIZE; } } ++pThis->iCurrPos; ++pC; } if(*pC == '"') { ++pThis->iCurrPos; /* 'eat' trailing quote */ } else { /* error - improperly quoted string! */ rsCStrDestruct (&pCStr); ABORT_FINALIZE(RS_RET_MISSING_TRAIL_QUOTE); } /* We got the string, let's finish it... */ if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) { rsCStrDestruct (&pCStr); FINALIZE; } /* done! */ *ppCStr = pCStr; finalize_it: RETiRet; }
/* this function can be used to obtain all stats lines. In this case, * a callback must be provided. This module than iterates over all objects and * submits each stats line to the callback. The callback has two parameters: * the first one is a caller-provided void*, the second one the cstr_t with the * line. If the callback reports an error, processing is stopped. */ static rsRetVal getAllStatsLines(rsRetVal(*cb)(void*, const char*), void *const usrptr, statsFmtType_t fmt, const int8_t bResetCtrs) { statsobj_t *o; cstr_t *cstr = NULL; DEFiRet; for(o = objRoot ; o != NULL ; o = o->next) { switch(fmt) { case statsFmt_Legacy: CHKiRet(getStatsLine(o, &cstr, bResetCtrs)); break; case statsFmt_CEE: case statsFmt_JSON: case statsFmt_JSON_ES: CHKiRet(getStatsLineCEE(o, &cstr, fmt, bResetCtrs)); break; } CHKiRet(cb(usrptr, (const char*)cstrGetSzStrNoNULL(cstr))); rsCStrDestruct(&cstr); if (o->read_notifier != NULL) { o->read_notifier(o, o->read_notifier_ctx); } } getSenderStats(cb, usrptr, fmt, bResetCtrs); finalize_it: if(cstr != NULL) { rsCStrDestruct(&cstr); } RETiRet; }
/* Parse string up to a delimiter. * * Input: * cDelim - the delimiter * The following two are for whitespace stripping, * 0 means "no", 1 "yes" * - bTrimLeading * - bTrimTrailing * * 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) { DEFiRet; register unsigned char *pC; cstr_t *pCStr; rsCHECKVALIDOBJECT(pThis, OIDrsPars); CHKiRet(rsCStrConstruct(&pCStr)); if(bTrimLeading) parsSkipWhitespace(pThis); pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos; while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && *pC != cDelim) { if((iRet = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { rsCStrDestruct(&pCStr); FINALIZE; } ++pThis->iCurrPos; ++pC; } if(*pC == cDelim) { ++pThis->iCurrPos; /* eat delimiter */ } /* We got the string, now take it and see if we need to * remove anything at its end. */ if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) { rsCStrDestruct (&pCStr); FINALIZE; } if(bTrimTrailing) { if((iRet = rsCStrTrimTrailingWhiteSpace(pCStr)) != RS_RET_OK) { rsCStrDestruct (&pCStr); FINALIZE; } } /* done! */ *ppCStr = pCStr; finalize_it: RETiRet; }
/* parse a syslog name from the string. This is the generic code that is * called by the facility/severity functions. Note that we do not check the * validity of numerical values, something that should probably change over * time (TODO). -- rgerhards, 2008-02-14 */ static rsRetVal doSyslogName(uchar **pp, rsRetVal (*pSetHdlr)(void*, int), void *pVal, syslogName_t *pNameTable) { DEFiRet; cstr_t *pStrB; int iNewVal; ASSERT(pp != NULL); ASSERT(*pp != NULL); CHKiRet(getWord(pp, &pStrB)); /* get word */ iNewVal = decodeSyslogName(cstrGetSzStr(pStrB), pNameTable); if(pSetHdlr == NULL) { /* we should set value directly to var */ *((int*)pVal) = iNewVal; /* set new one */ } else { /* we set value via a set function */ CHKiRet(pSetHdlr(pVal, iNewVal)); } skipWhiteSpace(pp); /* skip over any whitespace */ finalize_it: if(pStrB != NULL) rsCStrDestruct(&pStrB); RETiRet; }
/** * 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; }
/* this function can be used to obtain all stats lines. In this case, * a callback must be provided. This module than iterates over all objects and * submits each stats line to the callback. The callback has two parameters: * the first one is a caller-provided void*, the second one the cstr_t with the * line. If the callback reports an error, processing is stopped. */ static rsRetVal getAllStatsLines(rsRetVal(*cb)(void*, cstr_t*), void *usrptr, statsFmtType_t fmt, int8_t bResetCtrs) { statsobj_t *o; cstr_t *cstr; DEFiRet; for(o = objRoot ; o != NULL ; o = o->next) { switch(fmt) { case statsFmt_Legacy: CHKiRet(getStatsLine(o, &cstr, bResetCtrs)); break; case statsFmt_CEE: CHKiRet(getStatsLineCEE(o, &cstr, 1, bResetCtrs)); break; case statsFmt_JSON: CHKiRet(getStatsLineCEE(o, &cstr, 0, bResetCtrs)); break; } CHKiRet(cb(usrptr, cstr)); rsCStrDestruct(&cstr); } finalize_it: RETiRet; }
/* Parse and a word config line option. A word is a consequtive * sequence of non-whitespace characters. pVal must be * a pointer to a string which is to receive the option * value. The returned string must be freed by the caller. * rgerhards, 2007-09-07 * To facilitate multiple instances of the same command line * directive, doGetWord() now checks if pVal is already a * non-NULL pointer. If so, we assume it was created by a previous * incarnation and is automatically freed. This happens only when * no custom handler is defined. If it is, the customer handler * must do the cleanup. I have checked and this was al also memory * leak with some code. Obviously, not a large one. -- rgerhards, 2007-12-20 * Just to clarify: if pVal is parsed to a custom handler, this handler * is responsible for freeing pVal. -- rgerhards, 2008-03-20 */ static rsRetVal doGetWord(uchar **pp, rsRetVal (*pSetHdlr)(void*, uchar*), void *pVal) { DEFiRet; cstr_t *pStrB; uchar *pNewVal; ASSERT(pp != NULL); ASSERT(*pp != NULL); CHKiRet(getWord(pp, &pStrB)); CHKiRet(rsCStrConvSzStrAndDestruct(pStrB, &pNewVal, 0)); pStrB = NULL; /* we got the word, now set it */ if(pSetHdlr == NULL) { /* we should set value directly to var */ if(*((uchar**)pVal) != NULL) free(*((uchar**)pVal)); /* free previous entry */ *((uchar**)pVal) = pNewVal; /* set new one */ } else { /* we set value via a set function */ CHKiRet(pSetHdlr(pVal, pNewVal)); } skipWhiteSpace(pp); /* skip over any whitespace */ finalize_it: if(iRet != RS_RET_OK) { if(pStrB != NULL) rsCStrDestruct(&pStrB); } RETiRet; }
/** * 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; }
/* append a printf-style formated string */ rsRetVal rsCStrAppendStrf(cstr_t *pThis, uchar *fmt, ...) { DEFiRet; va_list ap; cstr_t *pStr = NULL; va_start(ap, fmt); iRet = rsCStrConstructFromszStrv(&pStr, fmt, ap); va_end(ap); CHKiRet(iRet); iRet = cstrAppendCStr(pThis, pStr); rsCStrDestruct(&pStr); finalize_it: 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. */ CHKiRet(cstrFinalize(pCStr)); if(bTrimTrailing) { CHKiRet(cstrTrimTrailingWhiteSpace(pCStr)); } /* done! */ *ppCStr = pCStr; finalize_it: if(iRet != RS_RET_OK) { if(pCStr != NULL) rsCStrDestruct(&pCStr); } RETiRet; }
rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits) { register uchar *pC; uchar *pszIP; uchar *pszTmp; struct addrinfo hints, *res = NULL; cstr_t *pCStr; DEFiRet; rsCHECKVALIDOBJECT(pThis, OIDrsPars); assert(pIP != NULL); assert(pBits != NULL); CHKiRet(rsCStrConstruct(&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 = rsCStrAppendChar(pCStr, *pC)) != RS_RET_OK) { rsCStrDestruct (&pCStr); FINALIZE; } ++pThis->iCurrPos; ++pC; } /* We got the string, let's finish it... */ if((iRet = rsCStrFinish(pCStr)) != RS_RET_OK) { rsCStrDestruct (&pCStr); FINALIZE; } /* now we have the string and must check/convert it to * an NetAddr structure. */ CHKiRet(rsCStrConvSzStrAndDestruct(pCStr, &pszIP, 0)); *pIP = calloc(1, sizeof(struct NetAddr)); if (*((char*)pszIP) == '[') { pszTmp = (uchar*)strchr ((char*)pszIP, ']'); if (pszTmp == NULL) { free (pszIP); ABORT_FINALIZE(RS_RET_INVALID_IP); } *pszTmp = '\0'; memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_INET6; # ifdef AI_ADDRCONFIG hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST; # else hints.ai_flags = AI_NUMERICHOST; # endif 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: F_SET((*pIP)->flags, ADDR_NAME|ADDR_PRI6); (*pIP)->addr.HostWildcard = strdup ((const char*)pszIP+1); break; default: free (pszIP); 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 (pszIP); 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; # ifdef AI_ADDRCONFIG hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICHOST; # else hints.ai_flags = AI_NUMERICHOST; # endif 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: F_SET((*pIP)->flags, ADDR_NAME); (*pIP)->addr.HostWildcard = strdup ((const char*)pszIP); break; default: free (pszIP); 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 (pszIP); 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; } } free(pszIP); /* no longer needed */ /* skip to next processable character */ while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && (*pC == ',' || isspace((int)*pC))) { ++pThis->iCurrPos; ++pC; } iRet = RS_RET_OK; finalize_it: RETiRet; }