Skip to content

Commit 2db9f8a

Browse files
committed
tlshd: Add a YAML parser
The TLS session tag definitions are to be specified using YAML. Signed-off-by: Chuck Lever <[email protected]>
1 parent d675332 commit 2db9f8a

File tree

1 file changed

+257
-2
lines changed

1 file changed

+257
-2
lines changed

src/tlshd/tags.c

Lines changed: 257 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,47 @@
2727
#include <gnutls/abstract.h>
2828

2929
#include <glib.h>
30+
#include <yaml.h>
3031

3132
#include "tlshd.h"
3233

34+
/* --- libyaml helpers --- */
35+
36+
/* This depends on the yaml_event_type_t enum being densely packed */
37+
static const char *show_yaml_event_type(const yaml_event_t *event)
38+
{
39+
static const char *labels[] = {
40+
[YAML_NO_EVENT] = "YAML_NO_EVENT",
41+
[YAML_STREAM_START_EVENT] = "YAML_STREAM_START_EVENT",
42+
[YAML_STREAM_END_EVENT] = "YAML_STREAM_END_EVENT",
43+
[YAML_DOCUMENT_START_EVENT] = "YAML_DOCUMENT_START_EVENT",
44+
[YAML_DOCUMENT_END_EVENT] = "YAML_DOCUMENT_END_EVENT",
45+
[YAML_ALIAS_EVENT] = "YAML_ALIAS_EVENT",
46+
[YAML_SCALAR_EVENT] = "YAML_SCALAR_EVENT",
47+
[YAML_SEQUENCE_START_EVENT] = "YAML_SEQUENCE_START_EVENT",
48+
[YAML_SEQUENCE_END_EVENT] = "YAML_SEQUENCE_END_EVENT",
49+
[YAML_MAPPING_START_EVENT] = "YAML_MAPPING_START_EVENT",
50+
[YAML_MAPPING_END_EVENT] = "YAML_MAPPING_END_EVENT",
51+
};
52+
53+
if (event->type > YAML_MAPPING_END_EVENT)
54+
return "invalid YAML event";
55+
return labels[event->type];
56+
}
57+
58+
/* --- Tag configuration file parsing --- */
59+
60+
enum tlshd_tags_fsm_state_index {
61+
PS_STOP,
62+
PS_START,
63+
PS_STREAM,
64+
PS_DOCUMENT,
65+
PS_TOP_LEVEL,
66+
67+
PS_UNEXPECTED_INPUT_TOKEN,
68+
PS_FAILURE,
69+
};
70+
3371
struct tlshd_tags_filter;
3472

