root/ext/filter/logical_filters.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_filter_parse_int
  2. php_filter_parse_octal
  3. php_filter_parse_hex
  4. php_filter_int
  5. php_filter_boolean
  6. php_filter_float
  7. php_filter_validate_regexp
  8. _php_filter_validate_domain
  9. php_filter_validate_domain
  10. php_filter_validate_url
  11. php_filter_validate_email
  12. _php_filter_validate_ipv4
  13. _php_filter_validate_ipv6
  14. php_filter_validate_ip
  15. php_filter_validate_mac

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

/* [<][>][^][v][top][bottom][index][help] */