Annotation of embedaddon/php/ext/filter/logical_filters.c, revision 1.1.1.3

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
1.1.1.3 ! misho       5:   | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       6:   +----------------------------------------------------------------------+
                      7:   | This source file is subject to version 3.01 of the PHP license,      |
                      8:   | that is bundled with this package in the file LICENSE, and is        |
                      9:   | available through the world-wide-web at the following url:           |
                     10:   | http://www.php.net/license/3_01.txt                                  |
                     11:   | If you did not receive a copy of the PHP license and are unable to   |
                     12:   | obtain it through the world-wide-web, please send a note to          |
                     13:   | license@php.net so we can mail you a copy immediately.               |
                     14:   +----------------------------------------------------------------------+
                     15:   | Authors: Derick Rethans <derick@php.net>                             |
                     16:   |          Pierre-A. Joye <pierre@php.net>                             |
                     17:   +----------------------------------------------------------------------+
                     18: */
                     19: 
1.1.1.2   misho      20: /* $Id$ */
1.1       misho      21: 
                     22: #include "php_filter.h"
                     23: #include "filter_private.h"
                     24: #include "ext/standard/url.h"
                     25: #include "ext/pcre/php_pcre.h"
                     26: 
                     27: #include "zend_multiply.h"
                     28: 
                     29: #if HAVE_ARPA_INET_H
                     30: # include <arpa/inet.h>
                     31: #endif
                     32: 
                     33: #ifndef INADDR_NONE
                     34: # define INADDR_NONE ((unsigned long int) -1)
                     35: #endif
                     36: 
                     37: 
                     38: /* {{{ FETCH_LONG_OPTION(var_name, option_name) */
                     39: #define FETCH_LONG_OPTION(var_name, option_name)                                                                         \
                     40:        var_name = 0;                                                                                                        \
                     41:        var_name##_set = 0;                                                                                                  \
                     42:        if (option_array) {                                                                                                  \
                     43:                if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
                     44:                        PHP_FILTER_GET_LONG_OPT(option_val, var_name);                                                          \
                     45:                        var_name##_set = 1;                                                                                          \
                     46:                }                                                                                                                \
                     47:        }
                     48: /* }}} */
                     49: 
                     50: /* {{{ FETCH_STRING_OPTION(var_name, option_name) */
                     51: #define FETCH_STRING_OPTION(var_name, option_name)                                                                       \
                     52:        var_name = NULL;                                                                                                     \
                     53:        var_name##_set = 0;                                                                                                  \
                     54:        var_name##_len = 0;                                                                                                  \
                     55:        if (option_array) {                                                                                                  \
                     56:                if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
                     57:                        if (Z_TYPE_PP(option_val) == IS_STRING) {                                                                    \
                     58:                                var_name = Z_STRVAL_PP(option_val);                                                                      \
                     59:                                var_name##_len = Z_STRLEN_PP(option_val);                                                                \
                     60:                                var_name##_set = 1;                                                                                      \
                     61:                        }                                                                                                            \
                     62:                }                                                                                                                \
                     63:        }
                     64: /* }}} */
                     65: 
                     66: #define FORMAT_IPV4    4
                     67: #define FORMAT_IPV6    6
                     68: 
                     69: static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
                     70:        long ctx_value;
                     71:        int sign = 0, digit = 0;
                     72:        const char *end = str + str_len;
                     73: 
                     74:        switch (*str) {
                     75:                case '-':
                     76:                        sign = 1;
                     77:                case '+':
                     78:                        str++;
                     79:                default:
                     80:                        break;
                     81:        }
                     82: 
1.1.1.3 ! misho      83:        if (*str == '0' && str + 1 == end) {
        !            84:                /* Special cases: +0 and -0 */
        !            85:                return 1;
        !            86:        }
        !            87: 
