php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
logical_filters.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Derick Rethans <derick@php.net> |
14 | Pierre-A. Joye <pierre@php.net> |
15 | Kévin Dunglas <dunglas@gmail.com> |
16 +----------------------------------------------------------------------+
17*/
18
19#include "php_filter.h"
20#include "filter_private.h"
21#include "ext/standard/url.h"
22#include "ext/pcre/php_pcre.h"
23
24#include "zend_multiply.h"
25
26#ifdef HAVE_ARPA_INET_H
27# include <arpa/inet.h>
28#endif
29
30#ifndef INADDR_NONE
31# define INADDR_NONE ((unsigned long int) -1)
32#endif
33
34
35/* {{{ FETCH_DOUBLE_OPTION(var_name, option_name) */
36#define FETCH_DOUBLE_OPTION(var_name, option_name) \
37 var_name = 0; \
38 var_name##_set = 0; \
39 if (option_array) { \
40 if ((option_val = zend_hash_str_find(Z_ARRVAL_P(option_array), option_name, sizeof(option_name) - 1)) != NULL) { \
41 var_name = zval_get_double(option_val); \
42 var_name##_set = 1; \
43 } \
44 }
45/* }}} */
46
47/* {{{ FETCH_LONG_OPTION(var_name, option_name) */
48#define FETCH_LONG_OPTION(var_name, option_name) \
49 var_name = 0; \
50 var_name##_set = 0; \
51 if (option_array) { \
52 if ((option_val = zend_hash_str_find(Z_ARRVAL_P(option_array), option_name, sizeof(option_name) - 1)) != NULL) { \
53 var_name = zval_get_long(option_val); \
54 var_name##_set = 1; \
55 } \
56 }
57/* }}} */
58
59/* {{{ FETCH_STRING_OPTION(var_name, option_name) */
60#define FETCH_STRING_OPTION(var_name, option_name) \
61 var_name = NULL; \
62 var_name##_set = 0; \
63 var_name##_len = 0; \
64 if (option_array) { \
65 if ((option_val = zend_hash_str_find_deref(Z_ARRVAL_P(option_array), option_name, sizeof(option_name) - 1)) != NULL) { \
66 if (Z_TYPE_P(option_val) == IS_STRING) { \
67 var_name = Z_STRVAL_P(option_val); \
68 var_name##_len = Z_STRLEN_P(option_val); \
69 var_name##_set = 1; \
70 } \
71 } \
72 }
73/* }}} */
74
75/* {{{ FETCH_STR_OPTION(var_name, option_name) */
76#define FETCH_STR_OPTION(var_name, option_name) \
77 var_name = NULL; \
78 var_name##_set = 0; \
79 if (option_array) { \
80 if ((option_val = zend_hash_str_find_deref(Z_ARRVAL_P(option_array), option_name, sizeof(option_name) - 1)) != NULL) { \
81 if (Z_TYPE_P(option_val) == IS_STRING) { \
82 var_name = Z_STR_P(option_val); \
83 var_name##_set = 1; \
84 } \
85 } \
86 }
87/* }}} */
88
89#define FORMAT_IPV4 4
90#define FORMAT_IPV6 6
91
92static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]);
93
94static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */
95 zend_long ctx_value;
96 int sign = 0, digit = 0;
97 const char *end = str + str_len;
98
99 switch (*str) {
100 case '-':
101 sign = 1;
103 case '+':
104 str++;
105 default:
106 break;
107 }
108
109 if (*str == '0' && str + 1 == end) {
110 /* Special cases: +0 and -0 */
111 return 1;
112 }
113
114 /* must start with 1..9*/
115 if (str < end && *str >= '1' && *str <= '9') {
116 ctx_value = ((sign)?-1:1) * ((*(str++)) - '0');
117 } else {
118 return -1;
119 }
120
121 if ((end - str > MAX_LENGTH_OF_LONG - 1) /* number too long */
122 || (SIZEOF_LONG == 4 && (end - str == MAX_LENGTH_OF_LONG - 1) && *str > '2')) {
123 /* overflow */
124 return -1;
125 }
126
127 while (str < end) {
128 if (*str >= '0' && *str <= '9') {
129 digit = (*(str++) - '0');
130 if ( (!sign) && ctx_value <= (ZEND_LONG_MAX-digit)/10 ) {
131 ctx_value = (ctx_value * 10) + digit;
132 } else if ( sign && ctx_value >= (ZEND_LONG_MIN+digit)/10) {
133 ctx_value = (ctx_value * 10) - digit;
134 } else {
135 return -1;
136 }
137 } else {
138 return -1;
139 }
140 }
141
142 *ret = ctx_value;
143 return 1;
144}
145/* }}} */
146
147static int php_filter_parse_octal(const char *str, size_t str_len, zend_long *ret) { /* {{{ */
148 zend_ulong ctx_value = 0;
149 const char *end = str + str_len;
150
151 while (str < end) {
152 if (*str >= '0' && *str <= '7') {
153 zend_ulong n = ((*(str++)) - '0');
154
155 if ((ctx_value > ((zend_ulong)(~(zend_long)0)) / 8) ||
156 ((ctx_value = ctx_value * 8) > ((zend_ulong)(~(zend_long)0)) - n)) {
157 return -1;
158 }
159 ctx_value += n;
160 } else {
161 return -1;
162 }
163 }
164
165 *ret = (zend_long)ctx_value;
166 return 1;
167}
168/* }}} */
169
170static int php_filter_parse_hex(const char *str, size_t str_len, zend_long *ret) { /* {{{ */
171 zend_ulong ctx_value = 0;
172 const char *end = str + str_len;
174
175 while (str < end) {
176 if (*str >= '0' && *str <= '9') {
177 n = ((*(str++)) - '0');
178 } else if (*str >= 'a' && *str <= 'f') {
179 n = ((*(str++)) - ('a' - 10));
180 } else if (*str >= 'A' && *str <= 'F') {
181 n = ((*(str++)) - ('A' - 10));
182 } else {
183 return -1;
184 }
185 if ((ctx_value > ((zend_ulong)(~(zend_long)0)) / 16) ||
186 ((ctx_value = ctx_value * 16) > ((zend_ulong)(~(zend_long)0)) - n)) {
187 return -1;
188 }
189 ctx_value += n;
190 }
191
192 *ret = (zend_long)ctx_value;
193 return 1;
194}
195/* }}} */
196
198{
199 zval *option_val;
200 zend_long min_range, max_range, option_flags;
201 int min_range_set, max_range_set;
202 int allow_octal = 0, allow_hex = 0;
203 size_t len;
204 int error = 0;
205 zend_long ctx_value;
206 char *p;
207
208 /* Parse options */
209 FETCH_LONG_OPTION(min_range, "min_range");
210 FETCH_LONG_OPTION(max_range, "max_range");
211 option_flags = flags;
212
214
215 if (len == 0) {
217 }
218
219 if (option_flags & FILTER_FLAG_ALLOW_OCTAL) {
220 allow_octal = 1;
221 }
222
223 if (option_flags & FILTER_FLAG_ALLOW_HEX) {
224 allow_hex = 1;
225 }
226
227 /* Start the validating loop */
228 p = Z_STRVAL_P(value);
229 ctx_value = 0;
230
232
233 if (*p == '0') {
234 p++; len--;
235 if (allow_hex && (*p == 'x' || *p == 'X')) {
236 p++; len--;
237 if (len == 0) {
239 }
240 if (php_filter_parse_hex(p, len, &ctx_value) < 0) {
241 error = 1;
242 }
243 } else if (allow_octal) {
244 /* Support explicit octal prefix notation */
245 if (*p == 'o' || *p == 'O') {
246 p++; len--;
247 if (len == 0) {
249 }
250 }
251 if (php_filter_parse_octal(p, len, &ctx_value) < 0) {
252 error = 1;
253 }
254 } else if (len != 0) {
255 error = 1;
256 }
257 } else {
258 if (php_filter_parse_int(p, len, &ctx_value) < 0) {
259 error = 1;
260 }
261 }
262
263 if (error > 0 || (min_range_set && (ctx_value < min_range)) || (max_range_set && (ctx_value > max_range))) {
265 } else {
267 ZVAL_LONG(value, ctx_value);
268 return;
269 }
270}
271/* }}} */
272
274{
275 char *str = Z_STRVAL_P(value);
276 size_t len = Z_STRLEN_P(value);
277 int ret;
278
280
281 /* returns true for "1", "true", "on" and "yes"
282 * returns false for "0", "false", "off", "no", and ""
283 * null otherwise. */
284 switch (len) {
285 case 0:
286 ret = 0;
287 break;
288 case 1:
289 if (*str == '1') {
290 ret = 1;
291 } else if (*str == '0') {
292 ret = 0;
293 } else {
294 ret = -1;
295 }
296 break;
297 case 2:
298 if (strncasecmp(str, "on", 2) == 0) {
299 ret = 1;
300 } else if (strncasecmp(str, "no", 2) == 0) {
301 ret = 0;
302 } else {
303 ret = -1;
304 }
305 break;
306 case 3:
307 if (strncasecmp(str, "yes", 3) == 0) {
308 ret = 1;
309 } else if (strncasecmp(str, "off", 3) == 0) {
310 ret = 0;
311 } else {
312 ret = -1;
313 }
314 break;
315 case 4:
316 if (strncasecmp(str, "true", 4) == 0) {
317 ret = 1;
318 } else {
319 ret = -1;
320 }
321 break;
322 case 5:
323 if (strncasecmp(str, "false", 5) == 0) {
324 ret = 0;
325 } else {
326 ret = -1;
327 }
328 break;
329 default:
330 ret = -1;
331 }
332
333 if (ret == -1) {
335 } else {
338 }
339}
340/* }}} */
341
343{
344 size_t len;
345 char *str, *end;
346 char *num, *p;
347 zval *option_val;
348 char *decimal;
349 int decimal_set;
350 size_t decimal_len;
351 char dec_sep = '.';
352 char *thousand;
353 int thousand_set;
354 size_t thousand_len;
355 char *tsd_sep;
356
357 zend_long lval;
358 double dval;
359 double min_range, max_range;
360 int min_range_set, max_range_set;
361
362 int first, n;
363
365 str = Z_STRVAL_P(value);
366
368 end = str + len;
369
370 FETCH_STRING_OPTION(decimal, "decimal");
371
372 if (decimal_set) {
373 if (decimal_len != 1) {
374 zend_value_error("%s(): \"decimal\" option must be one character long", get_active_function_name());
376 } else {
377 dec_sep = *decimal;
378 }
379 }
380
381 FETCH_STRING_OPTION(thousand, "thousand");
382
383 if (thousand_set) {
384 if (thousand_len < 1) {
385 zend_value_error("%s(): \"thousand\" option must not be empty", get_active_function_name());
387 } else {
388 tsd_sep = thousand;
389 }
390 } else {
391 tsd_sep = "',.";
392 }
393
394 FETCH_DOUBLE_OPTION(min_range, "min_range");
395 FETCH_DOUBLE_OPTION(max_range, "max_range");
396
397 num = p = emalloc(len+1);
398 if (str < end && (*str == '+' || *str == '-')) {
399 *p++ = *str++;
400 }
401 first = 1;
402 while (1) {
403 n = 0;
404 while (str < end && *str >= '0' && *str <= '9') {
405 ++n;
406 *p++ = *str++;
407 }
408 if (str == end || *str == dec_sep || *str == 'e' || *str == 'E') {
409 if (!first && n != 3) {
410 goto error;
411 }
412 if (*str == dec_sep) {
413 *p++ = '.';
414 str++;
415 while (str < end && *str >= '0' && *str <= '9') {
416 *p++ = *str++;
417 }
418 }
419 if (*str == 'e' || *str == 'E') {
420 *p++ = *str++;
421 if (str < end && (*str == '+' || *str == '-')) {
422 *p++ = *str++;
423 }
424 while (str < end && *str >= '0' && *str <= '9') {
425 *p++ = *str++;
426 }
427 }
428 break;
429 }
430 if ((flags & FILTER_FLAG_ALLOW_THOUSAND) && strchr(tsd_sep, *str)) {
431 if (first?(n < 1 || n > 3):(n != 3)) {
432 goto error;
433 }
434 first = 0;
435 str++;
436 } else {
437 goto error;
438 }
439 }
440 if (str != end) {
441 goto error;
442 }
443 *p = 0;
444
445 switch (is_numeric_string(num, p - num, &lval, &dval, 0)) {
446 case IS_LONG:
447 if ((min_range_set && (lval < min_range)) || (max_range_set && (lval > max_range))) {
448 goto error;
449 }
451 ZVAL_DOUBLE(value, (double)lval);
452 break;
453 case IS_DOUBLE:
454 if ((!dval && p - num > 1 && strpbrk(num, "123456789")) || !zend_finite(dval)) {
455 goto error;
456 }
457 if ((min_range_set && (dval < min_range)) || (max_range_set && (dval > max_range))) {
458 goto error;
459 }
462 break;
463 default:
464error:
465 efree(num);
467 }
468 efree(num);
469}
470/* }}} */
471
473{
474 zval *option_val;
475 zend_string *regexp;
476 int regexp_set;
477 pcre2_code *re = NULL;
478 pcre2_match_data *match_data = NULL;
479 uint32_t capture_count;
480 int rc;
481
482 /* Parse options */
483 FETCH_STR_OPTION(regexp, "regexp");
484
485 if (!regexp_set) {
486 zend_value_error("%s(): \"regexp\" option is missing", get_active_function_name());
488 }
489
490 re = pcre_get_compiled_regex(regexp, &capture_count);
491 if (!re) {
493 }
494 match_data = php_pcre_create_match_data(capture_count, re);
495 if (!match_data) {
497 }
498 rc = pcre2_match(re, (PCRE2_SPTR)Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, match_data, php_pcre_mctx());
499 php_pcre_free_match_data(match_data);
500
501 /* 0 means that the vector is too small to hold all the captured substring offsets */
502 if (rc < 0) {
504 }
505}
506
507static int _php_filter_validate_domain(char * domain, size_t len, zend_long flags) /* {{{ */
508{
509 char *e, *s, *t;
510 size_t l;
511 int hostname = flags & FILTER_FLAG_HOSTNAME;
512 unsigned char i = 1;
513
514 s = domain;
515 l = len;
516 e = domain + l;
517 t = e - 1;
518
519 /* Ignore trailing dot */
520 if (l > 0 && *t == '.') {
521 e = t;
522 l--;
523 }
524
525 /* The total length cannot exceed 253 characters (final dot not included) */
526 if (l > 253) {
527 return 0;
528 }
529
530 /* First char must be alphanumeric */
531 if(*s == '.' || (hostname && !isalnum((int)*(unsigned char *)s))) {
532 return 0;
533 }
534
535 while (s < e) {
536 if (*s == '.') {
537 /* The first and the last character of a label must be alphanumeric */
538 if (*(s + 1) == '.' || (hostname && (!isalnum((int)*(unsigned char *)(s - 1)) || !isalnum((int)*(unsigned char *)(s + 1))))) {
539 return 0;
540 }
541
542 /* Reset label length counter */
543 i = 1;
544 } else {
545 if (i > 63 || (hostname && (*s != '-' || *(s + 1) == '\0') && !isalnum((int)*(unsigned char *)s))) {
546 return 0;
547 }
548
549 i++;
550 }
551
552 s++;
553 }
554
555 return 1;
556}
557/* }}} */
558
560{
561 if (!_php_filter_validate_domain(Z_STRVAL_P(value), Z_STRLEN_P(value), flags)) {
563 }
564}
565/* }}} */
566
567static int is_userinfo_valid(zend_string *str)
568{
569 const char *valid = "-._~!$&'()*+,;=:";
570 const char *p = ZSTR_VAL(str);
571 while (p - ZSTR_VAL(str) < ZSTR_LEN(str)) {
572 if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) {
573 p++;
574 } else if (*p == '%' && p - ZSTR_VAL(str) <= ZSTR_LEN(str) - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) {
575 p += 3;
576 } else {
577 return 0;
578 }
579 }
580 return 1;
581}
582
583static bool php_filter_is_valid_ipv6_hostname(const char *s, size_t l)
584{
585 const char *e = s + l;
586 const char *t = e - 1;
587
588 return *s == '[' && *t == ']' && _php_filter_validate_ipv6(s + 1, l - 2, NULL);
589}
590
592{
593 php_url *url;
594 size_t old_len = Z_STRLEN_P(value);
595
596 php_filter_url(value, flags, option_array, charset);
597
598 if (Z_TYPE_P(value) != IS_STRING || old_len != Z_STRLEN_P(value)) {
600 }
601
602 /* Use parse_url - if it returns false, we return NULL */
604
605 if (url == NULL) {
607 }
608
609 if (url->scheme != NULL &&
611 const char *s;
612 size_t l;
613
614 if (url->host == NULL) {
615 goto bad_url;
616 }
617
618 s = ZSTR_VAL(url->host);
619 l = ZSTR_LEN(url->host);
620
621 if (
622 /* An IPv6 enclosed by square brackets is a valid hostname.*/
623 !php_filter_is_valid_ipv6_hostname(s, l) &&
624 /* Validate domain.
625 * This includes a loose check for an IPv4 address. */
626 !_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)
627 ) {
628 php_url_free(url);
630 }
631 }
632
633 if (
634 url->scheme == NULL ||
635 /* some schemas allow the host to be empty */
636 (url->host == NULL && (!zend_string_equals_literal(url->scheme, "mailto") && !zend_string_equals_literal(url->scheme, "news") && !zend_string_equals_literal(url->scheme, "file"))) ||
638 ) {
639bad_url:
640 php_url_free(url);
642 }
643
644 if ((url->user != NULL && !is_userinfo_valid(url->user))
645 || (url->pass != NULL && !is_userinfo_valid(url->pass))
646 ) {
647 php_url_free(url);
649
650 }
651
652 php_url_free(url);
653}
654/* }}} */
655
657{
658 /*
659 * The regex below is based on a regex by Michael Rushton.
660 * However, it is not identical. I changed it to only consider routeable
661 * addresses as valid. Michael's regex considers a@b a valid address
662 * which conflicts with section 2.3.5 of RFC 5321 which states that:
663 *
664 * Only resolvable, fully-qualified domain names (FQDNs) are permitted
665 * when domain names are used in SMTP. In other words, names that can
666 * be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
667 * in Section 5) are permitted, as are CNAME RRs whose targets can be
668 * resolved, in turn, to MX or address RRs. Local nicknames or
669 * unqualified names MUST NOT be used.
670 *
671 * This regex does not handle comments and folding whitespace. While
672 * this is technically valid in an email address, these parts aren't
673 * actually part of the address itself.
674 *
675 * Michael's regex carries this copyright:
676 *
677 * Copyright © Michael Rushton 2009-10
678 * http://squiloople.com/
679 * Feel free to use and redistribute this code. But please keep this copyright notice.
680 *
681 */
682 pcre2_code *re = NULL;
683 pcre2_match_data *match_data = NULL;
684 uint32_t capture_count;
685 zend_string *sregexp;
686 int rc;
687 const char regexp0[] = "/^(?!(?:(?:\\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\\pL\\pN]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F\\pL\\pN]|(?:\\x5C[\\x00-\\x7F]))*\\x22))(?:\\.(?:(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2F-\\x39\\x3D\\x3F\\x5E-\\x7E\\pL\\pN]+)|(?:\\x22(?:[\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x21\\x23-\\x5B\\x5D-\\x7F\\pL\\pN]|(?:\\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}))\\]))$/iDu";
688 const char regexp1[] = "/^(?!(?:(?:\\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";
689 const char *regexp;
690 size_t regexp_len;
691
693 regexp = regexp0;
694 regexp_len = sizeof(regexp0) - 1;
695 } else {
696 regexp = regexp1;
697 regexp_len = sizeof(regexp1) - 1;
698 }
699
700 /* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
701 if (Z_STRLEN_P(value) > 320) {
703 }
704
705 sregexp = zend_string_init(regexp, regexp_len, 0);
706 re = pcre_get_compiled_regex(sregexp, &capture_count);
707 zend_string_release_ex(sregexp, 0);
708 if (!re) {
710 }
711 match_data = php_pcre_create_match_data(capture_count, re);
712 if (!match_data) {
714 }
715 rc = pcre2_match(re, (PCRE2_SPTR)Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, match_data, php_pcre_mctx());
716 php_pcre_free_match_data(match_data);
717
718 /* 0 means that the vector is too small to hold all the captured substring offsets */
719 if (rc < 0) {
721 }
722
723}
724/* }}} */
725
726static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{ */
727{
728 const char *end = str + str_len;
729 int num, m;
730 int n = 0;
731
732 while (str < end) {
733 int leading_zero;
734 if (*str < '0' || *str > '9') {
735 return 0;
736 }
737 leading_zero = (*str == '0');
738 m = 1;
739 num = ((*(str++)) - '0');
740 while (str < end && (*str >= '0' && *str <= '9')) {
741 num = num * 10 + ((*(str++)) - '0');
742 if (num > 255 || ++m > 3) {
743 return 0;
744 }
745 }
746 /* don't allow a leading 0; that introduces octal numbers,
747 * which we don't support */
748 if (leading_zero && (num != 0 || m > 1))
749 return 0;
750 ip[n++] = num;
751 if (n == 4) {
752 return str == end;
753 } else if (str >= end || *(str++) != '.') {
754 return 0;
755 }
756 }
757 return 0;
758}
759/* }}} */
760
761static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) /* {{{ */
762{
763 int compressed_pos = -1;
764 int blocks = 0;
765 unsigned int num, n;
766 int i;
767 char *ipv4;
768 const char *end;
769 int ip4elm[4];
770 const char *s = str;
771
772 if (!memchr(str, ':', str_len)) {
773 return 0;
774 }
775
776 /* check for bundled IPv4 */
777 ipv4 = memchr(str, '.', str_len);
778 if (ipv4) {
779 while (ipv4 > str && *(ipv4-1) != ':') {
780 ipv4--;
781 }
782
783 if (!_php_filter_validate_ipv4(ipv4, (str_len - (ipv4 - str)), ip4elm)) {
784 return 0;
785 }
786
787 str_len = ipv4 - str; /* length excluding ipv4 */
788 if (str_len < 2) {
789 return 0;
790 }
791
792 if (ipv4[-2] != ':') {
793 /* don't include : before ipv4 unless it's a :: */
794 str_len--;
795 }
796
797 blocks = 2;
798 }
799
800 end = str + str_len;
801
802 while (str < end) {
803 if (*str == ':') {
804 if (++str >= end) {
805 /* cannot end in : without previous : */
806 return 0;
807 }
808 if (*str == ':') {
809 if (compressed_pos >= 0) {
810 return 0;
811 }
812 if (ip && blocks < 8) {
813 ip[blocks] = -1;
814 }
815 compressed_pos = blocks++; /* :: means 1 or more 16-bit 0 blocks */
816 if (++str == end) {
817 if (blocks > 8) {
818 return 0;
819 }
820 goto fixup_ip;
821 }
822 } else if ((str - 1) == s) {
823 /* don't allow leading : without another : following */
824 return 0;
825 }
826 }
827 num = n = 0;
828 while (str < end) {
829 if (*str >= '0' && *str <= '9') {
830 num = 16 * num + (*str - '0');
831 } else if (*str >= 'a' && *str <= 'f') {
832 num = 16 * num + (*str - 'a') + 10;
833 } else if (*str >= 'A' && *str <= 'F') {
834 num = 16 * num + (*str - 'A') + 10;
835 } else {
836 break;
837 }
838 n++;
839 str++;
840 }
841 if (ip && blocks < 8) {
842 ip[blocks] = num;
843 }
844 if (n < 1 || n > 4) {
845 return 0;
846 }
847 if (++blocks > 8)
848 return 0;
849 }
850
851fixup_ip:
852 if (ip && ipv4) {
853 for (i = 0; i < 5; i++) {
854 ip[i] = 0;
855}
856 ip[i++] = 0xffff;
857 ip[i++] = 256 * ip4elm[0] + ip4elm[1];
858 ip[i++] = 256 * ip4elm[2] + ip4elm[3];
859 } else if (ip && compressed_pos >= 0 && blocks <= 8) {
860 int offset = 8 - blocks;
861 for (i = 7; i > compressed_pos + offset; i--) {
862 ip[i] = ip[i - offset];
863 }
864 for (i = compressed_pos + offset; i >= compressed_pos; i--) {
865 ip[i] = 0;
866 }
867 }
868
869 return (compressed_pos >= 0 && blocks <= 8) || blocks == 8;
870}
871/* }}} */
872
873/* From the tables in RFC 6890 - Special-Purpose IP Address Registriesi
874 * Including errata: https://www.rfc-editor.org/errata_search.php?rfc=6890&rec_status=1 */
875static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private)
876{
877 *global = false;
878 *reserved = false;
879 *private = false;
880
881 if (ip[0] == 0) {
882 /* RFC 0791 - This network */
883 *reserved = true;
884 } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) {
885 /* RFC 1122 - This host on this network */
886 *reserved = true;
887 } else if (ip[0] == 10) {
888 /* RFC 1918 - Private Use */
889 *private = true;
890 } else if (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127) {
891 /* RFC 6598 - Shared Address Space */
892 } else if (ip[0] == 127) {
893 /* RFC 1122 - Loopback */
894 *reserved = true;
895 } else if (ip[0] == 169 && ip[1] == 254) {
896 /* RFC 3927 - Link Local */
897 *reserved = true;
898 } else if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) {
899 /* RFC 1918 - Private Use */
900 *private = true;
901 } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0) {
902 /* RFC 6890 - IETF Protocol Assignments */
903 } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 && ip[3] >= 0 && ip[3] <= 7) {
904 /* RFC 6333 - DS-Lite */
905 } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) {
906 /* RFC 5737 - Documentation */
907 } else if (ip[0] == 192 && ip[1] == 88 && ip[2] == 99) {
908 /* RFC 3068 - 6to4 Relay Anycast */
909 *global = true;
910 } else if (ip[0] == 192 && ip[1] == 168) {
911 /* RFC 1918 - Private Use */
912 *private = true;
913 } else if (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19) {
914 /* RFC 2544 - Benchmarking */
915 } else if (ip[0] == 198 && ip[1] == 51 && ip[2] == 100) {
916 /* RFC 5737 - Documentation */
917 } else if (ip[0] == 203 && ip[1] == 0 && ip[2] == 113) {
918 /* RFC 5737 - Documentation */
919 } else if (ip[0] >= 240 && ip[1] <= 255) {
920 /* RFC 1122 - Reserved */
921 *reserved = true;
922 } else if (ip[0] == 255 && ip[1] == 255 && ip[2] == 255 && ip[3] == 255) {
923 /* RFC 0919 - Limited Broadcast, Updated by RFC 8190, 2.2. */
924 *reserved = true;
925 } else {
926 return false;
927 }
928
929 return true;
930}
931
932/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */
933static bool ipv6_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private)
934{
935 *global = false;
936 *reserved = false;
937 *private = false;
938
939 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) {
940 /* RFC 4291 - Unspecified Address */
941 *reserved = true;
942 } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 1) {
943 /* RFC 4291 - Loopback Address */
944 *reserved = true;
945 } else if (ip[0] == 0x0064 && ip[1] == 0xff9b) {
946 /* RFC 6052 - IPv4-IPv6 Translation */
947 *global = true;
948 } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) {
949 /* RFC 4291 - IPv4-mapped Address */
950 *reserved = true;
951 } else if (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) {
952 /* RFC 6666 - Discard-Only Address Block */
953 } else if (ip[0] == 0x2001 && ip[1] == 0x0000) {
954 /* RFC 4380 - TEREDO */
955 } else if (ip[0] == 0x2001 && ip[1] <= 0x01ff) {
956 /* RFC 2928 - IETF Protocol Assignments */
957 } else if (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) {
958 /* RFC 5180 - Benchmarking */
959 } else if (ip[0] == 0x2001 && ip[1] == 0x0db8) {
960 /* RFC 3849 - Documentation */
961 } else if (ip[0] == 0x2001 && ip[1] >= 0x0010 && ip[1] <= 0x001f) {
962 /* RFC 4843 - ORCHID */
963 } else if (ip[0] == 0x2002) {
964 /* RFC 3056 - 6to4 */
965 } else if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) {
966 /* RFC 4193 - Unique-Local */
967 *private = true;
968 } else if (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) {
969 /* RFC 4291 - Linked-Scoped Unicast */
970 *reserved = true;
971 } else {
972 return false;
973 }
974
975 return true;
976}
977
978/* Validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a flag
979 * to throw out reserved ranges; multicast ranges... etc. If both allow_ipv4
980 * and allow_ipv6 flags flag are used, then the first dot or colon determine
981 * the format */
983{
984 int ip[8];
985 int mode;
986 bool flag_global, flag_reserved, flag_private; /* flags for ranges as determined by RFC 6890 */
987
988 if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) {
990 } else if (memchr(Z_STRVAL_P(value), '.', Z_STRLEN_P(value))) {
992 } else {
994 }
995
997 /* Both formats are cool */
998 } else if ((flags & FILTER_FLAG_IPV4) && mode == FORMAT_IPV6) {
1000 } else if ((flags & FILTER_FLAG_IPV6) && mode == FORMAT_IPV4) {
1002 }
1003
1004 if (mode == FORMAT_IPV4) {
1005 if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) {
1007 }
1008
1009 if (!ipv4_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) {
1010 return; /* no special block */
1011 }
1012 }
1013 else if (mode == FORMAT_IPV6) {
1014 if (_php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip) < 1) {
1016 }
1017
1018 if (!ipv6_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) {
1019 return; /* no special block */
1020 }
1021 }
1022
1023 if ((flags & FILTER_FLAG_GLOBAL_RANGE) && flag_global != true) {
1025 }
1026
1027 if ((flags & FILTER_FLAG_NO_PRIV_RANGE) && flag_private == true) {
1029 }
1030
1031 if ((flags & FILTER_FLAG_NO_RES_RANGE) && flag_reserved == true) {
1033 }
1034}
1035/* }}} */
1036
1038{
1039 char *input = Z_STRVAL_P(value);
1040 size_t input_len = Z_STRLEN_P(value);
1041 int tokens, length, i, offset, exp_separator_set;
1042 size_t exp_separator_len;
1043 char separator;
1044 char *exp_separator;
1045 zend_long ret = 0;
1046 zval *option_val;
1047
1048 FETCH_STRING_OPTION(exp_separator, "separator");
1049
1050 if (exp_separator_set && exp_separator_len != 1) {
1051 zend_value_error("%s(): \"separator\" option must be one character long", get_active_function_name());
1053 }
1054
1055 if (14 == input_len) {
1056 /* EUI-64 format: Four hexadecimal digits separated by dots. Less
1057 * commonly used but valid nonetheless.
1058 */
1059 tokens = 3;
1060 length = 4;
1061 separator = '.';
1062 } else if (17 == input_len && input[2] == '-') {
1063 /* IEEE 802 format: Six hexadecimal digits separated by hyphens. */
1064 tokens = 6;
1065 length = 2;
1066 separator = '-';
1067 } else if (17 == input_len && input[2] == ':') {
1068 /* IEEE 802 format: Six hexadecimal digits separated by colons. */
1069 tokens = 6;
1070 length = 2;
1071 separator = ':';
1072 } else {
1074 }
1075
1076 if (exp_separator_set && separator != exp_separator[0]) {
1078 }
1079
1080 /* Essentially what we now have is a set of tokens each consisting of
1081 * a hexadecimal number followed by a separator character. (With the
1082 * exception of the last token which does not have the separator.)
1083 */
1084 for (i = 0; i < tokens; i++) {
1085 offset = i * (length + 1);
1086
1087 if (i < tokens - 1 && input[offset + length] != separator) {
1088 /* The current token did not end with e.g. a "." */
1090 }
1091 if (php_filter_parse_hex(input + offset, length, &ret) < 0) {
1092 /* The current token is no valid hexadecimal digit */
1094 }
1095 }
1096}
1097/* }}} */
size_t len
Definition apprentice.c:174
strpbrk(string $string, string $characters)
strchr(string $haystack, string $needle, bool $before_needle=false)
sign
Definition bcmath.h:37
char s[4]
Definition cdf.c:77
#define SIZEOF_LONG
Definition config-win.h:32
error($message)
Definition ext_skel.php:22
zend_long n
Definition ffi.c:4979
#define RETURN_VALIDATION_FAILED
#define FILTER_FLAG_NO_RES_RANGE
#define FILTER_FLAG_NO_PRIV_RANGE
#define FILTER_FLAG_IPV6
#define FILTER_FLAG_ALLOW_THOUSAND
#define FILTER_FLAG_ALLOW_HEX
#define PHP_FILTER_TRIM_DEFAULT_EX(p, len, return_if_empty)
#define FILTER_FLAG_PATH_REQUIRED
#define FILTER_FLAG_ALLOW_OCTAL
#define FILTER_FLAG_QUERY_REQUIRED
#define FILTER_FLAG_HOSTNAME
#define FILTER_FLAG_EMAIL_UNICODE
#define FILTER_FLAG_GLOBAL_RANGE
#define PHP_FILTER_TRIM_DEFAULT(p, len)
#define FILTER_FLAG_IPV4
zend_long offset
char * mode
#define NULL
Definition gdcache.h:45
enum entity_charset charset
Definition html_tables.h:39
void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL)
#define FETCH_DOUBLE_OPTION(var_name, option_name)
void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL)
void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL)
void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL)
void php_filter_validate_domain(PHP_INPUT_FILTER_PARAM_DECL)
#define FETCH_STRING_OPTION(var_name, option_name)
void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL)
#define FETCH_STR_OPTION(var_name, option_name)
void php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL)
#define FETCH_LONG_OPTION(var_name, option_name)
#define FORMAT_IPV6
void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL)
#define FORMAT_IPV4
void php_filter_validate_mac(PHP_INPUT_FILTER_PARAM_DECL)
#define pcre2_code
Definition pcre2.h:822
#define pcre2_match
Definition pcre2.h:881
#define PCRE2_SPTR
Definition pcre2.h:820
#define pcre2_match_data
Definition pcre2.h:844
unsigned const char * end
Definition php_ffi.h:51
#define PHP_INPUT_FILTER_PARAM_DECL
Definition php_filter.h:58
void php_filter_url(PHP_INPUT_FILTER_PARAM_DECL)
PHPAPI pcre2_match_context * php_pcre_mctx(void)
Definition php_pcre.c:3054
PHPAPI pcre2_match_data * php_pcre_create_match_data(uint32_t capture_count, pcre2_code *re)
Definition php_pcre.c:906
PHPAPI pcre2_code * pcre_get_compiled_regex(zend_string *regex, uint32_t *capture_count)
Definition php_pcre.c:892
PHPAPI void php_pcre_free_match_data(pcre2_match_data *match_data)
Definition php_pcre.c:928
p
Definition session.c:1105
Definition url.h:20
zend_string * user
Definition url.h:22
zend_string * pass
Definition url.h:23
zend_string * host
Definition url.h:24
zend_string * scheme
Definition url.h:21
zend_string * query
Definition url.h:27
zend_string * path
Definition url.h:26
PHPAPI php_url * php_url_parse_ex(char const *str, size_t length)
Definition url.c:84
PHPAPI void php_url_free(php_url *theurl)
Definition url.c:32
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
Definition zend.c:1849
#define efree(ptr)
Definition zend_alloc.h:155
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
zend_string_release_ex(func->internal_function.function_name, 0)
#define strncasecmp(s1, s2, n)
ZEND_API const char * get_active_function_name(void)
int32_t zend_long
Definition zend_long.h:42
#define MAX_LENGTH_OF_LONG
Definition zend_long.h:109
#define ZEND_LONG_MIN
Definition zend_long.h:46
uint32_t zend_ulong
Definition zend_long.h:43
#define ZEND_LONG_MAX
Definition zend_long.h:45
struct _zend_string zend_string
#define zend_finite(a)
#define ZEND_FALLTHROUGH
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define zend_string_equals_literal(str, literal)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define zend_string_equals_literal_ci(str, c)
#define dval(x)
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define ZVAL_LONG(z, l)
#define IS_STRING
Definition zend_types.h:606
#define IS_DOUBLE
Definition zend_types.h:605
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
#define IS_LONG
Definition zend_types.h:604
#define ZVAL_DOUBLE(z, d)
#define ZVAL_BOOL(z, b)
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval * ret
value