summaryrefslogtreecommitdiffstats
path: root/plugins/mmdblookup/mmdblookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/mmdblookup/mmdblookup.c')
-rw-r--r--plugins/mmdblookup/mmdblookup.c517
1 files changed, 517 insertions, 0 deletions
diff --git a/plugins/mmdblookup/mmdblookup.c b/plugins/mmdblookup/mmdblookup.c
new file mode 100644
index 0000000..f9f3c73
--- /dev/null
+++ b/plugins/mmdblookup/mmdblookup.c
@@ -0,0 +1,517 @@
+/* mmdblookup.c
+ * Parse ipaddress field of the message into structured data using
+ * MaxMindDB.
+ *
+ * Copyright 2013 Rao Chenlin.
+ * Copyright 2017 Rainer Gerhards and 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 "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <pthread.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "parserif.h"
+
+#include "maxminddb.h"
+
+#define JSON_IPLOOKUP_NAME "!iplocation"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("mmdblookup")
+
+
+DEF_OMOD_STATIC_DATA
+
+/* config variables */
+typedef struct _instanceData {
+ char *pszKey;
+ char *pszMmdbFile;
+ struct {
+ int nmemb;
+ char **name;
+ char **varname;
+ } fieldList;
+ sbool reloadOnHup;
+} instanceData;
+
+typedef struct wrkrInstanceData {
+ instanceData *pData;
+ MMDB_s mmdb;
+ pthread_mutex_t mmdbMutex;
+ sbool mmdb_is_open;
+} wrkrInstanceData_t;
+
+struct modConfData_s {
+ /* our overall config object */
+ rsconf_t *pConf;
+ const char *container;
+};
+
+/* modConf ptr to use for the current load process */
+static modConfData_t *loadModConf = NULL;
+/* modConf ptr to use for the current exec process */
+static modConfData_t *runModConf = NULL;
+
+
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ { "container", eCmdHdlrGetWord, 0 },
+};
+static struct cnfparamblk modpblk =
+ { CNFPARAMBLK_VERSION,
+ sizeof(modpdescr)/sizeof(struct cnfparamdescr),
+ modpdescr
+ };
+
+/* tables for interfacing with the v6 config system
+ * action (instance) parameters */
+static struct cnfparamdescr actpdescr[] = {
+ { "key", eCmdHdlrGetWord, CNFPARAM_REQUIRED },
+ { "mmdbfile", eCmdHdlrGetWord, CNFPARAM_REQUIRED },
+ { "fields", eCmdHdlrArray, CNFPARAM_REQUIRED },
+ { "reloadonhup", eCmdHdlrBinary, 0 },
+};
+static struct cnfparamblk actpblk = {
+ CNFPARAMBLK_VERSION,
+ sizeof(actpdescr)/sizeof(struct cnfparamdescr),
+ actpdescr
+};
+
+
+/* protype functions */
+void str_split(char **membuf);
+
+int open_mmdb(const char *file, MMDB_s *mmdb);
+void close_mmdb(MMDB_s *mmdb);
+
+
+int open_mmdb(const char *file, MMDB_s *mmdb) {
+ int status = MMDB_open(file, MMDB_MODE_MMAP, mmdb);
+ if (MMDB_SUCCESS != status) {
+ dbgprintf("Can't open %s - %s\n", file, MMDB_strerror(status));
+ if (MMDB_IO_ERROR == status) {
+ dbgprintf(" IO error: %s\n", strerror(errno));
+ }
+ LogError(0, RS_RET_SUSPENDED, "maxminddb error: cannot open database file");
+ return RS_RET_SUSPENDED;
+ }
+
+ return RS_RET_OK;
+}
+
+void close_mmdb(MMDB_s *mmdb) {
+ MMDB_close(mmdb);
+}
+
+static rsRetVal wrkr_reopen_mmdb(wrkrInstanceData_t *pWrkrData) {
+ DEFiRet;
+ pthread_mutex_lock(&pWrkrData->mmdbMutex);
+ LogMsg(0, NO_ERRCODE, LOG_INFO, "mmdblookup: reopening MMDB file");
+ if (pWrkrData->mmdb_is_open) close_mmdb(&pWrkrData->mmdb);
+ pWrkrData->mmdb_is_open = 0;
+ CHKiRet(open_mmdb(pWrkrData->pData->pszMmdbFile, &pWrkrData->mmdb));
+ pWrkrData->mmdb_is_open = 1;
+
+finalize_it:
+ pthread_mutex_unlock(&pWrkrData->mmdbMutex);
+ RETiRet;
+}
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ pModConf->pConf = pConf;
+ENDbeginCnfLoad
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+BEGINcheckCnf
+CODESTARTcheckCnf
+ENDcheckCnf
+
+BEGINactivateCnf
+CODESTARTactivateCnf
+ runModConf = pModConf;
+ENDactivateCnf
+
+BEGINfreeCnf
+CODESTARTfreeCnf
+ free((void*)runModConf->container);
+ENDfreeCnf
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+BEGINcreateWrkrInstance
+CODESTARTcreateWrkrInstance
+ CHKiRet(open_mmdb(pData->pszMmdbFile, &pWrkrData->mmdb));
+ pWrkrData->mmdb_is_open = 1;
+ CHKiConcCtrl(pthread_mutex_init(&pWrkrData->mmdbMutex, NULL));
+finalize_it:
+ENDcreateWrkrInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ if(pData->fieldList.name != NULL) {
+ for(int i = 0 ; i < pData->fieldList.nmemb ; ++i) {
+ free(pData->fieldList.name[i]);
+ free(pData->fieldList.varname[i]);
+ }
+ free(pData->fieldList.name);
+ free(pData->fieldList.varname);
+ }
+ free(pData->pszKey);
+ free(pData->pszMmdbFile);
+ENDfreeInstance
+
+
+BEGINfreeWrkrInstance
+CODESTARTfreeWrkrInstance
+ if (pWrkrData->mmdb_is_open) close_mmdb(&pWrkrData->mmdb);
+ pWrkrData->mmdb_is_open = 0;
+ pthread_mutex_destroy(&pWrkrData->mmdbMutex);
+ENDfreeWrkrInstance
+
+
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+CODESTARTsetModCnf
+ loadModConf->container = NULL;
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if(pvals == NULL) {
+ LogError(0, RS_RET_MISSING_CNFPARAMS, "mmdblookup: error processing module "
+ "config parameters missing [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ if(Debug) {
+ dbgprintf("module (global) param blk for mmdblookup:\n");
+ cnfparamsPrint(&modpblk, pvals);
+ }
+
+ for(i = 0 ; i < modpblk.nParams ; ++i) {
+ if(!pvals[i].bUsed)
+ continue;
+ if(!strcmp(modpblk.descr[i].name, "container")) {
+ loadModConf->container = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else {
+ dbgprintf("mmdblookup: program error, non-handled "
+ "param '%s' in setModCnf\n", modpblk.descr[i].name);
+ }
+ }
+
+ if(loadModConf->container == NULL) {
+ CHKmalloc(loadModConf->container = strdup(JSON_IPLOOKUP_NAME));
+ }
+
+finalize_it:
+ if(pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+static inline void
+setInstParamDefaults(instanceData *pData)
+{
+ pData->pszKey = NULL;
+ pData->pszMmdbFile = NULL;
+ pData->fieldList.nmemb = 0;
+ pData->reloadOnHup = 1;
+}
+
+BEGINnewActInst
+ struct cnfparamvals *pvals;
+ int i;
+CODESTARTnewActInst
+ dbgprintf("newActInst (mmdblookup)\n");
+ if ((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ CODE_STD_STRING_REQUESTnewActInst(1)
+ CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
+ CHKiRet(createInstance(&pData));
+ setInstParamDefaults(pData);
+
+ for (i = 0; i < actpblk.nParams; ++i) {
+ if (!pvals[i].bUsed)
+ continue;
+ if (!strcmp(actpblk.descr[i].name, "key")) {
+ pData->pszKey = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if (!strcmp(actpblk.descr[i].name, "mmdbfile")) {
+ pData->pszMmdbFile = es_str2cstr(pvals[i].val.d.estr, NULL);
+ } else if (!strcmp(actpblk.descr[i].name, "fields")) {
+ pData->fieldList.nmemb = pvals[i].val.d.ar->nmemb;
+ CHKmalloc(pData->fieldList.name = calloc(pData->fieldList.nmemb, sizeof(char *)));
+ CHKmalloc(pData->fieldList.varname = calloc(pData->fieldList.nmemb, sizeof(char *)));
+ for (int j = 0; j < pvals[i].val.d.ar->nmemb; ++j) {
+ char *const param = es_str2cstr(pvals[i].val.d.ar->arr[j], NULL);
+ char *varname = NULL;
+ char *name;
+ if(*param == ':') {
+ char *b = strchr(param+1, ':');
+ if(b == NULL) {
+ parser_errmsg("mmdblookup: missing closing colon: '%s'", param);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ *b = '\0'; /* split name & varname */
+ varname = param+1;
+ name = b+1;
+ } else {
+ name = param;
+ }
+ if(*name == '!')
+ ++name;
+ CHKmalloc(pData->fieldList.name[j] = strdup(name));
+ char vnamebuf[1024];
+ snprintf(vnamebuf, sizeof(vnamebuf),
+ "%s!%s", loadModConf->container,
+ (varname == NULL) ? name : varname);
+ CHKmalloc(pData->fieldList.varname[j] = strdup(vnamebuf));
+ free(param);
+ }
+ } else if(!strcmp(actpblk.descr[i].name, "reloadonhup")) {
+ pData->reloadOnHup = pvals[i].val.d.n;
+ } else {
+ dbgprintf("mmdblookup: program error, non-handled"
+ " param '%s'\n", actpblk.descr[i].name);
+ }
+ }
+
+CODE_STD_FINALIZERnewActInst
+ cnfparamvalsDestruct(pvals, &actpblk);
+ENDnewActInst
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ iRet = wrkr_reopen_mmdb(pWrkrData);
+ENDtryResume
+
+
+void
+str_split(char **membuf)
+{
+ int in_quotes = 0;
+ char *buf = *membuf;
+ char tempbuf[strlen(buf)];
+ memset(tempbuf, 0, strlen(buf));
+
+ while (*buf++ != '\0') {
+ if (in_quotes) {
+ if (*buf == '"' && *(buf - 1) != '\\') {
+ in_quotes = !in_quotes;
+ strncat(tempbuf, buf, 1);
+ } else {
+ strncat(tempbuf, buf, 1);
+ }
+ } else {
+ if (*buf == '\n' || *buf == '\t' || *buf == ' ')
+ continue;
+ if (*buf == '<') {
+ char *p = strchr(buf, '>');
+ buf = buf + (int)(p - buf);
+ strcat(tempbuf, ",");
+ } else if (*buf == '}') {
+ strcat(tempbuf, "},");
+ } else if (*buf == ']') {
+ strcat(tempbuf, "],");
+ } else if (*buf == '"' && *(buf - 1) != '\\') {
+ in_quotes = !in_quotes;
+ strncat(tempbuf, buf, 1);
+ } else {
+ strncat(tempbuf, buf, 1);
+ }
+ }
+ }
+
+ memcpy(*membuf, tempbuf, strlen(tempbuf)+1);
+}
+
+
+BEGINdoAction_NoStrings
+ smsg_t **ppMsg = (smsg_t **) pMsgData;
+ smsg_t *pMsg = ppMsg[0];
+ struct json_object *keyjson = NULL;
+ const char *pszValue;
+ instanceData *const pData = pWrkrData->pData;
+ json_object *total_json = NULL;
+ MMDB_entry_data_list_s *entry_data_list = NULL;
+CODESTARTdoAction
+ /* ensure file is open before beginning */
+ if (!pWrkrData->mmdb_is_open) {
+ CHKiRet(wrkr_reopen_mmdb(pWrkrData));
+ }
+
+ /* key is given, so get the property json */
+ msgPropDescr_t pProp;
+ msgPropDescrFill(&pProp, (uchar*)pData->pszKey, strlen(pData->pszKey));
+ rsRetVal localRet = msgGetJSONPropJSON(pMsg, &pProp, &keyjson);
+ msgPropDescrDestruct(&pProp);
+
+ pthread_mutex_lock(&pWrkrData->mmdbMutex);
+ if (localRet != RS_RET_OK) {
+ /* key not found in the message. nothing to do */
+ ABORT_FINALIZE(RS_RET_OK);
+ }
+ /* key found, so get the value */
+ pszValue = (char*)json_object_get_string(keyjson);
+ if(pszValue == NULL) { /* json null object returns NULL! */
+ pszValue = "";
+ }
+
+ int gai_err, mmdb_err;
+ MMDB_lookup_result_s result = MMDB_lookup_string(&pWrkrData->mmdb, pszValue, &gai_err, &mmdb_err);
+
+ if (0 != gai_err) {
+ dbgprintf("Error from call to getaddrinfo for %s - %s\n", pszValue, gai_strerror(gai_err));
+ ABORT_FINALIZE(RS_RET_OK);
+ }
+ if (MMDB_SUCCESS != mmdb_err) {
+ dbgprintf("Got an error from the maxminddb library: %s\n", MMDB_strerror(mmdb_err));
+ close_mmdb(&pWrkrData->mmdb);
+ pWrkrData->mmdb_is_open = 0;
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+ if (!result.found_entry) {
+ dbgprintf("No entry found in database for '%s'\n", pszValue);
+ ABORT_FINALIZE(RS_RET_OK);
+ }
+
+
+ int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
+
+ if (MMDB_SUCCESS != status) {
+ dbgprintf("Got an error looking up the entry data - %s\n", MMDB_strerror(status));
+ ABORT_FINALIZE(RS_RET_OK);
+ }
+
+ size_t memlen;
+ char *membuf;
+ FILE *memstream;
+ CHKmalloc(memstream = open_memstream(&membuf, &memlen));
+
+ if (entry_data_list != NULL && memstream != NULL) {
+ MMDB_dump_entry_data_list(memstream, entry_data_list, 2);
+ fflush(memstream);
+ str_split(&membuf);
+ }
+
+ DBGPRINTF("maxmindb returns: '%s'\n", membuf);
+ total_json = json_tokener_parse(membuf);
+ fclose(memstream);
+ free(membuf);
+
+ /* extract and amend fields (to message) as configured */
+ for (int i = 0 ; i < pData->fieldList.nmemb; ++i) {
+ char *strtok_save;
+ char buf[(strlen((char *)(pData->fieldList.name[i])))+1];
+ strcpy(buf, (char *)pData->fieldList.name[i]);
+
+ json_object *temp_json = total_json;
+ json_object *sub_obj = temp_json;
+ int j = 0;
+ const char *SEP = "!";
+
+ /* find lowest level JSON object */
+ char *s = strtok_r(buf, SEP, &strtok_save);
+ for (; s != NULL; j++) {
+ json_object_object_get_ex(temp_json, s, &sub_obj);
+ temp_json = sub_obj;
+ s = strtok_r(NULL, SEP, &strtok_save);
+ }
+ /* temp_json now contains the value we want to have, so set it */
+ json_object_get(temp_json);
+ msgAddJSON(pMsg, (uchar *)pData->fieldList.varname[i], temp_json, 0, 0);
+ }
+
+finalize_it:
+ pthread_mutex_unlock(&pWrkrData->mmdbMutex);
+ if(entry_data_list != NULL)
+ MMDB_free_entry_data_list(entry_data_list);
+ json_object_put(keyjson);
+ if(total_json != NULL)
+ json_object_put(total_json);
+ENDdoAction
+
+// HUP handling for the worker...
+BEGINdoHUPWrkr
+CODESTARTdoHUPWrkr
+ dbgprintf("mmdblookup: HUP received\n");
+ if (pWrkrData->pData->reloadOnHup) {
+ iRet = wrkr_reopen_mmdb(pWrkrData);
+ }
+ENDdoHUPWrkr
+
+
+NO_LEGACY_CONF_parseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_STD_OMOD8_QUERIES
+CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
+CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
+CODEqueryEtryPt_STD_CONF2_QUERIES
+CODEqueryEtryPt_doHUPWrkr
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ /* we only support the current interface specification */
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+CODEmodInit_QueryRegCFSLineHdlr
+ dbgprintf("mmdblookup: module compiled with rsyslog version %s.\n", VERSION);
+ENDmodInit