13e41d09dSXin LI /*- 23e41d09dSXin LI * Copyright (c) 2016 Christos Zoulas 33e41d09dSXin LI * All rights reserved. 43e41d09dSXin LI * 53e41d09dSXin LI * Redistribution and use in source and binary forms, with or without 63e41d09dSXin LI * modification, are permitted provided that the following conditions 73e41d09dSXin LI * are met: 83e41d09dSXin LI * 1. Redistributions of source code must retain the above copyright 93e41d09dSXin LI * notice, this list of conditions and the following disclaimer. 103e41d09dSXin LI * 2. Redistributions in binary form must reproduce the above copyright 113e41d09dSXin LI * notice, this list of conditions and the following disclaimer in the 123e41d09dSXin LI * documentation and/or other materials provided with the distribution. 133e41d09dSXin LI * 143e41d09dSXin LI * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 153e41d09dSXin LI * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 163e41d09dSXin LI * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 173e41d09dSXin LI * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 183e41d09dSXin LI * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 193e41d09dSXin LI * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 203e41d09dSXin LI * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 213e41d09dSXin LI * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 223e41d09dSXin LI * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 233e41d09dSXin LI * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 243e41d09dSXin LI * POSSIBILITY OF SUCH DAMAGE. 253e41d09dSXin LI */ 263e41d09dSXin LI /* 273e41d09dSXin LI * DER (Distinguished Encoding Rules) Parser 283e41d09dSXin LI * 293e41d09dSXin LI * Sources: 303e41d09dSXin LI * https://en.wikipedia.org/wiki/X.690 313e41d09dSXin LI * http://fm4dd.com/openssl/certexamples.htm 323e41d09dSXin LI * http://blog.engelke.com/2014/10/17/parsing-ber-and-der-encoded-asn-1-objects/ 333e41d09dSXin LI */ 343e41d09dSXin LI #ifndef TEST_DER 353e41d09dSXin LI #include "file.h" 363e41d09dSXin LI 373e41d09dSXin LI #ifndef lint 38*40427ccaSGordon Tetlow FILE_RCSID("@(#)$File: der.c,v 1.12 2017/02/10 18:14:01 christos Exp $") 393e41d09dSXin LI #endif 403e41d09dSXin LI #endif 413e41d09dSXin LI 423e41d09dSXin LI #include <sys/types.h> 433e41d09dSXin LI 443e41d09dSXin LI #include <stdio.h> 453e41d09dSXin LI #include <fcntl.h> 463e41d09dSXin LI #include <stdlib.h> 473e41d09dSXin LI #include <string.h> 483e41d09dSXin LI #include <ctype.h> 493e41d09dSXin LI 503e41d09dSXin LI #ifndef TEST_DER 513e41d09dSXin LI #include "magic.h" 523e41d09dSXin LI #include "der.h" 5320f8619dSXin LI #else 54a5d223e6SXin LI #include <sys/mman.h> 55a5d223e6SXin LI #include <sys/stat.h> 5620f8619dSXin LI #include <err.h> 573e41d09dSXin LI #endif 583e41d09dSXin LI 593e41d09dSXin LI #define DER_BAD ((uint32_t)-1) 603e41d09dSXin LI 613e41d09dSXin LI #define DER_CLASS_UNIVERSAL 0 623e41d09dSXin LI #define DER_CLASS_APPLICATION 1 633e41d09dSXin LI #define DER_CLASS_CONTEXT 2 643e41d09dSXin LI #define DER_CLASS_PRIVATE 3 6520f8619dSXin LI #ifdef DEBUG_DER 663e41d09dSXin LI static const char der_class[] = "UACP"; 6720f8619dSXin LI #endif 683e41d09dSXin LI 693e41d09dSXin LI #define DER_TYPE_PRIMITIVE 0 703e41d09dSXin LI #define DER_TYPE_CONSTRUCTED 1 7120f8619dSXin LI #ifdef DEBUG_DER 723e41d09dSXin LI static const char der_type[] = "PC"; 7320f8619dSXin LI #endif 743e41d09dSXin LI 753e41d09dSXin LI #define DER_TAG_EOC 0x00 763e41d09dSXin LI #define DER_TAG_BOOLEAN 0x01 773e41d09dSXin LI #define DER_TAG_INTEGER 0x02 783e41d09dSXin LI #define DER_TAG_BIT STRING 0x03 793e41d09dSXin LI #define DER_TAG_OCTET_STRING 0x04 803e41d09dSXin LI #define DER_TAG_NULL 0x05 813e41d09dSXin LI #define DER_TAG_OBJECT_IDENTIFIER 0x06 823e41d09dSXin LI #define DER_TAG_OBJECT_DESCRIPTOR 0x07 833e41d09dSXin LI #define DER_TAG_EXTERNAL 0x08 843e41d09dSXin LI #define DER_TAG_REAL 0x09 853e41d09dSXin LI #define DER_TAG_ENUMERATED 0x0a 863e41d09dSXin LI #define DER_TAG_EMBEDDED_PDV 0x0b 873e41d09dSXin LI #define DER_TAG_UTF8_STRING 0x0c 883e41d09dSXin LI #define DER_TAG_RELATIVE_OID 0x0d 893e41d09dSXin LI #define DER_TAG_RESERVED_1 0x0e 903e41d09dSXin LI #define DER_TAG_RESERVED_2 0x0f 913e41d09dSXin LI #define DER_TAG_SEQUENCE 0x10 923e41d09dSXin LI #define DER_TAG_SET 0x11 933e41d09dSXin LI #define DER_TAG_NUMERIC_STRING 0x12 943e41d09dSXin LI #define DER_TAG_PRINTABLE_STRING 0x13 953e41d09dSXin LI #define DER_TAG_T61_STRING 0x14 963e41d09dSXin LI #define DER_TAG_VIDEOTEX_STRING 0x15 973e41d09dSXin LI #define DER_TAG_IA5_STRING 0x16 983e41d09dSXin LI #define DER_TAG_UTCTIME 0x17 993e41d09dSXin LI #define DER_TAG_GENERALIZED_TIME 0x18 1003e41d09dSXin LI #define DER_TAG_GRAPHIC_STRING 0x19 1013e41d09dSXin LI #define DER_TAG_VISIBLE_STRING 0x1a 1023e41d09dSXin LI #define DER_TAG_GENERAL_STRING 0x1b 1033e41d09dSXin LI #define DER_TAG_UNIVERSAL_STRING 0x1c 1043e41d09dSXin LI #define DER_TAG_CHARACTER_STRING 0x1d 1053e41d09dSXin LI #define DER_TAG_BMP_STRING 0x1e 1063e41d09dSXin LI #define DER_TAG_LONG 0x1f 1073e41d09dSXin LI 1083e41d09dSXin LI static const char *der__tag[] = { 1093e41d09dSXin LI "eoc", "bool", "int", "bit_str", "octet_str", 1103e41d09dSXin LI "null", "obj_id", "obj_desc", "ext", "real", 1113e41d09dSXin LI "enum", "embed", "utf8_str", "oid", "res1", 1123e41d09dSXin LI "res2", "seq", "set", "num_str", "prt_str", 1133e41d09dSXin LI "t61_str", "vid_str", "ia5_str", "utc_time", 1143e41d09dSXin LI "gen_time", "gr_str", "vis_str", "gen_str", 1153e41d09dSXin LI "char_str", "bmp_str", "long" 1163e41d09dSXin LI }; 1173e41d09dSXin LI 1183e41d09dSXin LI #ifdef DEBUG_DER 1193e41d09dSXin LI #define DPRINTF(a) printf a 1203e41d09dSXin LI #else 1213e41d09dSXin LI #define DPRINTF(a) 1223e41d09dSXin LI #endif 1233e41d09dSXin LI 1243e41d09dSXin LI #ifdef TEST_DER 1253e41d09dSXin LI static uint8_t 1263e41d09dSXin LI getclass(uint8_t c) 1273e41d09dSXin LI { 1283e41d09dSXin LI return c >> 6; 1293e41d09dSXin LI } 1303e41d09dSXin LI 1313e41d09dSXin LI static uint8_t 1323e41d09dSXin LI gettype(uint8_t c) 1333e41d09dSXin LI { 1343e41d09dSXin LI return (c >> 5) & 1; 1353e41d09dSXin LI } 1363e41d09dSXin LI #endif 1373e41d09dSXin LI 1383e41d09dSXin LI static uint32_t 1393e41d09dSXin LI gettag(const uint8_t *c, size_t *p, size_t l) 1403e41d09dSXin LI { 1413e41d09dSXin LI uint32_t tag; 1423e41d09dSXin LI 1433e41d09dSXin LI if (*p >= l) 1443e41d09dSXin LI return DER_BAD; 1453e41d09dSXin LI 1463e41d09dSXin LI tag = c[(*p)++] & 0x1f; 1473e41d09dSXin LI 1483e41d09dSXin LI if (tag != 0x1f) 1493e41d09dSXin LI return tag; 1503e41d09dSXin LI 1513e41d09dSXin LI if (*p >= l) 1523e41d09dSXin LI return DER_BAD; 1533e41d09dSXin LI 1543e41d09dSXin LI while (c[*p] >= 0x80) { 1553e41d09dSXin LI tag = tag * 128 + c[(*p)++] - 0x80; 1563e41d09dSXin LI if (*p >= l) 1573e41d09dSXin LI return DER_BAD; 1583e41d09dSXin LI } 1593e41d09dSXin LI return tag; 1603e41d09dSXin LI } 1613e41d09dSXin LI 162*40427ccaSGordon Tetlow /* 163*40427ccaSGordon Tetlow * Read the length of a DER tag from the input. 164*40427ccaSGordon Tetlow * 165*40427ccaSGordon Tetlow * `c` is the input, `p` is an output parameter that specifies how much of the 166*40427ccaSGordon Tetlow * input we consumed, and `l` is the maximum input length. 167*40427ccaSGordon Tetlow * 168*40427ccaSGordon Tetlow * Returns the length, or DER_BAD if the end of the input is reached or the 169*40427ccaSGordon Tetlow * length exceeds the remaining input. 170*40427ccaSGordon Tetlow */ 1713e41d09dSXin LI static uint32_t 1723e41d09dSXin LI getlength(const uint8_t *c, size_t *p, size_t l) 1733e41d09dSXin LI { 1743e41d09dSXin LI uint8_t digits, i; 1753e41d09dSXin LI size_t len; 176*40427ccaSGordon Tetlow int is_onebyte_result; 1773e41d09dSXin LI 1783e41d09dSXin LI if (*p >= l) 1793e41d09dSXin LI return DER_BAD; 1803e41d09dSXin LI 181*40427ccaSGordon Tetlow /* 182*40427ccaSGordon Tetlow * Digits can either be 0b0 followed by the result, or 0b1 183*40427ccaSGordon Tetlow * followed by the number of digits of the result. In either case, 184*40427ccaSGordon Tetlow * we verify that we can read so many bytes from the input. 185*40427ccaSGordon Tetlow */ 186*40427ccaSGordon Tetlow is_onebyte_result = (c[*p] & 0x80) == 0; 187*40427ccaSGordon Tetlow digits = c[(*p)++] & 0x7f; 1883e41d09dSXin LI if (*p + digits >= l) 1893e41d09dSXin LI return DER_BAD; 1903e41d09dSXin LI 191*40427ccaSGordon Tetlow if (is_onebyte_result) 192*40427ccaSGordon Tetlow return digits; 193*40427ccaSGordon Tetlow 194*40427ccaSGordon Tetlow /* 195*40427ccaSGordon Tetlow * Decode len. We've already verified that we're allowed to read 196*40427ccaSGordon Tetlow * `digits` bytes. 197*40427ccaSGordon Tetlow */ 198*40427ccaSGordon Tetlow len = 0; 1993e41d09dSXin LI for (i = 0; i < digits; i++) 2003e41d09dSXin LI len = (len << 8) | c[(*p)++]; 201*40427ccaSGordon Tetlow 202a5d223e6SXin LI if (*p + len >= l) 203a5d223e6SXin LI return DER_BAD; 204*40427ccaSGordon Tetlow return CAST(uint32_t, len); 2053e41d09dSXin LI } 2063e41d09dSXin LI 2073e41d09dSXin LI static const char * 2083e41d09dSXin LI der_tag(char *buf, size_t len, uint32_t tag) 2093e41d09dSXin LI { 2103e41d09dSXin LI if (tag < DER_TAG_LONG) 2113e41d09dSXin LI strlcpy(buf, der__tag[tag], len); 2123e41d09dSXin LI else 2133e41d09dSXin LI snprintf(buf, len, "%#x", tag); 2143e41d09dSXin LI return buf; 2153e41d09dSXin LI } 2163e41d09dSXin LI 2173e41d09dSXin LI #ifndef TEST_DER 2183e41d09dSXin LI static int 2193e41d09dSXin LI der_data(char *buf, size_t blen, uint32_t tag, const void *q, uint32_t len) 2203e41d09dSXin LI { 221a5d223e6SXin LI const uint8_t *d = CAST(const uint8_t *, q); 2223e41d09dSXin LI switch (tag) { 2233e41d09dSXin LI case DER_TAG_PRINTABLE_STRING: 2243e41d09dSXin LI case DER_TAG_UTF8_STRING: 2253e41d09dSXin LI case DER_TAG_IA5_STRING: 2263e41d09dSXin LI case DER_TAG_UTCTIME: 2273e41d09dSXin LI return snprintf(buf, blen, "%.*s", len, (const char *)q); 2283e41d09dSXin LI default: 2293e41d09dSXin LI break; 2303e41d09dSXin LI } 2313e41d09dSXin LI 2323e41d09dSXin LI for (uint32_t i = 0; i < len; i++) { 2333e41d09dSXin LI uint32_t z = i << 1; 2343e41d09dSXin LI if (z < blen - 2) 2353e41d09dSXin LI snprintf(buf + z, blen - z, "%.2x", d[i]); 2363e41d09dSXin LI } 2373e41d09dSXin LI return len * 2; 2383e41d09dSXin LI } 2393e41d09dSXin LI 2403e41d09dSXin LI int32_t 2413e41d09dSXin LI der_offs(struct magic_set *ms, struct magic *m, size_t nbytes) 2423e41d09dSXin LI { 243a5d223e6SXin LI const uint8_t *b = RCAST(const uint8_t *, ms->search.s); 244282e23f0SXin LI size_t offs = 0, len = ms->search.s_len ? ms->search.s_len : nbytes; 2453e41d09dSXin LI 2463e41d09dSXin LI if (gettag(b, &offs, len) == DER_BAD) 2473e41d09dSXin LI return -1; 2483e41d09dSXin LI DPRINTF(("%s1: %d %zu %u\n", __func__, ms->offset, offs, m->offset)); 2493e41d09dSXin LI 2503e41d09dSXin LI uint32_t tlen = getlength(b, &offs, len); 2513e41d09dSXin LI if (tlen == DER_BAD) 2523e41d09dSXin LI return -1; 2533e41d09dSXin LI DPRINTF(("%s2: %d %zu %u\n", __func__, ms->offset, offs, tlen)); 2543e41d09dSXin LI 2553e41d09dSXin LI offs += ms->offset + m->offset; 2563e41d09dSXin LI DPRINTF(("cont_level = %d\n", m->cont_level)); 2573e41d09dSXin LI #ifdef DEBUG_DER 2583e41d09dSXin LI for (size_t i = 0; i < m->cont_level; i++) 2593e41d09dSXin LI printf("cont_level[%zu] = %u\n", i, ms->c.li[i].off); 2603e41d09dSXin LI #endif 2613e41d09dSXin LI if (m->cont_level != 0) { 2623e41d09dSXin LI if (offs + tlen > nbytes) 263*40427ccaSGordon Tetlow return -1; 264*40427ccaSGordon Tetlow ms->c.li[m->cont_level - 1].off = CAST(int, offs + tlen); 2653e41d09dSXin LI DPRINTF(("cont_level[%u] = %u\n", m->cont_level - 1, 2663e41d09dSXin LI ms->c.li[m->cont_level - 1].off)); 2673e41d09dSXin LI } 268*40427ccaSGordon Tetlow return CAST(int32_t, offs); 2693e41d09dSXin LI } 2703e41d09dSXin LI 2713e41d09dSXin LI int 2723e41d09dSXin LI der_cmp(struct magic_set *ms, struct magic *m) 2733e41d09dSXin LI { 274a5d223e6SXin LI const uint8_t *b = RCAST(const uint8_t *, ms->search.s); 2753e41d09dSXin LI const char *s = m->value.s; 2763e41d09dSXin LI size_t offs = 0, len = ms->search.s_len; 2773e41d09dSXin LI uint32_t tag, tlen; 2783e41d09dSXin LI char buf[128]; 2793e41d09dSXin LI 2803e41d09dSXin LI tag = gettag(b, &offs, len); 2813e41d09dSXin LI if (tag == DER_BAD) 2823e41d09dSXin LI return -1; 2833e41d09dSXin LI 2843e41d09dSXin LI tlen = getlength(b, &offs, len); 2853e41d09dSXin LI if (tlen == DER_BAD) 2863e41d09dSXin LI return -1; 2873e41d09dSXin LI 2883e41d09dSXin LI der_tag(buf, sizeof(buf), tag); 2893e41d09dSXin LI if ((ms->flags & MAGIC_DEBUG) != 0) 2903e41d09dSXin LI fprintf(stderr, "%s: tag %p got=%s exp=%s\n", __func__, b, 2913e41d09dSXin LI buf, s); 2923e41d09dSXin LI size_t slen = strlen(buf); 2933e41d09dSXin LI 2943e41d09dSXin LI if (strncmp(buf, s, slen) != 0) 2953e41d09dSXin LI return 0; 2963e41d09dSXin LI 2973e41d09dSXin LI s += slen; 2983e41d09dSXin LI 2993e41d09dSXin LI again: 3003e41d09dSXin LI switch (*s) { 3013e41d09dSXin LI case '\0': 3023e41d09dSXin LI return 1; 3033e41d09dSXin LI case '=': 3043e41d09dSXin LI s++; 3053e41d09dSXin LI goto val; 3063e41d09dSXin LI default: 3073e41d09dSXin LI if (!isdigit((unsigned char)*s)) 3083e41d09dSXin LI return 0; 3093e41d09dSXin LI 3103e41d09dSXin LI slen = 0; 3113e41d09dSXin LI do 3123e41d09dSXin LI slen = slen * 10 + *s - '0'; 3133e41d09dSXin LI while (isdigit((unsigned char)*++s)); 3143e41d09dSXin LI if ((ms->flags & MAGIC_DEBUG) != 0) 3153e41d09dSXin LI fprintf(stderr, "%s: len %zu %u\n", __func__, 3163e41d09dSXin LI slen, tlen); 3173e41d09dSXin LI if (tlen != slen) 3183e41d09dSXin LI return 0; 3193e41d09dSXin LI goto again; 3203e41d09dSXin LI } 3213e41d09dSXin LI val: 3223e41d09dSXin LI DPRINTF(("%s: before data %zu %u\n", __func__, offs, tlen)); 3233e41d09dSXin LI der_data(buf, sizeof(buf), tag, b + offs, tlen); 3243e41d09dSXin LI if ((ms->flags & MAGIC_DEBUG) != 0) 3253e41d09dSXin LI fprintf(stderr, "%s: data %s %s\n", __func__, buf, s); 3263e41d09dSXin LI if (strcmp(buf, s) != 0 && strcmp("x", s) != 0) 3273e41d09dSXin LI return 0; 3283e41d09dSXin LI strlcpy(ms->ms_value.s, buf, sizeof(ms->ms_value.s)); 3293e41d09dSXin LI return 1; 3303e41d09dSXin LI } 3313e41d09dSXin LI #endif 3323e41d09dSXin LI 3333e41d09dSXin LI #ifdef TEST_DER 3343e41d09dSXin LI static void 3353e41d09dSXin LI printtag(uint32_t tag, const void *q, uint32_t len) 3363e41d09dSXin LI { 3373e41d09dSXin LI const uint8_t *d = q; 3383e41d09dSXin LI switch (tag) { 3393e41d09dSXin LI case DER_TAG_PRINTABLE_STRING: 3403e41d09dSXin LI case DER_TAG_UTF8_STRING: 3413e41d09dSXin LI printf("%.*s\n", len, (const char *)q); 3423e41d09dSXin LI return; 3433e41d09dSXin LI default: 3443e41d09dSXin LI break; 3453e41d09dSXin LI } 3463e41d09dSXin LI 3473e41d09dSXin LI for (uint32_t i = 0; i < len; i++) 3483e41d09dSXin LI printf("%.2x", d[i]); 3493e41d09dSXin LI printf("\n"); 3503e41d09dSXin LI } 3513e41d09dSXin LI 3523e41d09dSXin LI static void 3533e41d09dSXin LI printdata(size_t level, const void *v, size_t x, size_t l) 3543e41d09dSXin LI { 3553e41d09dSXin LI const uint8_t *p = v, *ep = p + l; 3563e41d09dSXin LI size_t ox; 3573e41d09dSXin LI char buf[128]; 3583e41d09dSXin LI 3593e41d09dSXin LI while (p + x < ep) { 3603e41d09dSXin LI const uint8_t *q; 3613e41d09dSXin LI uint8_t c = getclass(p[x]); 3623e41d09dSXin LI uint8_t t = gettype(p[x]); 3633e41d09dSXin LI ox = x; 3643e41d09dSXin LI if (x != 0) 3653e41d09dSXin LI printf("%.2x %.2x %.2x\n", p[x - 1], p[x], p[x + 1]); 3663e41d09dSXin LI uint32_t tag = gettag(p, &x, ep - p + x); 3673e41d09dSXin LI if (p + x >= ep) 3683e41d09dSXin LI break; 3693e41d09dSXin LI uint32_t len = getlength(p, &x, ep - p + x); 3703e41d09dSXin LI 3713e41d09dSXin LI printf("%zu %zu-%zu %c,%c,%s,%u:", level, ox, x, 3723e41d09dSXin LI der_class[c], der_type[t], 3733e41d09dSXin LI der_tag(buf, sizeof(buf), tag), len); 3743e41d09dSXin LI q = p + x; 3753e41d09dSXin LI if (p + len > ep) 3763e41d09dSXin LI errx(EXIT_FAILURE, "corrupt der"); 3773e41d09dSXin LI printtag(tag, q, len); 3783e41d09dSXin LI if (t != DER_TYPE_PRIMITIVE) 3793e41d09dSXin LI printdata(level + 1, p, x, len + x); 3803e41d09dSXin LI x += len; 3813e41d09dSXin LI } 3823e41d09dSXin LI } 3833e41d09dSXin LI 3843e41d09dSXin LI int 3853e41d09dSXin LI main(int argc, char *argv[]) 3863e41d09dSXin LI { 3873e41d09dSXin LI int fd; 3883e41d09dSXin LI struct stat st; 3893e41d09dSXin LI size_t l; 3903e41d09dSXin LI void *p; 3913e41d09dSXin LI 3923e41d09dSXin LI if ((fd = open(argv[1], O_RDONLY)) == -1) 3933e41d09dSXin LI err(EXIT_FAILURE, "open `%s'", argv[1]); 3943e41d09dSXin LI if (fstat(fd, &st) == -1) 3953e41d09dSXin LI err(EXIT_FAILURE, "stat `%s'", argv[1]); 3963e41d09dSXin LI l = (size_t)st.st_size; 3973e41d09dSXin LI if ((p = mmap(NULL, l, PROT_READ, MAP_FILE, fd, 0)) == MAP_FAILED) 3983e41d09dSXin LI err(EXIT_FAILURE, "mmap `%s'", argv[1]); 3993e41d09dSXin LI 4003e41d09dSXin LI printdata(0, p, 0, l); 4013e41d09dSXin LI munmap(p, l); 4023e41d09dSXin LI return 0; 4033e41d09dSXin LI } 4043e41d09dSXin LI #endif 405