asn_codecs_prim.c 7.24 KB
Newer Older
Lev Walkin's avatar
Lev Walkin committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*-
 * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * Redistribution and modifications are permitted subject to BSD license.
 */
#include <asn_internal.h>
#include <asn_codecs_prim.h>
#include <errno.h>

/*
 * Decode an always-primitive type.
 */
asn_dec_rval_t
ber_decode_primitive(asn_codec_ctx_t *opt_codec_ctx,
	asn_TYPE_descriptor_t *td,
Lev Walkin's avatar
Lev Walkin committed
15
	void **sptr, const void *buf_ptr, size_t size, int tag_mode) {
Lev Walkin's avatar
Lev Walkin committed
16
17
18
19
20
21
22
23
	ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)*sptr;
	asn_dec_rval_t rval;
	ber_tlv_len_t length;

	/*
	 * If the structure is not there, allocate it.
	 */
	if(st == NULL) {
24
		st = (ASN__PRIMITIVE_TYPE_t *)CALLOC(1, sizeof(*st));
Lev Walkin's avatar
Lev Walkin committed
25
		if(st == NULL) _ASN_DECODE_FAILED;
26
		*sptr = (void *)st;
Lev Walkin's avatar
Lev Walkin committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	}

	ASN_DEBUG("Decoding %s as plain primitive (tm=%d)",
		td->name, tag_mode);

	/*
	 * Check tags and extract value length.
	 */
	rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size,
			tag_mode, 0, &length, 0);
	if(rval.code != RC_OK)
		return rval;

	ASN_DEBUG("%s length is %d bytes", td->name, (int)length);

	/*
	 * Make sure we have this length.
	 */
Lev Walkin's avatar
Lev Walkin committed
45
	buf_ptr = ((const char *)buf_ptr) + rval.consumed;
Lev Walkin's avatar
Lev Walkin committed
46
47
48
49
50
51
52
	size -= rval.consumed;
	if(length > (ber_tlv_len_t)size) {
		rval.code = RC_WMORE;
		rval.consumed = 0;
		return rval;
	}

Lev Walkin's avatar
Lev Walkin committed
53
54
55
56
57
	st->size = (int)length;
	/* The following better be optimized away. */
	if(sizeof(st->size) != sizeof(length)
			&& (ber_tlv_len_t)st->size != length) {
		st->size = 0;
Lev Walkin's avatar
Lev Walkin committed
58
		_ASN_DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
59
60
	}

Lev Walkin's avatar
Lev Walkin committed
61
	st->buf = (uint8_t *)MALLOC(length + 1);
Lev Walkin's avatar
Lev Walkin committed
62
63
	if(!st->buf) {
		st->size = 0;
Lev Walkin's avatar
Lev Walkin committed
64
		_ASN_DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
	}

	memcpy(st->buf, buf_ptr, length);
	st->buf[length] = '\0';		/* Just in case */

	rval.code = RC_OK;
	rval.consumed += length;

	ASN_DEBUG("Took %ld/%ld bytes to encode %s",
		(long)rval.consumed,
		(long)length, td->name);

	return rval;
}

/*
 * Encode an always-primitive type using DER.
 */
asn_enc_rval_t
der_encode_primitive(asn_TYPE_descriptor_t *td, void *sptr,
	int tag_mode, ber_tlv_tag_t tag,
	asn_app_consume_bytes_f *cb, void *app_key) {
	asn_enc_rval_t erval;
	ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr;

	ASN_DEBUG("%s %s as a primitive type (tm=%d)",
		cb?"Encoding":"Estimating", td->name, tag_mode);

	erval.encoded = der_write_tags(td, st->size, tag_mode, 0, tag,
		cb, app_key);
	ASN_DEBUG("%s wrote tags %d", td->name, (int)erval.encoded);
	if(erval.encoded == -1) {
		erval.failed_type = td;
		erval.structure_ptr = sptr;
		return erval;
	}

	if(cb && st->buf) {
		if(cb(st->buf, st->size, app_key) < 0) {
			erval.encoded = -1;
			erval.failed_type = td;
			erval.structure_ptr = sptr;
			return erval;
		}
	} else {
		assert(st->buf || st->size == 0);
	}

	erval.encoded += st->size;
Lev Walkin's avatar
Lev Walkin committed
114
	_ASN_ENCODED_OK(erval);
Lev Walkin's avatar
Lev Walkin committed
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
}

