blob: 5b87703a33175f96e09c3125e4d83b4197a8f4ca [file] [log] [blame]
Lev Walkinfc776432005-03-29 17:21:14 +00001/*
Lev Walkin20696a42017-10-17 21:27:33 -07002 * Copyright (c) 2004-2017 Lev Walkin <vlm@lionet.info>. All rights reserved.
Lev Walkinfc776432005-03-29 17:21:14 +00003 * Redistribution and modifications are permitted subject to BSD license.
4 */
Lev Walkindc06f6b2004-10-20 15:50:55 +00005#include <asn_application.h>
6#include <asn_internal.h>
7#include <xer_support.h> /* XER/XML parsing support */
Lev Walkindc06f6b2004-10-20 15:50:55 +00008
9
10/*
11 * Decode the XER encoding of a given type.
12 */
13asn_dec_rval_t
Lev Walkin20696a42017-10-17 21:27:33 -070014xer_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;
Lev Walkindc06f6b2004-10-20 15:50:55 +000018
19 /*
Lev Walkin1d9e8dd2005-12-07 05:46:03 +000020 * Stack checker requires that the codec context
Lev Walkindc06f6b2004-10-20 15:50:55 +000021 * must be allocated on the stack.
22 */
Lev Walkin1d9e8dd2005-12-07 05:46:03 +000023 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));
Lev Walkin7c1dc052016-03-14 03:08:15 -070031 s_codec_ctx.max_stack_size = ASN__DEFAULT_STACK_MAX;
Lev Walkindc06f6b2004-10-20 15:50:55 +000032 opt_codec_ctx = &s_codec_ctx;
33 }
34
35 /*
36 * Invoke type-specific decoder.
37 */
Bi-Ruei, Chiu1f87ac02017-08-20 01:25:45 +080038 return td->op->xer_decoder(opt_codec_ctx, td, struct_ptr, 0, buffer, size);
Lev Walkindc06f6b2004-10-20 15:50:55 +000039}
40
41
42
43struct xer__cb_arg {
44 pxml_chunk_type_e chunk_type;
45 size_t chunk_size;
Lev Walkin0fab1a62005-03-09 22:19:25 +000046 const void *chunk_buf;
Lev Walkindc06f6b2004-10-20 15:50:55 +000047 int callback_not_invoked;
48};
49
50static int
Lev Walkin0fab1a62005-03-09 22:19:25 +000051xer__token_cb(pxml_chunk_type_e type, const void *_chunk_data, size_t _chunk_size, void *key) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000052 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 */
63ssize_t
Lev Walkin0fab1a62005-03-09 22:19:25 +000064xer_next_token(int *stateContext, const void *buffer, size_t size, pxer_chunk_type_e *ch_type) {
Lev Walkindc06f6b2004-10-20 15:50:55 +000065 struct xer__cb_arg arg;
Lev Walkin1e443962005-02-18 18:06:36 +000066 int new_stateContext = *stateContext;
Lev Walkindc06f6b2004-10-20 15:50:55 +000067 ssize_t ret;
68
69 arg.callback_not_invoked = 1;
Lev Walkin1e443962005-02-18 18:06:36 +000070 ret = pxml_parse(&new_stateContext, buffer, size, xer__token_cb, &arg);
Lev Walkindc06f6b2004-10-20 15:50:55 +000071 if(ret < 0) return -1;
72 if(arg.callback_not_invoked) {
73 assert(ret == 0); /* No data was consumed */
Lev Walkin97363482016-01-24 19:23:02 -080074 *ch_type = PXER_WMORE;
Lev Walkindc06f6b2004-10-20 15:50:55 +000075 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;
Lev Walkin97363482016-01-24 19:23:02 -080088 case PXML_TAG:
Lev Walkin5a0436c2017-08-26 18:44:50 -070089 *ch_type = PXER_WMORE;
90 return 0; /* Want more */
Lev Walkindc06f6b2004-10-20 15:50:55 +000091 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
Lev Walkin1e443962005-02-18 18:06:36 +0000100 *stateContext = new_stateContext;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000101 return arg.chunk_size;
102}
103
104#define CSLASH 0x2f /* '/' */
105#define LANGLE 0x3c /* '<' */
106#define RANGLE 0x3e /* '>' */
107
108xer_check_tag_e
109xer_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) {
Lev Walkinc61f3862005-02-14 17:21:22 +0000115 if(size >= 2)
Lev Walkinc9c6f4a2012-09-04 13:14:03 -0700116 ASN_DEBUG("Broken XML tag: \"%c...%c\"",
117 buf[0], buf[size - 1]);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000118 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
Lev Walkind1bfea62005-11-08 03:06:16 +0000139 /* 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
Lev Walkindc06f6b2004-10-20 15:50:55 +0000143 /*
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 }
Lev Walkin1e443962005-02-18 18:06:36 +0000157 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000158 }
159 if(b == 0)
160 return XCT_BROKEN; /* Embedded 0 in buf?! */
161 }
Lev Walkin904e65b2005-02-18 14:23:48 +0000162 if(*need_tag)
Lev Walkin1e443962005-02-18 18:06:36 +0000163 return (xer_check_tag_e)(XCT__UNK__MASK | ct);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000164
165 return ct;
166}
167
168
169#undef ADVANCE
170#define ADVANCE(num_bytes) do { \
171 size_t num = (num_bytes); \
Lev Walkin0fab1a62005-03-09 22:19:25 +0000172 buf_ptr = ((const char *)buf_ptr) + num; \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000173 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; \
Lev Walkin00918812006-09-18 21:19:32 +0000181 if(rval.code != RC_OK) \
182 ASN_DEBUG("Failed with %d", rval.code); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000183 return rval; \
184 } while(0)
185
Lev Walkin9357ec12005-02-18 12:26:20 +0000186#define XER_GOT_BODY(chunk_buf, chunk_size, size) do { \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000187 ssize_t converted_size = body_receiver \
Lev Walkin3256d6f2004-10-21 11:22:12 +0000188 (struct_key, chunk_buf, chunk_size, \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000189 (size_t)chunk_size < size); \
190 if(converted_size == -1) RETURN(RC_FAIL); \
Lev Walkin1e443962005-02-18 18:06:36 +0000191 if(converted_size == 0 \
192 && size == (size_t)chunk_size) \
Lev Walkin9357ec12005-02-18 12:26:20 +0000193 RETURN(RC_WMORE); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000194 chunk_size = converted_size; \
195 } while(0)
196#define XER_GOT_EMPTY() do { \
Lev Walkin9357ec12005-02-18 12:26:20 +0000197 if(body_receiver(struct_key, 0, 0, size > 0) == -1) \
198 RETURN(RC_FAIL); \
Lev Walkindc06f6b2004-10-20 15:50:55 +0000199 } while(0)
200
201/*
202 * Generalized function for decoding the primitive values.
203 */
204asn_dec_rval_t
Lev Walkinafbf2a92017-09-12 23:30:27 -0700205xer_decode_general(const asn_codec_ctx_t *opt_codec_ctx,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000206 asn_struct_ctx_t *ctx, /* Type decoder context */
Lev Walkin3256d6f2004-10-21 11:22:12 +0000207 void *struct_key,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000208 const char *xml_tag, /* Expected XML tag */
Lev Walkin0fab1a62005-03-09 22:19:25 +0000209 const void *buf_ptr, size_t size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000210 int (*opt_unexpected_tag_decoder)
Lev Walkin0fab1a62005-03-09 22:19:25 +0000211 (void *struct_key, const void *chunk_buf, size_t chunk_size),
Lev Walkindc06f6b2004-10-20 15:50:55 +0000212 ssize_t (*body_receiver)
Lev Walkin0fab1a62005-03-09 22:19:25 +0000213 (void *struct_key, const void *chunk_buf, size_t chunk_size,
Lev Walkindc06f6b2004-10-20 15:50:55 +0000214 int have_more)
215 ) {
216
217 asn_dec_rval_t rval;
218 ssize_t consumed_myself = 0;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000219
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);
Lev Walkin2eeeedc2005-02-18 16:10:40 +0000228 for(;;) {
Lev Walkind2de48a2004-10-23 13:27:03 +0000229 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 */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000232
233 /*
234 * Get the next part of the XML stream.
235 */
Lev Walkin1e443962005-02-18 18:06:36 +0000236 ch_size = xer_next_token(&ctx->context, buf_ptr, size,
237 &ch_type);
Lev Walkin97363482016-01-24 19:23:02 -0800238 if(ch_size == -1) {
239 RETURN(RC_FAIL);
240 } else {
Lev Walkindc06f6b2004-10-20 15:50:55 +0000241 switch(ch_type) {
Lev Walkin97363482016-01-24 19:23:02 -0800242 case PXER_WMORE:
243 RETURN(RC_WMORE);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000244 case PXER_COMMENT: /* Got XML comment */
245 ADVANCE(ch_size); /* Skip silently */
246 continue;
247 case PXER_TEXT:
248 if(ctx->phase == 0) {
Lev Walkincfeecfb2004-10-21 05:44:11 +0000249 /*
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 {
Lev Walkin9357ec12005-02-18 12:26:20 +0000256 XER_GOT_BODY(buf_ptr, ch_size, size);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000257 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000258 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);
Lev Walkind2de48a2004-10-23 13:27:03 +0000268 /*
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);
Lev Walkin904e65b2005-02-18 14:23:48 +0000293 case XCT_UNKNOWN_BO:
Lev Walkindc06f6b2004-10-20 15:50:55 +0000294 /*
Lev Walkind2de48a2004-10-23 13:27:03 +0000295 * Certain tags in the body may be expected.
Lev Walkindc06f6b2004-10-20 15:50:55 +0000296 */
Lev Walkind2de48a2004-10-23 13:27:03 +0000297 if(opt_unexpected_tag_decoder
298 && opt_unexpected_tag_decoder(struct_key,
Lev Walkin4efbfb72005-02-25 14:20:30 +0000299 buf_ptr, ch_size) >= 0) {
Lev Walkind2de48a2004-10-23 13:27:03 +0000300 /* Tag's processed fine */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000301 ADVANCE(ch_size);
Lev Walkine0b56e02005-02-25 12:10:27 +0000302 if(!ctx->phase) {
303 /* We are not expecting
304 * the closing tag anymore. */
305 ctx->phase = 2; /* Phase out */
306 RETURN(RC_OK);
307 }
Lev Walkindc06f6b2004-10-20 15:50:55 +0000308 continue;
Lev Walkindc06f6b2004-10-20 15:50:55 +0000309 }
Lev Walkind2de48a2004-10-23 13:27:03 +0000310 /* Fall through */
311 default:
312 break; /* Unexpected tag */
Lev Walkindc06f6b2004-10-20 15:50:55 +0000313 }
Lev Walkind2de48a2004-10-23 13:27:03 +0000314
Lev Walkin642962a2005-02-24 22:37:07 +0000315 ASN_DEBUG("Unexpected XML tag (expected \"%s\")", xml_tag);
Lev Walkindc06f6b2004-10-20 15:50:55 +0000316 break; /* Dark and mysterious things have just happened */
317 }
318
319 RETURN(RC_FAIL);
320}
321
Lev Walkindde25b32004-10-22 08:17:16 +0000322
Lev Walkinf7982282013-03-16 07:01:42 -0700323size_t
324xer_whitespace_span(const void *chunk_buf, size_t chunk_size) {
Lev Walkin0fab1a62005-03-09 22:19:25 +0000325 const char *p = (const char *)chunk_buf;
326 const char *pend = p + chunk_size;
Lev Walkindde25b32004-10-22 08:17:16 +0000327
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:
Lev Walkinf7982282013-03-16 07:01:42 -0700337 continue;
Lev Walkindde25b32004-10-22 08:17:16 +0000338 default:
Lev Walkinf7982282013-03-16 07:01:42 -0700339 break;
Lev Walkindde25b32004-10-22 08:17:16 +0000340 }
Lev Walkinf7982282013-03-16 07:01:42 -0700341 break;
Lev Walkindde25b32004-10-22 08:17:16 +0000342 }
Lev Walkinf7982282013-03-16 07:01:42 -0700343 return (p - (const char *)chunk_buf);
Lev Walkindde25b32004-10-22 08:17:16 +0000344}
345
Lev Walkin2eeeedc2005-02-18 16:10:40 +0000346/*
347 * This is a vastly simplified, non-validating XML tree skipper.
348 */
349int
350xer_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}