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