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_url
  9. php_filter_validate_email
  10. _php_filter_validate_ipv4
  11. _php_filter_validate_ipv6
  12. php_filter_validate_ip
  13. php_filter_validate_mac

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

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