1 /* parsing routines for the counted string class. for generic
2 * informaton see parse.h.
3 *
4 * begun 2005-09-15 rgerhards
5 *
6 * Copyright 2005-2017 Adiscon GmbH.
7 *
8 * This file is part of rsyslog.
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 * -or-
16 * see COPYING.ASL20 in the source distribution
17 *
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
23 */
24 #include "config.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <ctype.h>
31 #include <arpa/inet.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netdb.h>
35 #include "rsyslog.h"
36 #include "net.h" /* struct NetAddr */
37 #include "parse.h"
38 #include "debug.h"
39
40 /* ################################################################# *
41 * private members *
42 * ################################################################# */
43
44
45
46 /* ################################################################# *
47 * public members *
48 * ################################################################# */
49
50
51 /**
52 * Destruct a rsPars object and its associated string.
53 * rgerhards, 2005-09-26
54 */
rsParsDestruct(rsParsObj * pThis)55 rsRetVal rsParsDestruct(rsParsObj *pThis)
56 {
57 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
58
59 if(pThis->pCStr != NULL)
60 rsCStrDestruct(&pThis->pCStr);
61 RSFREEOBJ(pThis);
62 return RS_RET_OK;
63 }
64
65
66 /**
67 * Construct a rsPars object.
68 */
rsParsConstruct(rsParsObj ** ppThis)69 rsRetVal rsParsConstruct(rsParsObj **ppThis)
70 {
71 rsParsObj *pThis;
72
73 assert(ppThis != NULL);
74
75 if((pThis = (rsParsObj*) calloc(1, sizeof(rsParsObj))) == NULL)
76 return RS_RET_OUT_OF_MEMORY;
77
78 rsSETOBJTYPE(pThis, OIDrsPars);
79
80 *ppThis = pThis;
81 return RS_RET_OK;
82 }
83
84 /**
85 * Construct a rsPars object and populate it with a
86 * classical zero-terinated C-String.
87 * rgerhards, 2005-09-27
88 */
rsParsConstructFromSz(rsParsObj ** ppThis,unsigned char * psz)89 rsRetVal rsParsConstructFromSz(rsParsObj **ppThis, unsigned char *psz)
90 {
91 DEFiRet;
92 rsParsObj *pThis;
93 cstr_t *pCS;
94
95 assert(ppThis != NULL);
96 assert(psz != NULL);
97
98 /* create string for parser */
99 CHKiRet(rsCStrConstructFromszStr(&pCS, psz));
100
101 /* create parser */
102 if((iRet = rsParsConstruct(&pThis)) != RS_RET_OK) {
103 rsCStrDestruct(&pCS);
104 FINALIZE;
105 }
106
107 /* assign string to parser */
108 if((iRet = rsParsAssignString(pThis, pCS)) != RS_RET_OK) {
109 rsParsDestruct(pThis);
110 FINALIZE;
111 }
112 *ppThis = pThis;
113
114 finalize_it:
115 RETiRet;
116 }
117
118 /**
119 * Assign the to-be-parsed string.
120 */
rsParsAssignString(rsParsObj * pThis,cstr_t * pCStr)121 rsRetVal rsParsAssignString(rsParsObj *pThis, cstr_t *pCStr)
122 {
123 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
124 rsCHECKVALIDOBJECT(pCStr, OIDrsCStr);
125
126 pThis->pCStr = pCStr;
127 pThis->iCurrPos = 0;
128
129 return RS_RET_OK;
130 }
131
132 /* parse an integer. The parse pointer is advanced to the
133 * position directly after the last digit. If no digit is
134 * found at all, an error is returned and the parse pointer
135 * is NOT advanced.
136 * PORTABILITY WARNING: this function depends on the
137 * continues representation of digits inside the character
138 * set (as in ASCII).
139 * rgerhards 2005-09-27
140 */
parsInt(rsParsObj * pThis,int * pInt)141 rsRetVal parsInt(rsParsObj *pThis, int* pInt)
142 {
143 unsigned char *pC;
144 int iVal;
145
146 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
147 assert(pInt != NULL);
148
149 iVal = 0;
150 pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
151
152 /* order of checks is important, else we might do
153 * mis-addressing! (off by one)
154 */
155 if(pThis->iCurrPos >= rsCStrLen(pThis->pCStr))
156 return RS_RET_NO_MORE_DATA;
157 if(!isdigit((int)*pC))
158 return RS_RET_NO_DIGIT;
159
160 while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && isdigit((int)*pC)) {
161 iVal = iVal * 10 + *pC - '0';
162 ++pThis->iCurrPos;
163 ++pC;
164 }
165
166 *pInt = iVal;
167
168 return RS_RET_OK;
169 }
170
171 /* Skip everything up to a specified character.
172 * Returns with ParsePointer set BEHIND this character.
173 * Returns RS_RET_OK if found, RS_RET_NOT_FOUND if not
174 * found. In that case, the ParsePointer is moved to the
175 * last character of the string.
176 * 2005-09-19 rgerhards
177 */
parsSkipAfterChar(rsParsObj * pThis,char c)178 rsRetVal parsSkipAfterChar(rsParsObj *pThis, char c)
179 {
180 register unsigned char *pC;
181 DEFiRet;
182
183 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
184
185 pC = rsCStrGetBufBeg(pThis->pCStr);
186
187 while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) {
188 if(pC[pThis->iCurrPos] == c)
189 break;
190 ++pThis->iCurrPos;
191 }
192
193 /* delimiter found? */
194 if(pC[pThis->iCurrPos] == c) {
195 if(pThis->iCurrPos+1 < rsCStrLen(pThis->pCStr)) {
196 iRet = RS_RET_OK;
197 pThis->iCurrPos++; /* 'eat' delimiter */
198 } else {
199 iRet = RS_RET_FOUND_AT_STRING_END;
200 }
201 } else {
202 iRet = RS_RET_NOT_FOUND;
203 }
204
205 RETiRet;
206 }
207
208 /* Skip whitespace. Often used to trim parsable entries.
209 * Returns with ParsePointer set to first non-whitespace
210 * character (or at end of string).
211 * If bRequireOne is set to true, at least one whitespace
212 * must exist, else an error is returned.
213 */
parsSkipWhitespace(rsParsObj * pThis)214 rsRetVal parsSkipWhitespace(rsParsObj *pThis)
215 {
216 register unsigned char *pC;
217 int numSkipped;
218 DEFiRet;
219
220
221 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
222
223 pC = rsCStrGetBufBeg(pThis->pCStr);
224
225 numSkipped = 0;
226 while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)) {
227 if(!isspace((int)*(pC+pThis->iCurrPos)))
228 break;
229 ++pThis->iCurrPos;
230 ++numSkipped;
231 }
232
233 RETiRet;
234 }
235
236 /* Parse string up to a delimiter.
237 *
238 * Input:
239 * cDelim - the delimiter. Note that SP within a value always is a delimiter,
240 * so cDelim is actually an *additional* delimiter.
241 * The following two are for whitespace stripping,
242 * 0 means "no", 1 "yes"
243 * - bTrimLeading
244 * - bTrimTrailing
245 * - bConvLower - convert string to lower case?
246 *
247 * Output:
248 * ppCStr Pointer to the parsed string - must be freed by caller!
249 */
parsDelimCStr(rsParsObj * pThis,cstr_t ** ppCStr,char cDelim,int bTrimLeading,int bTrimTrailing,int bConvLower)250 rsRetVal parsDelimCStr(rsParsObj *pThis, cstr_t **ppCStr, char cDelim, int bTrimLeading, int bTrimTrailing,
251 int bConvLower)
252 {
253 DEFiRet;
254 register unsigned char *pC;
255 cstr_t *pCStr = NULL;
256
257 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
258
259 CHKiRet(rsCStrConstruct(&pCStr));
260
261 if(bTrimLeading)
262 parsSkipWhitespace(pThis);
263
264 pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
265
266 while(pThis->iCurrPos < rsCStrLen(pThis->pCStr) && *pC != cDelim) {
267 CHKiRet(cstrAppendChar(pCStr, bConvLower ? tolower(*pC) : *pC));
268 ++pThis->iCurrPos;
269 ++pC;
270 }
271
272 if(pThis->iCurrPos < cstrLen(pThis->pCStr)) { //BUGFIX!!
273 ++pThis->iCurrPos; /* eat delimiter */
274 }
275
276 /* We got the string, now take it and see if we need to
277 * remove anything at its end.
278 */
279 cstrFinalize(pCStr);
280
281 if(bTrimTrailing) {
282 cstrTrimTrailingWhiteSpace(pCStr);
283 }
284
285 /* done! */
286 *ppCStr = pCStr;
287
288 finalize_it:
289 if(iRet != RS_RET_OK) {
290 if(pCStr != NULL)
291 rsCStrDestruct(&pCStr);
292 }
293
294 RETiRet;
295 }
296
297 /* Parse a quoted string ("-some-data") from the given position.
298 * Leading whitespace before the first quote is skipped. During
299 * parsing, escape sequences are detected and converted:
300 * \\ - backslash character
301 * \" - quote character
302 * any other value \<somechar> is reserved for future use.
303 *
304 * After return, the parse pointer is paced after the trailing
305 * quote.
306 *
307 * Output:
308 * ppCStr Pointer to the parsed string - must be freed by caller and
309 * does NOT include the quotes.
310 * rgerhards, 2005-09-19
311 */
parsQuotedCStr(rsParsObj * pThis,cstr_t ** ppCStr)312 rsRetVal parsQuotedCStr(rsParsObj *pThis, cstr_t **ppCStr)
313 {
314 register unsigned char *pC;
315 cstr_t *pCStr = NULL;
316 DEFiRet;
317
318 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
319
320 CHKiRet(parsSkipAfterChar(pThis, '"'));
321 pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
322
323 /* OK, we most probably can obtain a value... */
324 CHKiRet(cstrConstruct(&pCStr));
325
326 while(pThis->iCurrPos < cstrLen(pThis->pCStr)) {
327 if(*pC == '"') {
328 break; /* we are done! */
329 } else if(*pC == '\\') {
330 ++pThis->iCurrPos;
331 ++pC;
332 if(pThis->iCurrPos < cstrLen(pThis->pCStr)) {
333 /* in this case, we copy the escaped character
334 * to the output buffer (but do not rely on this,
335 * we might later introduce other things, like \007!
336 */
337 CHKiRet(cstrAppendChar(pCStr, *pC));
338 }
339 } else { /* regular character */
340 CHKiRet(cstrAppendChar(pCStr, *pC));
341 }
342 ++pThis->iCurrPos;
343 ++pC;
344 }
345
346 if(*pC == '"') {
347 ++pThis->iCurrPos; /* 'eat' trailing quote */
348 } else {
349 /* error - improperly quoted string! */
350 cstrDestruct(&pCStr);
351 ABORT_FINALIZE(RS_RET_MISSING_TRAIL_QUOTE);
352 }
353
354 cstrFinalize(pCStr);
355
356 /* done! */
357 *ppCStr = pCStr;
358
359 finalize_it:
360 if(iRet != RS_RET_OK) {
361 if(pCStr != NULL)
362 cstrDestruct(&pCStr);
363 }
364
365 RETiRet;
366 }
367
368 /*
369 * Parsing routine for IPv4, IPv6 and domain name wildcards.
370 *
371 * Parses string in the format <addr>[/bits] where
372 * addr can be a IPv4 address (e.g.: 127.0.0.1), IPv6 address (e.g.: [::1]),
373 * full hostname (e.g.: localhost.localdomain) or hostname wildcard
374 * (e.g.: *.localdomain).
375 */
376 #ifdef SYSLOG_INET
parsAddrWithBits(rsParsObj * pThis,struct NetAddr ** pIP,int * pBits)377 rsRetVal parsAddrWithBits(rsParsObj *pThis, struct NetAddr **pIP, int *pBits)
378 {
379 register uchar *pC;
380 uchar *pszIP = NULL;
381 uchar *pszTmp;
382 struct addrinfo hints, *res = NULL;
383 cstr_t *pCStr;
384 DEFiRet;
385
386 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
387 assert(pIP != NULL);
388 assert(pBits != NULL);
389
390 CHKiRet(cstrConstruct(&pCStr));
391
392 parsSkipWhitespace(pThis);
393 pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
394
395 /* we parse everything until either '/', ',' or
396 * whitespace. Validity will be checked down below.
397 */
398 while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)
399 && *pC != '/' && *pC != ',' && !isspace((int)*pC)) {
400 if((iRet = cstrAppendChar(pCStr, *pC)) != RS_RET_OK) {
401 cstrDestruct (&pCStr);
402 FINALIZE;
403 }
404 ++pThis->iCurrPos;
405 ++pC;
406 }
407
408 cstrFinalize(pCStr);
409
410 /* now we have the string and must check/convert it to
411 * an NetAddr structure.
412 */
413 CHKiRet(cstrConvSzStrAndDestruct(&pCStr, &pszIP, 0));
414
415 if((*pIP = calloc(1, sizeof(struct NetAddr))) == NULL)
416 ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
417
418 if (*((char*)pszIP) == '[') {
419 pszTmp = (uchar*)strchr ((char*)pszIP, ']');
420 if (pszTmp == NULL) {
421 free (*pIP);
422 ABORT_FINALIZE(RS_RET_INVALID_IP);
423 }
424 *pszTmp = '\0';
425
426 memset (&hints, 0, sizeof (struct addrinfo));
427 hints.ai_family = AF_INET6;
428 hints.ai_flags = AI_NUMERICHOST;
429
430 switch(getaddrinfo ((char*)pszIP+1, NULL, &hints, &res)) {
431 case 0:
432 (*pIP)->addr.NetAddr = malloc (res->ai_addrlen);
433 memcpy ((*pIP)->addr.NetAddr, res->ai_addr, res->ai_addrlen);
434 freeaddrinfo (res);
435 break;
436 case EAI_NONAME:
437 /* The "address" is not an IP prefix but a wildcard */
438 F_SET((*pIP)->flags, ADDR_NAME|ADDR_PRI6);
439 (*pIP)->addr.HostWildcard = strdup ((const char*)pszIP+1);
440 break;
441 default:
442 free (*pIP);
443 ABORT_FINALIZE(RS_RET_ERR);
444 }
445
446 if(*pC == '/') {
447 /* mask bits follow, let's parse them! */
448 ++pThis->iCurrPos; /* eat slash */
449 if((iRet = parsInt(pThis, pBits)) != RS_RET_OK) {
450 free((*pIP)->addr.NetAddr);
451 free((*pIP)->addr.HostWildcard);
452 free (*pIP);
453 FINALIZE;
454 }
455 /* we need to refresh pointer (changed by parsInt()) */
456 pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
457 } else {
458 /* no slash, so we assume a single host (/128) */
459 *pBits = 128;
460 }
461 } else { /* now parse IPv4 */
462 memset (&hints, 0, sizeof (struct addrinfo));
463 hints.ai_family = AF_INET;
464 hints.ai_flags = AI_NUMERICHOST;
465
466 switch(getaddrinfo ((char*)pszIP, NULL, &hints, &res)) {
467 case 0:
468 (*pIP)->addr.NetAddr = malloc (res->ai_addrlen);
469 memcpy ((*pIP)->addr.NetAddr, res->ai_addr, res->ai_addrlen);
470 freeaddrinfo (res);
471 break;
472 case EAI_NONAME:
473 /* The "address" is not an IP prefix but a wildcard */
474 F_SET((*pIP)->flags, ADDR_NAME);
475 (*pIP)->addr.HostWildcard = strdup ((const char*)pszIP);
476 break;
477 default:
478 free (*pIP);
479 ABORT_FINALIZE(RS_RET_ERR);
480 }
481
482 if(*pC == '/') {
483 /* mask bits follow, let's parse them! */
484 ++pThis->iCurrPos; /* eat slash */
485 if((iRet = parsInt(pThis, pBits)) != RS_RET_OK) {
486 free((*pIP)->addr.NetAddr);
487 free((*pIP)->addr.HostWildcard);
488 free (*pIP);
489 FINALIZE;
490 }
491 /* we need to refresh pointer (changed by parsInt()) */
492 pC = rsCStrGetBufBeg(pThis->pCStr) + pThis->iCurrPos;
493 } else {
494 /* no slash, so we assume a single host (/32) */
495 *pBits = 32;
496 }
497 }
498
499 /* skip to next processable character */
500 while(pThis->iCurrPos < rsCStrLen(pThis->pCStr)
501 && (*pC == ',' || isspace((int)*pC))) {
502 ++pThis->iCurrPos;
503 ++pC;
504 }
505
506 iRet = RS_RET_OK;
507
508 finalize_it:
509 free(pszIP);
510 RETiRet;
511 }
512 #endif /* #ifdef SYSLOG_INET */
513
514
515 /* tell if the parsepointer is at the end of the
516 * to-be-parsed string. Returns 1, if so, 0
517 * otherwise. rgerhards, 2005-09-27
518 */
parsIsAtEndOfParseString(rsParsObj * pThis)519 int parsIsAtEndOfParseString(rsParsObj *pThis)
520 {
521 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
522
523 return (pThis->iCurrPos < rsCStrLen(pThis->pCStr)) ? 0 : 1;
524 }
525
526
527 /* return the position of the parse pointer
528 */
rsParsGetParsePointer(rsParsObj * pThis)529 int rsParsGetParsePointer(rsParsObj *pThis)
530 {
531 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
532
533 if(pThis->iCurrPos < rsCStrLen(pThis->pCStr))
534 return pThis->iCurrPos;
535 else
536 return rsCStrLen(pThis->pCStr) - 1;
537 }
538
539 /* peek at the character at the parse pointer
540 * the caller must ensure that the parse pointer is not
541 * at the end of the parse buffer (e.g. by first calling
542 * parsIsAtEndOfParseString).
543 * rgerhards, 2005-09-27
544 */
parsPeekAtCharAtParsPtr(rsParsObj * pThis)545 char parsPeekAtCharAtParsPtr(rsParsObj *pThis)
546 {
547 rsCHECKVALIDOBJECT(pThis, OIDrsPars);
548 assert(pThis->iCurrPos < rsCStrLen(pThis->pCStr));
549
550 return(*(pThis->pCStr->pBuf + pThis->iCurrPos));
551 }
552
553 /* return the current position inside the parse object.
554 * rgerhards, 2007-07-04
555 */
parsGetCurrentPosition(rsParsObj * pThis)556 int parsGetCurrentPosition(rsParsObj *pThis)
557 {
558 return pThis->iCurrPos;
559 }
560
561
562 /*
563 * Local variables:
564 * c-indent-level: 8
565 * c-basic-offset: 8
566 * tab-width: 8
567 * End:
568 * vi:set ai:
569 */
570