Skip to content

Commit 52bca1f

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 0b50210 commit 52bca1f

File tree

1 file changed

+306
-2
lines changed

1 file changed

+306
-2
lines changed

src/tlshd/tags.c

Lines changed: 306 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,64 @@
3131
#include <gnutls/abstract.h>
3232

3333
#include <glib.h>
34+
#include <yaml.h>
3435

3536
#include "tlshd.h"
3637

38+
/** @name tagsLibYamlHelpers
39+
*
40+
* libyaml helpers
41+
*/
42+
43+
///@{
44+
45+
/**
46+
* @brief Show a human-readable YAML event symbol
47+
* @param[in] event A YAML parser event
48+
*
49+
* This implementation depends on the yaml_event_type_t enum being
50+
* densely packed.
51+
*
52+
* @returns a constant NUL-terminated C string.
53+
*/
54+
static const char *show_yaml_event_type(const yaml_event_t *event)
55+
{
56+
static const char *labels[] = {
57+
[YAML_NO_EVENT] = "YAML_NO_EVENT",
58+
[YAML_STREAM_START_EVENT] = "YAML_STREAM_START_EVENT",
59+
[YAML_STREAM_END_EVENT] = "YAML_STREAM_END_EVENT",
60+
[YAML_DOCUMENT_START_EVENT] = "YAML_DOCUMENT_START_EVENT",
61+
[YAML_DOCUMENT_END_EVENT] = "YAML_DOCUMENT_END_EVENT",
62+
[YAML_ALIAS_EVENT] = "YAML_ALIAS_EVENT",
63+
[YAML_SCALAR_EVENT] = "YAML_SCALAR_EVENT",
64+
[YAML_SEQUENCE_START_EVENT] = "YAML_SEQUENCE_START_EVENT",
65+
[YAML_SEQUENCE_END_EVENT] = "YAML_SEQUENCE_END_EVENT",
66+
[YAML_MAPPING_START_EVENT] = "YAML_MAPPING_START_EVENT",
67+
[YAML_MAPPING_END_EVENT] = "YAML_MAPPING_END_EVENT",
68+
};
69+
70+
if (!event || event->type < 0 || event->type > ARRAY_SIZE(labels))
71+
return "invalid YAML event";
72+
return labels[event->type];
73+
}
74+
75+
///@}
76+
77+
/**
78+
* @enum tlshd_tags_fsm_state_index
79+
* YAML parser finite states
80+
*/
81+
enum tlshd_tags_fsm_state_index {
82+
PS_STOP,
83+
PS_START,
84+
PS_STREAM,
85+
PS_DOCUMENT,
86+
PS_TOP_LEVEL,
87+
88+
PS_UNEXPECTED_INPUT_TOKEN,
89+
PS_FAILURE,
90+
};
91+
3792
struct tlshd_tags_filter;
3893

3994
/**
@@ -53,6 +108,244 @@ struct tlshd_tags_filter_type {
53108
*/
54109
static GHashTable *tlshd_tags_filter_type_hash;
55110

111+
/**
112+
* @struct tlshd_tags_parser_state
113+
* @brief Global parser state
114+
*/
115+
struct tlshd_tags_parser_state {
116+
yaml_event_t ps_yaml_event;
117+
118+
enum tlshd_tags_fsm_state_index ps_fsm_state;
119+
};
120+
121+
static enum tlshd_tags_fsm_state_index
122+
tlshd_tags_top_level(struct tlshd_tags_parser_state *current)
123+
{
124+
const yaml_event_t *event = &current->ps_yaml_event;
125+
const char *mapping = (const char *)event->data.scalar.value;
126+
127+
tlshd_log_error("Unexpected mapping name: %s\n", mapping);
128+
return PS_UNEXPECTED_INPUT_TOKEN;
129+
}
130+
131+
/** @name tagsYamlFsm
132+
*
133+
* YAML parser finite state machine
134+
*/
135+
136+
///@{
137+
138+
typedef enum tlshd_tags_fsm_state_index
139+
(*tlshd_tags_action_fn)(struct tlshd_tags_parser_state *current);
140+
141+
/**
142+
* @struct tlshd_tags_fsm_transition
143+
* @brief One edge of the FSM transition graph
144+
*/
145+
struct tlshd_tags_fsm_transition {
146+
yaml_event_type_t pt_yaml_event;
147+
enum tlshd_tags_fsm_state_index pt_next_state;
148+
tlshd_tags_action_fn pt_action;
149+
};
150+
151+
#define NEXT_STATE(event, state) \
152+
{ \
153+
.pt_yaml_event = event, \
154+
.pt_next_state = state, \
155+
}
156+
157+
#define NEXT_ACTION(event, action) \
158+
{ \
159+
.pt_yaml_event = event, \
160+
.pt_action = action, \
161+
}
162+
163+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_start[] = {
164+
NEXT_STATE(YAML_STREAM_START_EVENT, PS_STREAM),
165+
};
166+
167+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_stream[] = {
168+
NEXT_STATE(YAML_DOCUMENT_START_EVENT, PS_DOCUMENT),
169+
NEXT_STATE(YAML_STREAM_END_EVENT, PS_STOP),
170+
};
171+
172+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_document[] = {
173+
NEXT_STATE(YAML_MAPPING_START_EVENT, PS_TOP_LEVEL),
174+
NEXT_STATE(YAML_DOCUMENT_END_EVENT, PS_STREAM),
175+
};
176+
177+
static const struct tlshd_tags_fsm_transition tlshd_tags_transitions_top_level[] = {
178+
NEXT_ACTION(YAML_SCALAR_EVENT, tlshd_tags_top_level),
179+
NEXT_STATE(YAML_MAPPING_END_EVENT, PS_DOCUMENT),
180+
};
181+
182+
struct tlshd_tags_fsm_state {
183+
const char *ts_name;
184+
const struct tlshd_tags_fsm_transition *ts_transitions;
185+
size_t ts_transition_count;
186+
};
187+
188+
#define FSM_STATE(name, array) \
189+
[name] = { \
190+
.ts_name = #name, \
191+
.ts_transitions = array, \
192+
.ts_transition_count = ARRAY_SIZE(array), \
193+
}
194+
195+
#define TERMINAL_STATE(name) \
196+
[name] = { \
197+
.ts_name = #name, \
198+
.ts_transition_count = 0, \
199+
}
200+
201+
/**
202+
* @var tlshd_tags_fsm_state_table
203+
* @brief YAML parser finite state machine table
204+
*/
205+
static const struct tlshd_tags_fsm_state tlshd_tags_fsm_state_table[] = {
206+
TERMINAL_STATE(PS_STOP),
207+
FSM_STATE(PS_START, tlshd_tags_transitions_start),
208+
FSM_STATE(PS_STREAM, tlshd_tags_transitions_stream),
209+
FSM_STATE(PS_DOCUMENT, tlshd_tags_transitions_document),
210+
FSM_STATE(PS_TOP_LEVEL, tlshd_tags_transitions_top_level),
211+
TERMINAL_STATE(PS_UNEXPECTED_INPUT_TOKEN),
212+
TERMINAL_STATE(PS_FAILURE),
213+
};
214+
215+
/**
216+
* @brief Process a YAML parsing event
217+
* @param [in,out] current Current YAML parser state
218+
*
219+
* Each libyaml event produces zero or one input tokens.
220+
* tlshd_tags_process_yaml_event() evaluates the event token based
221+
* on the current parser state, then advances to the next FSM state.
222+
*/
223+
static void
224+
tlshd_tags_process_yaml_event(struct tlshd_tags_parser_state *current)
225+
{
226+
const struct tlshd_tags_fsm_state *fsm_state =
227+
&tlshd_tags_fsm_state_table[current->ps_fsm_state];
228+
const yaml_event_t *event = &current->ps_yaml_event;
229+
const struct tlshd_tags_fsm_transition *transition;
230+
size_t i;
231+
232+
if (fsm_state->ts_transition_count == 0)
233+
return;
234+
235+
transition = NULL;
236+
for (i = 0; i < fsm_state->ts_transition_count; ++i) {
237+
if (fsm_state->ts_transitions[i].pt_yaml_event == event->type) {
238+
transition = &fsm_state->ts_transitions[i];
239+
break;
240+
}
241+
}
242+
if (transition == NULL) {
243+
tlshd_log_debug("ps_state=%s, unexpected event: %s\n",
244+
fsm_state->ts_name,
245+
show_yaml_event_type(event));
246+
current->ps_fsm_state = PS_FAILURE;
247+
return;
248+
}
249+
250+
if (tlshd_debug > 3)
251+
tlshd_log_debug("ps_state=%s yaml event=%s",
252+
fsm_state->ts_name,
253+
show_yaml_event_type(event));
254+
255+
if (transition->pt_action)
256+
current->ps_fsm_state = transition->pt_action(current);
257+
else
258+
current->ps_fsm_state = transition->pt_next_state;
259+
}
260+
261+
/**
262+
* @brief Read in one tag definition file
263+
* @param[in] filename pathname of file containing tag specifications
264+
*/
265+
static void tlshd_tags_parse_file(const char *filename)
266+
{
267+
struct tlshd_tags_parser_state current;
268+
yaml_parser_t parser;
269+
FILE *fh;
270+
271+
if (!yaml_parser_initialize(&parser)) {
272+
tlshd_log_error("Failed to initialize parser!\n");
273+
return;
274+
}
275+
276+
fh = fopen(filename, "r");
277+
if (!fh) {
278+
tlshd_log_perror("fopen");
279+
yaml_parser_delete(&parser);
280+
return;
281+
}
282+
yaml_parser_set_input_file(&parser, fh);
283+
284+
tlshd_log_debug("Parsing tags config file '%s'", filename);
285+
286+
current.ps_fsm_state = PS_START;
287+
do {
288+
if (!yaml_parser_parse(&parser, &current.ps_yaml_event)) {
289+
tlshd_log_error("Parser error %d\n",
290+
parser.error);
291+
break;
292+
}
293+
tlshd_tags_process_yaml_event(&current);
294+
yaml_event_delete(&current.ps_yaml_event);
295+
296+
if (current.ps_fsm_state == PS_FAILURE ||
297+
current.ps_fsm_state == PS_UNEXPECTED_INPUT_TOKEN) {
298+
tlshd_log_error("Tag parsing failed, line: %zu column: %zu file: %s\n",
299+
parser.mark.line + 1,
300+
parser.mark.column,
301+
filename);
302+
break;
303+
}
304+
} while (current.ps_fsm_state != PS_STOP);
305+
306+
yaml_parser_delete(&parser);
307+
fclose(fh);
308+
}
309+
310+
/**
311+
* @brief Read all the tag definition files in one directory
312+
* @param[in] tagsdir pathname of directory containing files that define tags
313+
*
314+
* @retval true Directory has been read without a permanent error
315+
* @retval false A permanent error occurred
316+
*/
317+
static bool tlshd_tags_read_directory(const char *tagsdir)
318+
{
319+
const gchar *filename;
320+
GError *error;
321+
GDir *dir;
322+
323+
error = NULL;
324+
dir = g_dir_open(tagsdir, 0, &error);
325+
if (!dir) {
326+
tlshd_log_gerror("Failed to open the tags directory", error);
327+
g_error_free(error);
328+
return false;
329+
}
330+
331+
while ((filename = g_dir_read_name(dir)) != NULL) {
332+
gchar *pathname;
333+
334+
if (!g_str_has_suffix(filename, ".yml") &&
335+
!g_str_has_suffix(filename, ".yaml"))
336+
continue;
337+
pathname = g_build_filename(tagsdir, filename, NULL);
338+
339+
tlshd_tags_parse_file(pathname);
340+
g_free(pathname);
341+
}
342+
343+
g_dir_close(dir);
344+
return true;
345+
}
346+
347+
///@}
348+
56349
/** @name tagsFilterTypes
57350
*
58351
* Add tag filter types to a hash table for fast lookup by type name.
@@ -183,9 +476,20 @@ static bool tlshd_tags_filter_type_hash_init(void)
183476
* @retval true Subsystem initialization succeeded
184477
* @retval false Subsystem initialization failed
185478
*/
186-
bool tlshd_tags_config_init(__attribute__ ((unused)) const char *tagsdir)
479+
bool tlshd_tags_config_init(const char *tagsdir)
187480
{
188-
return tlshd_tags_filter_type_hash_init();
481+
if (!tlshd_tags_filter_type_hash_init())
482+
goto out;
483+
484+
if (!tlshd_tags_read_directory(tagsdir))
485+
goto filter_type_hash;
486+
487+
return true;
488+
489+
filter_type_hash:
490+
tlshd_tags_filter_type_hash_destroy();
491+
out:
492+
return false;
189493
}
190494

191495
/**

0 commit comments

Comments
 (0)