php中parse_url函数的源码及分析
前言
看师傅们的文章时发现,parse_url出现的次数较多,单纯parse_url解析漏洞的考题也有很多,在此研究一下源码(太菜了看不懂,待日后再补充Orz)
源码
PHPAPI php_url *php_url_parse_ex(char const *str, size_t length)
{
    char port_buf[6];
    php_url *ret = ecalloc(1, sizeof(php_url));
    char const *s, *e, *p, *pp, *ue;
    s = str;
    ue = s + length;
    /* parse scheme */
    if ((e = memchr(s, ':', length)) && e != s) {
        /* validate scheme */
        p = s;
        while (p < e) {
            /* scheme = 1*[ lowalpha | digit | "+" | "-" | "." ] */
            if (!isalpha(*p) && !isdigit(*p) && *p != '+' && *p != '.' && *p != '-') {
                if (e + 1 < ue && e < s + strcspn(s, "?#")) {
                    goto parse_port;
                } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
                    s += 2;
                    e = 0;
                    goto parse_host;
                } else {
                    goto just_path;
                }
            }
            p++;
        }
        if (e + 1 == ue) { /* only scheme is available */
            ret->scheme = estrndup(s, (e - s));
            php_replace_controlchars_ex(ret->scheme, (e - s));
            return ret;
        }
        /*
         * certain schemas like mailto: and zlib: may not have any / after them
         * this check ensures we support those.
         */
        if (*(e+1) != '/') {
            /* check if the data we get is a port this allows us to
             * correctly parse things like a.com:80
             */
            p = e + 1;
            while (p < ue && isdigit(*p)) {
                p++;
            }
            if ((p == ue || *p == '/') && (p - e) < 7) {
                goto parse_port;
            }
            ret->scheme = estrndup(s, (e-s));
            php_replace_controlchars_ex(ret->scheme, (e - s));
            s = e + 1;
            goto just_path;
        } else {
            ret->scheme = estrndup(s, (e-s));
            php_replace_controlchars_ex(ret->scheme, (e - s));
            if (e + 2 < ue && *(e + 2) == '/') {
                s = e + 3;
                if (!strncasecmp("file", ret->scheme, sizeof("file"))) {
                    if (e + 3 < ue && *(e + 3) == '/') {
                        /* support windows drive letters as in:
                           file:///c:/somedir/file.txt
                        */
                        if (e + 5 < ue && *(e + 5) == ':') {
                            s = e + 4;
                        }
                        goto just_path;
                    }
                }
            } else {
                s = e + 1;
                goto just_path;
            }
        }
    } else if (e) { /* no scheme; starts with colon: look for port */
        parse_port:
        p = e + 1;
        pp = p;
        while (pp < ue && pp - p < 6 && isdigit(*pp)) {
            pp++;
        }
        if (pp - p > 0 && pp - p < 6 && (pp == ue || *pp == '/')) {
            zend_long port;
            memcpy(port_buf, p, (pp - p));
            port_buf[pp - p] = '\0';
            port = ZEND_STRTOL(port_buf, NULL, 10);
            if (port > 0 && port <= 65535) {
                ret->port = (unsigned short) port;
                if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
                    s += 2;
                }
            } else {
                if (ret->scheme) efree(ret->scheme);
                efree(ret);
                return NULL;
            }
        } else if (p == pp && pp == ue) {
            if (ret->scheme) efree(ret->scheme);
            efree(ret);
            return NULL;
        } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
            s += 2;
        } else {
            goto just_path;
        }
    } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */
        s += 2;
    } else {
        goto just_path;
    }
    parse_host:
    /* Binary-safe strcspn(s, "/?#") */
    e = ue;
    if ((p = memchr(s, '/', e - s))) {
        e = p;
    }
    if ((p = memchr(s, '?', e - s))) {
        e = p;
    }
    if ((p = memchr(s, '#', e - s))) {
        e = p;
    }
    /* check for login and password */
    if ((p = zend_memrchr(s, '@', (e-s)))) {
        if ((pp = memchr(s, ':', (p-s)))) {
            ret->user = estrndup(s, (pp-s));
            php_replace_controlchars_ex(ret->user, (pp - s));
            pp++;
            ret->pass = estrndup(pp, (p-pp));
            php_replace_controlchars_ex(ret->pass, (p-pp));
        } else {
            ret->user = estrndup(s, (p-s));
            php_replace_controlchars_ex(ret->user, (p-s));
        }
        s = p + 1;
    }
    /* check for port */
    if (s < ue && *s == '[' && *(e-1) == ']') {
        /* Short circuit portscan,
           we're dealing with an
           IPv6 embedded address */
        p = NULL;
    } else {
        p = zend_memrchr(s, ':', (e-s));
    }
    if (p) {
        if (!ret->port) {
            p++;
            if (e-p > 5) { /* port cannot be longer then 5 characters */
                if (ret->scheme) efree(ret->scheme);
                if (ret->user) efree(ret->user);
                if (ret->pass) efree(ret->pass);
                efree(ret);
                return NULL;
            } else if (e - p > 0) {
                zend_long port;
                memcpy(port_buf, p, (e - p));
                port_buf[e - p] = '\0';
                port = ZEND_STRTOL(port_buf, NULL, 10);
                if (port > 0 && port <= 65535) {
                    ret->port = (unsigned short)port;
                } else {
                    if (ret->scheme) efree(ret->scheme);
                    if (ret->user) efree(ret->user);
                    if (ret->pass) efree(ret->pass);
                    efree(ret);
                    return NULL;
                }
            }
            p--;
        }
    } else {
        p = e;
    }
    /* check if we have a valid host, if we don't reject the string as url */
    if ((p-s) < 1) {
        if (ret->scheme) efree(ret->scheme);
        if (ret->user) efree(ret->user);
        if (ret->pass) efree(ret->pass);
        efree(ret);
        return NULL;
    }
    ret->host = estrndup(s, (p-s));
    php_replace_controlchars_ex(ret->host, (p - s));
    if (e == ue) {
        return ret;
    }
    s = e;
    just_path:
    e = ue;
    p = memchr(s, '#', (e - s));
    if (p) {
        p++;
        if (p < e) {
            ret->fragment = estrndup(p, (e - p));
            php_replace_controlchars_ex(ret->fragment, (e - p));
        }
        e = p-1;
    }
    p = memchr(s, '?', (e - s));
    if (p) {
        p++;
        if (p < e) {
            ret->query = estrndup(p, (e - p));
            php_replace_controlchars_ex(ret->query, (e - p));
        }
        e = p-1;
    }
    if (s < e || s == ue) {
        ret->path = estrndup(s, (e - s));
        php_replace_controlchars_ex(ret->path, (e - s));
    }
    return ret;
}
/* {{{ proto mixed parse_url(string url, [int url_component])
   Parse a URL and return its components */
