不晓得说啥子
全部博文(42)
分类: LINUX
2014-11-02 11:12:30
最近在做路由器行为管理方面的研究,有一个功能要设域名的黑白名单,想也没有想,就用了下面的命令:*******-d x.x.x.x -j *****,我想很多新人都会这样做。整个框架用shell搭建起来了,测试发现这样做根本不行。好!问题来了,为什么这样做不行?当使用 -d 选项加域名iptabels是怎么去解析这条命令的?我猜最终是调用类如 gethostbyname()这样的函数解析到域名的IP,保存规则也是保存的具体IP ,而不是域名,也不会去动态的解析。怎么去证明呢?看源代码:
首先看iptables-standalone.c,这是iptables的主函数。在进行了一些必要的初始化之后,主函数调用了这个函数ret = do_command4(argc, argv, &table, &handle, false),函数do_command4用来解析用户的参数,在iptables.c中。
在函数int do_command4(这个函数用来解析iptables 命令行参数并将这个规则插入到内核空间)有这样一个片段:
case 's':
set_option(&cs.options, OPT_SOURCE, &cs.fw.ip.invflags, cs.invert);
shostnetworkmask = optarg;
break;
case 'd':
set_option(&cs.options, OPT_DESTINATION, &cs.fw.ip.invflags, cs.invert);
dhostnetworkmask = optarg;
break;
这两个case获取用户输入的-s 和 -d 选项的参数,并设置cs.option(command_state结构),这里就具体讲一下-d选项吧。在do_command4()函数后部分有这样一段代码:
if (shostnetworkmask)
xtables_ipparse_multiple(shostnetworkmask, &saddrs,&smasks, &nsaddrs);
if (dhostnetworkmask)
xtables_ipparse_multiple(dhostnetworkmask, &daddrs, &dmasks, &ndaddrs);
这里的dhostetworkmask(如果不为空)是在case ‘d’中得到的参数,如果不为空,这里调用Xtables_ipparse_multiple()来将IP地址和掩码解析到in_addr的结构中。下面来看看这个解析IP地址和掩码的函数,具体定义在xtables.c中。
void xtables_ipparse_multiple(const char *name, struct in_addr **addrpp,
struct in_addr **maskpp, unsigned int *naddrs)
{
struct in_addr *addrp;
char buf[256], *p, *next;
unsigned int len, i, j, n, count = 1;
const char *loop = name;
/*这个while循环得到参数中有几个IP地址或者是域名,这才知道—d 选项后面的IP和域名是可以输入很多,分别用逗号隔开就ok*/
while ((loop = strchr(loop, ',')) != NULL) {
++count;
++loop; /* skip ',' */
}
/*根据得到的参数个数为两个存储IP地址和MASK 的结构分配相应的内存*/
*addrpp = xables_malloc(sizeof(struct in_addr) * count);
*maskpp = xtables_malloc(sizeof(struct in_addr) * count);
loop = name;
/*for循环开始解析各个参数了*/
for (i = 0; i < count; ++i) {
while (isspace(*loop))
++loop;
next = strchr(loop, ',');
if (next != NULL)
len = next - loop;
else
len = strlen(loop);
if (len > sizeof(buf) - 1)
xt_params->exit_err(PARAMETER_PROBLEM,
"Hostname too long");
strncpy(buf, loop, len);
buf[len] = '\0';
/*解析掩码*/
if ((p = strrchr(buf, '/')) != NULL) {
*p = '\0';
addrp = parse_ipmask(p + 1);
} else {
addrp = parse_ipmask(NULL);
}
memcpy(*maskpp + i, addrp, sizeof(*addrp));
/* if a null mask is given, the name is ignored, like in "any/0" */
if ((*maskpp + i)->s_addr == 0)
/*
* A bit pointless to process multiple addresses
* in this case...
*/
strcpy(buf, "0.0.0.0");
/*重点在这里,解析IP地址*/
addrp = ipparse_hostnetwork(buf, &n);
if (n > 1) {
count += n - 1;
*addrpp = xtables_realloc(*addrpp,
sizeof(struct in_addr) * count);
*maskpp = xtables_realloc(*maskpp,
sizeof(struct in_addr) * count);
for (j = 0; j < n; ++j)
/* for each new addr */
memcpy(*addrpp + i + j, addrp + j,
sizeof(*addrp));
for (j = 1; j < n; ++j)
/* for each new mask */
memcpy(*maskpp + i + j, *maskpp + i,
sizeof(*addrp));
i += n - 1;
} else {
memcpy(*addrpp + i, addrp, sizeof(*addrp));
}
/* free what ipparse_hostnetwork had allocated: */
free(addrp);
if (next == NULL)
break;
loop = next + 1;
}
*naddrs = count;
for (i = 0; i < count; ++i)
(*addrpp+i)->s_addr &= (*maskpp+i)->s_addr;
}
ipparse_hostnetwork()函数具体定义如下:
static struct in_addr *
ipparse_hostnetwork(const char *name, unsigned int *naddrs)
{
struct in_addr *addrptmp, *addrp;
if ((addrptmp = xtables_numeric_to_ipaddr(name)) != NULL ||
(addrptmp = network_to_ipaddr(name)) != NULL) {
addrp = xtables_malloc(sizeof(struct in_addr));
memcpy(addrp, addrptmp, sizeof(*addrp));
*naddrs = 1;
return addrp;
}
if ((addrptmp = host_to_ipaddr(name, naddrs)) != NULL)
return addrptmp;
xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
}
这个函数中前部分先尝试直接将参数用两种方式解析成IP地址(不是域名的形式时),如果可以直接解析,则直接返回,naddrs记录解析到的IP个数。如果前两种方式不能解析,则调用函数host_to_ipaddr()用域名的方式解析。Host_to_ipaddr()函数具体定义如下:
static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
{
struct hostent *host;
struct in_addr *addr;
unsigned int i;
*naddr = 0;
if ((host = gethostbyname(name)) != NULL) {
if (host->h_addrtype != AF_INET ||
host->h_length != sizeof(struct in_addr))
return NULL;
while (host->h_addr_list[*naddr] != NULL)
++*naddr;
addr = xtables_calloc(*naddr, sizeof(struct in_addr));
for (i = 0; i < *naddr; i++)
memcpy(&addr[i], host->h_addr_list[i],
sizeof(struct in_addr));
return addr;
}
return NULL;
}
Host_to_addr()最终调用的是gethostbyname()函数来尝试用域名的方式解析这个参数,如果解析成功则将解析到的IP地址组和IP个数返回,如果不能解析则返回NULL。正如我之前猜的那样,-d选项确实是调用了gethostbyname()函数,得到的IP也只是它能解析出来的几个,所以这印证了我前面那个框架的错误。问题来了,只用iptables怎么样才能禁止或允许一个域名呢?
PS:第一次在CU发博客,iptables也是刚接触不久,只是记录一下自己的学习过程,如果哪里写错了还望大家多包涵。
接下来一篇文章会分析整个iptables的框架。