void
ASN__PRIMITIVE_TYPE_free(asn_TYPE_descriptor_t *td, void *sptr,
		int contents_only) {
	ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr;

	if(!td || !sptr)
		return;

	ASN_DEBUG("Freeing %s as a primitive type", td->name);

	if(st->buf)
		FREEMEM(st->buf);

	if(!contents_only)
		FREEMEM(st);
}


/*
 * Local internal type passed around as an argument.
 */
struct xdp_arg_s {
Lev Walkin's avatar
Lev Walkin committed
139
	asn_TYPE_descriptor_t *type_descriptor;
Lev Walkin's avatar
Lev Walkin committed
140
	void *struct_key;
141
	xer_primitive_body_decoder_f *prim_body_decoder;
Lev Walkin's avatar
Lev Walkin committed
142
143
144
145
	int decoded_something;
	int want_more;
};

146
147
148
149
150
/*
 * Since some kinds of primitive values can be encoded using value-specific
 * tags (<MINUS-INFINITY>, <enum-element>, etc), the primitive decoder must
 * be supplied with such tags to parse them as needed.
 */
151
152
static int
xer_decode__unexpected_tag(void *key, const void *chunk_buf, size_t chunk_size) {
Lev Walkin's avatar
Lev Walkin committed
153
	struct xdp_arg_s *arg = (struct xdp_arg_s *)key;
154
	enum xer_pbd_rval bret;
Lev Walkin's avatar
Lev Walkin committed
155

156
157
158
159
160
161
162
163
164
	/*
	 * The chunk_buf is guaranteed to start at '<'.
	 */
	assert(chunk_size && ((const char *)chunk_buf)[0] == 0x3c);

	/*
	 * Decoding was performed once already. Prohibit doing it again.
	 */
	if(arg->decoded_something)
Lev Walkin's avatar
Lev Walkin committed
165
166
		return -1;

167
	bret = arg->prim_body_decoder(arg->type_descriptor,
Lev Walkin's avatar
Lev Walkin committed
168
		arg->struct_key, chunk_buf, chunk_size);
169
170
171
172
173
174
	switch(bret) {
	case XPBD_SYSTEM_FAILURE:
	case XPBD_DECODER_LIMIT:
	case XPBD_BROKEN_ENCODING:
		break;
	case XPBD_BODY_CONSUMED:
Lev Walkin's avatar
Lev Walkin committed
175
176
		/* Tag decoded successfully */
		arg->decoded_something = 1;
177
178
		/* Fall through */
	case XPBD_NOT_BODY_IGNORE:	/* Safe to proceed further */
Lev Walkin's avatar
Lev Walkin committed
179
180
		return 0;
	}
181
182

	return -1;
Lev Walkin's avatar
Lev Walkin committed
183
184
185
}

static ssize_t
186
xer_decode__primitive_body(void *key, const void *chunk_buf, size_t chunk_size, int have_more) {
Lev Walkin's avatar
Lev Walkin committed
187
	struct xdp_arg_s *arg = (struct xdp_arg_s *)key;
188
	enum xer_pbd_rval bret;
189
	size_t lead_wsp_size;
Lev Walkin's avatar
Lev Walkin committed
190
191

	if(arg->decoded_something) {
192
		if(xer_whitespace_span(chunk_buf, chunk_size) == chunk_size) {
193
194
195
196
197
			/*
			 * Example:
			 * "<INTEGER>123<!--/--> </INTEGER>"
			 *                      ^- chunk_buf position.
			 */
Lev Walkin's avatar
Lev Walkin committed
198
			return chunk_size;
199
		}
Lev Walkin's avatar
Lev Walkin committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
		/*
		 * Decoding was done once already. Prohibit doing it again.
		 */
		return -1;
	}

	if(!have_more) {
		/*
		 * If we've received something like "1", we can't really
		 * tell whether it is really `1` or `123`, until we know
		 * that there is no more data coming.
		 * The have_more argument will be set to 1 once something
		 * like this is available to the caller of this callback:
		 * "1<tag_start..."
		 */
		arg->want_more = 1;
		return -1;
	}

219
	lead_wsp_size = xer_whitespace_span(chunk_buf, chunk_size);
Lev Walkin's avatar
Lev Walkin committed
220
	chunk_buf = (const char *)chunk_buf + lead_wsp_size;
221
222
	chunk_size -= lead_wsp_size;

223
	bret = arg->prim_body_decoder(arg->type_descriptor,
Lev Walkin's avatar
Lev Walkin committed
224
		arg->struct_key, chunk_buf, chunk_size);
225
226
227
228
229
230
231
	switch(bret) {
	case XPBD_SYSTEM_FAILURE:
	case XPBD_DECODER_LIMIT:
	case XPBD_BROKEN_ENCODING:
		break;
	case XPBD_BODY_CONSUMED:
		/* Tag decoded successfully */
Lev Walkin's avatar
Lev Walkin committed
232
		arg->decoded_something = 1;
233
234
		/* Fall through */
	case XPBD_NOT_BODY_IGNORE:	/* Safe to proceed further */
235
		return lead_wsp_size + chunk_size;
Lev Walkin's avatar
Lev Walkin committed
236
	}
237
238

	return -1;
Lev Walkin's avatar
Lev Walkin committed
239
240
241
242
243
244
}


