概述
在上一篇笔记 https://blog.youkuaiyun.com/xiaoyu_750516366/article/details/82750979 中,有一步比较关键的点没有细说,就是nsdispatch()的调用,这一步调用决定了到底后续是怎么进行DNS查询,这篇笔记就来看看该函数的实现逻辑。
数据结构
struct ns_src
ns_src定义了一个搜索源。
/*
* ns_src - `nsswitch source'
* Used by the nsparser routines to store a mapping between a source
* and its dispatch control flags for a given database.
*/
typedef struct {
//搜索源的名字,查询时,通过name与前面查询表项是否匹配来进行查询,见下文
const char *name;
uint32_t flags;
} ns_src;
当前,搜索源src可以取如下值:
/*
* Currently implemented sources.
*/
#define NSSRC_FILES "files" /* local files */
#define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */
#define NSSRC_NIS "nis" /* YP/NIS */
#define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */
查询表项struct ns_dtab
/*
* ns_dtab `callback' function signature.
*/
typedef int (*nss_method)(void *, void *, va_list);
/*
* ns_dtab - `nsswitch dispatch table'
* Contains an entry for each source and the appropriate function to call.
*/
typedef struct {
const char *src;
nss_method callback;
void *cb_data;
} ns_dtab;
在进行域名解析时,ns_dtab定义了一个查询表项,查询表项用于指定如何搜索一个搜索源。其中src为该查询表项的搜索源路径;nss_method指定了如何查询该搜索源;cb_data指向传递给callback的参数。
为了方便定义ns_dtab,系统还提供了一些宏:
/*
* Macros to help build an ns_dtab[]
*/
#define NS_FILES_CB(F,C) { NSSRC_FILES, F, __UNCONST(C) },
#define NS_COMPAT_CB(F,C) { NSSRC_COMPAT, F, __UNCONST(C) },
#ifdef HESIOD
# define NS_DNS_CB(F,C) { NSSRC_DNS, F, __UNCONST(C) },
#else
# define NS_DNS_CB(F,C)
#endif
#ifdef YP
# define NS_NIS_CB(F,C) { NSSRC_NIS, F, __UNCONST(C) },
#else
# define NS_NIS_CB(F,C)
#endif
#define NS_NULL_CB { .src = NULL },
搜索接口nsdispatch
@retval:
@disp_tab: 查询表
@database:流程中没有使用,只是检查了该参数不能为NULL
@method:流程中没有使用,只是检查了该阐述不能为NULL
@defaults:搜索源
@others:其它参数
int nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
const char *method, const ns_src defaults[], ...)
{
va_list ap;
int i, result;
const ns_src *srclist;
int srclistsize;
nss_method cb;
void *cb_data;
//输入参数的非空判断
/* retval may be NULL */
/* disp_tab may be NULL */
assert(database != NULL);
assert(method != NULL);
assert(defaults != NULL);
if (database == NULL || method == NULL || defaults == NULL)
return (NS_UNAVAIL);
//计算搜索源中有几个有效值
srclist = defaults;
srclistsize = 0;
while (srclist[srclistsize].name != NULL)
srclistsize++;
result = 0;
//遍历搜索源
for (i = 0; i < srclistsize; i++) {
//该函数会返回查询表中src与搜索源中name字段相同的表项的回调函数,后面使用该回调函数
//完成实际的查询,这里不知道为什么要设计的这么复杂
cb = _nsmethod(srclist[i].name, database, method,
disp_tab, &cb_data);
result = 0;
if (cb != NULL) {
va_start(ap, defaults);
//调用查询函数进行查询
result = (*cb)(retval, cb_data, ap);
va_end(ap);
//如果指定了NS_FORCEALL,表示强制查询搜有的搜索源,所以继续查询
if (defaults[0].flags & NS_FORCEALL)
continue;
//回调返回的结果与指定的标记符合(一般指查询成功),那么跳出
if (result & srclist[i].flags)
break;
//如果是提供的buffer空间不足,直接返回
/* Stop trying next resolver when there is a memory space fatal error. */
if ((result & NS_UNAVAIL) != 0 && errno == ENOSPC) {
break;
}
}
}
result &= NS_STATUSMASK; /* clear private flags in result */
return (result ? result : NS_NOTFOUND);
}
搜索源与查询表的匹配规则
static nss_method
_nsmethod(const char *source, const char *database __unused, const char *method __unused,
const ns_dtab disp_tab[], void **cb_data)
{
int curdisp;
if (disp_tab != NULL) {
//遍历查询表
for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) {
//寻找搜索源source和查询表项的scr一致的查询表项,然后返回其回调函数以及对应回调函数参数
if (strcasecmp(source, disp_tab[curdisp].src) == 0) {
*cb_data = disp_tab[curdisp].cb_data;
return (disp_tab[curdisp].callback);
}
}
}
*cb_data = NULL;
return (NULL);
}
综上所述,实际上ndispatch()要做的事情就是根据指定的搜索源(defaults参数)去匹配查询表(disp_tab参数)中匹配对应的搜索回调函数,这里通过这种机制实现了基于文件和域名系统的查询。