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