]> git.stg.codes - stg.git/blob - libs/smux/asn_codecs_prim.c
Add an option to opt-out Firebird store.
[stg.git] / libs / smux / asn_codecs_prim.c
1 /*-
2  * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
3  * Redistribution and modifications are permitted subject to BSD license.
4  */
5 #include <asn_internal.h>
6 #include <asn_codecs_prim.h>
7 #include <errno.h>
8
9 /*
10  * Decode an always-primitive type.
11  */
12 asn_dec_rval_t
13 ber_decode_primitive(const asn_codec_ctx_t *opt_codec_ctx,
14                      const asn_TYPE_descriptor_t *td, void **sptr,
15                      const void *buf_ptr, size_t size, int tag_mode) {
16     ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)*sptr;
17         asn_dec_rval_t rval;
18         ber_tlv_len_t length = 0; /* =0 to avoid [incorrect] warning. */
19
20         /*
21          * If the structure is not there, allocate it.
22          */
23         if(st == NULL) {
24                 st = (ASN__PRIMITIVE_TYPE_t *)CALLOC(1, sizeof(*st));
25                 if(st == NULL) ASN__DECODE_FAILED;
26                 *sptr = (void *)st;
27         }
28
29         ASN_DEBUG("Decoding %s as plain primitive (tm=%d)",
30                 td->name, tag_mode);
31
32         /*
33          * Check tags and extract value length.
34          */
35         rval = ber_check_tags(opt_codec_ctx, td, 0, buf_ptr, size,
36                         tag_mode, 0, &length, 0);
37         if(rval.code != RC_OK)
38                 return rval;
39
40         ASN_DEBUG("%s length is %d bytes", td->name, (int)length);
41
42         /*
43          * Make sure we have this length.
44          */
45         buf_ptr = ((const char *)buf_ptr) + rval.consumed;
46         size -= rval.consumed;
47         if(length > (ber_tlv_len_t)size) {
48                 rval.code = RC_WMORE;
49                 rval.consumed = 0;
50                 return rval;
51         }
52
53         st->size = (int)length;
54         /* The following better be optimized away. */
55         if(sizeof(st->size) != sizeof(length)
56                         && (ber_tlv_len_t)st->size != length) {
57                 st->size = 0;
58                 ASN__DECODE_FAILED;
59         }
60
61         st->buf = (uint8_t *)MALLOC(length + 1);
62         if(!st->buf) {
63                 st->size = 0;
64                 ASN__DECODE_FAILED;
65         }
66
67         memcpy(st->buf, buf_ptr, length);
68         st->buf[length] = '\0';         /* Just in case */
69
70         rval.code = RC_OK;
71         rval.consumed += length;
72
73         ASN_DEBUG("Took %ld/%ld bytes to encode %s",
74                 (long)rval.consumed,
75                 (long)length, td->name);
76
77         return rval;
78 }
79
80 /*
81  * Encode an always-primitive type using DER.
82  */
83 asn_enc_rval_t
84 der_encode_primitive(const asn_TYPE_descriptor_t *td, const void *sptr,
85                      int tag_mode, ber_tlv_tag_t tag,
86                      asn_app_consume_bytes_f *cb, void *app_key) {
87     asn_enc_rval_t erval;
88         const ASN__PRIMITIVE_TYPE_t *st = (const ASN__PRIMITIVE_TYPE_t *)sptr;
89
90         ASN_DEBUG("%s %s as a primitive type (tm=%d)",
91                 cb?"Encoding":"Estimating", td->name, tag_mode);
92
93         erval.encoded = der_write_tags(td, st->size, tag_mode, 0, tag,
94                 cb, app_key);
95         ASN_DEBUG("%s wrote tags %d", td->name, (int)erval.encoded);
96         if(erval.encoded == -1) {
97                 erval.failed_type = td;
98                 erval.structure_ptr = sptr;
99                 return erval;
100         }
101
102         if(cb && st->buf) {
103                 if(cb(st->buf, st->size, app_key) < 0) {
104                         erval.encoded = -1;
105                         erval.failed_type = td;
106                         erval.structure_ptr = sptr;
107                         return erval;
108                 }
109         } else {
110                 assert(st->buf || st->size == 0);
111         }
112
113         erval.encoded += st->size;
114         ASN__ENCODED_OK(erval);
115 }
116
117 void
118 ASN__PRIMITIVE_TYPE_free(const asn_TYPE_descriptor_t *td, void *sptr,
119                          enum asn_struct_free_method method) {
120     ASN__PRIMITIVE_TYPE_t *st = (ASN__PRIMITIVE_TYPE_t *)sptr;
121
122         if(!td || !sptr)
123                 return;
124
125         ASN_DEBUG("Freeing %s as a primitive type", td->name);
126
127         if(st->buf)
128                 FREEMEM(st->buf);
129
130     switch(method) {
131     case ASFM_FREE_EVERYTHING:
132         FREEMEM(sptr);
133         break;
134     case ASFM_FREE_UNDERLYING:
135         break;
136     case ASFM_FREE_UNDERLYING_AND_RESET:
137         memset(sptr, 0, sizeof(ASN__PRIMITIVE_TYPE_t));
138         break;
139     }
140 }
141
142
143 /*
144  * Local internal type passed around as an argument.
145  */
146 struct xdp_arg_s {
147     const asn_TYPE_descriptor_t *type_descriptor;
148     void *struct_key;
149         xer_primitive_body_decoder_f *prim_body_decoder;
150         int decoded_something;
151         int want_more;
152 };
153
154 /*
155  * Since some kinds of primitive values can be encoded using value-specific
156  * tags (<MINUS-INFINITY>, <enum-element>, etc), the primitive decoder must
157  * be supplied with such tags to parse them as needed.
158  */
159 static int
160 xer_decode__unexpected_tag(void *key, const void *chunk_buf, size_t chunk_size) {
161         struct xdp_arg_s *arg = (struct xdp_arg_s *)key;
162         enum xer_pbd_rval bret;
163
164         /*
165          * The chunk_buf is guaranteed to start at '<'.
166          */
167         assert(chunk_size && ((const char *)chunk_buf)[0] == 0x3c);
168
169         /*
170          * Decoding was performed once already. Prohibit doing it again.
171          */
172         if(arg->decoded_something)
173                 return -1;
174
175         bret = arg->prim_body_decoder(arg->type_descriptor,
176                 arg->struct_key, chunk_buf, chunk_size);
177         switch(bret) {
178         case XPBD_SYSTEM_FAILURE:
179         case XPBD_DECODER_LIMIT:
180         case XPBD_BROKEN_ENCODING:
181                 break;
182         case XPBD_BODY_CONSUMED:
183                 /* Tag decoded successfully */
184                 arg->decoded_something = 1;
185                 /* Fall through */
186         case XPBD_NOT_BODY_IGNORE:      /* Safe to proceed further */
187                 return 0;
188         }
189
190         return -1;
191 }
192
193 static ssize_t
194 xer_decode__primitive_body(void *key, const void *chunk_buf, size_t chunk_size, int have_more) {
195         struct xdp_arg_s *arg = (struct xdp_arg_s *)key;
196         enum xer_pbd_rval bret;
197         size_t lead_wsp_size;
198
199         if(arg->decoded_something) {
200                 if(xer_whitespace_span(chunk_buf, chunk_size) == chunk_size) {
201                         /*
202                          * Example:
203                          * "<INTEGER>123<!--/--> </INTEGER>"
204                          *                      ^- chunk_buf position.
205                          */
206                         return chunk_size;
207                 }
208                 /*
209                  * Decoding was done once already. Prohibit doing it again.
210                  */
211                 return -1;
212         }
213
214         if(!have_more) {
215                 /*
216                  * If we've received something like "1", we can't really
217                  * tell whether it is really `1` or `123`, until we know
218                  * that there is no more data coming.
219                  * The have_more argument will be set to 1 once something
220                  * like this is available to the caller of this callback:
221                  * "1<tag_start..."
222                  */
223                 arg->want_more = 1;
224                 return -1;
225         }
226
227         lead_wsp_size = xer_whitespace_span(chunk_buf, chunk_size);
228         chunk_buf = (const char *)chunk_buf + lead_wsp_size;
229         chunk_size -= lead_wsp_size;
230
231         bret = arg->prim_body_decoder(arg->type_descriptor,
232                 arg->struct_key, chunk_buf, chunk_size);
233         switch(bret) {
234         case XPBD_SYSTEM_FAILURE:
235         case XPBD_DECODER_LIMIT:
236         case XPBD_BROKEN_ENCODING:
237                 break;
238         case XPBD_BODY_CONSUMED:
239                 /* Tag decoded successfully */
240                 arg->decoded_something = 1;
241                 /* Fall through */
242         case XPBD_NOT_BODY_IGNORE:      /* Safe to proceed further */
243                 return lead_wsp_size + chunk_size;
244         }
245
246         return -1;
247 }
248
249
250 asn_dec_rval_t
251 xer_decode_primitive(const asn_codec_ctx_t *opt_codec_ctx,
252                      const asn_TYPE_descriptor_t *td, void **sptr,
253                      size_t struct_size, const char *opt_mname,
254                      const void *buf_ptr, size_t size,
255                      xer_primitive_body_decoder_f *prim_body_decoder) {
256     const char *xml_tag = opt_mname ? opt_mname : td->xml_tag;
257         asn_struct_ctx_t s_ctx;
258         struct xdp_arg_s s_arg;
259         asn_dec_rval_t rc;
260
261         /*
262          * Create the structure if does not exist.
263          */
264         if(!*sptr) {
265                 *sptr = CALLOC(1, struct_size);
266                 if(!*sptr) ASN__DECODE_FAILED;
267         }
268
269         memset(&s_ctx, 0, sizeof(s_ctx));
270         s_arg.type_descriptor = td;
271         s_arg.struct_key = *sptr;
272         s_arg.prim_body_decoder = prim_body_decoder;
273         s_arg.decoded_something = 0;
274         s_arg.want_more = 0;
275
276         rc = xer_decode_general(opt_codec_ctx, &s_ctx, &s_arg,
277                 xml_tag, buf_ptr, size,
278                 xer_decode__unexpected_tag, xer_decode__primitive_body);
279         switch(rc.code) {
280         case RC_OK:
281                 if(!s_arg.decoded_something) {
282                         char ch;
283                         ASN_DEBUG("Primitive body is not recognized, "
284                                 "supplying empty one");
285                         /*
286                          * Decoding opportunity has come and gone.
287                          * Where's the result?
288                          * Try to feed with empty body, see if it eats it.
289                          */
290                         if(prim_body_decoder(s_arg.type_descriptor,
291                                 s_arg.struct_key, &ch, 0)
292                                         != XPBD_BODY_CONSUMED) {
293                                 /*
294                                  * This decoder does not like empty stuff.
295                                  */
296                                 ASN__DECODE_FAILED;
297                         }
298                 }
299                 break;
300         case RC_WMORE:
301                 /*
302                  * Redo the whole thing later.
303                  * We don't have a context to save intermediate parsing state.
304                  */
305                 rc.consumed = 0;
306                 break;
307         case RC_FAIL:
308                 rc.consumed = 0;
309                 if(s_arg.want_more)
310                         rc.code = RC_WMORE;
311                 else
312                         ASN__DECODE_FAILED;
313                 break;
314         }
315         return rc;
316 }
317