acl/app/jaws/global/dns_lookup.c
2014-11-19 00:25:21 +08:00

270 lines
7.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "lib_acl.h"
#ifdef ACL_BCB_COMPILER
#pragma hdrstop
#endif
#include "dns.h"
#include "dns_lookup.h"
#define STRNCPY ACL_SAFE_STRNCPY
/*----------------------------------------------------------------------------*/
static void inner_nslookup_error(CLIENT_ENTRY *entry)
{
if (entry->client == NULL)
acl_msg_fatal("%s(%d): client null",
__FILE__, __LINE__);
if (entry->dns_errmsg) {
acl_aio_refer(entry->client);
acl_aio_writen(entry->client, entry->dns_errmsg,
(int) strlen(entry->dns_errmsg));
/* 恢复refer值 */
acl_aio_unrefer(entry->client);
}
entry->nslookup_notify_fn(entry, NSLOOKUP_ERR);
}
static void inner_nslookup_ok(CLIENT_ENTRY *entry, ACL_DNS_DB *dns_db)
{
ACL_ITER iter;
int i = 0;
entry->ip_idx = 0;
entry->dns_ctx.ip_cnt = 0;
acl_foreach(iter, dns_db) {
const ACL_HOST_INFO *info;
info = (const ACL_HOST_INFO*) iter.data;
ACL_SAFE_STRNCPY(entry->dns_ctx.ip[i], info->ip,
sizeof(entry->dns_ctx.ip[i]));
entry->dns_ctx.port[i++] = info->hport;
entry->dns_ctx.ip_cnt++;
if (entry->dns_ctx.ip_cnt >= DNS_IP_MAX)
break;
}
entry->nslookup_notify_fn(entry, NSLOOKUP_OK);
}
static void inner_nslookup_complete(ACL_DNS_DB *dns_db, void *ctx,
int errnum acl_unused)
{
CLIENT_ENTRY *entry = (CLIENT_ENTRY*) ctx;
if (dns_db != NULL)
inner_nslookup_ok(entry, dns_db);
else
inner_nslookup_error(entry);
}
/* 查询DNS信息采用协议发送的方式 */
static void inner_nslookup(SERVICE *service, CLIENT_ENTRY *entry,
const char *domain, int port)
{
char *ptr;
STRNCPY(entry->dns_ctx.domain_key, domain,
sizeof(entry->dns_ctx.domain_key));
acl_lowercase(entry->dns_ctx.domain_key);
ptr = strchr(entry->dns_ctx.domain_key, ':');
if (ptr)
*ptr = 0;
entry->server_port = port;
if (entry->server_port <= 0)
entry->server_port = 80;
STRNCPY(entry->domain_key, entry->dns_ctx.domain_key,
sizeof(entry->domain_key));
acl_dns_lookup(service->dns_handle, entry->domain_key,
inner_nslookup_complete, entry);
}
/*----------------------------------------------------------------------------*/
/* DNS查询结果之后的回调函数 */
static void thrpool_nslookup_complete(const DNS_CTX *dns_ctx)
{
const char *myname = "thrpool_nslookup_complte";
SERVICE *service = (SERVICE *) dns_ctx->context;
DNS_RING *list;
ACL_RING *ring_ptr;
CLIENT_ENTRY *entry;
time_t inter, now;
list = (DNS_RING *) acl_htable_find(service->dns_table,
dns_ctx->domain_key);
if (list == NULL) {
acl_msg_warn(NULL, "%s: domain(%s) not found maybe handled",
myname, dns_ctx->domain_key);
return;
}
time(&now);
inter = now - dns_ctx->begin;
/* 如果查询时间过长,则给出警告信息 */
if (inter >= 5)
acl_msg_warn("%s(%d): dns search time=%d, domain(%s)",
myname, __LINE__, time(NULL) - dns_ctx->begin,
dns_ctx->domain_key);
while (1) {
ring_ptr = acl_ring_pop_head(&list->ring);
if (ring_ptr == NULL)
break;
list->nrefer--;
entry = ACL_RING_TO_APPL(ring_ptr, CLIENT_ENTRY, dns_entry);
entry->tm.dns_lookup = now - entry->tm.stamp;
entry->tm.stamp = now;
if (dns_ctx->ip_cnt <= 0) {
acl_msg_error("%s(%d): dns not found domain(%s)",
myname, __LINE__, dns_ctx->domain_key);
if (entry->client == NULL)
acl_msg_fatal("%s(%d): client null",
__FILE__, __LINE__);
if (entry->dns_errmsg) {
/* XXX: 因为此处可能会有两处关闭 client 流的地方:
* acl_aio_writen 及 forward_complete为防止重复
* 关闭造成的内存访问非法,需要 * 在第一个可能关闭
* 的函数(acl_aio_writen)调用前提升 client 流的
* 引用值,并且在该函数返回后再恢复引用值
*/
acl_aio_refer(entry->client);
acl_aio_writen(entry->client, entry->dns_errmsg,
(int) strlen(entry->dns_errmsg));
/* 恢复refer值 */
acl_aio_unrefer(entry->client);
}
entry->nslookup_notify_fn(entry, NSLOOKUP_ERR);
continue;
}
if (acl_do_debug(20, 2)) {
int i;
for (i = 0; i < dns_ctx->ip_cnt; i++)
acl_msg_info("%s(%d): domain(%s), ip%d(%s)",
myname, __LINE__, dns_ctx->domain_key,
i, dns_ctx->ip[i]);
}
/* 将查得的所有DNS结果都拷贝至请求对象中 */
memcpy(&entry->dns_ctx, dns_ctx, sizeof(entry->dns_ctx));
/* 下面注释部分被打开,便可以测试连接重试功能:)-- zsx, 2008.2.28
* strcpy(proxy_entry->dns_ctx.ip[1], proxy_entry->dns_ctx.ip[0]);
* strcpy(proxy_entry->dns_ctx.ip[0], "127.0.0.1");
* if (proxy_entry->dns_ctx.ip_cnt < 2)
* proxy_entry->dns_ctx.ip_cnt = 2;
*/
entry->ip_idx = 0;
entry->nslookup_notify_fn(entry, NSLOOKUP_OK);
}
acl_htable_delete(service->dns_table, dns_ctx->domain_key, NULL);
if (list->nrefer <= 0)
acl_myfree(list);
else
acl_msg_fatal("%s(%d): list's nrefer=%d",
myname, __LINE__, list->nrefer);
}
/* 查询DNS信息采用外挂模块的方式 */
static void thrpool_nslookup(SERVICE *service, CLIENT_ENTRY *entry,
const char *domain, int port)
{
const char *myname = "thrpool_nslookup";
DNS_CTX dns_ctx;
DNS_RING *list;
char *ptr;
entry->tm.stamp = time(NULL);
memset(&dns_ctx, 0, sizeof(dns_ctx));
dns_ctx.begin = entry->tm.stamp;
STRNCPY(dns_ctx.domain_key, domain, sizeof(dns_ctx.domain_key));
ptr = strchr(dns_ctx.domain_key, ':');
/* 仅留下域名部分 */
if (ptr)
*ptr = 0;
entry->server_port = port;
if (entry->server_port <= 0)
entry->server_port = 80;
/* 将域名字符串都转换成小写,以便于进行哈希查询 */
acl_lowercase(dns_ctx.domain_key);
dns_ctx.context = service;
dns_ctx.callback = thrpool_nslookup_complete;
STRNCPY(entry->domain_key, dns_ctx.domain_key, sizeof(entry->domain_key));
/* 先查询DNS查询表中是否已经包含本次需要被查询的域名 */
list = (DNS_RING *) acl_htable_find(service->dns_table, dns_ctx.domain_key);
if (list) {
/* 将本次对同一域名的查询添加进同一个查询链中 */
acl_ring_prepend(&list->ring, &entry->dns_entry);
/* 将查询链对象的引用计数加1 */
list->nrefer++;
/* 如果该查询链已经存在,说明有查询任务等待返回,其返回后会一同将
* 本次任务进行触发,如果此处触发新任务,则会造成内存访问冲突,因为
* 查询DNS的过程是由一组线程池进行查询的。
* (void) dns_server_lookup(proxy_entry->aio_proxy->dns_server, &dns_ctx);
*/
return;
}
/* 创建一个新的查询链对象,并将本次查询任务加入该查询链中及将该查询链加入查询表中 */
list = (DNS_RING *) acl_mycalloc(1, sizeof(DNS_RING));
acl_ring_init(&list->ring);
STRNCPY(list->domain_key, dns_ctx.domain_key, sizeof(list->domain_key));
/* 将本次查询任务加入新的查询链中且将查询链的引用计数加1 */
acl_ring_prepend(&list->ring, &entry->dns_entry);
list->nrefer++;
/* 将新的查询链加入查询表中 */
if (acl_htable_enter(service->dns_table, list->domain_key, (char *) list) == NULL)
acl_msg_fatal("%s: add domain(%s) to table error", myname, list->domain_key);
/* 开始启动DNS查询过程 */
(void) dns_server_lookup(service->dns_server, &dns_ctx);
}
/*----------------------------------------------------------------------------*/
void dns_lookup(CLIENT_ENTRY *entry, const char *domain, int port)
{
const char *myname = "dns_lookup";
SERVICE *service = entry->service;
if (acl_ipv4_valid(domain)) {
entry->ip_idx = 0;
ACL_SAFE_STRNCPY(entry->dns_ctx.ip[0], domain,
sizeof(entry->dns_ctx.ip[0]));
entry->dns_ctx.port[0] = port;
entry->dns_ctx.ip_cnt = 1;
entry->nslookup_notify_fn(entry, NSLOOKUP_OK);
return;
} else if (service->dns_handle) {
inner_nslookup(service, entry, domain, port);
} else if (service->dns_server) {
thrpool_nslookup(service, entry, domain, port);
} else {
acl_msg_fatal("%s(%d): no dns lookup set", myname, __LINE__);
}
}