]> granicus.if.org Git - php/commitdiff
FILTER_VALIDATE_DOMAIN and RFC conformance for FILTER_VALIDATE_URL
authorKévin Dunglas <dunglas@gmail.com>
Tue, 16 Sep 2014 21:40:27 +0000 (23:40 +0200)
committerKévin Dunglas <dunglas@gmail.com>
Fri, 14 Nov 2014 09:01:53 +0000 (10:01 +0100)
ext/filter/filter.c
ext/filter/filter_private.h
ext/filter/logical_filters.c
ext/filter/php_filter.h
ext/filter/tests/015.phpt
ext/filter/tests/056.phpt [new file with mode: 0644]

index 530dce6f53302e8041f9c7cf79634f61ee747830..66bcaa94d675915ed225147aa82a67484bceb43f 100644 (file)
@@ -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;
index b07b6ca534ea9fdfd8b5f8e4c0fce0f6e7e60e43..8bfbb2df8b3d458daa300a2affb7c7527987e581 100644 (file)
@@ -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
 
index be1c2f0d40da5e9c27ea3f58f48d3602d95dfa84..01497192f04f3a82aec4547832be6f12a52802c0 100644 (file)
@@ -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
                }
        }
 
index 126a0c6c8b2d4c78deff141a98235ef8b9f13e18..a82b2e61596f1c2780178b37b5f90930dc28beba 100644 (file)
@@ -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);
index 476615ae377e4d5920404275ecf9d6577c09315c..a5d764934a3c7711324d47e902d1b5b8585ef6cf 100644 (file)
@@ -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 (file)
index 0000000..4a27a9f
--- /dev/null
@@ -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