PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/filter - logical_filters.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 419 432 97.0 %
Date: 2016-08-24 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Wed, 24 Aug 2016 12:20:20 +0000 (2 days ago)

Copyright © 2005-2016 The PHP Group
All rights reserved.