From 878fd02139394d17f3737429d5ff384f8869b4a7 Mon Sep 17 00:00:00 2001 From: Martin Jansen Date: Mon, 24 Dec 2012 14:14:24 +0100 Subject: [PATCH 1/3] ext/filter support for validating MAC addresses. --- ext/filter/filter.c | 2 ++ ext/filter/filter_private.h | 3 ++- ext/filter/logical_filters.c | 48 ++++++++++++++++++++++++++++++++++++ ext/filter/php_filter.h | 1 + ext/filter/tests/008.phpt | 26 ++++++++++--------- ext/filter/tests/033.phpt | 39 +++++++++++++++-------------- ext/filter/tests/033_run.inc | 6 +++-- ext/filter/tests/055.phpt | 34 +++++++++++++++++++++++++ 8 files changed, 125 insertions(+), 34 deletions(-) create mode 100644 ext/filter/tests/055.phpt diff --git a/ext/filter/filter.c b/ext/filter/filter.c index e523cdc88298..83098221cb35 100644 --- a/ext/filter/filter.c +++ b/ext/filter/filter.c @@ -47,6 +47,7 @@ static const filter_list_entry filter_list[] = { { "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 }, + { "validate_mac", FILTER_VALIDATE_MAC, php_filter_validate_mac }, { "string", FILTER_SANITIZE_STRING, php_filter_string }, { "stripped", FILTER_SANITIZE_STRING, php_filter_string }, @@ -233,6 +234,7 @@ PHP_MINIT_FUNCTION(filter) 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); + REGISTER_LONG_CONSTANT("FILTER_VALIDATE_MAC", FILTER_VALIDATE_MAC, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_DEFAULT", FILTER_DEFAULT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("FILTER_UNSAFE_RAW", FILTER_UNSAFE_RAW, CONST_CS | CONST_PERSISTENT); diff --git a/ext/filter/filter_private.h b/ext/filter/filter_private.h index 0f76d7a032a7..dbd20dbef44b 100644 --- a/ext/filter/filter_private.h +++ b/ext/filter/filter_private.h @@ -63,7 +63,8 @@ #define FILTER_VALIDATE_URL 0x0111 #define FILTER_VALIDATE_EMAIL 0x0112 #define FILTER_VALIDATE_IP 0x0113 -#define FILTER_VALIDATE_LAST 0x0113 +#define FILTER_VALIDATE_MAC 0x0114 +#define FILTER_VALIDATE_LAST 0x0114 #define FILTER_VALIDATE_ALL 0x0100 diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 4de6b83e00f4..46302fe0057b 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -779,6 +779,54 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ } /* }}} */ +void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ +{ + char *input = Z_STRVAL_P(value); + int input_len = Z_STRLEN_P(value); + int tokens, length, i, offset; + char separator; + long ret = 0; + + if (14 == input_len) { + /* EUI-64 format: Four hexadecimal digits separated by dots. Less + * commonly used but valid nonetheless. + */ + tokens = 3; + length = 4; + separator = '.'; + } else if (17 == input_len && memchr(input + 2, '-', 1)) { + /* IEEE 802 format: Six hexadecimal digits separated by hyphens. */ + tokens = 6; + length = 2; + separator = '-'; + } else if (17 == input_len && memchr(input + 2, ':', 1)) { + /* IEEE 802 format: Six hexadecimal digits separated by colons. */ + tokens = 6; + length = 2; + separator = ':'; + } else { + RETURN_VALIDATION_FAILED; + } + + /* Essentially what we now have is a set of tokens each consisting of + * a hexadecimal number followed by a separator character. (With the + * exception of the last token which does not have the separator.) + */ + for (i = 0; i < tokens; i++) { + offset = i * (length + 1); + + if (i < tokens - 1 && !memchr(input + offset + length, separator, 1)) { + /* The current token did not end with e.g. a "." */ + RETURN_VALIDATION_FAILED + } + if (php_filter_parse_hex(input + offset, length, &ret) < 0) { + /* The current token is no valid hexadecimal digit */ + RETURN_VALIDATION_FAILED + } + } +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/ext/filter/php_filter.h b/ext/filter/php_filter.h index 4c83969cf192..0496a648e7e3 100644 --- a/ext/filter/php_filter.h +++ b/ext/filter/php_filter.h @@ -78,6 +78,7 @@ void php_filter_validate_regexp(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); +void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_string(PHP_INPUT_FILTER_PARAM_DECL); void php_filter_encoded(PHP_INPUT_FILTER_PARAM_DECL); diff --git a/ext/filter/tests/008.phpt b/ext/filter/tests/008.phpt index 8a4340542f2d..a499219ee76e 100644 --- a/ext/filter/tests/008.phpt +++ b/ext/filter/tests/008.phpt @@ -11,7 +11,7 @@ var_dump(filter_list(array())); echo "Done\n"; ?> --EXPECTF-- -array(19) { +array(20) { [0]=> string(3) "int" [1]=> @@ -27,28 +27,30 @@ array(19) { [6]=> string(11) "validate_ip" [7]=> - string(6) "string" + string(12) "validate_mac" [8]=> - string(8) "stripped" + string(6) "string" [9]=> - string(7) "encoded" + string(8) "stripped" [10]=> - string(13) "special_chars" + string(7) "encoded" [11]=> - string(18) "full_special_chars" + string(13) "special_chars" [12]=> - string(10) "unsafe_raw" + string(18) "full_special_chars" [13]=> - string(5) "email" + string(10) "unsafe_raw" [14]=> - string(3) "url" + string(5) "email" [15]=> - string(10) "number_int" + string(3) "url" [16]=> - string(12) "number_float" + string(10) "number_int" [17]=> - string(12) "magic_quotes" + string(12) "number_float" [18]=> + string(12) "magic_quotes" + [19]=> string(8) "callback" } diff --git a/ext/filter/tests/033.phpt b/ext/filter/tests/033.phpt index 04daa6133397..d76f9ab3b8a5 100644 --- a/ext/filter/tests/033.phpt +++ b/ext/filter/tests/033.phpt @@ -10,22 +10,23 @@ default_charset=UTF-8 include dirname(__FILE__) . '/033_run.inc'; ?> --EXPECT-- -int 1 123 -boolean 1 -float 1 123 -validate_regexp O'Henry -validate_url http://a.b.c -validate_email foo@bar.com -validate_ip 1.2.3.4 -string PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc() O'Henry 하퍼 -stripped PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc() O'Henry 하퍼 -encoded PHP 1 foo%40bar.com http%3A%2F%2Fa.b.c 1.2.3.4 123 123abc%3C%3E%28%29 O%27Henry %ED%95%98%ED%8D%BC -special_chars PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry 하퍼 -full_special_chars PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry 하퍼 -unsafe_raw PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry 하퍼 -email PHP 1 foo@bar.com httpa.b.c 1.2.3.4 123 123abc O'Henry -url PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry -number_int 1 1234 123 123 -number_float 1 1234 123 123 -magic_quotes PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O\'Henry 하퍼 -callback PHP 1 FOO@BAR.COM HTTP://A.B.C 1.2.3.4 123 123ABC<>() O'HENRY 하퍼 +int 1 123 +boolean 1 +float 1 123 +validate_regexp O'Henry +validate_url http://a.b.c +validate_email foo@bar.com +validate_ip 1.2.3.4 +validate_mac aa:bb:cc:dd:ee:ff +string PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc() O'Henry 하퍼 aa:bb:cc:dd:ee:ff +stripped PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc() O'Henry 하퍼 aa:bb:cc:dd:ee:ff +encoded PHP 1 foo%40bar.com http%3A%2F%2Fa.b.c 1.2.3.4 123 123abc%3C%3E%28%29 O%27Henry %ED%95%98%ED%8D%BCaa%3Abb%3Acc%3Add%3Aee%3Aff +special_chars PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry 하퍼 aa:bb:cc:dd:ee:ff +full_special_chars PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry 하퍼 aa:bb:cc:dd:ee:ff +unsafe_raw PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry 하퍼 aa:bb:cc:dd:ee:ff +email PHP 1 foo@bar.com httpa.b.c 1.2.3.4 123 123abc O'Henry aabbccddeeff +url PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O'Henry aa:bb:cc:dd:ee:ff +number_int 1 1234 123 123 +number_float 1 1234 123 123 +magic_quotes PHP 1 foo@bar.com http://a.b.c 1.2.3.4 123 123abc<>() O\'Henry 하퍼 aa:bb:cc:dd:ee:ff +callback PHP 1 FOO@BAR.COM HTTP://A.B.C 1.2.3.4 123 123ABC<>() O'HENRY 하퍼 AA:BB:CC:DD:EE:FF \ No newline at end of file diff --git a/ext/filter/tests/033_run.inc b/ext/filter/tests/033_run.inc index e3b67387ca2a..ecb2cf7be160 100644 --- a/ext/filter/tests/033_run.inc +++ b/ext/filter/tests/033_run.inc @@ -16,7 +16,8 @@ $data = array( "123", "123abc<>()", "O'Henry", - "하퍼" + "하퍼", + "aa:bb:cc:dd:ee:ff", ); @@ -35,6 +36,7 @@ foreach(filter_list() as $filter) { printf("%-5s",$result[5]); printf("%-20s",$result[6]); printf("%-15s",$result[7]); - printf("%-10s\n",$result[8]); + printf("%-10s",$result[8]); + printf("%-10s\n",$result[9]); } ?> diff --git a/ext/filter/tests/055.phpt b/ext/filter/tests/055.phpt new file mode 100644 index 000000000000..bf94f3515f17 --- /dev/null +++ b/ext/filter/tests/055.phpt @@ -0,0 +1,34 @@ +--TEST-- +filter_var() and FILTER_VALIDATE_MAC +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(17) "01-23-45-67-89-ab" +string(17) "01-23-45-67-89-AB" +string(17) "01-23-45-67-89-aB" +string(17) "01:23:45:67:89:ab" +string(17) "01:23:45:67:89:AB" +string(17) "01:23:45:67:89:aB" +bool(false) +bool(false) +string(14) "0123.4567.89ab" +Done From af032ab5245f8530b763c6b6f67feb17fb65a47d Mon Sep 17 00:00:00 2001 From: Martin Jansen Date: Fri, 28 Dec 2012 15:20:19 +0100 Subject: [PATCH 2/3] There is no need to use memchr() for comparisons in these places. --- ext/filter/logical_filters.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 46302fe0057b..57a858ab1017 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -794,12 +794,12 @@ void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ tokens = 3; length = 4; separator = '.'; - } else if (17 == input_len && memchr(input + 2, '-', 1)) { + } else if (17 == input_len && input[2] == '-') { /* IEEE 802 format: Six hexadecimal digits separated by hyphens. */ tokens = 6; length = 2; separator = '-'; - } else if (17 == input_len && memchr(input + 2, ':', 1)) { + } else if (17 == input_len && input[2] == ':') { /* IEEE 802 format: Six hexadecimal digits separated by colons. */ tokens = 6; length = 2; @@ -815,7 +815,7 @@ void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ for (i = 0; i < tokens; i++) { offset = i * (length + 1); - if (i < tokens - 1 && !memchr(input + offset + length, separator, 1)) { + if (i < tokens - 1 && input[offset + length] != separator) { /* The current token did not end with e.g. a "." */ RETURN_VALIDATION_FAILED } From 6bbb9124a2552cefdccfbe174d2aa354198777c4 Mon Sep 17 00:00:00 2001 From: Martin Jansen Date: Sat, 5 Jan 2013 22:46:14 +0100 Subject: [PATCH 3/3] Add option to specific the expected separator character. --- ext/filter/logical_filters.c | 15 ++++++++++++++- ext/filter/tests/055.phpt | 36 +++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 57a858ab1017..0a2a64f835df 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -783,9 +783,18 @@ void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ { char *input = Z_STRVAL_P(value); int input_len = Z_STRLEN_P(value); - int tokens, length, i, offset; + int tokens, length, i, offset, exp_separator_set, exp_separator_len; char separator; + char *exp_separator; long ret = 0; + zval **option_val; + + FETCH_STRING_OPTION(exp_separator, "separator"); + + if (exp_separator_set && exp_separator_len != 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Separator must be exactly one character long"); + RETURN_VALIDATION_FAILED; + } if (14 == input_len) { /* EUI-64 format: Four hexadecimal digits separated by dots. Less @@ -808,6 +817,10 @@ void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ RETURN_VALIDATION_FAILED; } + if (exp_separator_set && separator != exp_separator[0]) { + RETURN_VALIDATION_FAILED; + } + /* Essentially what we now have is a set of tokens each consisting of * a hexadecimal number followed by a separator character. (With the * exception of the last token which does not have the separator.) diff --git a/ext/filter/tests/055.phpt b/ext/filter/tests/055.phpt index bf94f3515f17..688dbb2b5403 100644 --- a/ext/filter/tests/055.phpt +++ b/ext/filter/tests/055.phpt @@ -5,24 +5,32 @@ filter_var() and FILTER_VALIDATE_MAC --FILE-- array("separator" => "-"))), + array("01-23-45-67-89-ab", array("options" => array("separator" => "."))), + array("01-23-45-67-89-ab", array("options" => array("separator" => ":"))), + array("01-23-45-67-89-AB", null), + array("01-23-45-67-89-aB", null), + array("01:23:45:67:89:ab", null), + array("01:23:45:67:89:AB", null), + array("01:23:45:67:89:aB", null), + array("01:23:45-67:89:aB", null), + array("xx:23:45:67:89:aB", null), + array("0123.4567.89ab", null), + array("01-23-45-67-89-ab", array("options" => array("separator" => "--"))), + array("01-23-45-67-89-ab", array("options" => array("separator" => ""))), ); foreach ($values as $value) { - var_dump(filter_var($value, FILTER_VALIDATE_MAC)); + var_dump(filter_var($value[0], FILTER_VALIDATE_MAC, $value[1])); } echo "Done\n"; ?> ---EXPECT-- +--EXPECTF-- string(17) "01-23-45-67-89-ab" +string(17) "01-23-45-67-89-ab" +bool(false) +bool(false) string(17) "01-23-45-67-89-AB" string(17) "01-23-45-67-89-aB" string(17) "01:23:45:67:89:ab" @@ -31,4 +39,10 @@ string(17) "01:23:45:67:89:aB" bool(false) bool(false) string(14) "0123.4567.89ab" + +Warning: filter_var(): Separator must be exactly one character long in %s055.php on line %d +bool(false) + +Warning: filter_var(): Separator must be exactly one character long in %s055.php on line %d +bool(false) Done