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

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

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