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>