1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Derick Rethans <derick@php.net> |
16 | Pierre-A. Joye <pierre@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 /* $Id$ */
21
22 #include "php_filter.h"
23 #include "filter_private.h"
24 #include "ext/standard/url.h"
25 #include "ext/pcre/php_pcre.h"
26
27 #include "zend_multiply.h"
28
29 #if HAVE_ARPA_INET_H
30 # include <arpa/inet.h>
31 #endif
32
33 #ifndef INADDR_NONE
34 # define INADDR_NONE ((unsigned long int) -1)
35 #endif
36
37
38 /* {{{ FETCH_LONG_OPTION(var_name, option_name) */
39 #define FETCH_LONG_OPTION(var_name, option_name) \
40 var_name = 0; \
41 var_name##_set = 0; \
42 if (option_array) { \
43 if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
44 PHP_FILTER_GET_LONG_OPT(option_val, var_name); \
45 var_name##_set = 1; \
46 } \
47 }
48 /* }}} */
49
50 /* {{{ FETCH_STRING_OPTION(var_name, option_name) */
51 #define FETCH_STRING_OPTION(var_name, option_name) \
52 var_name = NULL; \
53 var_name##_set = 0; \
54 var_name##_len = 0; \
55 if (option_array) { \
56 if (zend_hash_find(HASH_OF(option_array), option_name, sizeof(option_name), (void **) &option_val) == SUCCESS) { \
57 if (Z_TYPE_PP(option_val) == IS_STRING) { \
58 var_name = Z_STRVAL_PP(option_val); \
59 var_name##_len = Z_STRLEN_PP(option_val); \
60 var_name##_set = 1; \
61 } \
62 } \
63 }
64 /* }}} */
65
66 #define FORMAT_IPV4 4
67 #define FORMAT_IPV6 6
68
php_filter_parse_int(const char * str,unsigned int str_len,long * ret TSRMLS_DC)69 static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
70 long ctx_value;
71 int sign = 0, digit = 0;
72 const char *end = str + str_len;
73
74 switch (*str) {
75 case '-':
76 sign = 1;
77 case '+':
78 str++;
79 default:
80 break;
81 }
82
83 /* must start with 1..9*/
84 if (str < end && *str >= '1' && *str <= '9') {
85 ctx_value = ((sign)?-1:1) * ((*(str++)) - '0');
86 } else {
87 return -1;
88 }
89
90 if ((end - str > MAX_LENGTH_OF_LONG - 1) /* number too long */
91 || (SIZEOF_LONG == 4 && (end - str == MAX_LENGTH_OF_LONG - 1) && *str > '2')) {
92 /* overflow */
93 return -1;
94 }
95
96 while (str < end) {
97 if (*str >= '0' && *str <= '9') {
98 digit = (*(str++) - '0');
99 if ( (!sign) && ctx_value <= (LONG_MAX-digit)/10 ) {
100 ctx_value = (ctx_value * 10) + digit;
101 } else if ( sign && ctx_value >= (LONG_MIN+digit)/10) {
102 ctx_value = (ctx_value * 10) - digit;
103 } else {
104 return -1;
105 }
106 } else {
107 return -1;
108 }
109 }
110
111 *ret = ctx_value;
112 return 1;
113 }
114 /* }}} */
115
php_filter_parse_octal(const char * str,unsigned int str_len,long * ret TSRMLS_DC)116 static int php_filter_parse_octal(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
117 unsigned long ctx_value = 0;
118 const char *end = str + str_len;
119
120 while (str < end) {
121 if (*str >= '0' && *str <= '7') {
122 unsigned long n = ((*(str++)) - '0');
123
124 if ((ctx_value > ((unsigned long)(~(long)0)) / 8) ||
125 ((ctx_value = ctx_value * 8) > ((unsigned long)(~(long)0)) - n)) {
126 return -1;
127 }
128 ctx_value += n;
129 } else {
130 return -1;
131 }
132 }
133
134 *ret = (long)ctx_value;
135 return 1;
136 }
137 /* }}} */
138
php_filter_parse_hex(const char * str,unsigned int str_len,long * ret TSRMLS_DC)139 static int php_filter_parse_hex(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */
140 unsigned long ctx_value = 0;
141 const char *end = str + str_len;
142 unsigned long n;
143
144 while (str < end) {
145 if (*str >= '0' && *str <= '9') {
146 n = ((*(str++)) - '0');
147 } else if (*str >= 'a' && *str <= 'f') {
148 n = ((*(str++)) - ('a' - 10));
149 } else if (*str >= 'A' && *str <= 'F') {
150 n = ((*(str++)) - ('A' - 10));
151 } else {
152 return -1;
153 }
154 if ((ctx_value > ((unsigned long)(~(long)0)) / 16) ||
155 ((ctx_value = ctx_value * 16) > ((unsigned long)(~(long)0)) - n)) {
156 return -1;
157 }
158 ctx_value += n;
159 }
160
161 *ret = (long)ctx_value;
162 return 1;
163 }
164 /* }}} */
165
php_filter_int(PHP_INPUT_FILTER_PARAM_DECL)166 void php_filter_int(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
167 {
168 zval **option_val;
169 long min_range, max_range, option_flags;
170 int min_range_set, max_range_set;
171 int allow_octal = 0, allow_hex = 0;
172 int len, error = 0;
173 long ctx_value;
174 char *p;
175
176 /* Parse options */
177 FETCH_LONG_OPTION(min_range, "min_range");
178 FETCH_LONG_OPTION(max_range, "max_range");
179 option_flags = flags;
180
181 len = Z_STRLEN_P(value);
182
183 if (len == 0) {
184 RETURN_VALIDATION_FAILED
185 }
186
187 if (option_flags & FILTER_FLAG_ALLOW_OCTAL) {
188 allow_octal = 1;
189 }
190
191 if (option_flags & FILTER_FLAG_ALLOW_HEX) {
192 allow_hex = 1;
193 }
194
195 /* Start the validating loop */
196 p = Z_STRVAL_P(value);
197 ctx_value = 0;
198
199 PHP_FILTER_TRIM_DEFAULT(p, len);
200
201 if (*p == '0') {
202 p++; len--;
203 if (allow_hex && (*p == 'x' || *p == 'X')) {
204 p++; len--;
205 if (php_filter_parse_hex(p, len, &ctx_value TSRMLS_CC) < 0) {
206 error = 1;
207 }
208 } else if (allow_octal) {
209 if (php_filter_parse_octal(p, len, &ctx_value TSRMLS_CC) < 0) {
210 error = 1;
211 }
212 } else if (len != 0) {
213 error = 1;
214 }
215 } else {
216 if (php_filter_parse_int(p, len, &ctx_value TSRMLS_CC) < 0) {
217 error = 1;
218 }
219 }
220
221 if (error > 0 || (min_range_set && (ctx_value < min_range)) || (max_range_set && (ctx_value > max_range))) {
222 RETURN_VALIDATION_FAILED
223 } else {
224 zval_dtor(value);
225 Z_TYPE_P(value) = IS_LONG;
226 Z_LVAL_P(value) = ctx_value;
227 return;
228 }
229 }
230 /* }}} */
231
php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL)232 void php_filter_boolean(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
233 {
234 char *str = Z_STRVAL_P(value);
235 int len = Z_STRLEN_P(value);
236 int ret;
237
238 PHP_FILTER_TRIM_DEFAULT(str, len);
239
240 /* returns true for "1", "true", "on" and "yes"
241 * returns false for "0", "false", "off", "no", and ""
242 * null otherwise. */
243 switch (len) {
244 case 1:
245 if (*str == '1') {
246 ret = 1;
247 } else if (*str == '0') {
248 ret = 0;
249 } else {
250 ret = -1;
251 }
252 break;
253 case 2:
254 if (strncasecmp(str, "on", 2) == 0) {
255 ret = 1;
256 } else if (strncasecmp(str, "no", 2) == 0) {
257 ret = 0;
258 } else {
259 ret = -1;
260 }
261 break;
262 case 3:
263 if (strncasecmp(str, "yes", 3) == 0) {
264 ret = 1;
265 } else if (strncasecmp(str, "off", 3) == 0) {
266 ret = 0;
267 } else {
268 ret = -1;
269 }
270 break;
271 case 4:
272 if (strncasecmp(str, "true", 4) == 0) {
273 ret = 1;
274 } else {
275 ret = -1;
276 }
277 break;
278 case 5:
279 if (strncasecmp(str, "false", 5) == 0) {
280 ret = 0;
281 } else {
282 ret = -1;
283 }
284 break;
285 default:
286 ret = -1;
287 }
288
289 if (ret == -1) {
290 RETURN_VALIDATION_FAILED
291 } else {
292 zval_dtor(value);
293 ZVAL_BOOL(value, ret);
294 }
295 }
296 /* }}} */
297
php_filter_float(PHP_INPUT_FILTER_PARAM_DECL)298 void php_filter_float(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
299 {
300 int len;
301 char *str, *end;
302 char *num, *p;
303
304 zval **option_val;
305 char *decimal;
306 int decimal_set, decimal_len;
307 char dec_sep = '.';
308 char tsd_sep[3] = "',.";
309
310 long lval;
311 double dval;
312
313 int first, n;
314
315 len = Z_STRLEN_P(value);
316 str = Z_STRVAL_P(value);
317
318 PHP_FILTER_TRIM_DEFAULT(str, len);
319 end = str + len;
320
321 FETCH_STRING_OPTION(decimal, "decimal");
322
323 if (decimal_set) {
324 if (decimal_len != 1) {
325 php_error_docref(NULL TSRMLS_CC, E_WARNING, "decimal separator must be one char");
326 RETURN_VALIDATION_FAILED
327 } else {
328 dec_sep = *decimal;
329 }
330 }
331
332 num = p = emalloc(len+1);
333 if (str < end && (*str == '+' || *str == '-')) {
334 *p++ = *str++;
335 }
336 first = 1;
337 while (1) {
338 n = 0;
339 while (str < end && *str >= '0' && *str <= '9') {
340 ++n;
341 *p++ = *str++;
342 }
343 if (str == end || *str == dec_sep || *str == 'e' || *str == 'E') {
344 if (!first && n != 3) {
345 goto error;
346 }
347 if (*str == dec_sep) {
348 *p++ = '.';
349 str++;
350 while (str < end && *str >= '0' && *str <= '9') {
351 *p++ = *str++;
352 }
353 }
354 if (*str == 'e' || *str == 'E') {
355 *p++ = *str++;
356 if (str < end && (*str == '+' || *str == '-')) {
357 *p++ = *str++;
358 }
359 while (str < end && *str >= '0' && *str <= '9') {
360 *p++ = *str++;
361 }
362 }
363 break;
364 }
365 if ((flags & FILTER_FLAG_ALLOW_THOUSAND) && (*str == tsd_sep[0] || *str == tsd_sep[1] || *str == tsd_sep[2])) {
366 if (first?(n < 1 || n > 3):(n != 3)) {
367 goto error;
368 }
369 first = 0;
370 str++;
371 } else {
372 goto error;
373 }
374 }
375 if (str != end) {
376 goto error;
377 }
378 *p = 0;
379
380 switch (is_numeric_string(num, p - num, &lval, &dval, 0)) {
381 case IS_LONG:
382 zval_dtor(value);
383 Z_TYPE_P(value) = IS_DOUBLE;
384 Z_DVAL_P(value) = lval;
385 break;
386 case IS_DOUBLE:
387 if ((!dval && p - num > 1 && strpbrk(num, "123456789")) || !zend_finite(dval)) {
388 goto error;
389 }
390 zval_dtor(value);
391 Z_TYPE_P(value) = IS_DOUBLE;
392 Z_DVAL_P(value) = dval;
393 break;
394 default:
395 error:
396 efree(num);
397 RETURN_VALIDATION_FAILED
398 }
399 efree(num);
400 }
401 /* }}} */
402
php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL)403 void php_filter_validate_regexp(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
404 {
405 zval **option_val;
406 char *regexp;
407 int regexp_len;
408 long option_flags;
409 int regexp_set, option_flags_set;
410
411 pcre *re = NULL;
412 pcre_extra *pcre_extra = NULL;
413 int preg_options = 0;
414
415 int ovector[3];
416 int matches;
417
418 /* Parse options */
419 FETCH_STRING_OPTION(regexp, "regexp");
420 FETCH_LONG_OPTION(option_flags, "flags");
421
422 if (!regexp_set) {
423 php_error_docref(NULL TSRMLS_CC, E_WARNING, "'regexp' option missing");
424 RETURN_VALIDATION_FAILED
425 }
426
427 re = pcre_get_compiled_regex(regexp, &pcre_extra, &preg_options TSRMLS_CC);
428 if (!re) {
429 RETURN_VALIDATION_FAILED
430 }
431 matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
432
433 /* 0 means that the vector is too small to hold all the captured substring offsets */
434 if (matches < 0) {
435 RETURN_VALIDATION_FAILED
436 }
437 }
438 /* }}} */
439
php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL)440 void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
441 {
442 php_url *url;
443 int old_len = Z_STRLEN_P(value);
444
445 php_filter_url(value, flags, option_array, charset TSRMLS_CC);
446
447 if (Z_TYPE_P(value) != IS_STRING || old_len != Z_STRLEN_P(value)) {
448 RETURN_VALIDATION_FAILED
449 }
450
451 /* Use parse_url - if it returns false, we return NULL */
452 url = php_url_parse_ex(Z_STRVAL_P(value), Z_STRLEN_P(value));
453
454 if (url == NULL) {
455 RETURN_VALIDATION_FAILED
456 }
457
458 if (url->scheme != NULL && (!strcasecmp(url->scheme, "http") || !strcasecmp(url->scheme, "https"))) {
459 char *e, *s;
460
461 if (url->host == NULL) {
462 goto bad_url;
463 }
464
465 e = url->host + strlen(url->host);
466 s = url->host;
467
468 /* First char of hostname must be alphanumeric */
469 if(!isalnum((int)*(unsigned char *)s)) {
470 goto bad_url;
471 }
472
473 while (s < e) {
474 if (!isalnum((int)*(unsigned char *)s) && *s != '-' && *s != '.') {
475 goto bad_url;
476 }
477 s++;
478 }
479
480 if (*(e - 1) == '.') {
481 goto bad_url;
482 }
483 }
484
485 if (
486 url->scheme == NULL ||
487 /* some schemas allow the host to be empty */
488 (url->host == NULL && (strcmp(url->scheme, "mailto") && strcmp(url->scheme, "news") && strcmp(url->scheme, "file"))) ||
489 ((flags & FILTER_FLAG_PATH_REQUIRED) && url->path == NULL) || ((flags & FILTER_FLAG_QUERY_REQUIRED) && url->query == NULL)
490 ) {
491 bad_url:
492 php_url_free(url);
493 RETURN_VALIDATION_FAILED
494 }
495 php_url_free(url);
496 }
497 /* }}} */
498
php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL)499 void php_filter_validate_email(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
500 {
501 /*
502 * The regex below is based on a regex by Michael Rushton.
503 * However, it is not identical. I changed it to only consider routeable
504 * addresses as valid. Michael's regex considers a@b a valid address
505 * which conflicts with section 2.3.5 of RFC 5321 which states that:
506 *
507 * Only resolvable, fully-qualified domain names (FQDNs) are permitted
508 * when domain names are used in SMTP. In other words, names that can
509 * be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed
510 * in Section 5) are permitted, as are CNAME RRs whose targets can be
511 * resolved, in turn, to MX or address RRs. Local nicknames or
512 * unqualified names MUST NOT be used.
513 *
514 * This regex does not handle comments and folding whitespace. While
515 * this is technically valid in an email address, these parts aren't
516 * actually part of the address itself.
517 *
518 * Michael's regex carries this copyright:
519 *
520 * Copyright © Michael Rushton 2009-10
521 * http://squiloople.com/
522 * Feel free to use and redistribute this code. But please keep this copyright notice.
523 *
524 */
525 const char regexp[] = "/^(?!(?:(?:\\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";
526
527 pcre *re = NULL;
528 pcre_extra *pcre_extra = NULL;
529 int preg_options = 0;
530 int ovector[150]; /* Needs to be a multiple of 3 */
531 int matches;
532
533
534 /* The maximum length of an e-mail address is 320 octets, per RFC 2821. */
535 if (Z_STRLEN_P(value) > 320) {
536 RETURN_VALIDATION_FAILED
537 }
538
539 re = pcre_get_compiled_regex((char *)regexp, &pcre_extra, &preg_options TSRMLS_CC);
540 if (!re) {
541 RETURN_VALIDATION_FAILED
542 }
543 matches = pcre_exec(re, NULL, Z_STRVAL_P(value), Z_STRLEN_P(value), 0, 0, ovector, 3);
544
545 /* 0 means that the vector is too small to hold all the captured substring offsets */
546 if (matches < 0) {
547 RETURN_VALIDATION_FAILED
548 }
549
550 }
551 /* }}} */
552
_php_filter_validate_ipv4(char * str,int str_len,int * ip)553 static int _php_filter_validate_ipv4(char *str, int str_len, int *ip) /* {{{ */
554 {
555 const char *end = str + str_len;
556 int num, m;
557 int n = 0;
558
559 while (str < end) {
560 int leading_zero;
561 if (*str < '0' || *str > '9') {
562 return 0;
563 }
564 leading_zero = (*str == '0');
565 m = 1;
566 num = ((*(str++)) - '0');
567 while (str < end && (*str >= '0' && *str <= '9')) {
568 num = num * 10 + ((*(str++)) - '0');
569 if (num > 255 || ++m > 3) {
570 return 0;
571 }
572 }
573 /* don't allow a leading 0; that introduces octal numbers,
574 * which we don't support */
575 if (leading_zero && (num != 0 || m > 1))
576 return 0;
577 ip[n++] = num;
578 if (n == 4) {
579 return str == end;
580 } else if (str >= end || *(str++) != '.') {
581 return 0;
582 }
583 }
584 return 0;
585 }
586 /* }}} */
587
_php_filter_validate_ipv6(char * str,int str_len TSRMLS_DC)588 static int _php_filter_validate_ipv6(char *str, int str_len TSRMLS_DC) /* {{{ */
589 {
590 int compressed = 0;
591 int blocks = 0;
592 int n;
593 char *ipv4;
594 char *end;
595 int ip4elm[4];
596 char *s = str;
597
598 if (!memchr(str, ':', str_len)) {
599 return 0;
600 }
601
602 /* check for bundled IPv4 */
603 ipv4 = memchr(str, '.', str_len);
604 if (ipv4) {
605 while (ipv4 > str && *(ipv4-1) != ':') {
606 ipv4--;
607 }
608
609 if (!_php_filter_validate_ipv4(ipv4, (str_len - (ipv4 - str)), ip4elm)) {
610 return 0;
611 }
612
613 str_len = ipv4 - str; /* length excluding ipv4 */
614 if (str_len < 2) {
615 return 0;
616 }
617
618 if (ipv4[-2] != ':') {
619 /* don't include : before ipv4 unless it's a :: */
620 str_len--;
621 }
622
623 blocks = 2;
624 }
625
626 end = str + str_len;
627
628 while (str < end) {
629 if (*str == ':') {
630 if (++str >= end) {
631 /* cannot end in : without previous : */
632 return 0;
633 }
634 if (*str == ':') {
635 if (compressed) {
636 return 0;
637 }
638 blocks++; /* :: means 1 or more 16-bit 0 blocks */
639 compressed = 1;
640
641 if (++str == end) {
642 return (blocks <= 8);
643 }
644 } else if ((str - 1) == s) {
645 /* dont allow leading : without another : following */
646 return 0;
647 }
648 }
649 n = 0;
650 while ((str < end) &&
651 ((*str >= '0' && *str <= '9') ||
652 (*str >= 'a' && *str <= 'f') ||
653 (*str >= 'A' && *str <= 'F'))) {
654 n++;
655 str++;
656 }
657 if (n < 1 || n > 4) {
658 return 0;
659 }
660 if (++blocks > 8)
661 return 0;
662 }
663 return ((compressed && blocks <= 8) || blocks == 8);
664 }
665 /* }}} */
666
php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL)667 void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
668 {
669 /* validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a
670 * flag to throw out reserved ranges; multicast ranges... etc. If both
671 * allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or
672 * colon determine the format */
673
674 int ip[4];
675 int mode;
676
677 if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) {
678 mode = FORMAT_IPV6;
679 } else if (memchr(Z_STRVAL_P(value), '.', Z_STRLEN_P(value))) {
680 mode = FORMAT_IPV4;
681 } else {
682 RETURN_VALIDATION_FAILED
683 }
684
685 if ((flags & FILTER_FLAG_IPV4) && (flags & FILTER_FLAG_IPV6)) {
686 /* Both formats are cool */
687 } else if ((flags & FILTER_FLAG_IPV4) && mode == FORMAT_IPV6) {
688 RETURN_VALIDATION_FAILED
689 } else if ((flags & FILTER_FLAG_IPV6) && mode == FORMAT_IPV4) {
690 RETURN_VALIDATION_FAILED
691 }
692
693 switch (mode) {
694 case FORMAT_IPV4:
695 if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) {
696 RETURN_VALIDATION_FAILED
697 }
698
699 /* Check flags */
700 if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
701 if (
702 (ip[0] == 10) ||
703 (ip[0] == 172 && (ip[1] >= 16 && ip[1] <= 31)) ||
704 (ip[0] == 192 && ip[1] == 168)
705 ) {
706 RETURN_VALIDATION_FAILED
707 }
708 }
709
710 if (flags & FILTER_FLAG_NO_RES_RANGE) {
711 if (
712 (ip[0] == 0) ||
713 (ip[0] == 128 && ip[1] == 0) ||
714 (ip[0] == 191 && ip[1] == 255) ||
715 (ip[0] == 169 && ip[1] == 254) ||
716 (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) ||
717 (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1) ||
718 (ip[0] >= 224 && ip[0] <= 255)
719 ) {
720 RETURN_VALIDATION_FAILED
721 }
722 }
723 break;
724
725 case FORMAT_IPV6:
726 {
727 int res = 0;
728 res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);
729 if (res < 1) {
730 RETURN_VALIDATION_FAILED
731 }
732 /* Check flags */
733 if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
734 if (Z_STRLEN_P(value) >=2 && (!strncasecmp("FC", Z_STRVAL_P(value), 2) || !strncasecmp("FD", Z_STRVAL_P(value), 2))) {
735 RETURN_VALIDATION_FAILED
736 }
737 }
738 if (flags & FILTER_FLAG_NO_RES_RANGE) {
739 switch (Z_STRLEN_P(value)) {
740 case 1: case 0:
741 break;
742 case 2:
743 if (!strcmp("::", Z_STRVAL_P(value))) {
744 RETURN_VALIDATION_FAILED
745 }
746 break;
747 case 3:
748 if (!strcmp("::1", Z_STRVAL_P(value)) || !strcmp("5f:", Z_STRVAL_P(value))) {
749 RETURN_VALIDATION_FAILED
750 }
751 break;
752 default:
753 if (Z_STRLEN_P(value) >= 5) {
754 if (
755 !strncasecmp("fe8", Z_STRVAL_P(value), 3) ||
756 !strncasecmp("fe9", Z_STRVAL_P(value), 3) ||
757 !strncasecmp("fea", Z_STRVAL_P(value), 3) ||
758 !strncasecmp("feb", Z_STRVAL_P(value), 3)
759 ) {
760 RETURN_VALIDATION_FAILED
761 }
762 }
763 if (
764 (Z_STRLEN_P(value) >= 9 && !strncasecmp("2001:0db8", Z_STRVAL_P(value), 9)) ||
765 (Z_STRLEN_P(value) >= 2 && !strncasecmp("5f", Z_STRVAL_P(value), 2)) ||
766 (Z_STRLEN_P(value) >= 4 && !strncasecmp("3ff3", Z_STRVAL_P(value), 4)) ||
767 (Z_STRLEN_P(value) >= 8 && !strncasecmp("2001:001", Z_STRVAL_P(value), 8))
768 ) {
769 RETURN_VALIDATION_FAILED
770 }
771 }
772 }
773 }
774 break;
775 }
776 }
777 /* }}} */
778
779 /*
780 * Local variables:
781 * tab-width: 4
782 * c-basic-offset: 4
783 * End:
784 * vim600: noet sw=4 ts=4 fdm=marker
785 * vim<600: noet sw=4 ts=4
786 */
787