3573
struct tlshd_tags_filter_type {
@@ -41,6 +79,212 @@ struct tlshd_tags_filter_type {
4179

4280
static GHashTable *tlshd_tags_filter_type_hash;
4381

82+
struct tlshd_tags_parser_state {
83+
yaml_event_t ps_yaml_event;
84+
85+
enum tlshd_tags_fsm_state_index ps_fsm_state;
86+
};
87+
88+
static enum tlshd_tags_fsm_state_index
89+
tlshd_tags_top_level(struct tlshd_tags_parser_state *current)
90+
{
91+
const yaml_event_t *event = &current->ps_yaml_event;
92+
const char *mapping = (const char *)event->data.scalar.value;
93+
94+
tlshd_log_error("Unexpected mapping name: %s\n", mapping);
95+
return PS_UNEXPECTED_INPUT_TOKEN;
96+
}
97+
98+
/* --- FSM states --- */
99+
100+
typedef enum tlshd_tags_fsm_state_index
101+
(*tlshd_tags_action_fn)(struct tlshd_tags_parser_state *current);
102+
103+
struct tlshd_tags_fsm_transition {
104+
yaml_event_type_t pt_yaml_event;
105+
enum tlshd_tags_fsm_state_index pt_next_state;
106+
tlshd_tags_action_fn pt_action;
107+
};
108+
109+
#define NEXT_STATE(event, state) \
110+
{ \
111+
.pt_yaml_event = event, \
112+
.pt_next_state = state, \
113+
}
114+
115+
#define NEXT_ACTION(event, action) \
116+
{ \
117+
.pt_yaml_event = event, \
118+
.pt_action = action, \
119+
}
120+
121+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_start[] = {
122+
NEXT_STATE(YAML_STREAM_START_EVENT, PS_STREAM),
123+
};
124+
125+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_stream[] = {
126+
NEXT_STATE(YAML_DOCUMENT_START_EVENT, PS_DOCUMENT),
127+
NEXT_STATE(YAML_STREAM_END_EVENT, PS_STOP),
128+
};
129+
130+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_document[] = {
131+
NEXT_STATE(YAML_MAPPING_START_EVENT, PS_TOP_LEVEL),
132+
NEXT_STATE(YAML_DOCUMENT_END_EVENT, PS_STREAM),
133+
};
134+
135+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_top_level[] = {
136+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_top_level),
137+
NEXT_STATE(YAML_MAPPING_END_EVENT, PS_DOCUMENT),
138+
};
139+
140+
struct tlshd_tags_fsm_state {
141+
const char *ts_name;
142+
const struct tlshd_tags_fsm_transition *ts_transitions;
143+
size_t ts_transition_count;
144+
};
145+
146+
#define FSM_STATE(name, array) \
147+
[name] = { \
148+
.ts_name = #name, \
149+
.ts_transitions = array, \
150+
.ts_transition_count = ARRAY_SIZE(array), \
151+
}
152+
153+
#define TERMINAL_STATE(name) \
154+
[name] = { \
155+
.ts_name = #name, \
156+
.ts_transition_count = 0, \
157+
}
158+
159+
static const struct tlshd_tags_fsm_state tlshd_tags_fsm_state_table[] = {
160+
TERMINAL_STATE(PS_STOP),
161+
FSM_STATE(PS_START, tlshd_tags_transitions_start),
162+
FSM_STATE(PS_STREAM, tlshd_tags_transitions_stream),
163+
FSM_STATE(PS_DOCUMENT, tlshd_tags_transitions_document),
164+
FSM_STATE(PS_TOP_LEVEL, tlshd_tags_transitions_top_level),
165+
TERMINAL_STATE(PS_UNEXPECTED_INPUT_TOKEN),
166+
TERMINAL_STATE(PS_FAILURE),
167+
};
168+
169+
/*
170+
* Each libyaml event produces zero or one input tokens.
171+
*
172+
* tlshd_tags_process_yaml_event() evaluates the event token based on
173+
* the current parser state, then advances to the next FSM state.
174+
*/
175+
static void
176+
tlshd_tags_process_yaml_event(struct tlshd_tags_parser_state *current)
177+
{
178+
const struct tlshd_tags_fsm_state *fsm_state =
179+
&tlshd_tags_fsm_state_table[current->ps_fsm_state];
180+
const yaml_event_t *event = &current->ps_yaml_event;
181+
const struct tlshd_tags_fsm_transition *transition;
182+
size_t i;
183+
184+
if (fsm_state->ts_transition_count == 0)
185+
return;
186+
187+
transition = NULL;
188+
for (i = 0; i < fsm_state->ts_transition_count; ++i) {
189+
if (fsm_state->ts_transitions[i].pt_yaml_event == event->type) {
190+
transition = &fsm_state->ts_transitions[i];
191+
break;
192+
}
193+
}
194+
if (transition == NULL) {
195+
tlshd_log_debug("ps_state=%s, unexpected event: %s\n",
196+
fsm_state->ts_name,
197+
show_yaml_event_type(event));
198+
current->ps_fsm_state = PS_FAILURE;
199+
return;
200+
}
201+
202+
if (tlshd_debug > 3)
203+
tlshd_log_debug("ps_state=%s yaml event=%s",
204+
fsm_state->ts_name,
205+
show_yaml_event_type(event));
206+
207+
if (transition->pt_action)
208+
current->ps_fsm_state = transition->pt_action(current);
209+
else
210+
current->ps_fsm_state = transition->pt_next_state;
211+
}
212+
213+
static void tlshd_tags_parse_file(const char *filename)
214+
{
215+
struct tlshd_tags_parser_state current;
216+
yaml_parser_t parser;
217+
FILE *fh;
218+
219+
if (!yaml_parser_initialize(&parser)) {
220+
tlshd_log_error("Failed to initialize parser!\n");
221+
return;
222+
}
223+
224+
fh = fopen(filename, "r");
225+
if (!fh) {
226+
tlshd_log_perror("fopen");
227+
yaml_parser_delete(&parser);
228+
return;
229+
}
230+
yaml_parser_set_input_file(&parser, fh);
231+
232+
tlshd_log_debug("Parsing tags config file '%s'", filename);
233+
234+
current.ps_fsm_state = PS_START;
235+
do {
236+
if (!yaml_parser_parse(&parser, &current.ps_yaml_event)) {
237+
tlshd_log_error("Parser error %d\n",
238+
parser.error);
239+
break;
240+
}
241+
tlshd_tags_process_yaml_event(&current);
242+
yaml_event_delete(&current.ps_yaml_event);
243+
244+
if (current.ps_fsm_state == PS_FAILURE ||
245+
current.ps_fsm_state == PS_UNEXPECTED_INPUT_TOKEN) {
246+
tlshd_log_error("Tag parsing failed, line: %zu column: %zu file: %s\n",
247+
parser.mark.line + 1,
248+
parser.mark.column,
249+
filename);
250+
break;
251+
}
252+
} while (current.ps_fsm_state != PS_STOP);
253+
254+
yaml_parser_delete(&parser);
255+
fclose(fh);
256+
}
257+
258+
static bool tlshd_tags_read_directory(const char *tagsdir)
259+
{
260+
const gchar *filename;
261+
GError *error;
262+
GDir *dir;
263+
264+
error = NULL;
265+
dir = g_dir_open(tagsdir, 0, &error);
266+
if (!dir) {
267+
tlshd_log_gerror("Failed to open the tags directory", error);
268+
g_error_free(error);
269+
return false;
270+
}
271+
272+
while ((filename = g_dir_read_name(dir)) != NULL) {
273+
gchar *pathname;
274+
275+
if (!g_str_has_suffix(filename, ".yml") &&
276+
!g_str_has_suffix(filename, ".yaml"))
277+
continue;
278+
pathname = g_build_filename(tagsdir, filename, NULL);
279+
280+
tlshd_tags_parse_file(pathname);
281+
g_free(pathname);
282+
}
283+
284+
g_dir_close(dir);
285+
return true;
286+
}
287+
44288
/* --- Filter Types --- */
45289

46290
static const struct tlshd_tags_filter_type tlshd_tags_static_filter_types[] = {
@@ -148,9 +392,20 @@ static bool tlshd_tags_filter_type_hash_init(void)
148392
* @tagsdir: pathname of directory containing files that define tags
149393
*
150394
*/
151-
bool tlshd_tags_config_init(__attribute__ ((unused)) const char *tagsdir)
395+
bool tlshd_tags_config_init(const char *tagsdir)
152396
{
153-
return tlshd_tags_filter_type_hash_init();
397+
if (!tlshd_tags_filter_type_hash_init())
398+
goto out;
399+
400+
if (!tlshd_tags_read_directory(tagsdir))
401+
goto filter_type_hash;
402+
403+
return true;
404+
405+
filter_type_hash:
406+
tlshd_tags_filter_type_hash_destroy();
407+
out:
408+
return false;
154409
}
155410

156411
/**

0 commit comments

Comments
 (0)