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