diff options
-rw-r--r-- | ext/filter/filter.c | 4 | ||||
-rw-r--r-- | ext/filter/filter_private.h | 5 | ||||
-rw-r--r-- | ext/filter/logical_filters.c | 87 | ||||
-rw-r--r-- | ext/filter/php_filter.h | 1 | ||||
-rw-r--r-- | ext/filter/tests/015.phpt | 30 | ||||
-rw-r--r-- | ext/filter/tests/056.phpt | 68 |
6 files changed, 183 insertions, 12 deletions
diff --git a/ext/filter/filter.c b/ext/filter/filter.c index 530dce6f53..66bcaa94d6 100644 --- a/ext/filter/filter.c +++ b/ext/filter/filter.c @@ -44,6 +44,7 @@ static const filter_list_entry filter_list[] = { { "float", FILTER_VALIDATE_FLOAT, php_filter_float }, { "validate_regexp", FILTER_VALIDATE_REGEXP, php_filter_validate_regexp }, + { "validate_domain", FILTER_VALIDATE_DOMAIN, php_filter_validate_domain }, { "validate_url", FILTER_VALIDATE_URL, php_filter_validate_url }, { "validate_email", FILTER_VALIDATE_EMAIL, php_filter_validate_email }, { "validate_ip", FILTER_VALIDATE_IP, php_filter_validate_ip }, @@ -231,6 +232,7 @@ PHP_MINIT_FUNCTION(filter) REGISTER_LONG_CONSTANT("FILTER_VALIDATE_FLOAT", FILTER_VALIDATE_FLOAT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_VALIDATE_REGEXP", FILTER_VALIDATE_REGEXP, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("FILTER_VALIDATE_DOMAIN", FILTER_VALIDATE_DOMAIN, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_VALIDATE_URL", FILTER_VALIDATE_URL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_VALIDATE_EMAIL", FILTER_VALIDATE_EMAIL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_VALIDATE_IP", FILTER_VALIDATE_IP, CONST_CS | CONST_PERSISTENT); @@ -278,6 +280,8 @@ PHP_MINIT_FUNCTION(filter) REGISTER_LONG_CONSTANT("FILTER_FLAG_NO_RES_RANGE", FILTER_FLAG_NO_RES_RANGE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_FLAG_NO_PRIV_RANGE", FILTER_FLAG_NO_PRIV_RANGE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("FILTER_FLAG_HOSTNAME", FILTER_FLAG_HOSTNAME, CONST_CS | CONST_PERSISTENT); + sapi_register_input_filter(php_sapi_filter, php_sapi_filter_init TSRMLS_CC); return SUCCESS; diff --git a/ext/filter/filter_private.h b/ext/filter/filter_private.h index b07b6ca534..8bfbb2df8b 100644 --- a/ext/filter/filter_private.h +++ b/ext/filter/filter_private.h @@ -55,6 +55,8 @@ #define FILTER_FLAG_NO_RES_RANGE 0x400000 #define FILTER_FLAG_NO_PRIV_RANGE 0x800000 +#define FILTER_FLAG_HOSTNAME 0x100000 + #define FILTER_VALIDATE_INT 0x0101 #define FILTER_VALIDATE_BOOLEAN 0x0102 #define FILTER_VALIDATE_FLOAT 0x0103 @@ -64,7 +66,8 @@ #define FILTER_VALIDATE_EMAIL 0x0112 #define FILTER_VALIDATE_IP 0x0113 #define FILTER_VALIDATE_MAC 0x0114 -#define FILTER_VALIDATE_LAST 0x0114 +#define FILTER_VALIDATE_DOMAIN 0x0115 +#define FILTER_VALIDATE_LAST 0x0115 #define FILTER_VALIDATE_ALL 0x0100 diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index be1c2f0d40..01497192f0 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -14,6 +14,7 @@ +----------------------------------------------------------------------+ | Authors: Derick Rethans <derick@php.net> | | Pierre-A. Joye <pierre@php.net> | + | Kévin Dunglas <dunglas@gmail.com> | +----------------------------------------------------------------------+ */ @@ -80,6 +81,8 @@ #define FORMAT_IPV4 4 #define FORMAT_IPV6 6 +static int _php_filter_validate_ipv6(char *str, size_t str_len TSRMLS_DC); + static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret TSRMLS_DC) { /* {{{ */ zend_long ctx_value; int sign = 0, digit = 0; @@ -452,6 +455,65 @@ void php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ RETURN_VALIDATION_FAILED } } + +static int _php_filter_validate_domain(char * domain, int len, zend_long flags) /* {{{ */ +{ + char *e, *s, *t; + size_t l; + int hostname = flags & FILTER_FLAG_HOSTNAME; + unsigned char i = 1; + + s = domain; + l = len; + e = domain + l; + t = e - 1; + + /* Ignore trailing dot */ + if (*t == '.') { + e = t; + l--; + } + + /* The total length cannot exceed 253 characters (final dot not included) */ + if (l > 253) { + return 0; + } + + /* First char must be alphanumeric */ + if(*s == '.' || (hostname && !isalnum((int)*(unsigned char *)s))) { + return 0; + } + + while (s < e) { + if (*s == '.') { + /* The first and the last character of a label must be alphanumeric */ + if (*(s + 1) == '.' || (hostname && (!isalnum((int)*(unsigned char *)(s - 1)) || !isalnum((int)*(unsigned char *)(s + 1))))) { + return 0; + } + + /* Reset label length counter */ + i = 1; + } else { + if (i > 63 || (hostname && *s != '-' && !isalnum((int)*(unsigned char *)s))) { + return 0; + } + + i++; + } + + s++; + } + + return 1; +} +/* }}} */ + +void php_filter_validate_domain(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ +{ + if (!_php_filter_validate_domain(Z_STRVAL_P(value), Z_STRLEN_P(value), flags)) { + RETURN_VALIDATION_FAILED + } +} /* }}} */ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ @@ -473,25 +535,28 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ } if (url->scheme != NULL && (!strcasecmp(url->scheme, "http") || !strcasecmp(url->scheme, "https"))) { - char *e, *s; + char *e, *s, *t; + size_t l; if (url->host == NULL) { goto bad_url; } - e = url->host + strlen(url->host); s = url->host; - - /* First char of hostname must be alphanumeric */ - if(!isalnum((int)*(unsigned char *)s)) { - goto bad_url; + l = strlen(s); + e = url->host + l; + t = e - 1; + + /* An IPv6 enclosed by square brackets is a valid hostname */ + if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2 TSRMLS_CC)) { + php_url_free(url); + return; } - while (s < e) { - if (!isalnum((int)*(unsigned char *)s) && *s != '-' && *s != '.') { - goto bad_url; - } - s++; + // Validate domain + if (!_php_filter_validate_domain(url->host, l, FILTER_FLAG_HOSTNAME)) { + php_url_free(url); + RETURN_VALIDATION_FAILED } } diff --git a/ext/filter/php_filter.h b/ext/filter/php_filter.h index 126a0c6c8b..a82b2e6159 100644 --- a/ext/filter/php_filter.h +++ b/ext/filter/php_filter.h @@ -75,6 +75,7 @@ void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL); +void php_filter_validate_domain(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL); diff --git a/ext/filter/tests/015.phpt b/ext/filter/tests/015.phpt index 476615ae37..a5d764934a 100644 --- a/ext/filter/tests/015.phpt +++ b/ext/filter/tests/015.phpt @@ -11,6 +11,21 @@ $values = Array( 'http://www.example/img/test.png', 'http://www.example/img/dir/', 'http://www.example/img/dir', +'http://www.thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com/', +'http://toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong.com', +'http://eauBcFReEmjLcoZwI0RuONNnwU4H9r151juCaqTI5VeIP5jcYIqhx1lh5vV00l2rTs6y7hOp7rYw42QZiq6VIzjcYrRm8gFRMk9U9Wi1grL8Mr5kLVloYLthHgyA94QK3SaXCATklxgo6XvcbXIqAGG7U0KxTr8hJJU1p2ZQ2mXHmp4DhYP8N9SRuEKzaCPcSIcW7uj21jZqBigsLsNAXEzU8SPXZjmVQVtwQATPWeWyGW4GuJhjP4Q8o0.com', +'http://kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.CQ1oT5Uq3jJt6Uhy3VH9u3Gi5YhfZCvZVKgLlaXNFhVKB1zJxvunR7SJa.com.', +'http://[2001:0db8:0000:85a3:0000:0000:ac1f:8001]', +'http://[2001:db8:0:85a3:0:0:ac1f:8001]:123/me.html', +'http://[2001:db8:0:85a3::ac1f:8001]/', +'http://[::1]', +'http://cont-ains.h-yph-en-s.com', +'http://..com', +'http://a.-bc.com', +'http://ab.cd-.com', +'http://-.abc.com', +'http://abc.-.abc.com', +'http://underscore_.example.com', 'http//www.example/wrong/url/', 'http:/www.example', 'file:///tmp/test.c', @@ -56,6 +71,21 @@ string(32) "http://www.example.com/index.php" string(31) "http://www.example/img/test.png" string(27) "http://www.example/img/dir/" string(26) "http://www.example/img/dir" +string(79) "http://www.thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com/" +bool(false) +bool(false) +string(261) "http://kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.CQ1oT5Uq3jJt6Uhy3VH9u3Gi5YhfZCvZVKgLlaXNFhVKB1zJxvunR7SJa.com." +string(48) "http://[2001:0db8:0000:85a3:0000:0000:ac1f:8001]" +string(50) "http://[2001:db8:0:85a3:0:0:ac1f:8001]:123/me.html" +string(36) "http://[2001:db8:0:85a3::ac1f:8001]/" +string(12) "http://[::1]" +string(31) "http://cont-ains.h-yph-en-s.com" +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) bool(false) bool(false) string(18) "file:///tmp/test.c" diff --git a/ext/filter/tests/056.phpt b/ext/filter/tests/056.phpt new file mode 100644 index 0000000000..4a27a9fa10 --- /dev/null +++ b/ext/filter/tests/056.phpt @@ -0,0 +1,68 @@ +--TEST-- +filter_var() and FILTER_VALIDATE_DOMAIN +--SKIPIF-- +<?php if (!extension_loaded("filter")) die("skip"); ?> +--FILE-- +<?php + +$values = Array( +'example.com', +'www.thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com', +'toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong.com', +'eauBcFReEmjLcoZwI0RuONNnwU4H9r151juCaqTI5VeIP5jcYIqhx1lh5vV00l2rTs6y7hOp7rYw42QZiq6VIzjcYrRm8gFRMk9U9Wi1grL8Mr5kLVloYLthHgyA94QK3SaXCATklxgo6XvcbXIqAGG7U0KxTr8hJJU1p2ZQ2mXHmp4DhYP8N9SRuEKzaCPcSIcW7uj21jZqBigsLsNAXEzU8SPXZjmVQVtwQATPWeWyGW4GuJhjP4Q8o0.com', +'kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.CQ1oT5Uq3jJt6Uhy3VH9u3Gi5YhfZCvZVKgLlaXNFhVKB1zJxvunR7SJa.com.', +'cont-ains.h-yph-en-s.com', +'..com', +'ab..cc.dd', +'a.-bc.com', +'ab.cd-.com', +'-.abc.com', +'abc.-.abc.com', +'underscore_.example.com', +'', +-1, +array(), +'\r\n', +); +foreach ($values as $value) { + var_dump(filter_var($value, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)); +} + +var_dump(filter_var('_example.com', FILTER_VALIDATE_DOMAIN)); +var_dump(filter_var('_example.com', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)); +var_dump(filter_var('test_.example.com', FILTER_VALIDATE_DOMAIN)); +var_dump(filter_var('test_.example.com', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)); +var_dump(filter_var('te_st.example.com', FILTER_VALIDATE_DOMAIN)); +var_dump(filter_var('te_st.example.com', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)); +var_dump(filter_var('test._example.com', FILTER_VALIDATE_DOMAIN)); +var_dump(filter_var('test._example.com', FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)); + +echo "Done\n"; +?> +--EXPECT-- +string(11) "example.com" +string(71) "www.thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com" +bool(false) +bool(false) +string(254) "kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.kDTvHt1PPDgX5EiP2MwiXjcoWNOhhTuOVAUWJ3TmpBYCC9QoJV114LMYrV3Zl58.CQ1oT5Uq3jJt6Uhy3VH9u3Gi5YhfZCvZVKgLlaXNFhVKB1zJxvunR7SJa.com." +string(24) "cont-ains.h-yph-en-s.com" +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +string(12) "_example.com" +bool(false) +string(17) "test_.example.com" +bool(false) +string(17) "te_st.example.com" +bool(false) +string(17) "test._example.com" +bool(false) +Done |