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