设为首页 收藏本站
查看: 861|回复: 0

[经验分享] [apache] 挂钩说明(2)

[复制链接]

尚未签到

发表于 2015-8-6 10:25:04 | 显示全部楼层 |阅读模式
  核心层实现了一个处理HTTP请求的基本框架,每个挂钩就对应着一次HTTP请求的一个处理阶段,核心层按照某个顺序运行每个挂钩具体实现中的挂钩调用函数。
  底层挂钩的代码被封装在apr-util库apr-util\include\apr_hooks.h,以宏的形式来声明。

1. 挂钩声明
  在Apache中声明一个挂钩,总是通过如下的宏来实现的。挂钩只能被声明一次。
  例如:header_parser挂钩



AP_DECLARE_HOOK(int,header_parser,(request_rec *r))
2. 挂钩结构/挂钩数组的声明
  每一个挂钩,都有一个apr_array_header_t数组来保存它的相关内容。APACHE2中不能直接访问挂钩数组,引入了APR_HOOK_STRUCT宏,所有对数组的操作都只能通过该宏来实现。
  该宏定义了一个限于模块内使用的结构_hooks,该模块内声明的所有挂钩对应的数组都保存为_hooks的成员,如server\config.c声明了含有9个挂钩的结构。



67 APR_HOOK_STRUCT(
68            APR_HOOK_LINK(header_parser)
69            APR_HOOK_LINK(pre_config)
70            APR_HOOK_LINK(post_config)
71            APR_HOOK_LINK(open_logs)
72            APR_HOOK_LINK(child_init)
73            APR_HOOK_LINK(handler)
74            APR_HOOK_LINK(quick_handler)
75            APR_HOOK_LINK(optional_fn_retrieve)
76            APR_HOOK_LINK(test_config)
77 )
  其中APR_HOOK_STRUCT和APR_HOOK_LINK的定义在apr-util\include\apr_hooks.h。



134 /** macro to declare the hook structure */  
135 define APR_HOOK_STRUCT(members) \
136 static struct { members } _hooks;
137
138 /** macro to link the hook structure */
139  define APR_HOOK_LINK(name) \
140     apr_array_header_t *link_##name;
  
  可以看出_hooks结构的定义为static,该结构是模块内的私有机构,外部模块无法直接访问_hooks变量,并且只要声明了挂钩,就应该有一个对应的_hooks变量。
  当某个模块想使用某个挂钩,它既不能直接访问该挂钩的挂钩数组,也不能访问被屏蔽的模块内的_hooks变量,它只能使用该挂钩的注册函数ap_。
  ap_函数所做的事情是访问_hooks结构中的某个数组,然后在数组中添加挂钩处理函数。
  例如把挂钩处理函数match_headers添加到header_parser挂钩数组中,就要用ap_hook_header_parser这个注册函数。
  modules\metadata\mod_setenvif.c:



573     ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
  

3. 挂钩注册
  如果模块对某个挂钩感兴趣,它就需要注册对应挂钩的处理函数。模块对挂钩函数的注册,通常是在模块结构中的register_hooks函数中调用对应挂钩的挂钩注册函数ap_来实现的。
  modules\metadata\mod_setenvif.c:



571 static void register_hooks(apr_pool_t *p)
572 {
573     ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
574     ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
575 }
  查遍APACHE的所有文件,也不能找到ap_hook_header_parser和ap_hook_post_read_request等函数声明和实现,这是因为挂钩注册函数是通过宏AP_IMPLEMENT_HOOK_VOID/AP_IMPLEMENT_HOOK_RUN_ALL/AP_IMPLEMENT_HOOK_RUN_FIRST来实现的。
  server\config.c



79 AP_IMPLEMENT_HOOK_RUN_ALL(int, header_parser,
80                           (request_rec *r), (r), OK, DECLINED)
81
82 AP_IMPLEMENT_HOOK_RUN_ALL(int, pre_config,
83                           (apr_pool_t *pconf, apr_pool_t *plog,
84                            apr_pool_t *ptemp),
85                           (pconf, plog, ptemp), OK, DECLINED)
86
87 AP_IMPLEMENT_HOOK_VOID(test_config,
88                        (apr_pool_t *pconf, server_rec *s),
89                        (pconf, s))
90
91 AP_IMPLEMENT_HOOK_RUN_ALL(int, post_config,
92                           (apr_pool_t *pconf, apr_pool_t *plog,
93                            apr_pool_t *ptemp, server_rec *s),
94                           (pconf, plog, ptemp, s), OK, DECLINED)
(略)
148 AP_IMPLEMENT_HOOK_RUN_ALL(int, open_logs,
149                           (apr_pool_t *pconf, apr_pool_t *plog,
150                            apr_pool_t *ptemp, server_rec *s),
151                           (pconf, plog, ptemp, s), OK, DECLINED)
152
153 AP_IMPLEMENT_HOOK_VOID(child_init,
154                        (apr_pool_t *pchild, server_rec *s),
155                        (pchild, s))
156
157 AP_IMPLEMENT_HOOK_RUN_FIRST(int, handler, (request_rec *r),
158                             (r), DECLINED)
159
160 AP_IMPLEMENT_HOOK_RUN_FIRST(int, quick_handler, (request_rec *r, int lookup),
161                             (r, lookup), DECLINED)
162
163 AP_IMPLEMENT_HOOK_VOID(optional_fn_retrieve, (void , ())
164
  AP_IMPLEMENT_HOOK_VOID/AP_IMPLEMENT_HOOK_RUN_ALL/AP_IMPLEMENT_HOOK_RUN_FIRST这3个宏的定义中引用了APR_IMPLEMENT_XXX开头的宏。



#define AP_IMPLEMENT_HOOK_VOID(name,args_decl,args_use) \
APR_IMPLEMENT_EXTERNAL_HOOK_VOID(ap,AP,name,args_decl,args_use)
#define AP_IMPLEMENT_HOOK_RUN_ALL(ret,name,args_decl,args_use,ok,decline) \
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ap,AP,ret,name,args_decl, \
args_use,ok,decline)
#define AP_IMPLEMENT_HOOK_RUN_FIRST(ret,name,args_decl,args_use,decline) \
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ap,AP,ret,name,args_decl, \
args_use,decline)
  APR_IMPLEMENT_XXX开头的宏才是关注的地方,他们都要用到APR_IMPLEMENT_EXTERNAL_HOOK_BASE这个宏。
  
  下面以header_parser挂钩来展开说明。
  APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL宏的定义如下:



