diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 1ffc467b43c16..fa6ae65ac5868 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -91,7 +91,7 @@ #define FORMAT_IPV4 4 #define FORMAT_IPV6 6 -static int _php_filter_validate_ipv6(char *str, size_t str_len); +static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]); static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */ zend_long ctx_value; @@ -609,7 +609,7 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ t = e - 1; /* An IPv6 enclosed by square brackets is a valid hostname */ - if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2)) { + if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2, NULL)) { php_url_free(url); return; } @@ -749,11 +749,11 @@ static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{ } /* }}} */ -static int _php_filter_validate_ipv6(char *str, size_t str_len) /* {{{ */ +static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]) /* {{{ */ { - int compressed = 0; + int compressed_pos = -1; int blocks = 0; - int n; + int num, n, i; char *ipv4; char *end; int ip4elm[4]; @@ -796,35 +796,67 @@ static int _php_filter_validate_ipv6(char *str, size_t str_len) /* {{{ */ return 0; } if (*str == ':') { - if (compressed) { + if (compressed_pos >= 0) { return 0; } - blocks++; /* :: means 1 or more 16-bit 0 blocks */ - compressed = 1; - + if (ip && blocks < 8) { + ip[blocks] = -1; + } + compressed_pos = blocks++; /* :: means 1 or more 16-bit 0 blocks */ if (++str == end) { - return (blocks <= 8); + if (blocks > 8) { + return 0; + } + goto fixup_ip; } } else if ((str - 1) == s) { /* don't allow leading : without another : following */ return 0; } } - n = 0; - while ((str < end) && - ((*str >= '0' && *str <= '9') || - (*str >= 'a' && *str <= 'f') || - (*str >= 'A' && *str <= 'F'))) { + num = n = 0; + while (str < end) { + if (*str >= '0' && *str <= '9') { + num = 16 * num + (*str - '0'); + } else if (*str >= 'a' && *str <= 'f') { + num = 16 * num + (*str - 'a') + 10; + } else if (*str >= 'A' && *str <= 'F') { + num = 16 * num + (*str - 'A') + 10; + } else { + break; + } n++; str++; } + if (ip && blocks < 8) { + ip[blocks] = num; + } if (n < 1 || n > 4) { return 0; } if (++blocks > 8) return 0; } - return ((compressed && blocks <= 8) || blocks == 8); + +fixup_ip: + if (ip && ipv4) { + for (i = 0; i < 5; i++) { + ip[i] = 0; + } + ip[i++] = 0xffff; + ip[i++] = 256 * ip4elm[0] + ip4elm[1]; + ip[i++] = 256 * ip4elm[2] + ip4elm[3]; + } else if (ip && compressed_pos >= 0 && blocks <= 8) { + int offset = 8 - blocks; + for (i = 7; i > compressed_pos + offset; i--) { + ip[i] = ip[i - offset]; + } + for (i = compressed_pos + offset; i >= compressed_pos; i--) { + ip[i] = 0; + } + } + + return (compressed_pos >= 0 && blocks <= 8) || blocks == 8; } /* }}} */ @@ -835,7 +867,7 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ * allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or * colon determine the format */ - int ip[4]; + int ip[8]; int mode; if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) { @@ -886,49 +918,25 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ case FORMAT_IPV6: { int res = 0; - res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value)); + res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip); if (res < 1) { RETURN_VALIDATION_FAILED } /* Check flags */ if (flags & FILTER_FLAG_NO_PRIV_RANGE) { - if (Z_STRLEN_P(value) >=2 && (!strncasecmp("FC", Z_STRVAL_P(value), 2) || !strncasecmp("FD", Z_STRVAL_P(value), 2))) { + if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) { RETURN_VALIDATION_FAILED } } if (flags & FILTER_FLAG_NO_RES_RANGE) { - switch (Z_STRLEN_P(value)) { - case 1: case 0: - break; - case 2: - if (!strcmp("::", Z_STRVAL_P(value))) { - RETURN_VALIDATION_FAILED - } - break; - case 3: - if (!strcmp("::1", Z_STRVAL_P(value)) || !strcmp("5f:", Z_STRVAL_P(value))) { - RETURN_VALIDATION_FAILED - } - break; - default: - if (Z_STRLEN_P(value) >= 5) { - if ( - !strncasecmp("fe8", Z_STRVAL_P(value), 3) || - !strncasecmp("fe9", Z_STRVAL_P(value), 3) || - !strncasecmp("fea", Z_STRVAL_P(value), 3) || - !strncasecmp("feb", Z_STRVAL_P(value), 3) - ) { - RETURN_VALIDATION_FAILED - } - } - if ( - (Z_STRLEN_P(value) >= 9 && !strncasecmp("2001:0db8", Z_STRVAL_P(value), 9)) || - (Z_STRLEN_P(value) >= 2 && !strncasecmp("5f", Z_STRVAL_P(value), 2)) || - (Z_STRLEN_P(value) >= 4 && !strncasecmp("3ff3", Z_STRVAL_P(value), 4)) || - (Z_STRLEN_P(value) >= 8 && !strncasecmp("2001:001", Z_STRVAL_P(value), 8)) - ) { - RETURN_VALIDATION_FAILED - } + if ((ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 + && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) + || (ip[0] == 0x5f) + || (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) + || ((ip[0] == 0x2001 && ip[1] == 0x0db8) || (ip[1] >= 0x0010 && ip[1] <= 0x001f)) + || (ip[0] == 0x3ff3) + ) { + RETURN_VALIDATION_FAILED } } } diff --git a/ext/filter/tests/bug47435.phpt b/ext/filter/tests/bug47435.phpt index f192c76a0bf24..e17142aad963d 100644 --- a/ext/filter/tests/bug47435.phpt +++ b/ext/filter/tests/bug47435.phpt @@ -10,8 +10,8 @@ var_dump(filter_var("::", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("::", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); -var_dump(filter_var("fe8:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); -var_dump(filter_var("fe8:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); +var_dump(filter_var("fe80:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); +var_dump(filter_var("fe80:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("2001:0db8::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("2001:0db8::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); @@ -26,7 +26,7 @@ string(2) "::" bool(false) string(3) "::1" bool(false) -string(10) "fe8:5:6::1" +string(11) "fe80:5:6::1" bool(false) string(12) "2001:0db8::1" bool(false) diff --git a/ext/filter/tests/bug61700.phpt b/ext/filter/tests/bug61700.phpt new file mode 100644 index 0000000000000..8174514f26c0e --- /dev/null +++ b/ext/filter/tests/bug61700.phpt @@ -0,0 +1,16 @@ +--TEST-- +Bug #61700 (FILTER_FLAG_IPV6/FILTER_FLAG_NO_PRIV|RES_RANGE failing) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(false) +string(18) "::ffff:192.168.1.1" +bool(false)