PHP_FUNCTION(parse_url)
{
    char *str;
    size_t str_len;
    php_url *resource;
    zend_long key = -1;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &str_len, &key) == FAILURE) {
        return;
    }
    resource = php_url_parse_ex(str, str_len);
    if (resource == NULL) {
        /* @todo Find a method to determine why php_url_parse_ex() failed */
        RETURN_FALSE;
}
分析
- 只要请求的url里不含有冒号(:)就会被当成path解析
php中parse_url函数的源码及分析的更多相关文章
- php中parse_url函数的源码及分析(scheme部分)
		前言 看师傅们的文章时发现,parse_url出现的次数较多,单纯parse_url解析漏洞的考题也有很多,在此研究一下源码(太菜了看不懂,待日后再补充Orz) 源码 在ext/standard/ur ... 
- Apache Spark源码走读之23 -- Spark MLLib中拟牛顿法L-BFGS的源码实现
		欢迎转载,转载请注明出处,徽沪一郎. 概要 本文就拟牛顿法L-BFGS的由来做一个简要的回顾,然后就其在spark mllib中的实现进行源码走读. 拟牛顿法 数学原理 代码实现 L-BFGS算法中使 ... 
- redis中set命令的源码分析
		首先在源码中的redis.c文件中有一个结构体:redisCommand redisCommandTable[],这个结构体中定义了每个命令对应的函数,源码中的set命令对应的函数是setComman ... 
