1 /*
2 * Copyright (c) 2002-2012 Balabit
3 * Copyright (c) 1998-2012 Balázs Scheidler
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * As an additional exemption you are allowed to compile & link against the
20 * OpenSSL libraries as published by the OpenSSL project. See the file
21 * COPYING for details.
22 *
23 */
24
25 #include "msg-format.h"
26 #include "cfg.h"
27 #include "plugin.h"
28 #include "plugin-types.h"
29 #include "find-crlf.h"
30 #include "scratch-buffers.h"
31
32 static gsize
_rstripped_message_length(const guchar * data,gsize length)33 _rstripped_message_length(const guchar *data, gsize length)
34 {
35 while (length > 0 && (data[length - 1] == '\n' || data[length - 1] == '\0'))
36 length--;
37 return length;
38 }
39
40 static void
msg_format_inject_parse_error(LogMessage * msg,const guchar * data,gsize length,gint problem_position)41 msg_format_inject_parse_error(LogMessage *msg, const guchar *data, gsize length, gint problem_position)
42 {
43 GString *buf = scratch_buffers_alloc();
44
45 log_msg_clear(msg);
46
47 msg->timestamps[LM_TS_STAMP] = msg->timestamps[LM_TS_RECVD];
48 log_msg_set_value(msg, LM_V_HOST, "", 0);
49
50 if (problem_position > 0)
51 g_string_printf(buf, "Error processing log message: %.*s>@<%.*s", (gint) problem_position-1,
52 data, (gint) (length-problem_position+1), data+problem_position-1);
53 else
54 g_string_printf(buf, "Error processing log message: %.*s", (gint) length, data);
55
56 log_msg_set_value(msg, LM_V_MESSAGE, buf->str, buf->len);
57 log_msg_set_value(msg, LM_V_PROGRAM, "syslog-ng", 9);
58 g_string_printf(buf, "%d", (int) getpid());
59 log_msg_set_value(msg, LM_V_PID, buf->str, buf->len);
60
61 msg->flags |= LF_LOCAL;
62 msg->pri = LOG_SYSLOG | LOG_ERR;
63 }
64
65 static void
msg_format_preprocess_message(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length)66 msg_format_preprocess_message(MsgFormatOptions *options, LogMessage *msg,
67 const guchar *data, gsize length)
68 {
69 if (options->flags & LP_STORE_RAW_MESSAGE)
70 {
71 log_msg_set_value(msg, LOG_MSG_GET_VALUE_HANDLE_STATIC("RAWMSG"),
72 (gchar *) data, _rstripped_message_length(data, length));
73 }
74 }
75
76 static void
msg_format_postprocess_message(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length)77 msg_format_postprocess_message(MsgFormatOptions *options, LogMessage *msg,
78 const guchar *data, gsize length)
79 {
80 if (options->flags & LP_NO_PARSE_DATE)
81 {
82 msg->timestamps[LM_TS_STAMP] = msg->timestamps[LM_TS_RECVD];
83 unix_time_set_timezone(&msg->timestamps[LM_TS_STAMP],
84 time_zone_info_get_offset(options->recv_time_zone_info,
85 msg->timestamps[LM_TS_RECVD].ut_sec));
86 }
87 if (G_UNLIKELY(options->flags & LP_NO_MULTI_LINE))
88 {
89 gssize msg_len;
90 gchar *msg_text;
91 gchar *p;
92
93 p = msg_text = (gchar *) log_msg_get_value(msg, LM_V_MESSAGE, &msg_len);
94 while ((p = find_cr_or_lf(p, msg_text + msg_len - p)))
95 {
96 *p = ' ';
97 p++;
98 }
99 }
100 if (options->flags & LP_LOCAL)
101 msg->flags |= LF_LOCAL;
102 if (options->flags & LP_ASSUME_UTF8)
103 msg->flags |= LF_UTF8;
104 }
105
106 static gboolean
msg_format_process_message(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length,gsize * problem_position)107 msg_format_process_message(MsgFormatOptions *options, LogMessage *msg,
108 const guchar *data, gsize length,
109 gsize *problem_position)
110 {
111 if ((options->flags & LP_NOPARSE) == 0)
112 {
113 return options->format_handler->parse(options, msg, data, length, problem_position);
114 }
115 else
116 {
117 log_msg_set_value(msg, LM_V_MESSAGE, (gchar *) data, _rstripped_message_length(data, length));
118 msg->pri = options->default_pri;
119 return TRUE;
120 }
121 }
122
123 gboolean
msg_format_parse_conditional(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length,gsize * problem_position)124 msg_format_parse_conditional(MsgFormatOptions *options, LogMessage *msg,
125 const guchar *data, gsize length,
126 gsize *problem_position)
127 {
128 if (G_UNLIKELY(!options->format_handler))
129 {
130 gchar buf[256];
131
132 g_snprintf(buf, sizeof(buf), "Error parsing message, format module %s is not loaded", options->format);
133 log_msg_set_value(msg, LM_V_MESSAGE, buf, -1);
134 return FALSE;
135 }
136
137 msg_format_preprocess_message(options, msg, data, length);
138
139 if (!msg_format_process_message(options, msg, data, length, problem_position))
140 return FALSE;
141
142 msg_format_postprocess_message(options, msg, data, length);
143 return TRUE;
144 }
145
146 void
msg_format_parse(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length)147 msg_format_parse(MsgFormatOptions *options, LogMessage *msg,
148 const guchar *data, gsize length)
149 {
150 gsize problem_position = 0;
151
152 if (!msg_format_parse_conditional(options, msg, data, length, &problem_position))
153 {
154 msg_format_inject_parse_error(msg, data, _rstripped_message_length(data, length), problem_position);
155
156 /* the injected error message needs to be postprocessed too */
157 msg_format_postprocess_message(options, msg, data, length);
158 }
159 }
160
161 void
msg_format_options_defaults(MsgFormatOptions * options)162 msg_format_options_defaults(MsgFormatOptions *options)
163 {
164 options->flags = LP_EXPECT_HOSTNAME | LP_STORE_LEGACY_MSGHDR;
165 options->recv_time_zone = NULL;
166 options->recv_time_zone_info = NULL;
167 options->bad_hostname = NULL;
168 options->default_pri = 0xFFFF;
169 options->sdata_param_value_max = 65535;
170 }
171
172 /* NOTE: _init needs to be idempotent when called multiple times w/o invoking _destroy */
173 void
msg_format_options_init(MsgFormatOptions * options,GlobalConfig * cfg)174 msg_format_options_init(MsgFormatOptions *options, GlobalConfig *cfg)
175 {
176 Plugin *p;
177
178 if (options->initialized)
179 return;
180
181 if (cfg->bad_hostname_compiled)
182 options->bad_hostname = &cfg->bad_hostname;
183 if (options->recv_time_zone == NULL)
184 options->recv_time_zone = g_strdup(cfg->recv_time_zone);
185 if (options->recv_time_zone_info == NULL)
186 options->recv_time_zone_info = time_zone_info_new(options->recv_time_zone);
187
188 if (!options->format)
189 options->format = g_strdup("syslog");
190
191 p = cfg_find_plugin(cfg, LL_CONTEXT_FORMAT, options->format);
192 if (p)
193 options->format_handler = plugin_construct(p);
194 options->initialized = TRUE;
195 }
196
197 void
msg_format_options_copy(MsgFormatOptions * options,const MsgFormatOptions * source)198 msg_format_options_copy(MsgFormatOptions *options, const MsgFormatOptions *source)
199 {
200 g_assert(!options->initialized);
201
202 options->format = g_strdup(source->format);
203 options->flags = source->flags;
204 options->default_pri = source->default_pri;
205 options->recv_time_zone = g_strdup(source->recv_time_zone);
206 options->sdata_param_value_max = source->sdata_param_value_max;
207 }
208
209 void
msg_format_options_destroy(MsgFormatOptions * options)210 msg_format_options_destroy(MsgFormatOptions *options)
211 {
212 if (options->format)
213 {
214 g_free(options->format);
215 options->format = NULL;
216 }
217 if (options->recv_time_zone)
218 {
219 g_free(options->recv_time_zone);
220 options->recv_time_zone = NULL;
221 }
222 if (options->recv_time_zone_info)
223 {
224 time_zone_info_free(options->recv_time_zone_info);
225 options->recv_time_zone_info = NULL;
226 }
227 options->initialized = FALSE;
228 }
229
230 CfgFlagHandler msg_format_flag_handlers[] =
231 {
232 { "no-parse", CFH_SET, offsetof(MsgFormatOptions, flags), LP_NOPARSE },
233 { "check-hostname", CFH_SET, offsetof(MsgFormatOptions, flags), LP_CHECK_HOSTNAME },
234 { "syslog-protocol", CFH_SET, offsetof(MsgFormatOptions, flags), LP_SYSLOG_PROTOCOL },
235 { "assume-utf8", CFH_SET, offsetof(MsgFormatOptions, flags), LP_ASSUME_UTF8 },
236 { "validate-utf8", CFH_SET, offsetof(MsgFormatOptions, flags), LP_VALIDATE_UTF8 },
237 { "sanitize-utf8", CFH_SET, offsetof(MsgFormatOptions, flags), LP_SANITIZE_UTF8 },
238 { "no-multi-line", CFH_SET, offsetof(MsgFormatOptions, flags), LP_NO_MULTI_LINE },
239 { "store-legacy-msghdr", CFH_SET, offsetof(MsgFormatOptions, flags), LP_STORE_LEGACY_MSGHDR },
240 { "store-raw-message", CFH_SET, offsetof(MsgFormatOptions, flags), LP_STORE_RAW_MESSAGE },
241 { "dont-store-legacy-msghdr", CFH_CLEAR, offsetof(MsgFormatOptions, flags), LP_STORE_LEGACY_MSGHDR },
242 { "expect-hostname", CFH_SET, offsetof(MsgFormatOptions, flags), LP_EXPECT_HOSTNAME },
243 { "no-hostname", CFH_CLEAR, offsetof(MsgFormatOptions, flags), LP_EXPECT_HOSTNAME },
244 { "guess-timezone", CFH_SET, offsetof(MsgFormatOptions, flags), LP_GUESS_TIMEZONE },
245 { "no-header", CFH_SET, offsetof(MsgFormatOptions, flags), LP_NO_HEADER },
246
247 { NULL },
248 };
249
250 gboolean
msg_format_options_process_flag(MsgFormatOptions * options,const gchar * flag)251 msg_format_options_process_flag(MsgFormatOptions *options, const gchar *flag)
252 {
253 return cfg_process_flag(msg_format_flag_handlers, options, flag);
254 }
255