]> git.stg.codes - stg.git/blob - libs/smux/xer_decoder.c
Disable cppcheck for foreign libs by default.
[stg.git] / libs / smux / xer_decoder.c
1 /*
2  * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  * Redistribution and modifications are permitted subject to BSD license.
4  */
5 #include <asn_application.h>
6 #include <asn_internal.h>
7 #include <xer_support.h>                /* XER/XML parsing support */
8
9
10 /*
11  * Decode the XER encoding of a given type.
12  */
13 asn_dec_rval_t
14 xer_decode(const asn_codec_ctx_t *opt_codec_ctx,
15            const asn_TYPE_descriptor_t *td, void **struct_ptr,
16            const void *buffer, size_t size) {
17     asn_codec_ctx_t s_codec_ctx;
18
19         /*
20          * Stack checker requires that the codec context
21          * must be allocated on the stack.
22          */
23         if(opt_codec_ctx) {
24                 if(opt_codec_ctx->max_stack_size) {
25                         s_codec_ctx = *opt_codec_ctx;
26                         opt_codec_ctx = &s_codec_ctx;
27                 }
28         } else {
29                 /* If context is not given, be security-conscious anyway */
30                 memset(&s_codec_ctx, 0, sizeof(s_codec_ctx));
31                 s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX;
32                 opt_codec_ctx = &s_codec_ctx;
33         }
34
35         /*
36          * Invoke type-specific decoder.
37          */
38         return td->op->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
39 }
40
41
42
43 struct xer__cb_arg {
44         pxml_chunk_type_e       chunk_type;
45         size_t                  chunk_size;
46         const void              *chunk_buf;
47         int callback_not_invoked;
48 };
49
50 static int
51 xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
52         struct xer__cb_arg *arg = (struct xer__cb_arg *)key;
53         arg->chunk_type = type;
54         arg->chunk_size = _chunk_size;
55         arg->chunk_buf = _chunk_data;
56         arg->callback_not_invoked = 0;
57         return -1;      /* Terminate the XML parsing */
58 }
59
60 /*
61  * Fetch the next token from the XER/XML stream.
62  */
63 ssize_t
64 xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
65         struct xer__cb_arg arg;
66         int new_stateContext = *stateContext;
67         ssize_t ret;
68
69         arg.callback_not_invoked = 1;
70         ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
71         if(ret < 0) return -1;
72         if(arg.callback_not_invoked) {
73                 assert(ret == 0);       /* No data was consumed */
74         *ch_type = PXER_WMORE;
75                 return 0;               /* Try again with more data */
76         } else {
77                 assert(arg.chunk_size);
78                 assert(arg.chunk_buf == buffer);
79         }
80
81         /*
82          * Translate the XML chunk types into more convenient ones.
83          */
84         switch(arg.chunk_type) {
85         case PXML_TEXT:
86                 *ch_type = PXER_TEXT;
87                 break;
88         case PXML_TAG:
89                 *ch_type = PXER_WMORE;
90                 return 0;       /* Want more */
91         case PXML_TAG_END:
92                 *ch_type = PXER_TAG;
93                 break;
94         case PXML_COMMENT:
95         case PXML_COMMENT_END:
96                 *ch_type = PXER_COMMENT;
97                 break;
98         }
99
100         *stateContext = new_stateContext;
101         return arg.chunk_size;
102 }
103
104 #define CSLASH  0x2f    /* '/' */
105 #define LANGLE  0x3c    /* '<' */
106 #define RANGLE  0x3e    /* '>' */
107
108 xer_check_tag_e
109 xer_check_tag(const void *buf_ptr, int size, const char *need_tag) {
110         const char *buf = (const char *)buf_ptr;
111         const char *end;
112         xer_check_tag_e ct = XCT_OPENING;
113
114         if(size < 2 || buf[0] != LANGLE || buf[size-1] != RANGLE) {
115                 if(size >= 2)
116                         ASN_DEBUG("Broken XML tag: \"%c...%c\"",
117                         buf[0], buf[size - 1]);
118                 return XCT_BROKEN;
119         }
120
121         /*
122          * Determine the tag class.
123          */
124         if(buf[1] == CSLASH) {
125                 buf += 2;       /* advance past "</" */
126                 size -= 3;      /* strip "</" and ">" */
127                 ct = XCT_CLOSING;
128                 if(size > 0 && buf[size-1] == CSLASH)
129                         return XCT_BROKEN;      /* </abc/> */
130         } else {
131                 buf++;          /* advance past "<" */
132                 size -= 2;      /* strip "<" and ">" */
133                 if(size > 0 && buf[size-1] == CSLASH) {
134                         ct = XCT_BOTH;
135                         size--; /* One more, for "/" */
136                 }
137         }
138
139         /* Sometimes we don't care about the tag */
140         if(!need_tag || !*need_tag)
141                 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
142
143         /*
144          * Determine the tag name.
145          */
146         for(end = buf + size; buf < end; buf++, need_tag++) {
147                 int b = *buf, n = *need_tag;
148                 if(b != n) {
149                         if(n == 0) {
150                                 switch(b) {
151                                 case 0x09: case 0x0a: case 0x0c: case 0x0d:
152                                 case 0x20:
153                                         /* "<abc def/>": whitespace is normal */
154                                         return ct;
155                                 }
156                         }
157                         return (xer_check_tag_e)(XCT__UNK__MASK | ct);
158                 }
159                 if(b == 0)
160                         return XCT_BROKEN;      /* Embedded 0 in buf?! */
161         }
162         if(*need_tag)
163                 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
164
165         return ct;
166 }
167
168
169 #undef  ADVANCE
170 #define ADVANCE(num_bytes)      do {                            \
171                 size_t num = (num_bytes);                       \
172                 buf_ptr = ((const char *)buf_ptr) + num;        \
173                 size -= num;                                    \
174                 consumed_myself += num;                         \
175         } while(0)
176
177 #undef  RETURN
178 #define RETURN(_code)   do {                                    \
179                 rval.code = _code;                              \
180                 rval.consumed = consumed_myself;                \
181                 if(rval.code != RC_OK)                          \
182                         ASN_DEBUG("Failed with %d", rval.code); \
183                 return rval;                                    \
184         } while(0)
185
186 #define XER_GOT_BODY(chunk_buf, chunk_size, size)       do {    \
187                 ssize_t converted_size = body_receiver          \
188                         (struct_key, chunk_buf, chunk_size,     \
189                                 (size_t)chunk_size < size);     \
190                 if(converted_size == -1) RETURN(RC_FAIL);       \
191                 if(converted_size == 0                          \
192                         && size == (size_t)chunk_size)          \
193                         RETURN(RC_WMORE);                       \
194                 chunk_size = converted_size;                    \
195         } while(0)
196 #define XER_GOT_EMPTY() do {                                    \
197         if(body_receiver(struct_key, 0, 0, size > 0) == -1)     \
198                         RETURN(RC_FAIL);                        \
199         } while(0)
200
201 /*
202  * Generalized function for decoding the primitive values.
203  */
204 asn_dec_rval_t
205 xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx,
206         asn_struct_ctx_t *ctx,  /* Type decoder context */
207         void *struct_key,
208         const char *xml_tag,    /* Expected XML tag */
209         const void *buf_ptr, size_t size,
210         int (*opt_unexpected_tag_decoder)
211                 (void *struct_key, const void *chunk_buf, size_t chunk_size),
212         ssize_t (*body_receiver)
213                 (void *struct_key, const void *chunk_buf, size_t chunk_size,
214                         int have_more)
215         ) {
216
217         asn_dec_rval_t rval;
218         ssize_t consumed_myself = 0;
219
220         (void)opt_codec_ctx;
221
222         /*
223          * Phases of XER/XML processing:
224          * Phase 0: Check that the opening tag matches our expectations.
225          * Phase 1: Processing body and reacting on closing tag.
226          */
227         if(ctx->phase > 1) RETURN(RC_FAIL);
228         for(;;) {
229                 pxer_chunk_type_e ch_type;      /* XER chunk type */
230                 ssize_t ch_size;                /* Chunk size */
231                 xer_check_tag_e tcv;            /* Tag check value */
232
233                 /*
234                  * Get the next part of the XML stream.
235                  */
236                 ch_size = xer_next_token(&ctx->context, buf_ptr, size,
237                         &ch_type);
238                 if(ch_size == -1) {
239             RETURN(RC_FAIL);
240         } else {
241                         switch(ch_type) {
242                         case PXER_WMORE:
243                 RETURN(RC_WMORE);
244                         case PXER_COMMENT:              /* Got XML comment */
245                                 ADVANCE(ch_size);       /* Skip silently */
246                                 continue;
247                         case PXER_TEXT:
248                                 if(ctx->phase == 0) {
249                                         /*
250                                          * We have to ignore whitespace here,
251                                          * but in order to be forward compatible
252                                          * with EXTENDED-XER (EMBED-VALUES, #25)
253                                          * any text is just ignored here.
254                                          */
255                                 } else {
256                                         XER_GOT_BODY(buf_ptr, ch_size, size);
257                                 }
258                                 ADVANCE(ch_size);
259                                 continue;
260                         case PXER_TAG:
261                                 break;  /* Check the rest down there */
262                         }
263                 }
264
265                 assert(ch_type == PXER_TAG && size);
266
267                 tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
268                 /*
269                  * Phase 0:
270                  *      Expecting the opening tag
271                  *      for the type being processed.
272                  * Phase 1:
273                  *      Waiting for the closing XML tag.
274                  */
275                 switch(tcv) {
276                 case XCT_BOTH:
277                         if(ctx->phase) break;
278                         /* Finished decoding of an empty element */
279                         XER_GOT_EMPTY();
280                         ADVANCE(ch_size);
281                         ctx->phase = 2; /* Phase out */
282                         RETURN(RC_OK);
283                 case XCT_OPENING:
284                         if(ctx->phase) break;
285                         ADVANCE(ch_size);
286                         ctx->phase = 1; /* Processing body phase */
287                         continue;
288                 case XCT_CLOSING:
289                         if(!ctx->phase) break;
290                         ADVANCE(ch_size);
291                         ctx->phase = 2; /* Phase out */
292                         RETURN(RC_OK);
293                 case XCT_UNKNOWN_BO:
294                         /*
295                          * Certain tags in the body may be expected.
296                          */
297                         if(opt_unexpected_tag_decoder
298                         && opt_unexpected_tag_decoder(struct_key,
299                                         buf_ptr, ch_size) >= 0) {
300                                 /* Tag's processed fine */
301                                 ADVANCE(ch_size);
302                                 if(!ctx->phase) {
303                                         /* We are not expecting
304                                          * the closing tag anymore. */
305                                         ctx->phase = 2; /* Phase out */
306                                         RETURN(RC_OK);
307                                 }
308                                 continue;
309                         }
310                         /* Fall through */
311                 default:
312                         break;          /* Unexpected tag */
313                 }
314
315                 ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
316                 break;  /* Dark and mysterious things have just happened */
317         }
318
319         RETURN(RC_FAIL);
320 }
321
322
323 size_t
324 xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
325         const char *p = (const char *)chunk_buf;
326         const char *pend = p + chunk_size;
327
328         for(; p < pend; p++) {
329                 switch(*p) {
330                 /* X.693, #8.1.4
331                  * HORISONTAL TAB (9)
332                  * LINE FEED (10) 
333                  * CARRIAGE RETURN (13) 
334                  * SPACE (32)
335                  */
336                 case 0x09: case 0x0a: case 0x0d: case 0x20:
337                         continue;
338                 default:
339                         break;
340                 }
341                 break;
342         }
343         return (p - (const char *)chunk_buf);
344 }
345
346 /*
347  * This is a vastly simplified, non-validating XML tree skipper.
348  */
349 int
350 xer_skip_unknown(xer_check_tag_e tcv, ber_tlv_len_t *depth) {
351         assert(*depth > 0);
352         switch(tcv) {
353         case XCT_BOTH:
354         case XCT_UNKNOWN_BO:
355                 /* These negate each other. */
356                 return 0;
357         case XCT_OPENING:
358         case XCT_UNKNOWN_OP:
359                 ++(*depth);
360                 return 0;
361         case XCT_CLOSING:
362         case XCT_UNKNOWN_CL:
363                 if(--(*depth) == 0)
364                         return (tcv == XCT_CLOSING) ? 2 : 1;
365                 return 0;
366         default:
367                 return -1;
368         }
369 }