- sleep函数——Gevent源码分析
		gevent是一个异步I/O框架,当遇到I/O操作的时候,会自动切换任务,从而能异步地完成I/O操作 但是在测试的情况下,可以使用sleep函数来让gevent进行任务切换.示例如下: import ... 
- Matlab.NET混合编程技巧之——直接调用Matlab内置函数(附源码)
		原文:[原创]Matlab.NET混合编程技巧之--直接调用Matlab内置函数(附源码) 在我的上一篇文章[原创]Matlab.NET混编技巧之——找出Matlab内置函数中,已经大概的介绍了mat ... 
- Generator函数执行器-co函数库源码解析
		一.co函数是什么 co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行.短小精悍只有短短200余行,就可以免去手动编写G ... 
- caffe-windows中classification.cpp的源码阅读
		caffe-windows中classification.cpp的源码阅读 命令格式: usage: classification string(模型描述文件net.prototxt) string( ... 
- Django框架rest_framework中APIView的as_view()源码解析、认证、权限、频率控制
		在上篇我们对Django原生View源码进行了局部解析:https://www.cnblogs.com/dongxixi/p/11130976.html 在前后端分离项目中前面我们也提到了各种认证需要 ... 
- RocketMQ中Broker的启动源码分析(一)
		在RocketMQ中,使用BrokerStartup作为启动类,相较于NameServer的启动,Broker作为RocketMQ的核心可复杂得多 [RocketMQ中NameServer的启动源码分 ... 
随机推荐
- sql数值比较
- AGC017C Snuke and Spells(巧妙的线段覆盖模型)
			题目大意: 给出n个球,每个球上都有数字,然后每次都进行如下操作 如果当前的球总共有k个,那么就把球上数字为k的所有球都消除掉 注意到,并不是每种情况都可以全部消光,所以你可以选择若干球,把它们标号改 ... 
- [Leetcode] plus one 加一
			Given a number represented as an array of digits, plus one to the number. 题意:给定数以数组的形式存储,然后计算该数加1的值. ... 
- [Leetcode] Best time to buy and sell stock 买卖股票的最佳时机
			Say you have an array for which the i th element is the price of a given stock on day i. If you were ... 
- 整理一些JavaScript时间处理扩展函数
			在JavaScript中,时间处理是经常需要用到的.最近想要慢慢建立自己的代码库,整理了几个之前用到的js处理时间的函数,发出来跟大家分享一下,以后的使用中会不断增加和修改代码库. 把字符串转换为日期 ... 
- taotao购物车
			功能分析: 1.在用户不登陆的情况下也可以使用购物车,那么就需要把购物车信息放入cookie中. 2.可以把商品信息,存放到pojo中,然后序列化成json存入cookie中. 3.取商品信息可以从c ... 
- npm install 权限的问题
			用ctrl+r切换到对象的目录,以管理圆的身份执行 npm cache clean first. If that doesn’t fix things, take a look in %APPDATA ... 
- Endnote 中文参考文献样式修改版
			http://blog.yuelong.info/post/endnote-gbt7714-2005.html 很多人不知道 EndNote 是自带中文参考文献引用样式的,即符合<文后参考文献著 ... 
- es6+最佳入门实践(13)
			13.模块化 13.1.什么是模块化 模块化是一种处理复杂系统分解为更好的可管理模块的方式.通俗的讲就是把一个复杂的功能拆分成多个小功能,并且以一种良好的机制管理起来,这样就可以认为是模块化.就像作家 ... 
- 学习正则表达式及c#应用
			1.0正则表达式语法 正则表达式是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”).模式描述在搜索文本时要匹配的一个或多个字符串. 正则表达式示例 表达式 ... 