asn_dec_rval_t
xer_decode_primitive(asn_codec_ctx_t *opt_codec_ctx,
	asn_TYPE_descriptor_t *td,
Lev Walkin's avatar
Lev Walkin committed
245
246
	void **sptr,
	size_t struct_size,
Lev Walkin's avatar
Lev Walkin committed
247
	const char *opt_mname,
Lev Walkin's avatar
Lev Walkin committed
248
	const void *buf_ptr, size_t size,
249
	xer_primitive_body_decoder_f *prim_body_decoder
Lev Walkin's avatar
Lev Walkin committed
250
251
252
253
254
255
256
257
258
259
) {
	const char *xml_tag = opt_mname ? opt_mname : td->xml_tag;
	asn_struct_ctx_t s_ctx;
	struct xdp_arg_s s_arg;
	asn_dec_rval_t rc;

	/*
	 * Create the structure if does not exist.
	 */
	if(!*sptr) {
Lev Walkin's avatar
Lev Walkin committed
260
		*sptr = CALLOC(1, struct_size);
Lev Walkin's avatar
Lev Walkin committed
261
		if(!*sptr) _ASN_DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
262
263
264
	}

	memset(&s_ctx, 0, sizeof(s_ctx));
Lev Walkin's avatar
Lev Walkin committed
265
	s_arg.type_descriptor = td;
Lev Walkin's avatar
Lev Walkin committed
266
	s_arg.struct_key = *sptr;
267
	s_arg.prim_body_decoder = prim_body_decoder;
Lev Walkin's avatar
Lev Walkin committed
268
269
270
271
272
	s_arg.decoded_something = 0;
	s_arg.want_more = 0;

	rc = xer_decode_general(opt_codec_ctx, &s_ctx, &s_arg,
		xml_tag, buf_ptr, size,
273
		xer_decode__unexpected_tag, xer_decode__primitive_body);
Lev Walkin's avatar
Lev Walkin committed
274
275
276
	switch(rc.code) {
	case RC_OK:
		if(!s_arg.decoded_something) {
Lev Walkin's avatar
Lev Walkin committed
277
			char ch;
278
279
280
281
282
283
284
285
286
287
			ASN_DEBUG("Primitive body is not recognized, "
				"supplying empty one");
			/*
			 * Decoding opportunity has come and gone.
			 * Where's the result?
			 * Try to feed with empty body, see if it eats it.
			 */
			if(prim_body_decoder(s_arg.type_descriptor,
				s_arg.struct_key, &ch, 0)
					!= XPBD_BODY_CONSUMED) {
Lev Walkin's avatar
Lev Walkin committed
288
289
290
				/*
				 * This decoder does not like empty stuff.
				 */
Lev Walkin's avatar
Lev Walkin committed
291
				_ASN_DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
292
			}
Lev Walkin's avatar
Lev Walkin committed
293
294
295
296
297
298
299
300
301
302
303
304
305
		}
		break;
	case RC_WMORE:
		/*
		 * Redo the whole thing later.
		 * We don't have a context to save intermediate parsing state.
		 */
		rc.consumed = 0;
		break;
	case RC_FAIL:
		rc.consumed = 0;
		if(s_arg.want_more)
			rc.code = RC_WMORE;
Lev Walkin's avatar
Lev Walkin committed
306
307
		else
			_ASN_DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
308
309
310
311
312
		break;
	}
	return rc;
}