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