mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-13 08:20:45 +01:00
This commit applies the new canonical formatting style using `clang-format` with custom settings (notably 4-space indentation), as part of our shift toward automated formatting normalization. ⚠️ No functional changes are included — only whitespace and layout modifications as produced by `clang-format`. This change is part of the formatting modernization strategy discussed in: https://github.com/rsyslog/rsyslog/issues/5747 Key context: - Formatting is now treated as a disposable view, normalized via tooling. - The `.clang-format` file defines the canonical style. - A fixup script (`devtools/format-code.sh`) handles remaining edge cases. - Formatting commits are added to `.git-blame-ignore-revs` to reduce noise. - Developers remain free to format code however they prefer locally.
536 lines
15 KiB
C
536 lines
15 KiB
C
/* 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).
|
|
*/
|
|
rsRetVal parsSkipWhitespace(rsParsObj *pThis) {
|
|
register unsigned char *pC;
|
|
DEFiRet;
|
|
|
|
|
|
rsCHECKVALIDOBJECT(pThis, OIDrsPars);
|
|
|
|
pC = rsCStrGetBufBeg(pThis->pCStr);
|
|
|
|
while (pThis->iCurrPos < rsCStrLen(pThis->pCStr)) {
|
|
if (!isspace((int)*(pC + pThis->iCurrPos))) break;
|
|
++pThis->iCurrPos;
|
|
}
|
|
|
|
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:
|
|
*/
|