]> git.ipfire.org Git - thirdparty/openssl.git/blame - ssl/quic/qlog.c
QLOG: Frontend: Implementation
[thirdparty/openssl.git] / ssl / quic / qlog.c
CommitLineData
00b27f33
HL
1/*
2 * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#include "internal/qlog.h"
11#include "internal/json_enc.h"
12#include "internal/common.h"
13#include "internal/cryptlib.h"
14
15# define BITS_PER_WORD (sizeof(size_t) * 8)
16# define NUM_ENABLED_W ((QLOG_EVENT_TYPE_NUM + BITS_PER_WORD - 1) / BITS_PER_WORD)
17
18static ossl_unused ossl_inline int bit_get(const size_t *p, uint32_t bit_no)
19{
20 return p[bit_no / BITS_PER_WORD] & (((size_t)1) << (bit_no % BITS_PER_WORD));
21}
22
23static ossl_unused ossl_inline void bit_set(size_t *p, uint32_t bit_no, int enable)
24{
25 size_t mask = (((size_t)1) << (bit_no % BITS_PER_WORD));
26
27 if (enable)
28 p[bit_no / BITS_PER_WORD] |= mask;
29 else
30 p[bit_no / BITS_PER_WORD] &= mask;
31}
32
33/* TODO abort */
34
35struct qlog_st {
36 QLOG_TRACE_INFO info;
37
38 BIO *bio;
39 size_t enabled[NUM_ENABLED_W];
40 uint32_t event_type;
41 const char *event_cat, *event_name, *event_combined_name;
42 OSSL_TIME event_time, prev_event_time;
43 JSON_ENC json;
44 int header_done, first_event_done;
45};
46
47static OSSL_TIME default_now(void *arg)
48{
49 return ossl_time_now();
50}
51
52/*
53 * Construction
54 * ============
55 */
56QLOG *ossl_qlog_new(const QLOG_TRACE_INFO *info)
57{
58 QLOG *qlog = OPENSSL_zalloc(sizeof(QLOG));
59
60 if (qlog == NULL)
61 return NULL;
62
63 qlog->info = *info;
64
65 qlog->info.title = NULL;
66 qlog->info.description = NULL;
67 qlog->info.group_id = NULL;
68
69 if (info->title != NULL
70 && (qlog->info.title = OPENSSL_strdup(info->title)) == NULL)
71 goto err;
72
73 if (info->description != NULL
74 && (qlog->info.description = OPENSSL_strdup(info->description)) == NULL)
75 goto err;
76
77 if (info->group_id != NULL
78 && (qlog->info.group_id = OPENSSL_strdup(info->group_id)) == NULL)
79 goto err;
80
81 if (!ossl_json_init(&qlog->json, NULL, JSON_FLAG_IJSON | JSON_FLAG_SEQ))
82 goto err;
83
84 if (qlog->info.now_cb == NULL)
85 qlog->info.now_cb = default_now;
86
87 return qlog;
88
89err:
90 if (qlog != NULL) {
91 OPENSSL_free((char *)qlog->info.title);
92 OPENSSL_free((char *)qlog->info.description);
93 OPENSSL_free((char *)qlog->info.group_id);
94 OPENSSL_free(qlog);
95 }
96 return NULL;
97}
98
99QLOG *ossl_qlog_new_from_env(const QLOG_TRACE_INFO *info)
100{
101 QLOG *qlog;
102 const char *qlogdir = ossl_safe_getenv("QLOGDIR");
103 const char *qfilter = ossl_safe_getenv("QFILTER");
104 char qlogdir_sep, *filename = NULL;
105 size_t i, l, strl;
106
107 if (info == NULL || qlogdir == NULL || strlen(qlogdir) == 0)
108 return NULL;
109
110 l = strlen(qlogdir);
111 qlogdir_sep = ossl_determine_dirsep(qlogdir);
112
113 /* strlen("client" / "server"); strlen(".sqlog"); _; separator; _; NUL */
114 strl = l + info->odcid.id_len * 2 + 6 + 7 + 2;
115 filename = OPENSSL_malloc(strl + 1);
116 if (filename == NULL)
117 return NULL;
118
119 memcpy(filename, qlogdir, l);
120 if (qlogdir_sep != '\0')
121 filename[l++] = qlogdir_sep;
122
123 for (i = 0; i < info->odcid.id_len; ++i)
124 l += sprintf(filename + l, "%02x", info->odcid.id[i]);
125
126 l += snprintf(filename + l, strl, "_%s.sqlog",
127 info->is_server ? "server" : "client");
128
129 qlog = ossl_qlog_new(info);
130 if (qlog == NULL) {
131 OPENSSL_free(filename);
132 return NULL;
133 }
134
135 if (!ossl_qlog_set_sink_filename(qlog, filename)) {
136 OPENSSL_free(filename);
137 ossl_qlog_free(qlog);
138 return NULL;
139 }
140
141 if (qfilter != NULL && !ossl_qlog_set_filter(qlog, qfilter)) {
142 OPENSSL_free(filename);
143 ossl_qlog_free(qlog);
144 return NULL;
145 }
146
147 OPENSSL_free(filename);
148 return qlog;
149}
150
151void ossl_qlog_free(QLOG *qlog)
152{
153 if (qlog == NULL)
154 return;
155
156 ossl_json_cleanup(&qlog->json);
157 BIO_free_all(qlog->bio);
158 OPENSSL_free((char *)qlog->info.title);
159 OPENSSL_free((char *)qlog->info.description);
160 OPENSSL_free((char *)qlog->info.group_id);
161 OPENSSL_free(qlog);
162}
163
164/*
165 * Configuration
166 * =============
167 */
168int ossl_qlog_set_sink_bio(QLOG *qlog, BIO *bio)
169{
170 if (qlog == NULL)
171 return 0;
172
173 ossl_qlog_flush(qlog); /* best effort */
174 BIO_free_all(qlog->bio);
175 qlog->bio = bio;
176 ossl_json_set_sink(&qlog->json, bio);
177 return 1;
178}
179
180#ifndef OPENSSL_NO_STDIO
181
182int ossl_qlog_set_sink_file(QLOG *qlog, FILE *f, int close_flag)
183{
184 BIO *bio;
185
186 if (qlog == NULL)
187 return 0;
188
189 bio = BIO_new_fp(f, BIO_CLOSE);
190 if (bio == NULL)
191 return 0;
192
193 if (!ossl_qlog_set_sink_bio(qlog, bio)) {
194 BIO_free_all(bio);
195 return 0;
196 }
197
198 return 1;
199}
200
201#endif
202
203int ossl_qlog_set_sink_filename(QLOG *qlog, const char *filename)
204{
205 BIO *bio;
206
207 if (qlog == NULL)
208 return 0;
209
210 /*
211 * We supply our own text encoding as JSON requires UTF-8, so disable any
212 * OS-specific processing here.
213 */
214 bio = BIO_new_file(filename, "wb");
215 if (bio == NULL)
216 return 0;
217
218 if (!ossl_qlog_set_sink_bio(qlog, bio)) {
219 BIO_free_all(bio);
220 return 0;
221 }
222
223 return 1;
224}
225
226int ossl_qlog_flush(QLOG *qlog)
227{
228 if (qlog == NULL)
229 return 1;
230
231 return ossl_json_flush(&qlog->json);
232}
233
234int ossl_qlog_set_event_type_enabled(QLOG *qlog, uint32_t event_type,
235 int enabled)
236{
237 if (qlog == NULL || event_type >= QLOG_EVENT_TYPE_NUM)
238 return 0;
239
240 bit_set(qlog->enabled, event_type, enabled);
241 return 1;
242}
243
244int ossl_qlog_enabled(QLOG *qlog, uint32_t event_type)
245{
246 if (qlog == NULL)
247 return 0;
248
249 return bit_get(qlog->enabled, event_type);
250}
251
252/*
253 * Event Lifecycle
254 * ===============
255 */
256static void write_str_once(QLOG *qlog, const char *key, char **p)
257{
258 if (*p == NULL)
259 return;
260
261 ossl_json_key(&qlog->json, key);
262 ossl_json_str(&qlog->json, *p);
263
264 OPENSSL_free(*p);
265 *p = NULL;
266}
267
268static void qlog_event_seq_header(QLOG *qlog)
269{
270 if (qlog->header_done)
271 return;
272
273 ossl_json_object_begin(&qlog->json);
274 {
275 ossl_json_key(&qlog->json, "qlog_version");
276 ossl_json_str(&qlog->json, "0.3");
277
278 ossl_json_key(&qlog->json, "qlog_format");
279 ossl_json_str(&qlog->json, "JSON-SEQ");
280
281 write_str_once(qlog, "title", (char **)&qlog->info.title);
282 write_str_once(qlog, "description", (char **)&qlog->info.description);
283
284 ossl_json_key(&qlog->json, "trace");
285 ossl_json_object_begin(&qlog->json);
286 {
287 ossl_json_key(&qlog->json, "common_fields");
288 ossl_json_object_begin(&qlog->json);
289 {
290 ossl_json_key(&qlog->json, "time_format");
291 ossl_json_str(&qlog->json, "delta");
292
293 ossl_json_key(&qlog->json, "protocol_type");
294 ossl_json_array_begin(&qlog->json);
295 {
296 ossl_json_str(&qlog->json, "QUIC");
297 }
298 ossl_json_array_end(&qlog->json);
299
300 write_str_once(qlog, "group_id", (char **)&qlog->info.group_id);
301
302 ossl_json_key(&qlog->json, "system_info");
303 ossl_json_object_begin(&qlog->json);
304 {
305 ossl_json_key(&qlog->json, "process_id");
306 ossl_json_u64(&qlog->json, (uint64_t)getpid());
307 }
308 ossl_json_object_end(&qlog->json);
309 }
310 ossl_json_object_end(&qlog->json);
311
312 ossl_json_key(&qlog->json, "vantage_point");
313 ossl_json_object_begin(&qlog->json);
314 {
315 ossl_json_key(&qlog->json, "type");
316 ossl_json_str(&qlog->json, qlog->info.is_server
317 ? "server" : "client");
318 }
319 ossl_json_object_end(&qlog->json);
320 }
321 ossl_json_object_end(&qlog->json);
322 }
323 ossl_json_object_end(&qlog->json);
324
325 qlog->header_done = 1;
326}
327
328static void qlog_event_prologue(QLOG *qlog)
329{
330 qlog_event_seq_header(qlog);
331
332 ossl_json_object_begin(&qlog->json);
333
334 ossl_json_key(&qlog->json, "name");
335 ossl_json_str(&qlog->json, qlog->event_combined_name);
336
337 ossl_json_key(&qlog->json, "data");
338 ossl_json_object_begin(&qlog->json);
339}
340
341static void qlog_event_epilogue(QLOG *qlog)
342{
343 ossl_json_object_end(&qlog->json);
344
345 ossl_json_key(&qlog->json, "time");
346 if (!qlog->first_event_done) {
347 ossl_json_u64(&qlog->json, ossl_time2ms(qlog->event_time));
348 qlog->prev_event_time = qlog->event_time;
349 qlog->first_event_done = 1;
350 } else {
351 OSSL_TIME delta = ossl_time_subtract(qlog->event_time,
352 qlog->prev_event_time);
353
354 ossl_json_u64(&qlog->json, ossl_time2ms(delta));
355 qlog->prev_event_time = qlog->event_time;
356 }
357
358 ossl_json_object_end(&qlog->json);
359}
360
361int ossl_qlog_event_try_begin(QLOG *qlog,
362 uint32_t event_type,
363 const char *event_cat,
364 const char *event_name,
365 const char *event_combined_name)
366{
367 if (qlog == NULL)
368 return 0;
369
370 if (!ossl_assert(qlog->event_type == QLOG_EVENT_TYPE_NONE)
371 || !ossl_qlog_enabled(qlog, event_type))
372 return 0;
373
374 qlog->event_type = event_type;
375 qlog->event_cat = event_cat;
376 qlog->event_name = event_name;
377 qlog->event_combined_name = event_combined_name;
378 qlog->event_time = qlog->info.now_cb(qlog->info.now_cb_arg);
379
380 qlog_event_prologue(qlog);
381 return 1;
382}
383
384static void qlog_event_end(QLOG *qlog, int abort)
385{
386 if (!ossl_assert(qlog != NULL && qlog->event_type != QLOG_EVENT_TYPE_NONE))
387 return;
388
389 qlog_event_epilogue(qlog);
390 qlog->event_type = QLOG_EVENT_TYPE_NONE;
391}
392
393void ossl_qlog_event_end(QLOG *qlog)
394{
395 qlog_event_end(qlog, /*abort=*/0);
396}
397
398void ossl_qlog_event_abort(QLOG *qlog)
399{
400 qlog_event_end(qlog, /*abort=*/1);
401}
402
403/*
404 * Field Generators
405 * ================
406 */
407void ossl_qlog_group_begin(QLOG *qlog, const char *name)
408{
409 if (name != NULL)
410 ossl_json_key(&qlog->json, name);
411
412 ossl_json_object_begin(&qlog->json);
413}
414
415void ossl_qlog_group_end(QLOG *qlog)
416{
417 ossl_json_object_end(&qlog->json);
418}
419
420void ossl_qlog_array_begin(QLOG *qlog, const char *name)
421{
422 if (name != NULL)
423 ossl_json_key(&qlog->json, name);
424
425 ossl_json_array_begin(&qlog->json);
426}
427
428void ossl_qlog_array_end(QLOG *qlog)
429{
430 ossl_json_array_end(&qlog->json);
431}
432
433void ossl_qlog_override_time(QLOG *qlog, OSSL_TIME event_time)
434{
435 qlog->event_time = event_time;
436}
437
438void ossl_qlog_str(QLOG *qlog, const char *name, const char *value)
439{
440 if (name != NULL)
441 ossl_json_key(&qlog->json, name);
442
443 ossl_json_str(&qlog->json, value);
444}
445
446void ossl_qlog_str_len(QLOG *qlog, const char *name,
447 const char *value, size_t value_len)
448{
449 if (name != NULL)
450 ossl_json_key(&qlog->json, name);
451
452 ossl_json_str_len(&qlog->json, value, value_len);
453}
454
455void ossl_qlog_u64(QLOG *qlog, const char *name, uint64_t value)
456{
457 if (name != NULL)
458 ossl_json_key(&qlog->json, name);
459
460 ossl_json_u64(&qlog->json, value);
461}
462
463void ossl_qlog_i64(QLOG *qlog, const char *name, int64_t value)
464{
465 if (name != NULL)
466 ossl_json_key(&qlog->json, name);
467
468 ossl_json_i64(&qlog->json, value);
469}
470
471void ossl_qlog_bool(QLOG *qlog, const char *name, int value)
472{
473 if (name != NULL)
474 ossl_json_key(&qlog->json, name);
475
476 ossl_json_bool(&qlog->json, value);
477}
478
479void ossl_qlog_bin(QLOG *qlog, const char *name,
480 const void *value, size_t value_len)
481{
482 if (name != NULL)
483 ossl_json_key(&qlog->json, name);
484
485 ossl_json_str_hex(&qlog->json, value, value_len);
486}
487
488/*
489 * Filter Parsing
490 * ==============
491 */
492struct lexer {
493 const char *p, *term_end, *end;
494};
495
496static ossl_inline int is_term_sep_ws(char c)
497{
498 return c == ' ' || c == '\r' || c == '\n' || c == '\t';
499}
500
501static ossl_inline int is_name_char(char c)
502{
503 return (c >= 'a' && c <= 'z')
504 || (c >= 'A' && c <= 'Z')
505 || c == '_' || c == '-';
506}
507
508static int lex_init(struct lexer *lex, const char *in, size_t in_len)
509{
510 if (in == NULL)
511 return 0;
512
513 lex->p = in;
514 lex->term_end = in;
515 lex->end = in + in_len;
516 return 1;
517}
518
519static int lex_do(struct lexer *lex)
520{
521 const char *p = lex->term_end, *end = lex->end, *term_end;
522
523 for (; is_term_sep_ws(*p) && p < end; ++p);
524
525 if (p == end) {
526 lex->p = end;
527 lex->term_end = end;
528 return 0;
529 }
530
531 for (term_end = p; !is_term_sep_ws(*term_end) && term_end < end; ++term_end);
532
533 lex->p = p;
534 lex->term_end = term_end;
535 return 1;
536}
537
538static int lex_eot(struct lexer *lex)
539{
540 return lex->p == lex->term_end;
541}
542
543static int lex_peek_char(struct lexer *lex)
544{
545 return lex_eot(lex) ? -1 : *lex->p;
546}
547
548static int lex_skip_char(struct lexer *lex)
549{
550 if (lex_eot(lex))
551 return 0;
552
553 ++lex->p;
554 return 1;
555}
556
557static int lex_match(struct lexer *lex, const char *s, size_t s_len)
558{
559 if ((size_t)(lex->term_end - lex->p) != s_len)
560 return 0;
561
562 if (memcmp(lex->p, s, s_len))
563 return 0;
564
565 return 1;
566}
567
568static void lex_get_rest(struct lexer *lex, const char **str, size_t *str_l)
569{
570 *str = lex->p;
571 *str_l = lex->term_end - lex->p;
572}
573
574static int lex_extract_to(struct lexer *lex, char c,
575 const char **str, size_t *str_l)
576{
577 const char *p = lex->p, *term_end = lex->term_end, *s;
578
579 for (s = p; s < term_end && *s != c; ++s);
580 if (s == term_end)
581 return 0;
582
583 *str = p;
584 *str_l = term_end - s;
585 lex->p = ++s;
586 return 1;
587}
588
589static int ossl_unused filter_match_event(const char *cat, size_t cat_l,
590 const char *event, size_t event_l,
591 const char *expect_cat,
592 const char *expect_event)
593{
594 size_t expect_cat_l = strlen(expect_cat);
595 size_t expect_event_l = strlen(expect_event);
596
597 if ((cat != NULL && cat_l != expect_cat_l)
598 || (event != NULL && event_l != expect_event_l)
599 || (cat != NULL && memcmp(cat, expect_cat, expect_cat_l))
600 || (event != NULL && memcmp(event, expect_event, expect_event_l)))
601 return 0;
602
603 return 1;
604}
605
606/*
607 * enabled: event enablement bitmask Array of size NUM_ENABLED_W.
608 * add: 1 to enable an event, 0 to disable.
609 * cat: Category name/length. Not necessarily zero terminated.
610 * NULL to match any.
611 * event: Event name/length. Not necessarily zero terminated.
612 * NULL to match any.
613 */
614static void filter_apply(size_t *enabled, int add,
615 const char *cat, size_t cat_l,
616 const char *event, size_t event_l)
617{
618 /* Find events which match the given filters. */
619# define QLOG_EVENT(e_cat, e_name) \
620 if (filter_match_event(cat, cat_l, event, event_l, \
621 #e_cat, #e_name)) \
622 bit_set(enabled, QLOG_EVENT_TYPE_##e_cat##_##e_name, add);
623# include "internal/qlog_events.h"
624# undef QLOG_EVENT
625}
626
627static int lex_fail(struct lexer *lex, const char *msg)
628{
629 lex->p = lex->term_end = lex->end;
630 return 0;
631}
632
633static int validate_name(const char **p, size_t *l)
634{
635 const char *p_ = *p;
636 size_t i, l_ = *l;
637
638 if (l_ == 1 && *p_ == '*') {
639 *p = NULL;
640 *l = 0;
641 return 1;
642 }
643
644 if (l_ == 0)
645 return 0;
646
647 for (i = 0; i < l_; ++i)
648 if (!is_name_char(p_[i]))
649 return 0;
650
651 return 1;
652}
653
654int ossl_qlog_set_filter(QLOG *qlog, const char *filter)
655{
656 struct lexer lex = {0};
657 char c;
658 const char *cat, *event;
659 size_t cat_l, event_l, enabled[NUM_ENABLED_W];
660 int add;
661
662 memcpy(enabled, qlog->enabled, sizeof(enabled));
663
664 if (!lex_init(&lex, filter, strlen(filter)))
665 return 0;
666
667 while (lex_do(&lex)) {
668 c = lex_peek_char(&lex);
669 if (c == '+' || c == '-') {
670 add = (c == '+');
671 lex_skip_char(&lex);
672
673 c = lex_peek_char(&lex);
674 if (!is_name_char(c) && c != '*')
675 return lex_fail(&lex, "expected alphanumeric name or '*'"
676 " after +/-");
677 } else if (!is_name_char(c) && c != '*') {
678 return lex_fail(&lex, "expected +/- or alphanumeric name or '*'");
679 } else {
680 add = 1;
681 }
682
683 if (lex_match(&lex, "*", 1)) {
684 filter_apply(enabled, add, NULL, 0, NULL, 0);
685 continue;
686 }
687
688 if (!lex_extract_to(&lex, ':', &cat, &cat_l))
689 return lex_fail(&lex, "expected ':' after category name");
690
691 lex_get_rest(&lex, &event, &event_l);
692 if (!validate_name(&cat, &cat_l))
693 return lex_fail(&lex, "expected alphanumeric category name or '*'");
694 if (!validate_name(&event, &event_l))
695 return lex_fail(&lex, "expected alphanumeric event name or '*'");
696
697 filter_apply(enabled, add, cat, cat_l, event, event_l);
698 }
699
700 memcpy(qlog->enabled, enabled, sizeof(enabled));
701 return 1;
702}