1.1       misho      88:        /* must start with 1..9*/
                     89:        if (str < end && *str >= '1' && *str <= '9') {
                     90:                ctx_value = ((sign)?-1:1) * ((*(str++)) - '0');
                     91:        } else {
                     92:                return -1;
                     93:        }
                     94: 
                     95:        if ((end - str > MAX_LENGTH_OF_LONG - 1) /* number too long */
                     96:         || (SIZEOF_LONG == 4 && (end - str == MAX_LENGTH_OF_LONG - 1) && *str > '2')) {
                     97:                /* overflow */
                     98:                return -1;
                     99:        }
                    100: 
                    101:        while (str < end) {
                    102:                if (*str >= '0' && *str <= '9') {
                    103:                        digit = (*(str++) - '0');
                    104:                        if ( (!sign) && ctx_value <= (LONG_MAX-digit)/10 ) {
                    105:                                ctx_value = (ctx_value * 10) + digit;
                    106:                        } else if ( sign && ctx_value >= (LONG_MIN+digit)/10) {
                    107:                                ctx_value = (ctx_value * 10) - digit;
                    108:                        } else {
                    109:                                return -1;
                    110:                        }
                    111:                } else {
                    112:                        return -1;
                    113:                }
                    114:        }
                    115: 
                    116:        *ret = ctx_value;
                    117:        return 1;
                    118: }
                    119: /* }}} */
                    120: 
                    121: static int php_filter_parse_octal(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
                    122:        unsigned long ctx_value = 0;
                    123:        const char *end = str + str_len;
                    124: 
                    125:        while (str < end) {
                    126:                if (*str >= '0' && *str <= '7') {
                    127:                        unsigned long n = ((*(str++)) - '0');
                    128: 
                    129:                        if ((ctx_value > ((unsigned long)(~(long)0)) / 8) ||
                    130:                                ((ctx_value = ctx_value * 8) > ((unsigned long)(~(long)0)) - n)) {
                    131:                                return -1;
                    132:                        }
                    133:                        ctx_value += n;
                    134:                } else {
                    135:                        return -1;
                    136:                }
                    137:        }
                    138:        
                    139:        *ret = (long)ctx_value;
                    140:        return 1;
                    141: }
                    142: /* }}} */
                    143: 
                    144: static int php_filter_parse_hex(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
                    145:        unsigned long ctx_value = 0;
                    146:        const char *end = str + str_len;
                    147:        unsigned long n;
                    148: 
                    149:        while (str < end) {
                    150:                if (*str >= '0' && *str <= '9') {
                    151:                        n = ((*(str++)) - '0');
                    152:                } else if (*str >= 'a' && *str <= 'f') {
                    153:                        n = ((*(str++)) - ('a' - 10));
                    154:                } else if (*str >= 'A' && *str <= 'F') {
                    155:                        n = ((*(str++)) - ('A' - 10));
                    156:                } else {
                    157:                        return -1;
                    158:                }
                    159:                if ((ctx_value > ((unsigned long)(~(long)0)) / 16) ||
                    160:                        ((ctx_value = ctx_value * 16) > ((unsigned long)(~(long)0)) - n)) {
                    161:                        return -1;
                    162:                }
                    163:                ctx_value += n;
                    164:        }
                    165: 
                    166:        *ret = (long)ctx_value;
                    167:        return 1;
                    168: }
                    169: /* }}} */
                    170: 
                    171: void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    172: {
                    173:        zval **option_val;
                    174:        long   min_range, max_range, option_flags;
                    175:        int    min_range_set, max_range_set;
                    176:        int    allow_octal = 0, allow_hex = 0;
                    177:        int        len, error = 0;
                    178:        long   ctx_value;
                    179:        char *p;
                    180: 
                    181:        /* Parse options */
                    182:        FETCH_LONG_OPTION(min_range,    "min_range");
                    183:        FETCH_LONG_OPTION(max_range,    "max_range");
                    184:        option_flags = flags;
                    185: 
                    186:        len = Z_STRLEN_P(value);
                    187: 
                    188:        if (len == 0) {
                    189:                RETURN_VALIDATION_FAILED
                    190:        }
                    191: 
                    192:        if (option_flags & FILTER_FLAG_ALLOW_OCTAL) {
                    193:                allow_octal = 1;
                    194:        }
                    195: 
                    196:        if (option_flags & FILTER_FLAG_ALLOW_HEX) {
                    197:                allow_hex = 1;
                    198:        }
                    199: 
                    200:        /* Start the validating loop */
                    201:        p = Z_STRVAL_P(value);
                    202:        ctx_value = 0;
                    203: 
                    204:        PHP_FILTER_TRIM_DEFAULT(p, len);
                    205: 
                    206:        if (*p == '0') {
                    207:                p++; len--;
                    208:                if (allow_hex && (*p == 'x' || *p == 'X')) {
                    209:                        p++; len--;
                    210:                        if (php_filter_parse_hex(p, len, &ctx_value TSRMLS_CC) < 0) {
                    211:                                error = 1;
                    212:                        }
                    213:                } else if (allow_octal) {
                    214:                        if (php_filter_parse_octal(p, len, &ctx_value TSRMLS_CC) < 0) {
                    215:                                error = 1;
                    216:                        }
                    217:                } else if (len != 0) {
                    218:                        error = 1;
                    219:                }
                    220:        } else {
                    221:                if (php_filter_parse_int(p, len, &ctx_value TSRMLS_CC) < 0) {
                    222:                        error = 1;
                    223:                }
                    224:        }
                    225: 
                    226:        if (error > 0 || (min_range_set && (ctx_value < min_range)) || (max_range_set && (ctx_value > max_range))) {
                    227:                RETURN_VALIDATION_FAILED
                    228:        } else {
                    229:                zval_dtor(value);
                    230:                Z_TYPE_P(value) = IS_LONG;
                    231:                Z_LVAL_P(value) = ctx_value;
                    232:                return;
                    233:        }
                    234: }
                    235: /* }}} */
                    236: 
                    237: void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    238: {
                    239:        char *str = Z_STRVAL_P(value);
                    240:        int len = Z_STRLEN_P(value);
                    241:        int ret;
                    242: 
1.1.1.3 ! misho     243:        PHP_FILTER_TRIM_DEFAULT_EX(str, len, 0);
1.1       misho     244: 
                    245:        /* returns true for "1", "true", "on" and "yes"
                    246:         * returns false for "0", "false", "off", "no", and ""
                    247:         * null otherwise. */
                    248:        switch (len) {
1.1.1.3 ! misho     249:                case 0:
        !           250:                        ret = 0;
        !           251:                        break;
1.1       misho     252:                case 1:
                    253:                        if (*str == '1') {
                    254:                                ret = 1;
                    255:                        } else if (*str == '0') {
                    256:                                ret = 0;
                    257:                        } else {
                    258:                                ret = -1;
                    259:                        }
                    260:                        break;
                    261:                case 2:
                    262:                        if (strncasecmp(str, "on", 2) == 0) {
                    263:                                ret = 1;
                    264:                        } else if (strncasecmp(str, "no", 2) == 0) {
                    265:                                ret = 0;
                    266:                        } else {
                    267:                                ret = -1;
                    268:                        }
                    269:                        break;
                    270:                case 3:
                    271:                        if (strncasecmp(str, "yes", 3) == 0) {
                    272:                                ret = 1;
                    273:                        } else if (strncasecmp(str, "off", 3) == 0) {
                    274:                                ret = 0;
                    275:                        } else {
                    276:                                ret = -1;
                    277:                        }
                    278:                        break;
                    279:                case 4:
                    280:                        if (strncasecmp(str, "true", 4) == 0) {
                    281:                                ret = 1;
                    282:                        } else {
                    283:                                ret = -1;
                    284:                        }
                    285:                        break;
                    286:                case 5:
                    287:                        if (strncasecmp(str, "false", 5) == 0) {
                    288:                                ret = 0;
                    289:                        } else {
                    290:                                ret = -1;
                    291:                        }
                    292:                        break;
                    293:                default:
                    294:                        ret = -1;
                    295:        }
                    296: 
1.1.1.3 ! misho     297:        if (ret == -1) {
1.1       misho     298:                RETURN_VALIDATION_FAILED
                    299:        } else {
                    300:                zval_dtor(value);
                    301:                ZVAL_BOOL(value, ret);
                    302:        }
                    303: }
                    304: /* }}} */
                    305: 
                    306: void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    307: {
                    308:        int len;
                    309:        char *str, *end;
                    310:        char *num, *p;
                    311: 
                    312:        zval **option_val;
                    313:        char *decimal;
                    314:        int decimal_set, decimal_len;
                    315:        char dec_sep = '.';
                    316:        char tsd_sep[3] = "',.";
                    317: 
                    318:        long lval;
                    319:        double dval;
                    320: 
                    321:        int first, n;
                    322: 
                    323:        len = Z_STRLEN_P(value);
                    324:        str = Z_STRVAL_P(value);
                    325: 
                    326:        PHP_FILTER_TRIM_DEFAULT(str, len);
                    327:        end = str + len;
                    328: 
                    329:        FETCH_STRING_OPTION(decimal, "decimal");
                    330: 
                    331:        if (decimal_set) {
                    332:                if (decimal_len != 1) {
                    333:                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "decimal separator must be one char");
                    334:                        RETURN_VALIDATION_FAILED
                    335:                } else {
                    336:                        dec_sep = *decimal;
                    337:                }
                    338:        }
                    339: 
                    340:        num = p = emalloc(len+1);
                    341:        if (str < end && (*str == '+' || *str == '-')) {
                    342:                *p++ = *str++;
                    343:        }
                    344:        first = 1;
                    345:        while (1) {
                    346:                n = 0;
                    347:                while (str < end && *str >= '0' && *str <= '9') {
                    348:                        ++n;
                    349:                        *p++ = *str++;
                    350:                }
                    351:                if (str == end || *str == dec_sep || *str == 'e' || *str == 'E') {
                    352:                        if (!first && n != 3) {
                    353:                                goto error;
                    354:                        }
                    355:                        if (*str == dec_sep) {
                    356:                                *p++ = '.';
                    357:                                str++;
                    358:                                while (str < end && *str >= '0' && *str <= '9') {
                    359:                                        *p++ = *str++;
                    360:                                }
                    361:                        }
                    362:                        if (*str == 'e' || *str == 'E') {
                    363:                                *p++ = *str++;
                    364:                                if (str < end && (*str == '+' || *str == '-')) {
                    365:                                        *p++ = *str++;
                    366:                                }
                    367:                                while (str < end && *str >= '0' && *str <= '9') {
                    368:                                        *p++ = *str++;
                    369:                                }
                    370:                        }
                    371:                        break;
                    372:                }
                    373:                if ((flags & FILTER_FLAG_ALLOW_THOUSAND) && (*str == tsd_sep[0] || *str == tsd_sep[1] || *str == tsd_sep[2])) {
                    374:                        if (first?(n < 1 || n > 3):(n != 3)) {
                    375:                                goto error;
                    376:                        }
                    377:                        first = 0;
                    378:                        str++;
                    379:                } else {
                    380:                        goto error;
                    381:                }
                    382:        }
                    383:        if (str != end) {
                    384:                goto error;
                    385:        }
                    386:        *p = 0;
                    387: 
                    388:        switch (is_numeric_string(num, p - num, &lval, &dval, 0)) {
                    389:                case IS_LONG:
                    390:                        zval_dtor(value);
                    391:                        Z_TYPE_P(value) = IS_DOUBLE;
                    392:                        Z_DVAL_P(value) = lval;
                    393:                        break;
                    394:                case IS_DOUBLE:
                    395:                        if ((!dval && p - num > 1 && strpbrk(num, "123456789")) || !zend_finite(dval)) {
                    396:                                goto error;
                    397:                        }
                    398:                        zval_dtor(value);
                    399:                        Z_TYPE_P(value) = IS_DOUBLE;
                    400:                        Z_DVAL_P(value) = dval;
                    401:                        break;
                    402:                default:
                    403: error:
                    404:                        efree(num);
                    405:                        RETURN_VALIDATION_FAILED
                    406:        }
                    407:        efree(num);     
                    408: }
                    409: /* }}} */
                    410: 
                    411: void php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    412: {
                    413:        zval **option_val;
                    414:        char  *regexp;
                    415:        int regexp_len;
                    416:        long   option_flags;
                    417:        int    regexp_set, option_flags_set;
                    418: 
                    419:        pcre       *re = NULL;
                    420:        pcre_extra *pcre_extra = NULL;
                    421:        int preg_options = 0;
                    422: 
                    423:        int         ovector[3];
                    424:        int         matches;
                    425: 
                    426:        /* Parse options */
                    427:        FETCH_STRING_OPTION(regexp, "regexp");
                    428:        FETCH_LONG_OPTION(option_flags, "flags");
                    429: 
                    430:        if (!regexp_set) {
                    431:                php_error_docref(NULL TSRMLS_CC, E_WARNING, "'regexp' option missing");
                    432:                RETURN_VALIDATION_FAILED
                    433:        }
                    434: 
                    435:        re = pcre_get_compiled_regex(regexp, &pcre_extra, &preg_options TSRMLS_CC);
                    436:        if (!re) {
                    437:                RETURN_VALIDATION_FAILED
                    438:        }
                    439:        matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
                    440: 
                    441:        /* 0 means that the vector is too small to hold all the captured substring offsets */
                    442:        if (matches < 0) {
                    443:                RETURN_VALIDATION_FAILED
                    444:        }
                    445: }
                    446: /* }}} */
                    447: 
                    448: void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    449: {
                    450:        php_url *url;
                    451:        int old_len = Z_STRLEN_P(value);
                    452:        
                    453:        php_filter_url(value, flags, option_array, charset TSRMLS_CC);
                    454: 
                    455:        if (Z_TYPE_P(value) != IS_STRING || old_len != Z_STRLEN_P(value)) {
                    456:                RETURN_VALIDATION_FAILED
                    457:        }
                    458: 
                    459:        /* Use parse_url - if it returns false, we return NULL */
                    460:        url = php_url_parse_ex(Z_STRVAL_P(value), Z_STRLEN_P(value));
                    461: 
                    462:        if (url == NULL) {
                    463:                RETURN_VALIDATION_FAILED
                    464:        }
                    465: 
                    466:        if (url->scheme != NULL && (!strcasecmp(url->scheme, "http") || !strcasecmp(url->scheme, "https"))) {
                    467:                char *e, *s;
                    468: 
                    469:                if (url->host == NULL) {
                    470:                        goto bad_url;
                    471:                }
                    472: 
                    473:                e = url->host + strlen(url->host);
                    474:                s = url->host;
                    475: 
                    476:                /* First char of hostname must be alphanumeric */
                    477:                if(!isalnum((int)*(unsigned char *)s)) { 
                    478:                        goto bad_url;
                    479:                }
                    480: 
                    481:                while (s < e) {
                    482:                        if (!isalnum((int)*(unsigned char *)s) && *s != '-' && *s != '.') {
                    483:                                goto bad_url;
                    484:                        }
                    485:                        s++;
                    486:                }
                    487: 
                    488:                if (*(e - 1) == '.') {
                    489:                        goto bad_url;
                    490:                }
                    491:        }
                    492: 
                    493:        if (
                    494:                url->scheme == NULL || 
                    495:                /* some schemas allow the host to be empty */
                    496:                (url->host == NULL && (strcmp(url->scheme, "mailto") && strcmp(url->scheme, "news") && strcmp(url->scheme, "file"))) ||
                    497:                ((flags & FILTER_FLAG_PATH_REQUIRED) && url->path == NULL) || ((flags & FILTER_FLAG_QUERY_REQUIRED) && url->query == NULL)
                    498:        ) {
                    499: bad_url:
                    500:                php_url_free(url);
                    501:                RETURN_VALIDATION_FAILED
                    502:        }
                    503:        php_url_free(url);
                    504: }
                    505: /* }}} */
                    506: 
                    507: void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    508: {
                    509:        /*
                    510:         * The regex below is based on a regex by Michael Rushton.
                    511:         * However, it is not identical.  I changed it to only consider routeable
                    512:         * addresses as valid.  Michael's regex considers a@b a valid address
                    513:         * which conflicts with section 2.3.5 of RFC 5321 which states that:
                    514:         *
                    515:         *   Only resolvable, fully-qualified domain names (FQDNs) are permitted
                    516:         *   when domain names are used in SMTP.  In other words, names that can
                    517:         *   be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
                    518:         *   in Section 5) are permitted, as are CNAME RRs whose targets can be
                    519:         *   resolved, in turn, to MX or address RRs.  Local nicknames or
                    520:         *   unqualified names MUST NOT be used.
                    521:         *
                    522:         * This regex does not handle comments and folding whitespace.  While
                    523:         * this is technically valid in an email address, these parts aren't
                    524:         * actually part of the address itself.
                    525:         *
                    526:         * Michael's regex carries this copyright:
                    527:         *
                    528:         * Copyright © Michael Rushton 2009-10
                    529:         * http://squiloople.com/
                    530:         * Feel free to use and redistribute this code. But please keep this copyright notice.
                    531:         *
                    532:         */
                    533:        const char regexp[] = "/^(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){255,})(?!(?:(?:\\x22?\\x5C[\\x00-\\x7E]\\x22?)|(?:\\x22?[^\\x5C\\x22]\\x22?)){65,}@)(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F]|(?:\\x5C[\\x00-\\x7F]))*\\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:(?:xn--)?[a-z0-9]+(?:-+[a-z0-9]+)*\\.){1,126}){1,}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-+[a-z0-9]+)*)|(?:\\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\\]))$/iD";
                    534: 
                    535:        pcre       *re = NULL;
                    536:        pcre_extra *pcre_extra = NULL;
                    537:        int preg_options = 0;
                    538:        int         ovector[150]; /* Needs to be a multiple of 3 */
                    539:        int         matches;
                    540: 
                    541: 
                    542:        /* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
                    543:        if (Z_STRLEN_P(value) > 320) {
                    544:                RETURN_VALIDATION_FAILED
                    545:        }
                    546: 
                    547:        re = pcre_get_compiled_regex((char *)regexp, &pcre_extra, &preg_options TSRMLS_CC);
                    548:        if (!re) {
                    549:                RETURN_VALIDATION_FAILED
                    550:        }
                    551:        matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
                    552: 
                    553:        /* 0 means that the vector is too small to hold all the captured substring offsets */
                    554:        if (matches < 0) {
                    555:                RETURN_VALIDATION_FAILED
                    556:        }
                    557: 
                    558: }
                    559: /* }}} */
                    560: 
                    561: static int _php_filter_validate_ipv4(char *str, int str_len, int *ip) /* {{{ */
                    562: {
                    563:        const char *end = str + str_len;
                    564:        int num, m;
                    565:        int n = 0;
                    566: 
                    567:        while (str < end) {
                    568:                int leading_zero;
                    569:                if (*str < '0' || *str > '9') {
                    570:                        return 0;
                    571:                }
                    572:                leading_zero = (*str == '0');
                    573:                m = 1;
                    574:                num = ((*(str++)) - '0');
                    575:                while (str < end && (*str >= '0' && *str <= '9')) {
                    576:                        num = num * 10 + ((*(str++)) - '0');
                    577:                        if (num > 255 || ++m > 3) {
                    578:                                return 0;
                    579:                        }
                    580:                }
                    581:                /* don't allow a leading 0; that introduces octal numbers,
                    582:                 * which we don't support */
                    583:                if (leading_zero && (num != 0 || m > 1))
                    584:                        return 0;
                    585:                ip[n++] = num;
                    586:                if (n == 4) {
                    587:                        return str == end;
                    588:                } else if (str >= end || *(str++) != '.') {
                    589:                        return 0;
                    590:                }
                    591:        }
                    592:        return 0;               
                    593: }
                    594: /* }}} */
                    595: 
                    596: static int _php_filter_validate_ipv6(char *str, int str_len TSRMLS_DC) /* {{{ */
                    597: {
                    598:        int compressed = 0;
                    599:        int blocks = 0;
                    600:        int n;
                    601:        char *ipv4;
                    602:        char *end;
                    603:        int ip4elm[4];
                    604:        char *s = str;
                    605: 
                    606:        if (!memchr(str, ':', str_len)) {
                    607:                return 0;
                    608:        }
                    609: 
                    610:        /* check for bundled IPv4 */
                    611:        ipv4 = memchr(str, '.', str_len);
                    612:        if (ipv4) {
                    613:                while (ipv4 > str && *(ipv4-1) != ':') {
                    614:                        ipv4--;
                    615:                }
                    616: 
                    617:                if (!_php_filter_validate_ipv4(ipv4, (str_len - (ipv4 - str)), ip4elm)) {
                    618:                        return 0;
                    619:                }
                    620: 
                    621:                str_len = ipv4 - str; /* length excluding ipv4 */
                    622:                if (str_len < 2) {
                    623:                        return 0;
                    624:                }
                    625: 
                    626:                if (ipv4[-2] != ':') {
                    627:                        /* don't include : before ipv4 unless it's a :: */
                    628:                        str_len--;
                    629:                }
                    630: 
                    631:                blocks = 2;
                    632:        }
                    633: 
                    634:        end = str + str_len;
                    635: 
                    636:        while (str < end) {
                    637:                if (*str == ':') {
                    638:                        if (++str >= end) {
                    639:                                /* cannot end in : without previous : */
                    640:                                return 0;
                    641:                        }
                    642:                        if (*str == ':') {
                    643:                                if (compressed) {
                    644:                                        return 0;
                    645:                                }
                    646:                                blocks++; /* :: means 1 or more 16-bit 0 blocks */
                    647:                                compressed = 1;
                    648: 
                    649:                                if (++str == end) {
                    650:                                        return (blocks <= 8);
                    651:                                }
                    652:                        } else if ((str - 1) == s) {
                    653:                                /* dont allow leading : without another : following */
                    654:                                return 0;
                    655:                        }                               
                    656:                }
                    657:                n = 0;
                    658:                while ((str < end) &&
                    659:                       ((*str >= '0' && *str <= '9') ||
                    660:                        (*str >= 'a' && *str <= 'f') ||
                    661:                        (*str >= 'A' && *str <= 'F'))) {
                    662:                        n++;
                    663:                        str++;
                    664:                }
                    665:                if (n < 1 || n > 4) {
                    666:                        return 0;
                    667:                }
                    668:                if (++blocks > 8)
                    669:                        return 0;
                    670:        }
                    671:        return ((compressed && blocks <= 8) || blocks == 8);
                    672: }
                    673: /* }}} */
                    674: 
                    675: void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
                    676: {
                    677:        /* validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a
                    678:         * flag to throw out reserved ranges; multicast ranges... etc. If both
                    679:         * allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or
                    680:         * colon determine the format */
                    681: 
                    682:        int            ip[4];
                    683:        int            mode;
                    684: 
                    685:        if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) {
                    686:                mode = FORMAT_IPV6;
                    687:        } else if (memchr(Z_STRVAL_P(value), '.', Z_STRLEN_P(value))) {
                    688:                mode = FORMAT_IPV4;
                    689:        } else {
                    690:                RETURN_VALIDATION_FAILED
                    691:        }
                    692: 
                    693:        if ((flags & FILTER_FLAG_IPV4) && (flags & FILTER_FLAG_IPV6)) {
                    694:                /* Both formats are cool */
                    695:        } else if ((flags & FILTER_FLAG_IPV4) && mode == FORMAT_IPV6) {
                    696:                RETURN_VALIDATION_FAILED
                    697:        } else if ((flags & FILTER_FLAG_IPV6) && mode == FORMAT_IPV4) {
                    698:                RETURN_VALIDATION_FAILED
                    699:        }
                    700: 
                    701:        switch (mode) {
                    702:                case FORMAT_IPV4:
                    703:                        if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) {
                    704:                                RETURN_VALIDATION_FAILED
                    705:                        }
                    706: 
                    707:                        /* Check flags */
                    708:                        if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
                    709:                                if (
                    710:                                        (ip[0] == 10) ||
                    711:                                        (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) ||
                    712:                                        (ip[0] == 192 && ip[1] == 168)
                    713:                                ) {
                    714:                                        RETURN_VALIDATION_FAILED
                    715:                                }
                    716:                        }
                    717: 
                    718:                        if (flags & FILTER_FLAG_NO_RES_RANGE) {
                    719:                                if (
                    720:                                        (ip[0] == 0) ||
                    721:                                        (ip[0] == 128 && ip[1] == 0) ||
                    722:                                        (ip[0] == 191 && ip[1] == 255) ||
                    723:                                        (ip[0] == 169 && ip[1] == 254) ||
                    724:                                        (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) ||
                    725:                                        (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1) ||
                    726:                                        (ip[0] >= 224 && ip[0] <= 255)
                    727:                                ) {
                    728:                                        RETURN_VALIDATION_FAILED
                    729:                                }
                    730:                        }
                    731:                        break;
                    732: 
                    733:                case FORMAT_IPV6:
                    734:                        {
                    735:                                int res = 0;
                    736:                                res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);
                    737:                                if (res < 1) {
                    738:                                        RETURN_VALIDATION_FAILED
                    739:                                }
                    740:                                /* Check flags */
                    741:                                if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
                    742:                                        if (Z_STRLEN_P(value) >=2 && (!strncasecmp("FC", Z_STRVAL_P(value), 2) || !strncasecmp("FD", Z_STRVAL_P(value), 2))) {
                    743:                                                RETURN_VALIDATION_FAILED
                    744:                                        }
                    745:                                }
                    746:                                if (flags & FILTER_FLAG_NO_RES_RANGE) {
                    747:                                        switch (Z_STRLEN_P(value)) {
                    748:                                                case 1: case 0:
                    749:                                                        break;
                    750:                                                case 2:
                    751:                                                        if (!strcmp("::", Z_STRVAL_P(value))) {
                    752:                                                                RETURN_VALIDATION_FAILED
                    753:                                                        }
                    754:                                                        break;
                    755:                                                case 3:
                    756:                                                        if (!strcmp("::1", Z_STRVAL_P(value)) || !strcmp("5f:", Z_STRVAL_P(value))) {
                    757:                                                                RETURN_VALIDATION_FAILED
                    758:                                                        }
                    759:                                                        break;
                    760:                                                default:
                    761:                                                        if (Z_STRLEN_P(value) >= 5) {
                    762:                                                                if (
                    763:                                                                        !strncasecmp("fe8", Z_STRVAL_P(value), 3) ||
                    764:                                                                        !strncasecmp("fe9", Z_STRVAL_P(value), 3) ||
                    765:                                                                        !strncasecmp("fea", Z_STRVAL_P(value), 3) ||
                    766:                                                                        !strncasecmp("feb", Z_STRVAL_P(value), 3)
                    767:                                                                ) {
                    768:                                                                        RETURN_VALIDATION_FAILED
                    769:                                                                }
                    770:                                                        }
                    771:                                                        if (
                    772:                                                                (Z_STRLEN_P(value) >= 9 &&  !strncasecmp("2001:0db8", Z_STRVAL_P(value), 9)) ||
                    773:                                                                (Z_STRLEN_P(value) >= 2 &&  !strncasecmp("5f", Z_STRVAL_P(value), 2)) ||
                    774:                                                                (Z_STRLEN_P(value) >= 4 &&  !strncasecmp("3ff3", Z_STRVAL_P(value), 4)) ||
                    775:                                                                (Z_STRLEN_P(value) >= 8 &&  !strncasecmp("2001:001", Z_STRVAL_P(value), 8))
                    776:                                                        ) {
                    777:                                                                RETURN_VALIDATION_FAILED
                    778:                                                        }
                    779:                                        }
                    780:                                }
                    781:                        }
                    782:                        break;
                    783:        }
                    784: }
                    785: /* }}} */
                    786: 
                    787: /*
                    788:  * Local variables:
                    789:  * tab-width: 4
                    790:  * c-basic-offset: 4
                    791:  * End:
                    792:  * vim600: noet sw=4 ts=4 fdm=marker
                    793:  * vim<600: noet sw=4 ts=4
                    794:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>