master
c 717 lines 26.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "windows-events.h"
4
5 static void wevt_event_done(WEVT_LOG *log);
6
7 static uint64_t wevt_log_file_size(const wchar_t *channel);
8
9 // --------------------------------------------------------------------------------------------------------------------
10
11 static const char *EvtGetExtendedStatus_utf8(void) {
12 static __thread wchar_t wbuf[4096];
13 static __thread char buf[4096];
14 DWORD wbuf_used = 0;
15
16 if(EvtGetExtendedStatus(sizeof(wbuf) / sizeof(wchar_t), wbuf, &wbuf_used) == ERROR_SUCCESS) {
17 wbuf[sizeof(wbuf) / sizeof(wchar_t) - 1] = 0;
18 unicode2utf8(buf, sizeof(buf), wbuf);
19 }
20 else
21 buf[0] = '\0';
22
23 // the EvtGetExtendedStatus() may be successful with an empty message
24 if(!buf[0])
25 strncpyz(buf, "no additional information", sizeof(buf) - 1);
26
27 return buf;
28 }
29
30 // --------------------------------------------------------------------------------------------------------------------
31
32 bool EvtFormatMessage_utf16(
33 TXT_UTF16 *dst, EVT_HANDLE hMetadata, EVT_HANDLE hEvent, DWORD dwMessageId, EVT_FORMAT_MESSAGE_FLAGS flags) {
34 dst->used = 0;
35
36 DWORD size = 0;
37 if(!dst->data) {
38 EvtFormatMessage(hMetadata, hEvent, dwMessageId, 0, NULL, flags, 0, NULL, &size);
39 if(!size) {
40 // nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtFormatMessage() to get message size failed.");
41 goto cleanup;
42 }
43 txt_utf16_resize(dst, size, false);
44 }
45
46 // First, try to get the message using the existing buffer
47 if (!EvtFormatMessage(hMetadata, hEvent, dwMessageId, 0, NULL, flags, dst->size, dst->data, &size) || !dst->data) {
48 if (dst->data && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
49 // nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtFormatMessage() failed.");
50 goto cleanup;
51 }
52
53 // Try again with the resized buffer
54 txt_utf16_resize(dst, size, false);
55 if (!EvtFormatMessage(hMetadata, hEvent, dwMessageId, 0, NULL, flags, dst->size, dst->data, &size)) {
56 // nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtFormatMessage() failed after resizing buffer.");
57 goto cleanup;
58 }
59 }
60
61 // make sure it is null terminated
62 if(size <= dst->size)
63 dst->data[size - 1] = 0;
64 else
65 dst->data[dst->size - 1] = 0;
66
67 // unfortunately we have to calculate the length every time
68 // the size returned may not be the length of the dst string
69 dst->used = wcslen(dst->data) + 1;
70
71 return true;
72
73 cleanup:
74 dst->used = 0;
75 return false;
76 }
77
78 static bool EvtFormatMessage_utf8(
79 TXT_UTF16 *tmp, PROVIDER_META_HANDLE *p, EVT_HANDLE hEvent,
80 TXT_UTF8 *dst, EVT_FORMAT_MESSAGE_FLAGS flags) {
81
82 dst->src = TXT_SOURCE_EVENT_LOG;
83
84 if(EvtFormatMessage_utf16(tmp, provider_handle(p), hEvent, 0, flags))
85 return txt_utf16_to_utf8(dst, tmp);
86
87 txt_utf8_empty(dst);
88 return false;
89 }
90
91 bool EvtFormatMessage_Event_utf8(TXT_UTF16 *tmp, PROVIDER_META_HANDLE *p, EVT_HANDLE hEvent, TXT_UTF8 *dst) {
92 return EvtFormatMessage_utf8(tmp, p, hEvent, dst, EvtFormatMessageEvent);
93 }
94
95 bool EvtFormatMessage_Xml_utf8(TXT_UTF16 *tmp, PROVIDER_META_HANDLE *p, EVT_HANDLE hEvent, TXT_UTF8 *dst) {
96 return EvtFormatMessage_utf8(tmp, p, hEvent, dst, EvtFormatMessageXml);
97 }
98
99 // --------------------------------------------------------------------------------------------------------------------
100
101 static void wevt_get_field_from_cache(
102 WEVT_LOG *log, uint64_t value, PROVIDER_META_HANDLE *h,
103 TXT_UTF8 *dst, const ND_UUID *provider,
104 WEVT_FIELD_TYPE cache_type, EVT_FORMAT_MESSAGE_FLAGS flags) {
105
106 if (field_cache_get(cache_type, provider, value, dst))
107 return;
108
109 EvtFormatMessage_utf8(&log->ops.unicode, h, log->hEvent, dst, flags);
110 field_cache_set(cache_type, provider, value, dst);
111 }
112
113 // --------------------------------------------------------------------------------------------------------------------
114 // Level
115
116 #define SET_LEN_AND_RETURN(constant) *len = sizeof(constant) - 1; return constant
117
118 static inline const char *wevt_level_hardcoded(uint64_t level, size_t *len) {
119 switch(level) {
120 case WEVT_LEVEL_NONE: SET_LEN_AND_RETURN(WEVT_LEVEL_NAME_NONE);
121 case WEVT_LEVEL_CRITICAL: SET_LEN_AND_RETURN(WEVT_LEVEL_NAME_CRITICAL);
122 case WEVT_LEVEL_ERROR: SET_LEN_AND_RETURN(WEVT_LEVEL_NAME_ERROR);
123 case WEVT_LEVEL_WARNING: SET_LEN_AND_RETURN(WEVT_LEVEL_NAME_WARNING);
124 case WEVT_LEVEL_INFORMATION: SET_LEN_AND_RETURN(WEVT_LEVEL_NAME_INFORMATION);
125 case WEVT_LEVEL_VERBOSE: SET_LEN_AND_RETURN(WEVT_LEVEL_NAME_VERBOSE);
126 default: *len = 0; return NULL;
127 }
128 }
129
130 static void wevt_get_level(WEVT_LOG *log, WEVT_EVENT *ev, PROVIDER_META_HANDLE *h) {
131 TXT_UTF8 *dst = &log->ops.level;
132 uint64_t value = ev->level;
133
134 txt_utf8_empty(dst);
135
136 EVT_FORMAT_MESSAGE_FLAGS flags = EvtFormatMessageLevel;
137 WEVT_FIELD_TYPE cache_type = WEVT_FIELD_TYPE_LEVEL;
138 bool is_provider = is_valid_provider_level(value, true);
139
140 if(!is_provider) {
141 size_t len;
142 const char *hardcoded = wevt_level_hardcoded(value, &len);
143 if(hardcoded) {
144 txt_utf8_set(dst, hardcoded, len);
145 dst->src = TXT_SOURCE_HARDCODED;
146 }
147 else {
148 // since this is not a provider value
149 // we expect to get the system description of it
150 wevt_get_field_from_cache(log, value, h, dst, &ev->provider, cache_type, flags);
151 }
152 }
153 else if (!provider_get_level(dst, h, value)) {
154 // not found in the manifest, get it from the cache
155 wevt_get_field_from_cache(log, value, h, dst, &ev->provider, cache_type, flags);
156 }
157
158 txt_utf8_set_numeric_if_empty(
159 dst, WEVT_PREFIX_LEVEL, sizeof(WEVT_PREFIX_LEVEL) - 1, ev->level);
160 }
161
162 // --------------------------------------------------------------------------------------------------------------------
163 // Opcode
164
165 static inline const char *wevt_opcode_hardcoded(uint64_t opcode, size_t *len) {
166 switch(opcode) {
167 case WEVT_OPCODE_INFO: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_INFO);
168 case WEVT_OPCODE_START: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_START);
169 case WEVT_OPCODE_STOP: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_STOP);
170 case WEVT_OPCODE_DC_START: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_DC_START);
171 case WEVT_OPCODE_DC_STOP: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_DC_STOP);
172 case WEVT_OPCODE_EXTENSION: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_EXTENSION);
173 case WEVT_OPCODE_REPLY: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_REPLY);
174 case WEVT_OPCODE_RESUME: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_RESUME);
175 case WEVT_OPCODE_SUSPEND: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_SUSPEND);
176 case WEVT_OPCODE_SEND: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_SEND);
177 case WEVT_OPCODE_RECEIVE: SET_LEN_AND_RETURN(WEVT_OPCODE_NAME_RECEIVE);
178 default: *len = 0; return NULL;
179 }
180 }
181
182 static void wevt_get_opcode(WEVT_LOG *log, WEVT_EVENT *ev, PROVIDER_META_HANDLE *h) {
183 TXT_UTF8 *dst = &log->ops.opcode;
184 uint64_t value = ev->opcode;
185
186 txt_utf8_empty(dst);
187
188 EVT_FORMAT_MESSAGE_FLAGS flags = EvtFormatMessageOpcode;
189 WEVT_FIELD_TYPE cache_type = WEVT_FIELD_TYPE_OPCODE;
190 bool is_provider = is_valid_provider_opcode(value, true);
191
192 if(!is_provider) {
193 size_t len;
194 const char *hardcoded = wevt_opcode_hardcoded(value, &len);
195 if(hardcoded) {
196 txt_utf8_set(dst, hardcoded, len);
197 dst->src = TXT_SOURCE_HARDCODED;
198 }
199 else {
200 // since this is not a provider value
201 // we expect to get the system description of it
202 wevt_get_field_from_cache(log, value, h, dst, &ev->provider, cache_type, flags);
203 }
204 }
205 else if (!provider_get_opcode(dst, h, value)) {
206 // not found in the manifest, get it from the cache
207 wevt_get_field_from_cache(log, value, h, dst, &ev->provider, cache_type, flags);
208 }
209
210 txt_utf8_set_numeric_if_empty(
211 dst, WEVT_PREFIX_OPCODE, sizeof(WEVT_PREFIX_OPCODE) - 1, ev->opcode);
212 }
213
214 // --------------------------------------------------------------------------------------------------------------------
215 // Task
216
217 static const char *wevt_task_hardcoded(uint64_t task, size_t *len) {
218 switch(task) {
219 case WEVT_TASK_NONE: SET_LEN_AND_RETURN(WEVT_TASK_NAME_NONE);
220 default: *len = 0; return NULL;
221 }
222 }
223
224 static void wevt_get_task(WEVT_LOG *log, WEVT_EVENT *ev, PROVIDER_META_HANDLE *h) {
225 TXT_UTF8 *dst = &log->ops.task;
226 uint64_t value = ev->task;
227
228 txt_utf8_empty(dst);
229
230 EVT_FORMAT_MESSAGE_FLAGS flags = EvtFormatMessageTask;
231 WEVT_FIELD_TYPE cache_type = WEVT_FIELD_TYPE_TASK;
232 bool is_provider = is_valid_provider_task(value, true);
233
234 if(!is_provider) {
235 size_t len;
236 const char *hardcoded = wevt_task_hardcoded(value, &len);
237 if(hardcoded) {
238 txt_utf8_set(dst, hardcoded, len);
239 dst->src = TXT_SOURCE_HARDCODED;
240 }
241 else {
242 // since this is not a provider value
243 // we expect to get the system description of it
244 wevt_get_field_from_cache(log, value, h, dst, &ev->provider, cache_type, flags);
245 }
246 }
247 else if (!provider_get_task(dst, h, value)) {
248 // not found in the manifest, get it from the cache
249 wevt_get_field_from_cache(log, value, h, dst, &ev->provider, cache_type, flags);
250 }
251
252 txt_utf8_set_numeric_if_empty(
253 dst, WEVT_PREFIX_TASK, sizeof(WEVT_PREFIX_TASK) - 1, ev->task);
254 }
255
256 // --------------------------------------------------------------------------------------------------------------------
257 // Keyword
258
259 #define SET_BITS(msk, txt) { .mask = msk, .name = txt, .len = sizeof(txt) - 1, }
260
261 static uint64_t wevt_keyword_handle_reserved(uint64_t value, TXT_UTF8 *dst) {
262 struct {
263 uint64_t mask;
264 const char *name;
265 size_t len;
266 } bits[] = {
267 SET_BITS(WEVT_KEYWORD_EVENTLOG_CLASSIC, WEVT_KEYWORD_NAME_EVENTLOG_CLASSIC),
268 SET_BITS(WEVT_KEYWORD_CORRELATION_HINT, WEVT_KEYWORD_NAME_CORRELATION_HINT),
269 SET_BITS(WEVT_KEYWORD_AUDIT_SUCCESS, WEVT_KEYWORD_NAME_AUDIT_SUCCESS),
270 SET_BITS(WEVT_KEYWORD_AUDIT_FAILURE, WEVT_KEYWORD_NAME_AUDIT_FAILURE),
271 SET_BITS(WEVT_KEYWORD_SQM, WEVT_KEYWORD_NAME_SQM),
272 SET_BITS(WEVT_KEYWORD_WDI_DIAG, WEVT_KEYWORD_NAME_WDI_DIAG),
273 SET_BITS(WEVT_KEYWORD_WDI_CONTEXT, WEVT_KEYWORD_NAME_WDI_CONTEXT),
274 SET_BITS(WEVT_KEYWORD_RESPONSE_TIME, WEVT_KEYWORD_NAME_RESPONSE_TIME),
275 };
276
277 txt_utf8_empty(dst);
278
279 for(size_t i = 0; i < sizeof(bits) / sizeof(bits[0]) ;i++) {
280 if((value & bits[i].mask) == bits[i].mask) {
281 txt_utf8_add_keywords_separator_if_needed(dst);
282 txt_utf8_append(dst, bits[i].name, bits[i].len);
283 value &= ~(bits[i].mask);
284 dst->src = TXT_SOURCE_HARDCODED;
285 }
286 }
287
288 // return it without any remaining reserved bits
289 return value & 0x0000FFFFFFFFFFFF;
290 }
291
292 static void wevt_get_keyword(WEVT_LOG *log, WEVT_EVENT *ev, PROVIDER_META_HANDLE *h) {
293 TXT_UTF8 *dst = &log->ops.keywords;
294
295 if(ev->keywords == WEVT_KEYWORD_NONE) {
296 txt_utf8_set(dst, WEVT_KEYWORD_NAME_NONE, sizeof(WEVT_KEYWORD_NAME_NONE) - 1);
297 dst->src = TXT_SOURCE_HARDCODED;
298 }
299
300 uint64_t value = wevt_keyword_handle_reserved(ev->keywords, dst);
301
302 EVT_FORMAT_MESSAGE_FLAGS flags = EvtFormatMessageKeyword;
303 WEVT_FIELD_TYPE cache_type = WEVT_FIELD_TYPE_KEYWORD;
304
305 if(!value && dst->used <= 1) {
306 // no hardcoded info in the buffer, make it None
307 txt_utf8_set(dst, WEVT_KEYWORD_NAME_NONE, sizeof(WEVT_KEYWORD_NAME_NONE) - 1);
308 dst->src = TXT_SOURCE_HARDCODED;
309 }
310 else if (value && !provider_get_keywords(dst, h, value) && dst->used <= 1) {
311 // the provider did not provide any info and the description is still empty.
312 // the system returns 1 keyword, the highest bit, not a list
313 // so, when we call the system, we pass the original value (ev->keywords)
314 wevt_get_field_from_cache(log, ev->keywords, h, dst, &ev->provider, cache_type, flags);
315 }
316
317 txt_utf8_set_hex_if_empty(
318 dst, WEVT_PREFIX_KEYWORDS, sizeof(WEVT_PREFIX_KEYWORDS) - 1, ev->keywords);
319 }
320
321 // --------------------------------------------------------------------------------------------------------------------
322 // Fetching Events
323
324 static inline bool wEvtRender(WEVT_LOG *log, EVT_HANDLE context, WEVT_VARIANT *raw) {
325 DWORD bytes_used = 0, property_count = 0;
326 if (!EvtRender(context, log->hEvent, EvtRenderEventValues, raw->size, raw->data, &bytes_used, &property_count)) {
327 // information exceeds the allocated space
328 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
329 nd_log(NDLS_COLLECTORS, NDLP_ERR,
330 "EvtRender() failed, hRenderSystemContext: 0x%lx, hEvent: 0x%lx, content: 0x%lx, size: %u, extended info: %s",
331 (uintptr_t)context, (uintptr_t)log->hEvent, (uintptr_t)raw->data, raw->size,
332 EvtGetExtendedStatus_utf8());
333 return false;
334 }
335
336 wevt_variant_resize(raw, bytes_used);
337 if (!EvtRender(context, log->hEvent, EvtRenderEventValues, raw->size, raw->data, &bytes_used, &property_count)) {
338 nd_log(NDLS_COLLECTORS, NDLP_ERR,
339 "EvtRender() failed, after bytes_used increase, extended info: %s",
340 EvtGetExtendedStatus_utf8());
341 return false;
342 }
343 }
344 raw->used = bytes_used;
345 raw->count = property_count;
346
347 return true;
348 }
349
350 static bool wevt_get_next_event_one(WEVT_LOG *log, WEVT_EVENT *ev) {
351 bool ret = false;
352
353 if(!wEvtRender(log, log->hRenderSystemContext, &log->ops.raw.system))
354 goto cleanup;
355
356 EVT_VARIANT *content = log->ops.raw.system.data;
357
358 ev->id = wevt_field_get_uint64(&content[EvtSystemEventRecordId]);
359 ev->event_id = wevt_field_get_uint16(&content[EvtSystemEventID]);
360 ev->level = wevt_field_get_uint8(&content[EvtSystemLevel]);
361 ev->opcode = wevt_field_get_uint8(&content[EvtSystemOpcode]);
362 ev->keywords = wevt_field_get_uint64_hex(&content[EvtSystemKeywords]);
363 ev->version = wevt_field_get_uint8(&content[EvtSystemVersion]);
364 ev->task = wevt_field_get_uint16(&content[EvtSystemTask]);
365 ev->qualifiers = wevt_field_get_uint16(&content[EvtSystemQualifiers]);
366 ev->process_id = wevt_field_get_uint32(&content[EvtSystemProcessID]);
367 ev->thread_id = wevt_field_get_uint32(&content[EvtSystemThreadID]);
368 ev->created_ns = wevt_field_get_filetime_to_ns(&content[EvtSystemTimeCreated]);
369
370 if(log->type & WEVT_QUERY_EXTENDED) {
371 wevt_field_get_string_utf8(&content[EvtSystemChannel], &log->ops.channel);
372 wevt_field_get_string_utf8(&content[EvtSystemComputer], &log->ops.computer);
373 wevt_field_get_string_utf8(&content[EvtSystemProviderName], &log->ops.provider);
374 wevt_get_uuid_by_type(&content[EvtSystemProviderGuid], &ev->provider);
375 wevt_get_uuid_by_type(&content[EvtSystemActivityID], &ev->activity_id);
376 wevt_get_uuid_by_type(&content[EvtSystemRelatedActivityID], &ev->related_activity_id);
377 wevt_field_get_sid(&content[EvtSystemUserID], &log->ops.account, &log->ops.domain, &log->ops.sid);
378
379 PROVIDER_META_HANDLE *p = log->provider =
380 provider_get(ev->provider, content[EvtSystemProviderName].StringVal);
381
382 ev->platform = provider_get_platform(p);
383
384 wevt_get_level(log, ev, p);
385 wevt_get_task(log, ev, p);
386 wevt_get_opcode(log, ev, p);
387 wevt_get_keyword(log, ev, p);
388
389 if(log->type & WEVT_QUERY_EVENT_DATA && wEvtRender(log, log->hRenderUserContext, &log->ops.raw.user)) {
390 #if (ON_FTS_PRELOAD_MESSAGE == 1)
391 EvtFormatMessage_Event_utf8(&log->ops.unicode, log->provider, log->hEvent, &log->ops.event);
392 #endif
393 #if (ON_FTS_PRELOAD_XML == 1)
394 EvtFormatMessage_Xml_utf8(&log->ops.unicode, log->provider, log->hEvent, &log->ops.xml);
395 #endif
396 #if (ON_FTS_PRELOAD_EVENT_DATA == 1)
397 for(size_t i = 0; i < log->ops.raw.user.count ;i++)
398 evt_variant_to_buffer(log->ops.event_data, &log->ops.raw.user.data[i], " ||| ");
399 #endif
400 }
401 }
402
403 ret = true;
404
405 cleanup:
406 return ret;
407 }
408
409 bool wevt_get_next_event(WEVT_LOG *log, WEVT_EVENT *ev) {
410 DWORD size = (log->type & WEVT_QUERY_EXTENDED) ? BATCH_NEXT_EVENT : 1;
411 DWORD max_failures = 10;
412
413 fatal_assert(log && log->hQuery && log->hRenderSystemContext);
414
415 while(max_failures > 0) {
416 if (log->batch.used >= log->batch.size) {
417 log->batch.size = 0;
418 log->batch.used = 0;
419 DWORD err;
420 if(!EvtNext(log->hQuery, size, log->batch.hEvents, INFINITE, 0, &log->batch.size)) {
421 err = GetLastError();
422 if(err == ERROR_NO_MORE_ITEMS)
423 return false; // no data available, return failure
424 }
425
426 if(!log->batch.size) {
427 if(size == 1) {
428 nd_log(NDLS_COLLECTORS, NDLP_ERR,
429 "EvtNext() failed, hQuery: 0x%lx, size: %zu, extended info: %s",
430 (uintptr_t)log->hQuery, (size_t)size, EvtGetExtendedStatus_utf8());
431 return false;
432 }
433
434 // EvtNext() returns true when it can full the array
435 // so, let's retry with a smaller array.
436 size /= 2;
437 if(size < 1) size = 1;
438 continue;
439 }
440 }
441
442 log->query_stats.event_count++;
443 log->log_stats.event_count++;
444
445 // cleanup any previous event data
446 wevt_event_done(log);
447
448 log->hEvent = log->batch.hEvents[log->batch.used];
449 log->batch.hEvents[log->batch.used] = NULL;
450 log->batch.used++;
451
452 if(wevt_get_next_event_one(log, ev))
453 return true;
454 else {
455 log->query_stats.failed_count++;
456 log->log_stats.failed_count++;
457 max_failures--;
458 }
459 }
460
461 return false;
462 }
463
464 static void wevt_event_done(WEVT_LOG *log) {
465 if (log->provider) {
466 provider_release(log->provider);
467 log->provider = NULL;
468 }
469
470 if (log->hEvent) {
471 EvtClose(log->hEvent);
472 log->hEvent = NULL;
473 }
474
475 log->ops.channel.src = TXT_SOURCE_UNKNOWN;
476 log->ops.provider.src = TXT_SOURCE_UNKNOWN;
477 log->ops.computer.src = TXT_SOURCE_UNKNOWN;
478 log->ops.account.src = TXT_SOURCE_UNKNOWN;
479 log->ops.domain.src = TXT_SOURCE_UNKNOWN;
480 log->ops.sid.src = TXT_SOURCE_UNKNOWN;
481
482 log->ops.event.src = TXT_SOURCE_UNKNOWN;
483 log->ops.level.src = TXT_SOURCE_UNKNOWN;
484 log->ops.keywords.src = TXT_SOURCE_UNKNOWN;
485 log->ops.opcode.src = TXT_SOURCE_UNKNOWN;
486 log->ops.task.src = TXT_SOURCE_UNKNOWN;
487 log->ops.xml.src = TXT_SOURCE_UNKNOWN;
488
489 log->ops.channel.used = 0;
490 log->ops.provider.used = 0;
491 log->ops.computer.used = 0;
492 log->ops.account.used = 0;
493 log->ops.domain.used = 0;
494 log->ops.sid.used = 0;
495
496 log->ops.event.used = 0;
497 log->ops.level.used = 0;
498 log->ops.keywords.used = 0;
499 log->ops.opcode.used = 0;
500 log->ops.task.used = 0;
501 log->ops.xml.used = 0;
502
503 if(log->ops.event_data)
504 log->ops.event_data->len = 0;
505 }
506
507 // --------------------------------------------------------------------------------------------------------------------
508 // Query management
509
510 bool wevt_query(WEVT_LOG *log, LPCWSTR channel, LPCWSTR query, EVT_QUERY_FLAGS direction) {
511 wevt_query_done(log);
512 log->log_stats.queries_count++;
513
514 EVT_HANDLE hQuery = EvtQuery(NULL, channel, query, EvtQueryChannelPath | (direction & (EvtQueryReverseDirection | EvtQueryForwardDirection)) | EvtQueryTolerateQueryErrors);
515 if (!hQuery) {
516 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtQuery() failed, query: %s | extended info: %s",
517 query2utf8(query), EvtGetExtendedStatus_utf8());
518
519 log->log_stats.queries_failed++;
520 return false;
521 }
522
523 log->hQuery = hQuery;
524 return true;
525 }
526
527 void wevt_query_done(WEVT_LOG *log) {
528 // close the last working hEvent
529 wevt_event_done(log);
530
531 // close all batched hEvents
532 for(DWORD i = log->batch.used; i < log->batch.size ;i++) {
533 if(log->batch.hEvents[i])
534 EvtClose(log->batch.hEvents[i]);
535
536 log->batch.hEvents[i] = NULL;
537 }
538 log->batch.used = 0;
539 log->batch.size = 0;
540
541 if (log->hQuery) {
542 EvtClose(log->hQuery);
543 log->hQuery = NULL;
544 }
545
546 log->query_stats.event_count = 0;
547 log->query_stats.failed_count = 0;
548 }
549
550 // --------------------------------------------------------------------------------------------------------------------
551 // Log management
552
553 WEVT_LOG *wevt_openlog6(WEVT_QUERY_TYPE type) {
554 WEVT_LOG *log = callocz(1, sizeof(*log));
555 log->type = type;
556
557 // create the system render
558 log->hRenderSystemContext = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
559 if (!log->hRenderSystemContext) {
560 nd_log(NDLS_COLLECTORS, NDLP_ERR,
561 "EvtCreateRenderContext() on system context failed, extended info: %s",
562 EvtGetExtendedStatus_utf8());
563 goto cleanup;
564 }
565
566 if(type & WEVT_QUERY_EVENT_DATA) {
567 log->hRenderUserContext = EvtCreateRenderContext(0, NULL, EvtRenderContextUser);
568 if (!log->hRenderUserContext) {
569 nd_log(NDLS_COLLECTORS, NDLP_ERR,
570 "EvtCreateRenderContext failed, on user context failed, extended info: %s",
571 EvtGetExtendedStatus_utf8());
572 goto cleanup;
573 }
574
575 log->ops.event_data = buffer_create(4096, NULL);
576 }
577
578 return log;
579
580 cleanup:
581 wevt_closelog6(log);
582 return NULL;
583 }
584
585 void wevt_closelog6(WEVT_LOG *log) {
586 wevt_query_done(log);
587
588 if (log->hRenderSystemContext)
589 EvtClose(log->hRenderSystemContext);
590
591 if (log->hRenderUserContext)
592 EvtClose(log->hRenderUserContext);
593
594 wevt_variant_cleanup(&log->ops.raw.system);
595 wevt_variant_cleanup(&log->ops.raw.user);
596 txt_utf16_cleanup(&log->ops.unicode);
597 txt_utf8_cleanup(&log->ops.channel);
598 txt_utf8_cleanup(&log->ops.provider);
599 txt_utf8_cleanup(&log->ops.computer);
600 txt_utf8_cleanup(&log->ops.account);
601 txt_utf8_cleanup(&log->ops.domain);
602 txt_utf8_cleanup(&log->ops.sid);
603
604 txt_utf8_cleanup(&log->ops.event);
605 txt_utf8_cleanup(&log->ops.level);
606 txt_utf8_cleanup(&log->ops.keywords);
607 txt_utf8_cleanup(&log->ops.opcode);
608 txt_utf8_cleanup(&log->ops.task);
609 txt_utf8_cleanup(&log->ops.xml);
610
611 buffer_free(log->ops.event_data);
612
613 freez(log);
614 }
615
616 // --------------------------------------------------------------------------------------------------------------------
617 // Retention
618
619 bool wevt_channel_retention(WEVT_LOG *log, const wchar_t *channel, const wchar_t *query, EVT_RETENTION *retention) {
620 bool ret = false;
621
622 // get the number of the oldest record in the log
623 // "EvtGetLogInfo()" does not work properly with "EvtLogOldestRecordNumber"
624 // we have to get it from the first EventRecordID
625
626 // query the eventlog
627 log->hQuery = EvtQuery(NULL, channel, query, EvtQueryChannelPath | EvtQueryForwardDirection | EvtQueryTolerateQueryErrors);
628 if (!log->hQuery) {
629 if (GetLastError() == ERROR_EVT_CHANNEL_NOT_FOUND)
630 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtQuery() for retention failed, channel '%s' not found, cannot get retention, extended info: %s",
631 channel2utf8(channel), EvtGetExtendedStatus_utf8());
632 else
633 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtQuery() for retention on channel '%s' failed, cannot get retention, extended info: %s",
634 channel2utf8(channel), EvtGetExtendedStatus_utf8());
635
636 goto cleanup;
637 }
638
639 if (!wevt_get_next_event(log, &retention->first_event))
640 goto cleanup;
641
642 if (!retention->first_event.id) {
643 // no data in the event log
644 retention->first_event = retention->last_event = WEVT_EVENT_EMPTY;
645 ret = true;
646 goto cleanup;
647 }
648 EvtClose(log->hQuery);
649
650 log->hQuery = EvtQuery(NULL, channel, query, EvtQueryChannelPath | EvtQueryReverseDirection | EvtQueryTolerateQueryErrors);
651 if (!log->hQuery) {
652 if (GetLastError() == ERROR_EVT_CHANNEL_NOT_FOUND)
653 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtQuery() for retention failed, channel '%s' not found, extended info: %s",
654 channel2utf8(channel), EvtGetExtendedStatus_utf8());
655 else
656 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtQuery() for retention on channel '%s' failed, extended info: %s",
657 channel2utf8(channel), EvtGetExtendedStatus_utf8());
658
659 goto cleanup;
660 }
661
662 if (!wevt_get_next_event(log, &retention->last_event) || retention->last_event.id == 0) {
663 // no data in eventlog
664 retention->last_event = retention->first_event;
665 }
666 retention->last_event.id += 1; // we should read the last record
667 ret = true;
668
669 cleanup:
670 wevt_query_done(log);
671
672 if(ret) {
673 retention->entries = (channel && !query) ? retention->last_event.id - retention->first_event.id : 0;
674
675 if(retention->last_event.created_ns >= retention->first_event.created_ns)
676 retention->duration_ns = retention->last_event.created_ns - retention->first_event.created_ns;
677 else
678 retention->duration_ns = retention->first_event.created_ns - retention->last_event.created_ns;
679
680 retention->size_bytes = wevt_log_file_size(channel);
681 }
682 else
683 memset(retention, 0, sizeof(*retention));
684
685 return ret;
686 }
687
688 static uint64_t wevt_log_file_size(const wchar_t *channel) {
689 EVT_HANDLE hLog = NULL;
690 EVT_VARIANT evtVariant;
691 DWORD bufferUsed = 0;
692 uint64_t file_size = 0;
693
694 // Open the event log channel
695 hLog = EvtOpenLog(NULL, channel, EvtOpenChannelPath);
696 if (!hLog) {
697 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtOpenLog() on channel '%s' failed, extended info: %s",
698 channel2utf8(channel), EvtGetExtendedStatus_utf8());
699 goto cleanup;
700 }
701
702 // Get the file size of the log
703 if (!EvtGetLogInfo(hLog, EvtLogFileSize, sizeof(evtVariant), &evtVariant, &bufferUsed)) {
704 nd_log(NDLS_COLLECTORS, NDLP_ERR, "EvtGetLogInfo() on channel '%s' failed, extended info: %s",
705 channel2utf8(channel), EvtGetExtendedStatus_utf8());
706 goto cleanup;
707 }
708
709 // Extract the file size from the EVT_VARIANT structure
710 file_size = evtVariant.UInt64Val;
711
712 cleanup:
713 if (hLog)
714 EvtClose(hLog);
715
716 return file_size;
717 }