Back to index

php5  5.3.10
logical_filters.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 1997-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Authors: Derick Rethans <derick@php.net>                             |
00016   |          Pierre-A. Joye <pierre@php.net>                             |
00017   +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: logical_filters.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #include "php_filter.h"
00023 #include "filter_private.h"
00024 #include "ext/standard/url.h"
00025 #include "ext/pcre/php_pcre.h"
00026 
00027 #include "zend_multiply.h"
00028 
00029 #if HAVE_ARPA_INET_H
00030 # include <arpa/inet.h>
00031 #endif
00032 
00033 #ifndef INADDR_NONE
00034 # define INADDR_NONE ((unsigned long int) -1)
00035 #endif
00036 
00037 
00038 /* {{{ FETCH_LONG_OPTION(var_name, option_name) */
00039 #define FETCH_LONG_OPTION(var_name, option_name)                                                                         \
00040        var_name = 0;                                                                                                        \
00041        var_name##_set = 0;                                                                                                  \
00042        if (option_array) {                                                                                                  \
00043               if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
00044                      PHP_FILTER_GET_LONG_OPT(option_val, var_name);                                                    \
00045                      var_name##_set = 1;                                                                                          \
00046               }                                                                                                                \
00047        }
00048 /* }}} */
00049 
00050 /* {{{ FETCH_STRING_OPTION(var_name, option_name) */
00051 #define FETCH_STRING_OPTION(var_name, option_name)                                                                       \
00052        var_name = NULL;                                                                                                     \
00053        var_name##_set = 0;                                                                                                  \
00054        var_name##_len = 0;                                                                                                  \
00055        if (option_array) {                                                                                                  \
00056               if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
00057                      if (Z_TYPE_PP(option_val) == IS_STRING) {                                                                    \
00058                             var_name = Z_STRVAL_PP(option_val);                                                                      \
00059                             var_name##_len = Z_STRLEN_PP(option_val);                                                                \
00060                             var_name##_set = 1;                                                                                      \
00061                      }                                                                                                            \
00062               }                                                                                                                \
00063        }
00064 /* }}} */
00065 
00066 #define FORMAT_IPV4    4
00067 #define FORMAT_IPV6    6
00068 
00069 static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
00070        long ctx_value;
00071        int sign = 0, digit = 0;
00072        const char *end = str + str_len;
00073 
00074        switch (*str) {
00075               case '-':
00076                      sign = 1;
00077               case '+':
00078                      str++;
00079               default:
00080                      break;
00081        }
00082 
00083        /* must start with 1..9*/
00084        if (str < end && *str >= '1' && *str <= '9') {
00085               ctx_value = ((sign)?-1:1) * ((*(str++)) - '0');
00086        } else {
00087               return -1;
00088        }
00089 
00090        if ((end - str > MAX_LENGTH_OF_LONG - 1) /* number too long */
00091         || (SIZEOF_LONG == 4 && (end - str == MAX_LENGTH_OF_LONG - 1) && *str > '2')) {
00092               /* overflow */
00093               return -1;
00094        }
00095 
00096        while (str < end) {
00097               if (*str >= '0' && *str <= '9') {
00098                      digit = (*(str++) - '0');
00099                      if ( (!sign) && ctx_value <= (LONG_MAX-digit)/10 ) {
00100                             ctx_value = (ctx_value * 10) + digit;
00101                      } else if ( sign && ctx_value >= (LONG_MIN+digit)/10) {
00102                             ctx_value = (ctx_value * 10) - digit;
00103                      } else {
00104                             return -1;
00105                      }
00106               } else {
00107                      return -1;
00108               }
00109        }
00110 
00111        *ret = ctx_value;
00112        return 1;
00113 }
00114 /* }}} */
00115 
00116 static int php_filter_parse_octal(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
00117        unsigned long ctx_value = 0;
00118        const char *end = str + str_len;
00119 
00120        while (str < end) {
00121               if (*str >= '0' && *str <= '7') {
00122                      unsigned long n = ((*(str++)) - '0');
00123 
00124                      if ((ctx_value > ((unsigned long)(~(long)0)) / 8) ||
00125                             ((ctx_value = ctx_value * 8) > ((unsigned long)(~(long)0)) - n)) {
00126                             return -1;
00127                      }
00128                      ctx_value += n;
00129               } else {
00130                      return -1;
00131               }
00132        }
00133        
00134        *ret = (long)ctx_value;
00135        return 1;
00136 }
00137 /* }}} */
00138 
00139 static int php_filter_parse_hex(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
00140        unsigned long ctx_value = 0;
00141        const char *end = str + str_len;
00142        unsigned long n;
00143 
00144        while (str < end) {
00145               if (*str >= '0' && *str <= '9') {
00146                      n = ((*(str++)) - '0');
00147               } else if (*str >= 'a' && *str <= 'f') {
00148                      n = ((*(str++)) - ('a' - 10));
00149               } else if (*str >= 'A' && *str <= 'F') {
00150                      n = ((*(str++)) - ('A' - 10));
00151               } else {
00152                      return -1;
00153               }
00154               if ((ctx_value > ((unsigned long)(~(long)0)) / 16) ||
00155                      ((ctx_value = ctx_value * 16) > ((unsigned long)(~(long)0)) - n)) {
00156                      return -1;
00157               }
00158               ctx_value += n;
00159        }
00160 
00161        *ret = (long)ctx_value;
00162        return 1;
00163 }
00164 /* }}} */
00165 
00166 void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00167 {
00168        zval **option_val;
00169        long   min_range, max_range, option_flags;
00170        int    min_range_set, max_range_set;
00171        int    allow_octal = 0, allow_hex = 0;
00172        int       len, error = 0;
00173        long   ctx_value;
00174        char *p;
00175 
00176        /* Parse options */
00177        FETCH_LONG_OPTION(min_range,    "min_range");
00178        FETCH_LONG_OPTION(max_range,    "max_range");
00179        option_flags = flags;
00180 
00181        len = Z_STRLEN_P(value);
00182 
00183        if (len == 0) {
00184               RETURN_VALIDATION_FAILED
00185        }
00186 
00187        if (option_flags & FILTER_FLAG_ALLOW_OCTAL) {
00188               allow_octal = 1;
00189        }
00190 
00191        if (option_flags & FILTER_FLAG_ALLOW_HEX) {
00192               allow_hex = 1;
00193        }
00194 
00195        /* Start the validating loop */
00196        p = Z_STRVAL_P(value);
00197        ctx_value = 0;
00198 
00199        PHP_FILTER_TRIM_DEFAULT(p, len);
00200 
00201        if (*p == '0') {
00202               p++; len--;
00203               if (allow_hex && (*p == 'x' || *p == 'X')) {
00204                      p++; len--;
00205                      if (php_filter_parse_hex(p, len, &ctx_value TSRMLS_CC) < 0) {
00206                             error = 1;
00207                      }
00208               } else if (allow_octal) {
00209                      if (php_filter_parse_octal(p, len, &ctx_value TSRMLS_CC) < 0) {
00210                             error = 1;
00211                      }
00212               } else if (len != 0) {
00213                      error = 1;
00214               }
00215        } else {
00216               if (php_filter_parse_int(p, len, &ctx_value TSRMLS_CC) < 0) {
00217                      error = 1;
00218               }
00219        }
00220 
00221        if (error > 0 || (min_range_set && (ctx_value < min_range)) || (max_range_set && (ctx_value > max_range))) {
00222               RETURN_VALIDATION_FAILED
00223        } else {
00224               zval_dtor(value);
00225               Z_TYPE_P(value) = IS_LONG;
00226               Z_LVAL_P(value) = ctx_value;
00227               return;
00228        }
00229 }
00230 /* }}} */
00231 
00232 void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00233 {
00234        char *str = Z_STRVAL_P(value);
00235        int len = Z_STRLEN_P(value);
00236        int ret;
00237 
00238        PHP_FILTER_TRIM_DEFAULT(str, len);
00239 
00240        /* returns true for "1", "true", "on" and "yes"
00241         * returns false for "0", "false", "off", "no", and ""
00242         * null otherwise. */
00243        switch (len) {
00244               case 1:
00245                      if (*str == '1') {
00246                             ret = 1;
00247                      } else if (*str == '0') {
00248                             ret = 0;
00249                      } else {
00250                             ret = -1;
00251                      }
00252                      break;
00253               case 2:
00254                      if (strncasecmp(str, "on", 2) == 0) {
00255                             ret = 1;
00256                      } else if (strncasecmp(str, "no", 2) == 0) {
00257                             ret = 0;
00258                      } else {
00259                             ret = -1;
00260                      }
00261                      break;
00262               case 3:
00263                      if (strncasecmp(str, "yes", 3) == 0) {
00264                             ret = 1;
00265                      } else if (strncasecmp(str, "off", 3) == 0) {
00266                             ret = 0;
00267                      } else {
00268                             ret = -1;
00269                      }
00270                      break;
00271               case 4:
00272                      if (strncasecmp(str, "true", 4) == 0) {
00273                             ret = 1;
00274                      } else {
00275                             ret = -1;
00276                      }
00277                      break;
00278               case 5:
00279                      if (strncasecmp(str, "false", 5) == 0) {
00280                             ret = 0;
00281                      } else {
00282                             ret = -1;
00283                      }
00284                      break;
00285               default:
00286                      ret = -1;
00287        }
00288 
00289        if (ret == -1) {     
00290               RETURN_VALIDATION_FAILED
00291        } else {
00292               zval_dtor(value);
00293               ZVAL_BOOL(value, ret);
00294        }
00295 }
00296 /* }}} */
00297 
00298 void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00299 {
00300        int len;
00301        char *str, *end;
00302        char *num, *p;
00303 
00304        zval **option_val;
00305        char *decimal;
00306        int decimal_set, decimal_len;
00307        char dec_sep = '.';
00308        char tsd_sep[3] = "',.";
00309 
00310        long lval;
00311        double dval;
00312 
00313        int first, n;
00314 
00315        len = Z_STRLEN_P(value);
00316        str = Z_STRVAL_P(value);
00317 
00318        PHP_FILTER_TRIM_DEFAULT(str, len);
00319        end = str + len;
00320 
00321        FETCH_STRING_OPTION(decimal, "decimal");
00322 
00323        if (decimal_set) {
00324               if (decimal_len != 1) {
00325                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "decimal separator must be one char");
00326                      RETURN_VALIDATION_FAILED
00327               } else {
00328                      dec_sep = *decimal;
00329               }
00330        }
00331 
00332        num = p = emalloc(len+1);
00333        if (str < end && (*str == '+' || *str == '-')) {
00334               *p++ = *str++;
00335        }
00336        first = 1;
00337        while (1) {
00338               n = 0;
00339               while (str < end && *str >= '0' && *str <= '9') {
00340                      ++n;
00341                      *p++ = *str++;
00342               }
00343               if (str == end || *str == dec_sep || *str == 'e' || *str == 'E') {
00344                      if (!first && n != 3) {
00345                             goto error;
00346                      }
00347                      if (*str == dec_sep) {
00348                             *p++ = '.';
00349                             str++;
00350                             while (str < end && *str >= '0' && *str <= '9') {
00351                                    *p++ = *str++;
00352                             }
00353                      }
00354                      if (*str == 'e' || *str == 'E') {
00355                             *p++ = *str++;
00356                             if (str < end && (*str == '+' || *str == '-')) {
00357                                    *p++ = *str++;
00358                             }
00359                             while (str < end && *str >= '0' && *str <= '9') {
00360                                    *p++ = *str++;
00361                             }
00362                      }
00363                      break;
00364               }
00365               if ((flags & FILTER_FLAG_ALLOW_THOUSAND) && (*str == tsd_sep[0] || *str == tsd_sep[1] || *str == tsd_sep[2])) {
00366                      if (first?(n < 1 || n > 3):(n != 3)) {
00367                             goto error;
00368                      }
00369                      first = 0;
00370                      str++;
00371               } else {
00372                      goto error;
00373               }
00374        }
00375        if (str != end) {
00376               goto error;
00377        }
00378        *p = 0;
00379 
00380        switch (is_numeric_string(num, p - num, &lval, &dval, 0)) {
00381               case IS_LONG:
00382                      zval_dtor(value);
00383                      Z_TYPE_P(value) = IS_DOUBLE;
00384                      Z_DVAL_P(value) = lval;
00385                      break;
00386               case IS_DOUBLE:
00387                      if ((!dval && p - num > 1 && strpbrk(num, "123456789")) || !zend_finite(dval)) {
00388                             goto error;
00389                      }
00390                      zval_dtor(value);
00391                      Z_TYPE_P(value) = IS_DOUBLE;
00392                      Z_DVAL_P(value) = dval;
00393                      break;
00394               default:
00395 error:
00396                      efree(num);
00397                      RETURN_VALIDATION_FAILED
00398        }
00399        efree(num);   
00400 }
00401 /* }}} */
00402 
00403 void php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00404 {
00405        zval **option_val;
00406        char  *regexp;
00407        int regexp_len;
00408        long   option_flags;
00409        int    regexp_set, option_flags_set;
00410 
00411        pcre       *re = NULL;
00412        pcre_extra *pcre_extra = NULL;
00413        int preg_options = 0;
00414 
00415        int         ovector[3];
00416        int         matches;
00417 
00418        /* Parse options */
00419        FETCH_STRING_OPTION(regexp, "regexp");
00420        FETCH_LONG_OPTION(option_flags, "flags");
00421 
00422        if (!regexp_set) {
00423               php_error_docref(NULL TSRMLS_CC, E_WARNING, "'regexp' option missing");
00424               RETURN_VALIDATION_FAILED
00425        }
00426 
00427        re = pcre_get_compiled_regex(regexp, &pcre_extra, &preg_options TSRMLS_CC);
00428        if (!re) {
00429               RETURN_VALIDATION_FAILED
00430        }
00431        matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
00432 
00433        /* 0 means that the vector is too small to hold all the captured substring offsets */
00434        if (matches < 0) {
00435               RETURN_VALIDATION_FAILED
00436        }
00437 }
00438 /* }}} */
00439 
00440 void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00441 {
00442        php_url *url;
00443        int old_len = Z_STRLEN_P(value);
00444        
00445        php_filter_url(value, flags, option_array, charset TSRMLS_CC);
00446 
00447        if (Z_TYPE_P(value) != IS_STRING || old_len != Z_STRLEN_P(value)) {
00448               RETURN_VALIDATION_FAILED
00449        }
00450 
00451        /* Use parse_url - if it returns false, we return NULL */
00452        url = php_url_parse_ex(Z_STRVAL_P(value), Z_STRLEN_P(value));
00453 
00454        if (url == NULL) {
00455               RETURN_VALIDATION_FAILED
00456        }
00457 
00458        if (url->scheme != NULL && (!strcasecmp(url->scheme, "http") || !strcasecmp(url->scheme, "https"))) {
00459               char *e, *s;
00460 
00461               if (url->host == NULL) {
00462                      goto bad_url;
00463               }
00464 
00465               e = url->host + strlen(url->host);
00466               s = url->host;
00467 
00468               /* First char of hostname must be alphanumeric */
00469               if(!isalnum((int)*(unsigned char *)s)) { 
00470                      goto bad_url;
00471               }
00472 
00473               while (s < e) {
00474                      if (!isalnum((int)*(unsigned char *)s) && *s != '-' && *s != '.') {
00475                             goto bad_url;
00476                      }
00477                      s++;
00478               }
00479 
00480               if (*(e - 1) == '.') {
00481                      goto bad_url;
00482               }
00483        }
00484 
00485        if (
00486               url->scheme == NULL || 
00487               /* some schemas allow the host to be empty */
00488               (url->host == NULL && (strcmp(url->scheme, "mailto") && strcmp(url->scheme, "news") && strcmp(url->scheme, "file"))) ||
00489               ((flags & FILTER_FLAG_PATH_REQUIRED) && url->path == NULL) || ((flags & FILTER_FLAG_QUERY_REQUIRED) && url->query == NULL)
00490        ) {
00491 bad_url:
00492               php_url_free(url);
00493               RETURN_VALIDATION_FAILED
00494        }
00495        php_url_free(url);
00496 }
00497 /* }}} */
00498 
00499 void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00500 {
00501        /*
00502         * The regex below is based on a regex by Michael Rushton.
00503         * However, it is not identical.  I changed it to only consider routeable
00504         * addresses as valid.  Michael's regex considers a@b a valid address
00505         * which conflicts with section 2.3.5 of RFC 5321 which states that:
00506         *
00507         *   Only resolvable, fully-qualified domain names (FQDNs) are permitted
00508         *   when domain names are used in SMTP.  In other words, names that can
00509         *   be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
00510         *   in Section 5) are permitted, as are CNAME RRs whose targets can be
00511         *   resolved, in turn, to MX or address RRs.  Local nicknames or
00512         *   unqualified names MUST NOT be used.
00513         *
00514         * This regex does not handle comments and folding whitespace.  While
00515         * this is technically valid in an email address, these parts aren't
00516         * actually part of the address itself.
00517         *
00518         * Michael's regex carries this copyright:
00519         *
00520         * Copyright © Michael Rushton 2009-10
00521         * http://squiloople.com/
00522         * Feel free to use and redistribute this code. But please keep this copyright notice.
00523         *
00524         */
00525        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";
00526 
00527        pcre       *re = NULL;
00528        pcre_extra *pcre_extra = NULL;
00529        int preg_options = 0;
00530        int         ovector[150]; /* Needs to be a multiple of 3 */
00531        int         matches;
00532 
00533 
00534        /* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
00535        if (Z_STRLEN_P(value) > 320) {
00536               RETURN_VALIDATION_FAILED
00537        }
00538 
00539        re = pcre_get_compiled_regex((char *)regexp, &pcre_extra, &preg_options TSRMLS_CC);
00540        if (!re) {
00541               RETURN_VALIDATION_FAILED
00542        }
00543        matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
00544 
00545        /* 0 means that the vector is too small to hold all the captured substring offsets */
00546        if (matches < 0) {
00547               RETURN_VALIDATION_FAILED
00548        }
00549 
00550 }
00551 /* }}} */
00552 
00553 static int _php_filter_validate_ipv4(char *str, int str_len, int *ip) /* {{{ */
00554 {
00555        const char *end = str + str_len;
00556        int num, m;
00557        int n = 0;
00558 
00559        while (str < end) {
00560               int leading_zero;
00561               if (*str < '0' || *str > '9') {
00562                      return 0;
00563               }
00564               leading_zero = (*str == '0');
00565               m = 1;
00566               num = ((*(str++)) - '0');
00567               while (str < end && (*str >= '0' && *str <= '9')) {
00568                      num = num * 10 + ((*(str++)) - '0');
00569                      if (num > 255 || ++m > 3) {
00570                             return 0;
00571                      }
00572               }
00573               /* don't allow a leading 0; that introduces octal numbers,
00574                * which we don't support */
00575               if (leading_zero && (num != 0 || m > 1))
00576                      return 0;
00577               ip[n++] = num;
00578               if (n == 4) {
00579                      return str == end;
00580               } else if (str >= end || *(str++) != '.') {
00581                      return 0;
00582               }
00583        }
00584        return 0;            
00585 }
00586 /* }}} */
00587 
00588 static int _php_filter_validate_ipv6(char *str, int str_len TSRMLS_DC) /* {{{ */
00589 {
00590        int compressed = 0;
00591        int blocks = 0;
00592        int n;
00593        char *ipv4;
00594        char *end;
00595        int ip4elm[4];
00596        char *s = str;
00597 
00598        if (!memchr(str, ':', str_len)) {
00599               return 0;
00600        }
00601 
00602        /* check for bundled IPv4 */
00603        ipv4 = memchr(str, '.', str_len);
00604        if (ipv4) {
00605               while (ipv4 > str && *(ipv4-1) != ':') {
00606                      ipv4--;
00607               }
00608 
00609               if (!_php_filter_validate_ipv4(ipv4, (str_len - (ipv4 - str)), ip4elm)) {
00610                      return 0;
00611               }
00612 
00613               str_len = ipv4 - str; /* length excluding ipv4 */
00614               if (str_len < 2) {
00615                      return 0;
00616               }
00617 
00618               if (ipv4[-2] != ':') {
00619                      /* don't include : before ipv4 unless it's a :: */
00620                      str_len--;
00621               }
00622 
00623               blocks = 2;
00624        }
00625 
00626        end = str + str_len;
00627 
00628        while (str < end) {
00629               if (*str == ':') {
00630                      if (++str >= end) {
00631                             /* cannot end in : without previous : */
00632                             return 0;
00633                      }
00634                      if (*str == ':') {
00635                             if (compressed) {
00636                                    return 0;
00637                             }
00638                             blocks++; /* :: means 1 or more 16-bit 0 blocks */
00639                             compressed = 1;
00640 
00641                             if (++str == end) {
00642                                    return (blocks <= 8);
00643                             }
00644                      } else if ((str - 1) == s) {
00645                             /* dont allow leading : without another : following */
00646                             return 0;
00647                      }                           
00648               }
00649               n = 0;
00650               while ((str < end) &&
00651                      ((*str >= '0' && *str <= '9') ||
00652                       (*str >= 'a' && *str <= 'f') ||
00653                       (*str >= 'A' && *str <= 'F'))) {
00654                      n++;
00655                      str++;
00656               }
00657               if (n < 1 || n > 4) {
00658                      return 0;
00659               }
00660               if (++blocks > 8)
00661                      return 0;
00662        }
00663        return ((compressed && blocks <= 8) || blocks == 8);
00664 }
00665 /* }}} */
00666 
00667 void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
00668 {
00669        /* validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a
00670         * flag to throw out reserved ranges; multicast ranges... etc. If both
00671         * allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or
00672         * colon determine the format */
00673 
00674        int            ip[4];
00675        int            mode;
00676 
00677        if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) {
00678               mode = FORMAT_IPV6;
00679        } else if (memchr(Z_STRVAL_P(value), '.', Z_STRLEN_P(value))) {
00680               mode = FORMAT_IPV4;
00681        } else {
00682               RETURN_VALIDATION_FAILED
00683        }
00684 
00685        if ((flags & FILTER_FLAG_IPV4) && (flags & FILTER_FLAG_IPV6)) {
00686               /* Both formats are cool */
00687        } else if ((flags & FILTER_FLAG_IPV4) && mode == FORMAT_IPV6) {
00688               RETURN_VALIDATION_FAILED
00689        } else if ((flags & FILTER_FLAG_IPV6) && mode == FORMAT_IPV4) {
00690               RETURN_VALIDATION_FAILED
00691        }
00692 
00693        switch (mode) {
00694               case FORMAT_IPV4:
00695                      if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) {
00696                             RETURN_VALIDATION_FAILED
00697                      }
00698 
00699                      /* Check flags */
00700                      if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
00701                             if (
00702                                    (ip[0] == 10) ||
00703                                    (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) ||
00704                                    (ip[0] == 192 && ip[1] == 168)
00705                             ) {
00706                                    RETURN_VALIDATION_FAILED
00707                             }
00708                      }
00709 
00710                      if (flags & FILTER_FLAG_NO_RES_RANGE) {
00711                             if (
00712                                    (ip[0] == 0) ||
00713                                    (ip[0] == 128 && ip[1] == 0) ||
00714                                    (ip[0] == 191 && ip[1] == 255) ||
00715                                    (ip[0] == 169 && ip[1] == 254) ||
00716                                    (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) ||
00717                                    (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1) ||
00718                                    (ip[0] >= 224 && ip[0] <= 255)
00719                             ) {
00720                                    RETURN_VALIDATION_FAILED
00721                             }
00722                      }
00723                      break;
00724 
00725               case FORMAT_IPV6:
00726                      {
00727                             int res = 0;
00728                             res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);
00729                             if (res < 1) {
00730                                    RETURN_VALIDATION_FAILED
00731                             }
00732                             /* Check flags */
00733                             if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
00734                                    if (Z_STRLEN_P(value) >=2 && (!strncasecmp("FC", Z_STRVAL_P(value), 2) || !strncasecmp("FD", Z_STRVAL_P(value), 2))) {
00735                                           RETURN_VALIDATION_FAILED
00736                                    }
00737                             }
00738                             if (flags & FILTER_FLAG_NO_RES_RANGE) {
00739                                    switch (Z_STRLEN_P(value)) {
00740                                           case 1: case 0:
00741                                                  break;
00742                                           case 2:
00743                                                  if (!strcmp("::", Z_STRVAL_P(value))) {
00744                                                         RETURN_VALIDATION_FAILED
00745                                                  }
00746                                                  break;
00747                                           case 3:
00748                                                  if (!strcmp("::1", Z_STRVAL_P(value)) || !strcmp("5f:", Z_STRVAL_P(value))) {
00749                                                         RETURN_VALIDATION_FAILED
00750                                                  }
00751                                                  break;
00752                                           default:
00753                                                  if (Z_STRLEN_P(value) >= 5) {
00754                                                         if (
00755                                                                !strncasecmp("fe8", Z_STRVAL_P(value), 3) ||
00756                                                                !strncasecmp("fe9", Z_STRVAL_P(value), 3) ||
00757                                                                !strncasecmp("fea", Z_STRVAL_P(value), 3) ||
00758                                                                !strncasecmp("feb", Z_STRVAL_P(value), 3)
00759                                                         ) {
00760                                                                RETURN_VALIDATION_FAILED
00761                                                         }
00762                                                  }
00763                                                  if (
00764                                                         (Z_STRLEN_P(value) >= 9 &&  !strncasecmp("2001:0db8", Z_STRVAL_P(value), 9)) ||
00765                                                         (Z_STRLEN_P(value) >= 2 &&  !strncasecmp("5f", Z_STRVAL_P(value), 2)) ||
00766                                                         (Z_STRLEN_P(value) >= 4 &&  !strncasecmp("3ff3", Z_STRVAL_P(value), 4)) ||
00767                                                         (Z_STRLEN_P(value) >= 8 &&  !strncasecmp("2001:001", Z_STRVAL_P(value), 8))
00768                                                  ) {
00769                                                         RETURN_VALIDATION_FAILED
00770                                                  }
00771                                    }
00772                             }
00773                      }
00774                      break;
00775        }
00776 }
00777 /* }}} */
00778 
00779 /*
00780  * Local variables:
00781  * tab-width: 4
00782  * c-basic-offset: 4
00783  * End:
00784  * vim600: noet sw=4 ts=4 fdm=marker
00785  * vim<600: noet sw=4 ts=4
00786  */