#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__); } }