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+
3792struct tlshd_tags_filter ;
3893
3994/**
@@ -53,6 +108,244 @@ struct tlshd_tags_filter_type {
53108 */
54109static 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