File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / filter / logical_filters.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:48 2014 UTC (10 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>