mirror of
https://gitee.com/acl-dev/acl.git
synced 2024-12-13 16:35:35 +08:00
270 lines
7.6 KiB
C
270 lines
7.6 KiB
C
#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__);
|
||
}
|
||
}
|