1 /*
2 * Copyright (c) 2017 Balabit
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation, or (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 *
17 * As an additional exemption you are allowed to compile & link against the
18 * OpenSSL libraries as published by the OpenSSL project. See the file
19 * COPYING for details.
20 *
21 */
22
23 #include "plugin.h"
24 #include "plugin-types.h"
25 #include "template/simple-function.h"
26 #include "messages.h"
27 #include "cfg.h"
28
29 #include "syslog-ng-config.h"
30 #include "maxminddb-helper.h"
31 #include "geoip-parser.h"
32
33 typedef struct
34 {
35 TFSimpleFuncState super;
36 MMDB_s *database;
37 gchar *database_path;
38 gchar **entry_path;
39 } TFMaxMindDBState;
40
41 static inline gboolean
tf_maxminddb_init(TFMaxMindDBState * state)42 tf_maxminddb_init(TFMaxMindDBState *state)
43 {
44 state->database = g_new0(MMDB_s, 1);
45
46 if (!mmdb_open_database(state->database_path, state->database))
47 {
48 g_free(state->database);
49 state->database = NULL;
50 return FALSE;
51 }
52
53 return TRUE;
54 }
55
56 static gboolean
tf_geoip_maxminddb_prepare(LogTemplateFunction * self,gpointer s,LogTemplate * parent,gint argc,gchar * argv[],GError ** error)57 tf_geoip_maxminddb_prepare(LogTemplateFunction *self, gpointer s, LogTemplate *parent,
58 gint argc, gchar *argv[], GError **error)
59 {
60 TFMaxMindDBState *state = (TFMaxMindDBState *) s;
61 gchar *field = NULL;
62 state->database_path = NULL;
63
64 GOptionEntry maxminddb_options[] =
65 {
66 { "database", 'd', 0, G_OPTION_ARG_FILENAME, &state->database_path, "mmdb database location", NULL },
67 { "field", 'f', 0, G_OPTION_ARG_STRING, &field, "data path in database. For example: country.iso_code", NULL },
68 { NULL }
69 };
70
71 GOptionContext *ctx = g_option_context_new("geoip2");
72 g_option_context_add_main_entries(ctx, maxminddb_options, NULL);
73
74 if (!g_option_context_parse(ctx, &argc, &argv, error))
75 {
76 g_option_context_free(ctx);
77 return FALSE;
78 }
79 g_option_context_free(ctx);
80
81 if (!state->database_path)
82 state->database_path = mmdb_default_database();
83
84 if (!state->database_path || argc < 1)
85 {
86 g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE,
87 "geoip2: format must be: $(geoip2 --database <db.mmdb> [ --field path.child ] ${HOST})\n");
88 goto error;
89 }
90
91 if (field)
92 state->entry_path = g_strsplit(field, ".", -1);
93 else
94 state->entry_path = g_strsplit("country.iso_code", ".", -1);
95
96 if (!tf_simple_func_prepare(self, state, parent, argc, argv, error))
97 {
98 g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE,
99 "geoip2: prepare failed");
100 goto error;
101 }
102
103 if (!tf_maxminddb_init(state))
104 {
105 g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE,
106 "geoip2: could not init database");
107 goto error;
108 }
109
110 return TRUE;
111
112 error:
113 g_free(state->database_path);
114 state->database_path = NULL;
115 g_strfreev(state->entry_path);
116 state->entry_path = NULL;
117 g_free(field);
118 return FALSE;
119
120 }
121
122 static void
tf_geoip_maxminddb_call(LogTemplateFunction * self,gpointer s,const LogTemplateInvokeArgs * args,GString * result)123 tf_geoip_maxminddb_call(LogTemplateFunction *self, gpointer s, const LogTemplateInvokeArgs *args, GString *result)
124 {
125 TFMaxMindDBState *state = (TFMaxMindDBState *) s;
126
127 int _gai_error, mmdb_error;
128 MMDB_lookup_result_s mmdb_result =
129 MMDB_lookup_string(state->database, args->argv[0]->str, &_gai_error, &mmdb_error);
130
131 if (!mmdb_result.found_entry)
132 {
133 goto error;
134 }
135
136 MMDB_entry_data_s entry_data;
137 mmdb_error = MMDB_aget_value(&mmdb_result.entry, &entry_data, (const char *const* const)state->entry_path);
138 if (mmdb_error != MMDB_SUCCESS)
139 {
140 goto error;
141 }
142
143 if (entry_data.has_data)
144 append_mmdb_entry_data_to_gstring(result, &entry_data);
145
146 return;
147
148 error:
149 if (_gai_error != 0)
150 msg_error("$(geoip2): getaddrinfo failed",
151 evt_tag_str("ip", args->argv[0]->str),
152 evt_tag_str("gai_error", gai_strerror(_gai_error)));
153
154 if (mmdb_error != MMDB_SUCCESS )
155 msg_error("$(geoip2): maxminddb error",
156 evt_tag_str("ip", args->argv[0]->str),
157 evt_tag_str("error", MMDB_strerror(mmdb_error)));
158 }
159
160 static void
tf_geoip_maxminddb_free_state(gpointer s)161 tf_geoip_maxminddb_free_state(gpointer s)
162 {
163 TFMaxMindDBState *state = (TFMaxMindDBState *) s;
164
165 g_free(state->database_path);
166 g_strfreev(state->entry_path);
167 tf_simple_func_free_state(&state->super);
168 }
169
170 TEMPLATE_FUNCTION(TFMaxMindDBState, tf_geoip_maxminddb, tf_geoip_maxminddb_prepare,
171 tf_simple_func_eval, tf_geoip_maxminddb_call, tf_geoip_maxminddb_free_state, NULL);
172