1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2014 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
69static 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
121static 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
144static 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
171void 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
237void 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
306void 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:
403error:
404            efree(num);
405            RETURN_VALIDATION_FAILED
406    }
407    efree(num);
408}
409/* }}} */
410
411void 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
448void 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    ) {
495bad_url:
496        php_url_free(url);
497        RETURN_VALIDATION_FAILED
498    }
499    php_url_free(url);
500}
501/* }}} */
502
503void 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
557static 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
592static 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
671void 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] == 100 && (ip[1] >= 64 && ip[1] <= 127)) ||
718                    (ip[0] == 169 && ip[1] == 254) ||
719                    (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) ||
720                    (ip[0] == 127 && ip[1] == 0 && ip[2] == 0 && ip[3] == 1) ||
721                    (ip[0] >= 224 && ip[0] <= 255)
722                ) {
723                    RETURN_VALIDATION_FAILED
724                }
725            }
726            break;
727
728        case FORMAT_IPV6:
729            {
730                int res = 0;
731                res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);
732                if (res < 1) {
733                    RETURN_VALIDATION_FAILED
734                }
735                /* Check flags */
736                if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
737                    if (Z_STRLEN_P(value) >=2 && (!strncasecmp("FC", Z_STRVAL_P(value), 2) || !strncasecmp("FD", Z_STRVAL_P(value), 2))) {
738                        RETURN_VALIDATION_FAILED
739                    }
740                }
741                if (flags & FILTER_FLAG_NO_RES_RANGE) {
742                    switch (Z_STRLEN_P(value)) {
743                        case 1: case 0:
744                            break;
745                        case 2:
746                            if (!strcmp("::", Z_STRVAL_P(value))) {
747                                RETURN_VALIDATION_FAILED
748                            }
749                            break;
750                        case 3:
751                            if (!strcmp("::1", Z_STRVAL_P(value)) || !strcmp("5f:", Z_STRVAL_P(value))) {
752                                RETURN_VALIDATION_FAILED
753                            }
754                            break;
755                        default:
756                            if (Z_STRLEN_P(value) >= 5) {
757                                if (
758                                    !strncasecmp("fe8", Z_STRVAL_P(value), 3) ||
759                                    !strncasecmp("fe9", Z_STRVAL_P(value), 3) ||
760                                    !strncasecmp("fea", Z_STRVAL_P(value), 3) ||
761                                    !strncasecmp("feb", Z_STRVAL_P(value), 3)
762                                ) {
763                                    RETURN_VALIDATION_FAILED
764                                }
765                            }
766                            if (
767                                (Z_STRLEN_P(value) >= 9 &&  !strncasecmp("2001:0db8", Z_STRVAL_P(value), 9)) ||
768                                (Z_STRLEN_P(value) >= 2 &&  !strncasecmp("5f", Z_STRVAL_P(value), 2)) ||
769                                (Z_STRLEN_P(value) >= 4 &&  !strncasecmp("3ff3", Z_STRVAL_P(value), 4)) ||
770                                (Z_STRLEN_P(value) >= 8 &&  !strncasecmp("2001:001", Z_STRVAL_P(value), 8))
771                            ) {
772                                RETURN_VALIDATION_FAILED
773                            }
774                    }
775                }
776            }
777            break;
778    }
779}
780/* }}} */
781
782/*
783 * Local variables:
784 * tab-width: 4
785 * c-basic-offset: 4
786 * End:
787 * vim600: noet sw=4 ts=4 fdm=marker
788 * vim<600: noet sw=4 ts=4
789 */
790