#define APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(ns,link,ret,name,args_decl,args_use,ok,decline) \
APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \
link##_DECLARE(ret) ns##_run_##name args_decl \
{ \
ns##_LINK_##name##_t *pHook; \
int n; \
ret rv = ok; \
APR_HOOK_INT_DCL_UD; \
\
APR_HOOK_PROBE_ENTRY(ud, ns, name, args_use); \
\
if(_hooks.link_##name) \
{ \
pHook=(ns##_LINK_##name##_t *)_hooks.link_##name->elts; \
for(n=0 ; n < _hooks.link_##name->nelts ; ++n) \
{ \
APR_HOOK_PROBE_INVOKE(ud, ns, name, (char *)pHook[n].szName, args_use); \
rv=pHook[n].pFunc args_use; \
APR_HOOK_PROBE_COMPLETE(ud, ns, name, (char *)pHook[n].szName, rv, args_use); \
if(rv != ok && rv != decline) \
break; \
rv = ok; \
} \
} \
\
APR_HOOK_PROBE_RETURN(ud, ns, name, rv, args_use); \
\
return rv; \
}
  其中APR_IMPLEMENT_EXTERNAL_HOOK_BASE的宏定义:
  



#define APR_IMPLEMENT_EXTERNAL_HOOK_BASE(ns,link,name) \
link##_DECLARE(void) ns##_hook_##name(ns##_HOOK_##name##_t *pf,const char * const *aszPre, \
const char * const *aszSucc,int nOrder) \
{ \
ns##_LINK_##name##_t *pHook; \
if(!_hooks.link_##name) \
{ \
_hooks.link_##name=apr_array_make(apr_hook_global_pool,1,sizeof(ns##_LINK_##name##_t)); \
apr_hook_sort_register(#name,&_hooks.link_##name); \
} \
pHook=apr_array_push(_hooks.link_##name); \
pHook->pFunc=pf; \
pHook->aszPredecessors=aszPre; \
pHook->aszSuccessors=aszSucc; \
pHook->nOrder=nOrder; \
pHook->szName=apr_hook_debug_current; \
if(apr_hook_debug_enabled) \
apr_hook_debug_show(#name,aszPre,aszSucc); \
} \
APR_IMPLEMENT_HOOK_GET_PROTO(ns,link,name) \
{ \
return _hooks.link_##name; \
}
  对APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL/APR_IMPLEMENT_EXTERNAL_HOOK_BASE宏进行展开:
  



    void ap_hook_header_parser(ap_HOOK_header_parser_t *pf,const char * const *aszPre,
const char * const *aszSucc,int nOrder)
{
ap_LINK_header_parser_t *pHook;
if(!_hooks.link_header_parser)
{
_hooks.link_header_parser=apr_array_make(apr_hook_global_pool,1,sizeof(ap_LINK_header_parser_t));
apr_hook_sort_register("header_parser",&_hooks.link_header_parser);
}
pHook=apr_array_push(_hooks.link_header_parser);
pHook->pFunc=pf;
pHook->aszPredecessors=aszPre;
pHook->aszSuccessors=aszSucc;
pHook->nOrder=nOrder;
pHook->szName=apr_hook_debug_current;
if(apr_hook_debug_enabled)
apr_hook_debug_show("header_parser",aszPre,aszSucc);
}
apr_array_header_t *ap_hook_get_header_parser(void)
{
return _hooks.link_header_parser;
}
int ap_run_header_parser (request_rec *r)
{
ap_LINK_header_parser_t *pHook;
int n;
int rv = OK;
if(_hooks.link_header_parser)
{
pHook=(ap_LINK_header_parser_t *)_hooks.link_header_parser->elts;
for(n=0 ; n < _hooks.link_header_parser->nelts ; ++n)
{
rv=pHook[n].pFunc (r);
if(rv != OK && rv != DECLINED)
break;
rv = OK;
}
}
return rv;
}
  
  可以看到上面定义了ap_hook_header_parser、ap_run_header_parser和ap_hook_get_header_parser这3个函数,分别是注册函数、调用函数、获取挂钩数组函数。

4. 挂钩调用
  所有的挂钩对外提供的调用形式都是一样的ap_run_。
  挂钩的类型的类型用三个宏:AP_IMPLEMENT_HOOK_VOID、AP_IMPLEMENT_HOOK_RUN_FIRST及AP_IMPLEMENT_HOOK_RUN_ALL来区分。
  三者的内部实现不尽相同, 具体差别见[apache] 挂钩说明(1)
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-94666-1-1.html 上篇帖子: tftp、samba和apache任务总结 下篇帖子: Apache / PHP 5.x Remote Code Execution Exploit
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表