/* * Copyright (c) 2018, Harshvardhan Shrivastava * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of the original author; nor the names of any contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #ifndef _AIX #include #endif #include #include #ifdef USE_HASH_XXHASH # include #endif #include "rsyslog.h" #include "parserif.h" #include "module-template.h" #include "rainerscript.h" MODULE_TYPE_FUNCTION MODULE_TYPE_NOKEEP DEF_FMOD_STATIC_DATA typedef uint64_t hash_t; typedef uint32_t seed_t; typedef struct hash_context_s hash_context_t; typedef hash_t (*hash_impl)(const void*, size_t, seed_t); typedef rsRetVal (*hash_wrapper_2)(struct svar *__restrict__ const , struct svar *__restrict__ const, hash_context_t*, hash_t*); typedef rsRetVal (*hash_wrapper_3)(struct svar *__restrict__ const, struct svar *__restrict__ const , struct svar *__restrict__ const, hash_context_t*, hash_t*); struct hash_context_s { hash_impl hashXX; hash_wrapper_2 hash_wrapper_1_2; hash_wrapper_3 hash_wrapper_2_3; }; /* * Fowler–Noll–Vo hash 32 bit * http://www.isthe.com/chongo/src/fnv/hash_32.c */ #if defined(__clang__) #pragma GCC diagnostic ignored "-Wunknown-attributes" #endif static hash_t #if defined(__clang__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif fnv_32(const void* input, size_t len, seed_t seed) { unsigned char *bp = (unsigned char *)input; /* start of buffer */ /* * FNV-1 hash each octet in the buffer */ size_t i; for (i = 0; i < len; i++) { /* multiply by the 32 bit FNV magic prime mod 2^32 */ seed += (seed<<1) + (seed<<4) + (seed<<7) + (seed<<8) + (seed<<24); /* xor the bottom with the current octet */ seed ^= (seed_t)*bp++; } /* return our new hash value */ return seed; } /* * Modified Bernstein * http://www.eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #if defined(__clang__) #pragma GCC diagnostic ignored "-Wunknown-attributes" #endif static hash_t #if defined(__clang__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif djb_hash(const void* input, size_t len, seed_t seed) { const char *p = input; hash_t hash = 5381; size_t i; for (i = 0; i < len; i++) { hash = 33 * hash ^ p[i]; } return hash + seed; } /*Get 32 bit hash for input*/ static hash_t hash32(const void* input, size_t len, seed_t seed) { hash_t xhash = 0; #ifdef USE_HASH_XXHASH xhash = XXH32(input, len, seed); #else xhash = fnv_32(input, len, seed); #endif return xhash; } /*Get 64 bit hash for input*/ static hash_t hash64(const void* input, size_t len, seed_t seed) { hash_t xhash = 0; #ifdef USE_HASH_XXHASH xhash = XXH64(input, len, seed); #else xhash = djb_hash(input, len, seed); #endif return xhash; } static rsRetVal hash_wrapper2(struct svar *__restrict__ const sourceVal , struct svar *__restrict__ const seedVal, hash_context_t* hcontext, hash_t* xhash) { DEFiRet; int freeHashStr = 0, success = 0; char *hashStr = NULL; seed_t seed = 0; if(seedVal) { seed = var2Number(seedVal, &success); if (!success) { parser_warnmsg("fmhash: hashXX(string, seed) didn't get a valid 'seed' limit" ", defaulting hash value to 0"); ABORT_FINALIZE(RS_RET_PARAM_ERROR); } } hashStr = (char*)var2CString(sourceVal, &freeHashStr); size_t len = strlen(hashStr); (*xhash) = hcontext->hashXX(hashStr, len, seed); DBGPRINTF("fmhash: hashXX generated hash %" PRIu64 " for string(%.*s)" , (*xhash), (int)len, hashStr); finalize_it: if (freeHashStr) { free(hashStr); } RETiRet; } static rsRetVal hash_wrapper3(struct svar *__restrict__ const sourceVal, struct svar *__restrict__ const modVal , struct svar *__restrict__ const seedVal, hash_context_t* hcontext, hash_t* xhash) { DEFiRet; int success = 0; hash_t mod = var2Number(modVal, &success); if (! success) { parser_warnmsg("fmhash: hashXXmod(string, mod)/hash64mod(string, mod, seed) didn't" " get a valid 'mod' limit, defaulting hash value to 0"); ABORT_FINALIZE(RS_RET_PARAM_ERROR); } if(mod == 0) { parser_warnmsg("fmhash: hashXXmod(string, mod)/hash64mod(string, mod, seed) invalid" ", 'mod' is zero, , defaulting hash value to 0"); ABORT_FINALIZE(RS_RET_PARAM_ERROR); } CHKiRet((hcontext->hash_wrapper_1_2(sourceVal, seedVal, hcontext, xhash))); if(mod != 0) { (*xhash) = (*xhash) % mod; } DBGPRINTF("fmhash: hashXXmod generated hash-mod %" PRIu64 ".", (*xhash)); finalize_it: RETiRet; } static void init_hash32_context(hash_context_t* hash32_context) { hash32_context->hashXX = hash32; hash32_context->hash_wrapper_1_2 = hash_wrapper2; hash32_context->hash_wrapper_2_3 = hash_wrapper3; }; static void init_hash64_context(hash_context_t* hash64_context) { hash64_context->hashXX = hash64; hash64_context->hash_wrapper_1_2 = hash_wrapper2; hash64_context->hash_wrapper_2_3 = hash_wrapper3; }; static void ATTR_NONNULL() fmHashXX(struct cnffunc *__restrict__ const func, struct svar *__restrict__ const ret, void *__restrict__ const usrptr, wti_t *__restrict__ const pWti) { DEFiRet; struct svar hashStrVal; struct svar seedVal; hash_context_t* hcontext = NULL; hash_t xhash = 0; cnfexprEval(func->expr[0], &hashStrVal, usrptr, pWti); if(func->nParams == 2) cnfexprEval(func->expr[1], &seedVal, usrptr, pWti); ret->d.n = 0; ret->datatype = 'N'; hcontext = (hash_context_t*) func->funcdata; CHKiRet((hcontext->hash_wrapper_1_2(&hashStrVal , (func->nParams == 2 ? &seedVal : NULL) , hcontext, &xhash))); ret->d.n = xhash; finalize_it: varFreeMembers(&hashStrVal); if(func->nParams == 2) varFreeMembers(&seedVal); } static void ATTR_NONNULL() fmHashXXmod(struct cnffunc *__restrict__ const func, struct svar *__restrict__ const ret, void *__restrict__ const usrptr, wti_t *__restrict__ const pWti) { DEFiRet; struct svar hashStrVal; struct svar modVal; struct svar seedVal; hash_context_t* hcontext = NULL; hash_t xhash = 0; cnfexprEval(func->expr[0], &hashStrVal, usrptr, pWti); cnfexprEval(func->expr[1], &modVal, usrptr, pWti); if(func->nParams == 3) cnfexprEval(func->expr[2], &seedVal, usrptr, pWti); ret->d.n = 0; ret->datatype = 'N'; hcontext = (hash_context_t*) func->funcdata; CHKiRet((hcontext->hash_wrapper_2_3(&hashStrVal , &modVal, func->nParams > 2 ? &seedVal : NULL , hcontext, &xhash))); ret->d.n = xhash; finalize_it: varFreeMembers(&hashStrVal); varFreeMembers(&modVal); if(func->nParams == 3) varFreeMembers(&seedVal); } static inline sbool check_param_count_hash(unsigned short nParams) { return (nParams != 1 && nParams != 2); } static inline sbool check_param_count_hashmod(unsigned short nParams) { return (nParams != 2 && nParams != 3); } static rsRetVal ATTR_NONNULL(1) init_fmHash64(struct cnffunc *const func) { DEFiRet; hash_context_t *hash_context = NULL; if(check_param_count_hash(func->nParams)) { parser_errmsg("fmhash: hash64(string) / hash64(string, seed)" " insufficient params.\n"); ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); } func->destructable_funcdata = 1; CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); init_hash64_context(hash_context); func->funcdata = (void*)hash_context; finalize_it: RETiRet; } static rsRetVal ATTR_NONNULL(1) init_fmHash64mod(struct cnffunc *const func) { DEFiRet; hash_context_t *hash_context = NULL; if(check_param_count_hashmod(func->nParams)) { parser_errmsg("fmhash: hash64mod(string, mod)/hash64mod(string, mod, seed)" " insufficient params.\n"); ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); } func->destructable_funcdata = 1; CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); init_hash64_context(hash_context); func->funcdata = (void*)hash_context; finalize_it: RETiRet; } static rsRetVal ATTR_NONNULL(1) init_fmHash32(struct cnffunc *const func) { DEFiRet; hash_context_t *hash_context = NULL; if(check_param_count_hash(func->nParams)) { parser_errmsg("fmhash: hash32(string) / hash32(string, seed)" " insufficient params.\n"); ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); } func->destructable_funcdata = 1; CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); init_hash32_context(hash_context); func->funcdata = (void*)hash_context; finalize_it: RETiRet; } static rsRetVal ATTR_NONNULL(1) init_fmHash32mod(struct cnffunc *const func) { DEFiRet; hash_context_t *hash_context = NULL; if(check_param_count_hashmod(func->nParams)) { parser_errmsg("fmhash: hash32mod(string, mod)/hash32mod(string, mod, seed)" " insufficient params.\n"); ABORT_FINALIZE(RS_RET_INVLD_NBR_ARGUMENTS); } func->destructable_funcdata = 1; CHKmalloc(hash_context = calloc(1, sizeof(hash_context_t))); init_hash32_context(hash_context); func->funcdata = (void*)hash_context; finalize_it: RETiRet; } static struct scriptFunct functions[] = { {"hash64", 1, 2, fmHashXX, init_fmHash64, NULL}, {"hash64mod", 2, 3, fmHashXXmod, init_fmHash64mod, NULL}, {"hash32", 1, 2, fmHashXX, init_fmHash32, NULL}, {"hash32mod", 2, 3, fmHashXXmod, init_fmHash32mod, NULL}, {NULL, 0, 0, NULL, NULL, NULL} //last element to check end of array }; BEGINgetFunctArray CODESTARTgetFunctArray dbgprintf("Hash: fmhhash\n"); *version = 1; *functArray = functions; ENDgetFunctArray BEGINmodExit CODESTARTmodExit ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_FMOD_QUERIES ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr dbgprintf("rsyslog fmhash init called, compiled with version %s\n", VERSION); ENDmodInit