]> git.stg.codes - stg.git/blob - libs/smux/OBJECT_IDENTIFIER.c
Add async pool (to replace EVENT_LOOP).
[stg.git] / libs / smux / OBJECT_IDENTIFIER.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 <INTEGER.h>
7 #include <OBJECT_IDENTIFIER.h>
8 #include <OCTET_STRING.h>
9 #include <limits.h>     /* for CHAR_BIT */
10 #include <errno.h>
11
12 /*
13  * OBJECT IDENTIFIER basic type description.
14  */
15 static const ber_tlv_tag_t asn_DEF_OBJECT_IDENTIFIER_tags[] = {
16         (ASN_TAG_CLASS_UNIVERSAL | (6 << 2))
17 };
18 asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER = {
19         "OBJECT IDENTIFIER",
20         "OBJECT_IDENTIFIER",
21         ASN__PRIMITIVE_TYPE_free,
22         OBJECT_IDENTIFIER_print,
23         OBJECT_IDENTIFIER_constraint,
24         ber_decode_primitive,
25         der_encode_primitive,
26         OBJECT_IDENTIFIER_decode_xer,
27         OBJECT_IDENTIFIER_encode_xer,
28         OCTET_STRING_decode_uper,
29         OCTET_STRING_encode_uper,
30         0, /* Use generic outmost tag fetcher */
31         asn_DEF_OBJECT_IDENTIFIER_tags,
32         sizeof(asn_DEF_OBJECT_IDENTIFIER_tags)
33             / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]),
34         asn_DEF_OBJECT_IDENTIFIER_tags, /* Same as above */
35         sizeof(asn_DEF_OBJECT_IDENTIFIER_tags)
36             / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]),
37         0,      /* No PER visible constraints */
38         0, 0,   /* No members */
39         0       /* No specifics */
40 };
41
42
43 int
44 OBJECT_IDENTIFIER_constraint(asn_TYPE_descriptor_t *td, const void *sptr,
45                 asn_app_constraint_failed_f *ctfailcb, void *app_key) {
46         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
47
48         if(st && st->buf) {
49                 if(st->size < 1) {
50                         ASN__CTFAIL(app_key, td, sptr,
51                                 "%s: at least one numerical value "
52                                 "expected (%s:%d)",
53                                 td->name, __FILE__, __LINE__);
54                         return -1;
55                 }
56         } else {
57                 ASN__CTFAIL(app_key, td, sptr,
58                         "%s: value not given (%s:%d)",
59                         td->name, __FILE__, __LINE__);
60                 return -1;
61         }
62
63         return 0;
64 }
65
66
67 int
68 OBJECT_IDENTIFIER_get_single_arc(const uint8_t *arcbuf, unsigned int arclen, signed int add, void *rvbufp, unsigned int rvsize) {
69         unsigned LE GCC_NOTUSED = 1; /* Little endian (x86) */
70         const uint8_t *arcend = arcbuf + arclen;        /* End of arc */
71         unsigned int cache = 0; /* No more than 14 significant bits */
72         unsigned char *rvbuf = (unsigned char *)rvbufp;
73         unsigned char *rvstart = rvbuf; /* Original start of the value buffer */
74         int inc;        /* Return value growth direction */
75
76         rvsize *= CHAR_BIT;     /* bytes to bits */
77         arclen *= 7;            /* bytes to bits */
78
79         /*
80          * The arc has the number of bits
81          * cannot be represented using supplied return value type.
82          */
83         if(arclen > rvsize) {
84                 if(arclen > (rvsize + CHAR_BIT)) {
85                         errno = ERANGE; /* Overflow */
86                         return -1;
87                 } else {
88                         /*
89                          * Even if the number of bits in the arc representation
90                          * is higher than the width of supplied * return value
91                          * type, there is still possible to fit it when there
92                          * are few unused high bits in the arc value
93                          * representaion.
94                          * 
95                          * Moreover, there is a possibility that the
96                          * number could actually fit the arc space, given
97                          * that add is negative, but we don't handle
98                          * such "temporary lack of precision" situation here.
99                          * May be considered as a bug.
100                          */
101                         uint8_t mask = (0xff << (7-(arclen - rvsize))) & 0x7f;
102                         if((*arcbuf & mask)) {
103                                 errno = ERANGE; /* Overflow */
104                                 return -1;
105                         }
106                         /* Fool the routine computing unused bits */
107                         arclen -= 7;
108                         cache = *arcbuf & 0x7f;
109                         arcbuf++;
110                 }
111         }
112
113         /* Faster path for common size */
114         if(rvsize == (CHAR_BIT * sizeof(unsigned long))) {
115                 unsigned long accum;
116                 /* Gather all bits into the accumulator */
117                 for(accum = cache; arcbuf < arcend; arcbuf++)
118                         accum = (accum << 7) | (*arcbuf & ~0x80);
119                 if(accum < (unsigned)-add) {
120                         errno = ERANGE; /* Overflow */
121                         return -1;
122                 }
123                 *(unsigned long *)(void *)rvbuf = accum + add;  /* alignment OK! */
124                 return 0;
125         }
126
127 #ifndef WORDS_BIGENDIAN
128         if(*(unsigned char *)&LE) {     /* Little endian (x86) */
129                 /* "Convert" to big endian */
130                 rvbuf += rvsize / CHAR_BIT - 1;
131                 rvstart--;
132                 inc = -1;       /* Descending */
133         } else
134 #endif  /* !WORDS_BIGENDIAN */
135                 inc = +1;       /* Big endian is known [at compile time] */
136
137         {
138                 int bits;       /* typically no more than 3-4 bits */
139
140                 /* Clear the high unused bits */
141                 for(bits = rvsize - arclen;
142                         bits > CHAR_BIT;
143                                 rvbuf += inc, bits -= CHAR_BIT)
144                                 *rvbuf = 0;
145
146                 /* Fill the body of a value */
147                 for(; arcbuf < arcend; arcbuf++) {
148                         cache = (cache << 7) | (*arcbuf & 0x7f);
149                         bits += 7;
150                         if(bits >= CHAR_BIT) {
151                                 bits -= CHAR_BIT;
152                                 *rvbuf = (cache >> bits);
153                                 rvbuf += inc;
154                         }
155                 }
156                 if(bits) {
157                         *rvbuf = cache;
158                         rvbuf += inc;
159                 }
160         }
161
162         if(add) {
163                 for(rvbuf -= inc; rvbuf != rvstart; rvbuf -= inc) {
164                         int v = add + *rvbuf;
165                         if(v & ((unsigned)~0 << CHAR_BIT)) {
166                                 *rvbuf = (unsigned char)(v + (1 << CHAR_BIT));
167                                 add = -1;
168                         } else {
169                                 *rvbuf = v;
170                                 break;
171                         }
172                 }
173                 if(rvbuf == rvstart) {
174                         /* No space to carry over */
175                         errno = ERANGE; /* Overflow */
176                         return -1;
177                 }
178         }
179
180         return 0;
181 }
182
183 ssize_t
184 OBJECT_IDENTIFIER__dump_arc(const uint8_t *arcbuf, int arclen, int add,
185                 asn_app_consume_bytes_f *cb, void *app_key) {
186         char scratch[64];       /* Conservative estimate */
187         unsigned long accum;    /* Bits accumulator */
188         char *p;                /* Position in the scratch buffer */
189
190         if(OBJECT_IDENTIFIER_get_single_arc(arcbuf, arclen, add,
191                         &accum, sizeof(accum)))
192                 return -1;
193
194         if(accum) {
195                 ssize_t len;
196
197                 /* Fill the scratch buffer in reverse. */
198                 p = scratch + sizeof(scratch);
199                 for(; accum; accum /= 10)
200                         *(--p) = (char)(accum % 10) + 0x30; /* Put a digit */
201
202                 len = sizeof(scratch) - (p - scratch);
203                 if(cb(p, len, app_key) < 0)
204                         return -1;
205                 return len;
206         } else {
207                 *scratch = 0x30;
208                 if(cb(scratch, 1, app_key) < 0)
209                         return -1;
210                 return 1;
211         }
212 }
213
214 int
215 OBJECT_IDENTIFIER_print_arc(const uint8_t *arcbuf, int arclen, int add,
216                 asn_app_consume_bytes_f *cb, void *app_key) {
217
218         if(OBJECT_IDENTIFIER__dump_arc(arcbuf, arclen, add, cb, app_key) < 0)
219                 return -1;
220
221         return 0;
222 }
223
224 static ssize_t
225 OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_bytes_f *cb, void *app_key) {
226         ssize_t wrote_len = 0;
227         int startn;
228         int add = 0;
229         int i;
230
231         for(i = 0, startn = 0; i < st->size; i++) {
232                 uint8_t b = st->buf[i];
233                 if((b & 0x80))                  /* Continuation expected */
234                         continue;
235
236                 if(startn == 0) {
237                         /*
238                          * First two arcs are encoded through the backdoor.
239                          */
240                         if(i) {
241                                 add = -80;
242                                 if(cb("2", 1, app_key) < 0) return -1;
243                         } else if(b <= 39) {
244                                 add = 0;
245                                 if(cb("0", 1, app_key) < 0) return -1;
246                         } else if(b < 79) {
247                                 add = -40;
248                                 if(cb("1", 1, app_key) < 0) return -1;
249                         } else {
250                                 add = -80;
251                                 if(cb("2", 1, app_key) < 0) return -1;
252                         }
253                         wrote_len += 1;
254                 }
255
256                 if(cb(".", 1, app_key) < 0)     /* Separate arcs */
257                         return -1;
258
259                 add = OBJECT_IDENTIFIER__dump_arc(&st->buf[startn],
260                                 i - startn + 1, add, cb, app_key);
261                 if(add < 0) return -1;
262                 wrote_len += 1 + add;
263                 startn = i + 1;
264                 add = 0;
265         }
266
267         return wrote_len;
268 }
269
270 static enum xer_pbd_rval
271 OBJECT_IDENTIFIER__xer_body_decode(asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) {
272         OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr;
273         const char *chunk_end = (const char *)chunk_buf + chunk_size;
274         const char *endptr;
275         long s_arcs[10];
276         long *arcs = s_arcs;
277         int arcs_count;
278         int ret;
279
280         (void)td;
281
282         arcs_count = OBJECT_IDENTIFIER_parse_arcs(
283                 (const char *)chunk_buf, chunk_size, arcs,
284                         sizeof(s_arcs)/sizeof(s_arcs[0]), &endptr);
285         if(arcs_count < 0) {
286                 /* Expecting more than zero arcs */
287                 return XPBD_BROKEN_ENCODING;
288         } else if(arcs_count == 0) {
289                 return XPBD_NOT_BODY_IGNORE;
290         }
291         assert(endptr == chunk_end);
292
293         if((size_t)arcs_count > sizeof(s_arcs)/sizeof(s_arcs[0])) {
294                 arcs = (long *)MALLOC(arcs_count * sizeof(long));
295                 if(!arcs) return XPBD_SYSTEM_FAILURE;
296                 ret = OBJECT_IDENTIFIER_parse_arcs(
297                         (const char *)chunk_buf, chunk_size,
298                         arcs, arcs_count, &endptr);
299                 if(ret != arcs_count)
300                         return XPBD_SYSTEM_FAILURE;     /* assert?.. */
301         }
302
303         /*
304          * Convert arcs into BER representation.
305          */
306         ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, sizeof(*arcs), arcs_count);
307         if(arcs != s_arcs) FREEMEM(arcs);
308
309         return ret ? XPBD_SYSTEM_FAILURE : XPBD_BODY_CONSUMED;
310 }
311
312 asn_dec_rval_t
313 OBJECT_IDENTIFIER_decode_xer(asn_codec_ctx_t *opt_codec_ctx,
314         asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname,
315                 const void *buf_ptr, size_t size) {
316
317         return xer_decode_primitive(opt_codec_ctx, td,
318                 sptr, sizeof(OBJECT_IDENTIFIER_t), opt_mname,
319                         buf_ptr, size, OBJECT_IDENTIFIER__xer_body_decode);
320 }
321
322 asn_enc_rval_t
323 OBJECT_IDENTIFIER_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
324         int ilevel, enum xer_encoder_flags_e flags,
325                 asn_app_consume_bytes_f *cb, void *app_key) {
326         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
327         asn_enc_rval_t er;
328
329         (void)ilevel;
330         (void)flags;
331
332         if(!st || !st->buf)
333                 ASN__ENCODE_FAILED;
334
335         er.encoded = OBJECT_IDENTIFIER__dump_body(st, cb, app_key);
336         if(er.encoded < 0) ASN__ENCODE_FAILED;
337
338         ASN__ENCODED_OK(er);
339 }
340
341 int
342 OBJECT_IDENTIFIER_print(asn_TYPE_descriptor_t *td, const void *sptr,
343         int ilevel, asn_app_consume_bytes_f *cb, void *app_key) {
344         const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr;
345
346         (void)td;       /* Unused argument */
347         (void)ilevel;   /* Unused argument */
348
349         if(!st || !st->buf)
350                 return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
351
352         /* Dump preamble */
353         if(cb("{ ", 2, app_key) < 0)
354                 return -1;
355
356         if(OBJECT_IDENTIFIER__dump_body(st, cb, app_key) < 0)
357                 return -1;
358
359         return (cb(" }", 2, app_key) < 0) ? -1 : 0;
360 }
361
362 int
363 OBJECT_IDENTIFIER_get_arcs(const OBJECT_IDENTIFIER_t *oid, void *arcs,
364                 unsigned int arc_type_size, unsigned int arc_slots) {
365         void *arcs_end = (char *)arcs + (arc_type_size * arc_slots);
366         int num_arcs = 0;
367         int startn = 0;
368         int add = 0;
369         int i;
370
371         if(!oid || !oid->buf || (arc_slots && arc_type_size <= 1)) {
372                 errno = EINVAL;
373                 return -1;
374         }
375
376         for(i = 0; i < oid->size; i++) {
377                 uint8_t b = oid->buf[i];
378                 if((b & 0x80))                  /* Continuation expected */
379                         continue;
380
381                 if(num_arcs == 0) {
382                         /*
383                          * First two arcs are encoded through the backdoor.
384                          */
385                         unsigned LE = 1;        /* Little endian */
386                         int first_arc;
387                         num_arcs++;
388                         if(!arc_slots) { num_arcs++; continue; }
389
390                         if(i) first_arc = 2;
391                         else if(b <= 39) first_arc = 0;
392                         else if(b < 79) first_arc = 1;
393                         else first_arc = 2;
394
395                         add = -40 * first_arc;
396                         memset(arcs, 0, arc_type_size);
397                         *(unsigned char *)((char *)arcs
398                                 + ((*(char *)&LE)?0:(arc_type_size - 1)))
399                                         = first_arc;
400                         arcs = ((char *)arcs) + arc_type_size;
401                 }
402
403                 /* Decode, if has space */
404                 if(arcs < arcs_end) {
405                         if(OBJECT_IDENTIFIER_get_single_arc(&oid->buf[startn],
406                                 i - startn + 1, add,
407                                         arcs, arc_type_size))
408                                 return -1;
409                         startn = i + 1;
410                         arcs = ((char *)arcs) + arc_type_size;
411                         add = 0;
412                 }
413                 num_arcs++;
414         }
415
416         return num_arcs;
417 }
418
419
420 /*
421  * Save the single value as an object identifier arc.
422  */
423 int
424 OBJECT_IDENTIFIER_set_single_arc(uint8_t *arcbuf, const void *arcval, unsigned int arcval_size, int prepared_order) {
425         /*
426          * The following conditions must hold:
427          * assert(arcval);
428          * assert(arcval_size > 0);
429          * assert(arcval_size <= 16);
430          * assert(arcbuf);
431          */
432 #ifdef  WORDS_BIGENDIAN
433         const unsigned isLittleEndian = 0;
434 #else
435         unsigned LE = 1;
436         unsigned isLittleEndian = *(char *)&LE;
437 #endif
438         const uint8_t *tend, *tp;
439         unsigned int cache;
440         uint8_t *bp = arcbuf;
441         int bits;
442         uint8_t buffer[16];
443
444         if(isLittleEndian && !prepared_order) {
445                 const uint8_t *a = (const unsigned char *)arcval + arcval_size - 1;
446                 const uint8_t *aend = (const uint8_t *)arcval;
447                 uint8_t *msb = buffer + arcval_size - 1;
448                 uint8_t *tb;
449                 for(tb = buffer; a >= aend; tb++, a--)
450                         if((*tb = *a) && (tb < msb))
451                                 msb = tb;
452                 tend = &buffer[arcval_size];
453                 tp = msb;       /* Most significant non-zero byte */
454         } else {
455                 /* Look for most significant non-zero byte */
456                 tend = (const unsigned char *)arcval + arcval_size;
457                 for(tp = (const uint8_t *)arcval; tp < tend - 1; tp++)
458                         if(*tp) break;
459         }
460
461         /*
462          * Split the value in 7-bits chunks.
463          */
464         bits = ((tend - tp) * CHAR_BIT) % 7;
465         if(bits) {
466                 cache = *tp >> (CHAR_BIT - bits);
467                 if(cache) {
468                         *bp++ = cache | 0x80;
469                         cache = *tp++;
470                         bits = CHAR_BIT - bits;
471                 } else {
472                         bits = -bits;
473                 }
474         } else {
475                 cache = 0;
476         }
477         for(; tp < tend; tp++) {
478                 cache = (cache << CHAR_BIT) + *tp;
479                 bits += CHAR_BIT;
480                 while(bits >= 7) {
481                         bits -= 7;
482                         *bp++ = 0x80 | (cache >> bits);
483                 }
484         }
485         if(bits) *bp++ = cache;
486         bp[-1] &= 0x7f; /* Clear the last bit */
487
488         return bp - arcbuf;
489 }
490
491 int
492 OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *oid, const void *arcs, unsigned int arc_type_size, unsigned int arc_slots) {
493         uint8_t *buf;
494         uint8_t *bp;
495         unsigned LE = 1;        /* Little endian (x86) */
496         unsigned isLittleEndian = *((char *)&LE);
497         unsigned int arc0;
498         unsigned int arc1;
499         unsigned size;
500         unsigned i;
501
502         if(!oid || !arcs || arc_type_size < 1
503         || arc_type_size > 16
504         || arc_slots < 2) {
505                 errno = EINVAL;
506                 return -1;
507         }
508
509         switch(arc_type_size) {
510         case sizeof(char):
511                 arc0 = ((const unsigned char *)arcs)[0];
512                 arc1 = ((const unsigned char *)arcs)[1];
513                 break;
514         case sizeof(short):
515                 arc0 = ((const unsigned short *)arcs)[0];
516                 arc1 = ((const unsigned short *)arcs)[1];
517                 break;
518         case sizeof(int):
519                 arc0 = ((const unsigned int *)arcs)[0];
520                 arc1 = ((const unsigned int *)arcs)[1];
521                 break;
522         default:
523                 arc1 = arc0 = 0;
524                 if(isLittleEndian) {    /* Little endian (x86) */
525                         const unsigned char *ps, *pe;
526                         /* If more significant bytes are present,
527                          * make them > 255 quick */
528                         for(ps = (const unsigned char *)arcs + 1, pe = ps+arc_type_size;
529                                         ps < pe; ps++)
530                                 arc0 |= *ps, arc1 |= *(ps + arc_type_size);
531                         arc0 <<= CHAR_BIT, arc1 <<= CHAR_BIT;
532                         arc0 = *((const unsigned char *)arcs + 0);
533                         arc1 = *((const unsigned char *)arcs + arc_type_size);
534                 } else {
535                         const unsigned char *ps, *pe;
536                         /* If more significant bytes are present,
537                          * make them > 255 quick */
538                         for(ps = (const unsigned char *)arcs, pe = ps+arc_type_size - 1; ps < pe; ps++)
539                                 arc0 |= *ps, arc1 |= *(ps + arc_type_size);
540                         arc0 = *((const unsigned char *)arcs + arc_type_size - 1);
541                         arc1 = *((const unsigned char *)arcs +(arc_type_size<< 1)-1);
542                 }
543         }
544
545         /*
546          * The previous chapter left us with the first and the second arcs.
547          * The values are not precise (that is, they are valid only if
548          * they're less than 255), but OK for the purposes of making
549          * the sanity test below.
550          */
551         if(arc0 <= 1) {
552                 if(arc1 >= 39) {
553                         /* 8.19.4: At most 39 subsequent values (including 0) */
554                         errno = ERANGE;
555                         return -1;
556                 }
557         } else if(arc0 > 2) {
558                 /* 8.19.4: Only three values are allocated from the root node */
559                 errno = ERANGE;
560                 return -1;
561         }
562         /*
563          * After above tests it is known that the value of arc0 is completely
564          * trustworthy (0..2). However, the arc1's value is still meaningless.
565          */
566
567         /*
568          * Roughly estimate the maximum size necessary to encode these arcs.
569          * This estimation implicitly takes in account the following facts,
570          * that cancel each other:
571          *      * the first two arcs are encoded in a single value.
572          *      * the first value may require more space (+1 byte)
573          *      * the value of the first arc which is in range (0..2)
574          */
575         size = ((arc_type_size * CHAR_BIT + 6) / 7) * arc_slots;
576         bp = buf = (uint8_t *)MALLOC(size + 1);
577         if(!buf) {
578                 /* ENOMEM */
579                 return -1;
580         }
581
582         /*
583          * Encode the first two arcs.
584          * These require special treatment.
585          */
586         {
587                 uint8_t *tp;
588                 uint8_t first_value[1 + 16];    /* of two arcs */
589                 uint8_t *fv = first_value;
590
591                 /*
592                  * Simulate first_value = arc0 * 40 + arc1;
593                  */
594                 /* Copy the second (1'st) arcs[1] into the first_value */
595                 *fv++ = 0;
596                 arcs = ((const char *)arcs) + arc_type_size;
597                 if(isLittleEndian) {
598                         const uint8_t *aend = (const unsigned char *)arcs - 1;
599                         const uint8_t *a1 = (const unsigned char *)arcs + arc_type_size - 1;
600                         for(; a1 > aend; fv++, a1--) *fv = *a1;
601                 } else {
602                         const uint8_t *a1 = (const uint8_t *)arcs;
603                         const uint8_t *aend = a1 + arc_type_size;
604                         for(; a1 < aend; fv++, a1++) *fv = *a1;
605                 }
606                 /* Increase the first_value by arc0 */
607                 arc0 *= 40;     /* (0..80) */
608                 for(tp = first_value + arc_type_size; tp >= first_value; tp--) {
609                         unsigned int v = *tp;
610                         v += arc0;
611                         *tp = v;
612                         if(v >= (1 << CHAR_BIT)) arc0 = v >> CHAR_BIT;
613                         else break;
614                 }
615
616                 assert(tp >= first_value);
617
618                 bp += OBJECT_IDENTIFIER_set_single_arc(bp, first_value,
619                         fv - first_value, 1);
620         }
621
622         /*
623          * Save the rest of arcs.
624          */
625         for(arcs = ((const char *)arcs) + arc_type_size, i = 2;
626                 i < arc_slots;
627                         i++, arcs = ((const char *)arcs) + arc_type_size) {
628                 bp += OBJECT_IDENTIFIER_set_single_arc(bp,
629                         arcs, arc_type_size, 0);
630         }
631
632         assert((unsigned)(bp - buf) <= size);
633
634         /*
635          * Replace buffer.
636          */
637         oid->size = bp - buf;
638         bp = oid->buf;
639         oid->buf = buf;
640         if(bp) FREEMEM(bp);
641
642         return 0;
643 }
644
645
646 int
647 OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length,
648         long *arcs, unsigned int arcs_slots, const char **opt_oid_text_end) {
649         unsigned int arcs_count = 0;
650         const char *oid_end;
651         enum {
652                 ST_LEADSPACE,
653                 ST_TAILSPACE,
654                 ST_AFTERVALUE,  /* Next character ought to be '.' or a space */
655                 ST_WAITDIGITS   /* Next character is expected to be a digit */
656         } state = ST_LEADSPACE;
657
658         if(!oid_text || oid_txt_length < -1 || (arcs_slots && !arcs)) {
659                 if(opt_oid_text_end) *opt_oid_text_end = oid_text;
660                 errno = EINVAL;
661                 return -1;
662         }
663
664         if(oid_txt_length == -1)
665                 oid_txt_length = strlen(oid_text);
666
667 #define _OID_CAPTURE_ARC(oid_text, oid_end)             do {    \
668         const char *endp = oid_end;                             \
669         long value;                                             \
670         switch(asn_strtol_lim(oid_text, &endp, &value)) {       \
671         case ASN_STRTOL_EXTRA_DATA:                             \
672         case ASN_STRTOL_OK:                                     \
673                 if(arcs_count < arcs_slots)                     \
674                         arcs[arcs_count] = value;               \
675                 arcs_count++;                                   \
676                 oid_text = endp - 1;                            \
677                 break;                                          \
678         case ASN_STRTOL_ERROR_RANGE:                            \
679                 if(opt_oid_text_end)                            \
680                         *opt_oid_text_end = oid_text;           \
681                 errno = ERANGE;                                 \
682                 return -1;                                      \
683         case ASN_STRTOL_ERROR_INVAL:                            \
684         case ASN_STRTOL_EXPECT_MORE:                            \
685                 if(opt_oid_text_end)                            \
686                         *opt_oid_text_end = oid_text;           \
687                 errno = EINVAL;                                 \
688                 return -1;                                      \
689         }                                                       \
690   } while(0)
691
692         for(oid_end = oid_text + oid_txt_length; oid_text<oid_end; oid_text++) {
693             switch(*oid_text) {
694             case 0x09: case 0x0a: case 0x0d: case 0x20: /* whitespace */
695                 switch(state) {
696                 case ST_LEADSPACE:
697                 case ST_TAILSPACE:
698                         continue;
699                 case ST_AFTERVALUE:
700                         state = ST_TAILSPACE;
701                         continue;
702                 case ST_WAITDIGITS:
703                         break;  /* Digits expected after ".", got whitespace */
704                 }
705                 break;
706             case 0x2e:  /* '.' */
707                 switch(state) {
708                 case ST_LEADSPACE:
709                 case ST_TAILSPACE:
710                 case ST_WAITDIGITS:
711                         if(opt_oid_text_end)
712                                 *opt_oid_text_end = oid_text;
713                         errno = EINVAL; /* Broken OID */
714                         return -1;
715                         break;
716                 case ST_AFTERVALUE:
717                         state = ST_WAITDIGITS;
718                         continue;
719                 }
720                 break;
721             case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
722             case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
723                 switch(state) {
724                 case ST_TAILSPACE:
725                 case ST_AFTERVALUE:
726                         if(opt_oid_text_end)
727                                 *opt_oid_text_end = oid_text;
728                         errno = EINVAL; /* "1. 1" => broken OID */
729                         return -1;
730                 case ST_LEADSPACE:
731                 case ST_WAITDIGITS:
732                         _OID_CAPTURE_ARC(oid_text, oid_end);
733                         state = ST_AFTERVALUE;
734                         continue;
735                 }
736                 break;
737             default:
738                 /* Unexpected symbols */
739                 state = ST_WAITDIGITS;
740                 break;
741             } /* switch() */
742             break;
743         } /* for() */
744
745
746         if(opt_oid_text_end) *opt_oid_text_end = oid_text;
747
748         /* Finalize last arc */
749         switch(state) {
750         case ST_LEADSPACE:
751                 return 0; /* No OID found in input data */
752         case ST_WAITDIGITS:
753                 errno = EINVAL; /* Broken OID */
754                 return -1;
755         case ST_AFTERVALUE:
756         case ST_TAILSPACE:
757                 return arcs_count;
758         }
759
760         errno = EINVAL; /* Broken OID */
761         return -1;
762 }
763
764