📄 dns_client_reply.c
字号:
} /* * nothing in the cache, so we need to construct a new query for the * CNAME. cache any useful information, it could come in handy when * the new query creates its name server list. if we already have * an active query outstanding, clean up and exit. we only want one * outstanding query at a time. */ dns_add_AA2cache(dnsmsg, 0); if ( query->nxquery != NULL ) { dnsmsg_cleanup(dnsmsg); return; } /* * we also have a limit on how many queries we allow for a particular * request. if a new query will exceed that limit, then we don't do * it. */ if ( (query->nquerys + 1) > MAXQUERYDEPTH ) { dnsmsg_cleanup(dnsmsg); dns_response_timeout(query); return; } /* * so, looks like we're going to do the new query. get a dns_hostent * structure and an ATMOS_MESSAGE. fill in the necessary information * to indicate its a query for a cname and kick off the query. */ if ((hp = dns_hent_alloc()) == NULL) { dprintf("%C no free dns_hostent structures for internal query\n");#ifdef DNS_DEBUG print_resource_pool();#endif dnsmsg_cleanup(dnsmsg); dns_response_timeout(query); return; } hp->hent.h_numaddrs = ENTADDRS; /* this is a set amount */ qinfo.qtype = addr_t; strcpy(hp->name, cRR->r_data); qinfo.hostname = hp->name; qinfo.cache_flag = query->use_cache; qinfo.qcnt = ++query->nquerys; qinfo.amsg = &hp->amsg; qinfo.hptr = &hp->hent; if ( start_new_query(&qinfo) < 0 ) { dprintf("%C: attempt to start internal query failed\n"); dns_hent_free(hp); dnsmsg_cleanup(dnsmsg); dns_response_timeout(query); return; } query->nxquery = &hp->amsg; /* we use this to find this query later */ query->qstate = wf_cqaddr; query->dns_qreply = cname_reply; dnsmsg_cleanup(dnsmsg); /* clean up */ return; } /* we have at least one matching record. so we've got our answer. handle * formating and such. */ fmt_cname_answer(query, &query->dnsreply);#ifdef DNS_DEBUG if ( dns_verbosity > 0 ) { print_responding_server(query); print_dns_message(&query->dnsreply); }#endif dns_update_resolver_cache(&query->dnsreply); sendreply(query->reply); dnsmsg_cleanup(&query->dnsreply); /* be sure to clean up */ free_querystate(query); /* and to free the query state structure */} /* end dns_cname_reply() *//* * dns_ns_reply() * * this routine is called when the name server sent us a reply that suggests we * use a different name server for our query. if the name server sent us the * address of the name server it suggests we try, then all is simple. we check * to see if the new name server is better than the ones we already have in our * list and if so we add it to our list and continue as if we had timed out * waiting for a reply. name servers can send other name servers to try in the * authority (or NS) records. we need to check through those also. * * first we try to get a list of name servers from the DNS message. if we do, * we add that to our current list or replace elements of the current list. if * that's not successful, it means either we don't have addresses for the new * servers or they're not good servers to use. rather than just go and try to * get an address for a server we see if we've already tried all in the current * list. if we haven't, we forget the whole thing. one of the servers in our * current list my be able to give us the results we want so we don't just go * and start another query to get a name server address. if we have, then we find * the first good new name server and start a query to get its address. * * this routine is expensive. worst case not including any cache search: * find_ns_list() is O(n*N) where n = all the records in both answer and * NS and N is all the records in the additional section. since n ~ N * the routine is O(n**2). but normally the name server will send us the * addresses of the name servers it thinks we should try so it won't be a * problem. */static voiddns_ns_reply(QueryState *query){ Dcmp domain; Dcmp new_ns; NS_t nslist[MAXNS]; DnsRRec *RR; int i, index, nums; Qinfo_t qinfo; nslook_t *hp; DNSC_DEBUG("entry\n"); /* * try to construct a list of name servers to try. note that we don't just * look at the name server suggested in the answer records, we also look at * the NS records if there are any. */ domain.dname = query->domain; domain.dparts = count_domain_parts(query->domain); if ((nums = find_ns_list(query, nslist, MAXNS)) > 0) { for ( i = 0; i < nums; i++ ) { new_ns.dname = nslist[i].ns_name; new_ns.dparts = count_domain_parts(nslist[i].ns_name); if ((index = find_replaceable_ns(query, &domain, &new_ns)) >= 0) { strcpy(query->ns_addrs[index].ns_name, nslist[i].ns_name); query->ns_addrs[index].ns_addr = nslist[i].ns_addr; query->ns_addrs[index].ns_error = 0; query->ns_addrs[index].ns_recursion = 0; query->ns_addrs[index].ns_try = 0; if ( index >= query->num_ns ) query->num_ns = index + 1; } } /* since we know there are name servers and addresses, we update the * cache with this information. */ dns_update_resolver_cache(&query->dnsreply); dnsmsg_cleanup(&query->dnsreply); /* don't need this any more */ dns_response_timeout(query); /* do query with next server */ DNSC_DEBUG("exit\n"); return; } /* * if we get here then there may be some name servers suggested in the * DNS response but the server wasn't kind enough to send us the addresses * along with them. so, if we haven't already tried all of the name servers * in our current list at least once, we ignore the "delegation". */ if ( ns_list_complete(query->ns_addrs, query->num_ns) == FALSE ) { dnsmsg_cleanup(&query->dnsreply); /* don't need this any more */ dns_response_timeout(query); /* do query with next server */ DNSC_DEBUG("exit\n"); return; } /* * if we've already tried all the servers in our current list then we have * to try to acquire the address of a suggested name server, that is if any * of them are worth trying. we get the first one that is valid and set up * a query attempt for it. */ if ((RR = find_ns_record(query, &domain, &query->dnsreply)) == NULL) { dnsmsg_cleanup(&query->dnsreply); /* don't need this any more */ dns_response_timeout(query); /* do query with next server */ DNSC_DEBUG("exit\n"); return; } /* * okay, let's try to start a new query for the address of the suggessted * name server. first we have to do a couple of checks. if we already * have a outstanding internal query, we don't start another. and we have * a limit on the number of queries that can be done for a particular host. * if these tests pass we get a dns_hostent structure and set up for the new * query. */ if ( query->nxquery != NULL ) { dnsmsg_cleanup(&query->dnsreply); return; } if ( (query->nquerys + 1) > MAXQUERYDEPTH ) { dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query); return; } /* okay we've verified its okay to start another query, we get a dns_hostent * structure and an ATMOS_MESSAGE. */ /* * pab: here we must first ask an IPv6 address of NS */ if ((hp = dns_hent_alloc()) == NULL) { DNSC_TRACE("no free dns_hostent structures for internal NS query\n");#ifdef DNS_DEBUG print_resource_pool();#endif dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query); DNSC_DEBUG("exit - dns_hent_alloc failed\n"); return; } /* fill in the structure and kick off the new query. */#ifdef DNS_HAVE_IP6STACK qinfo.qtype = aaaa_t; qinfo.cache_flag = TRUE;#else hp->hent.h_numaddrs = ENTADDRS; /* set for the max amount */ qinfo.qtype = addr_t; qinfo.hptr = &hp->hent;#endif /* DNS_HAVE_IP6STACK */ strcpy(hp->name, RR->r_data); qinfo.hostname = hp->name; qinfo.qcnt = ++query->nquerys; qinfo.amsg = &hp->amsg; if ( start_new_query(&qinfo) < 0 ) { dprintf("%C; attempt to start internal NS query failed\n"); dns_hent_free(hp); dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query); DNSC_DEBUG("exit - start_new_query failed\n"); return; } query->nxquery = &hp->amsg; /* we use this to find this query later */#ifdef DNS_HAVE_IP6STACK query->qstate = wf_nsaddr6; query->dns_qreply = ns6_reply; /* set the handler */#else query->qstate = wf_nsaddr; query->dns_qreply = ns_reply; /* set the handler */#endif /* DNS_HAVE_IP6STACK */ dnsmsg_cleanup(&query->dnsreply); DNSC_DEBUG("exit - start_new_query passed\n"); return;} /* end dns_ns_reply() *//* * find_replaceable_ns() * * each query maintains a list of NS to use. if an NS reply indicates another * to use, we have to see which one in our current list should be replaced. * if the current NS list is not full, then we can just add the new name server * to the end of the list. if there is a member of the current list that does * not have a name, which indicates its a default server, we assume the new one * is better and will replace the first default name server in the list. otherwise * we need to compare the domains. * * return: NS list index of the name server to replace, or * -1 if none should be replaced. */static intfind_replaceable_ns(QueryState *qstate, Dcmp *domain, Dcmp *new_ns){ NS_t *nlist; Dcmp current; int i; DNSC_DEBUG("entry\n"); /* if the list of servers is not full, just stick the new server * at the end of the list. */ if ( qstate->num_ns < MAXNS ) return qstate->num_ns; /* * loop through the list. if a name server entry doesn't have a name * then its a default name server and we assume the new name server * is better. */ nlist = qstate->ns_addrs; for ( i = 0; i < qstate->num_ns; i++ ) { if ( nlist[i].ns_name[0] == '\0' ) /* no name indicates a default server */ return i; current.dname = nlist[i].ns_name; current.dparts = count_domain_parts(nlist[i].ns_name); if ( domain_compare(domain, ¤t, new_ns) == 1 ) return i; } return -1;} /* end find_replaceable_ns() *//* * validate_domain() * * this routine is used to see if a domain name from an NS record is the same as * the domain for the host name we're doing our search for. what we do here is to * look at the last so many parts of the domain names. if MINMATCHDOMAIN is 2 then * we just check the last two parts. for example if the host name we're searching * for is host.domain.com we just compare domain.com. this is just verifying the * suggested NS is reasonable, not that its the best choice, (see RFC 1035). * * return: 0 is a domain match, * -1 will indicate an error or not a match. */static intvalidate_domain(Dcmp *dm, Dcmp *nsdm){ const char *dptr; /* pointer to just the domain part of our domain name */ const char *nptr; /* pointer to the same number parts of the NS name */ DNSC_DEBUG("entry\n"); if ( nsdm->dparts < MINMATCHDOMAIN ) /* must match at least this many */ return -1; if ((dptr = get_last_domain_parts(dm->dname, dm->dparts, MINMATCHDOMAIN)) == NULL) { dprintf("%C: bad domain format %s and %s\n", dm->dname, nsdm->dname); return -1; } if ((nptr = get_last_domain_parts(nsdm->dname, nsdm->dparts, MINMATCHDOMAIN)) == NULL) { dprintf("%C: bad domain format %s and %s\n", dm->dname, nsdm->dname); return -1; } if ( strcmp(dptr, nptr) == 0 ) return 0; return -1;} /* end validate_domain() *//* * find_ns_list() * * this routine will try to construct a list of name servers and their addresses. * we attempt to find the number requested and return the number we actually find. * * return: the number of name server address pairs. */static intfind_ns_list(QueryState *qstate, NS_t *ns, int numns){ DnsRRec *RR, *addrr, *ans; DnsMsg *dnsmsg; Dcmp dcmp, nscmp; int cnt = 0; DNSC_DEBUG("entry\n"); dnsmsg = &qstate->dnsreply; dcmp.dname = qstate->domain; dcmp.dparts = count_domain_parts(qstate->domain); ans = RR = dnsmsg->m_answers; addrr = dnsmsg->m_additionals; while ( RR != NULL && RR->r_type == QT_NS && cnt < numns ) { nscmp.dname = RR->r_data; nscmp.dparts = count_domain_parts(RR->r_data); if ( validate_domain(&dcmp, &nscmp) < 0 ) { RR = RR->r_next; continue; } if ( find_replaceable_ns(qstate, &dcmp, &nscmp) < 0 ) { RR = RR->r_next; continue; } /* check both the answer records and the additional records for the address */ if ( find_ns_address(RR->r_data, ans, &ns[cnt], qstate->use_cache) == 0 ) ++cnt; else if ( find_ns_address(RR->r_data, addrr, &ns[cnt], qstate->use_cache) == 0 ) ++cnt; RR = RR->r_next; } RR = dnsmsg->m_authoritys; while ( RR != NULL && RR->r_type == QT_NS && cnt < numns ) { nscmp.dname = RR->r_data; nscmp.dparts = count_domain_parts(RR->r_data); if ( validate_domain(&dcmp, &nscmp) < 0 ) { RR = RR->r_next; continue; } if ( find_replaceable_ns(qstate, &dcmp, &nscmp) < 0 ) { RR = RR->r_next; continue; } if ( find_ns_address(RR->r_data, addrr, &ns[cnt], qstate->use_cache) == 0 ) ++cnt; RR = RR->r_next; } return cnt;} /* end find_ns_list() */ /* * find_ns_address() * * this routine takes the name of a name server and trys to find the matching * address record is the list passed in. if not in the list and its okay, we'll * try the cache. if we find the address, we fill in the NS_address structure * if not we leave the structure unchanged. * * return: 0 means we found the address, * -1 otherwise. */static intfind_ns_address(const char *name, DnsRRec *rlist, NS_t *ns, BOOL cache){ DnsRRec *RR; DNSC_DEBUG("entry\n"); if (((RR = find_match_rrecord(name, rlist, QT_AAAA)) != NULL) || ((RR = find_match_rrecord(name, rlist, QT_A6)) != NULL)) { strcpy(ns->ns_name, name); data2sock(&ns->ns_addr, RR->r_data); return 0; } if ((RR = find_match_rrecord(name, rlist, QT_A)) != NULL) { strcpy(ns->ns_name, name); long2sock(&ns->ns_addr, *(U32 *)RR->r_data); return 0; } if ( TRUE == cache && dns_query_cache_ns(name, ns) == 0 ) return 0; return -1;} /* end find_ns_address() */ /* * find_ns_record() * * this routine will look through the answer records and the authority (NS) records * and return the first NS record that is valid and that is better than something * in our current list. * * return: pointer to the first NS record that meets our criteria, or * NULL if there isn't any. */static DnsRRec *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -