📄 dns_client_reply.c
字号:
/* ############################################################################ (c) Copyright Virata Limited 2001 ## Virata Limited Confidential and Proprietary## The following software source code ("Software") is strictly confidential and# is proprietary to Virata Limited ("Virata"). It may only be read, used,# copied, adapted, modified or otherwise dealt with by you if you have# entered into a confidentiality agreement with Virata and then subject to the# terms of that confidentiality agreement and any other applicable agreement# between you and Virata. If you are in any doubt as to whether you are # entitled to access, read, use, copy, adapt, modify or otherwise deal with# the Software or whether you are entitled to disclose the Software to any# other person you should contact Virata. If you have not entered into a# confidentiality agreement with Virata granting access to this Software you# should forthwith return all media, copies and printed listings containing# the Software to Virata. ## Virata reserves the right to take legal action against you should you breach# the above provisions.## If you are unsure, or to report violations, please contact # support@virata.com# ##########################################################################*//* * file: dns_client_reply.c * * description: this file contains routines for handling the reply from * the NS. (doesn't contain the parsing routines). * */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <errno.h>#include "messages.h"#include "dns_client.h"#include "dns_client_query.h"#include "dns_client_resource.h"#include "dns_client_cache.h"#include "dns_client_util6.h"#include "dns_client_statics.h"#ifdef DNS_DEBUGextern int dns_verbosity;#endifstatic int verify_dns_response(QueryState *qstate);static int find_replaceable_ns(QueryState *qstate, Dcmp *domain, Dcmp *new_ns);static int validate_domain(Dcmp *domain, Dcmp *nsdomain);static int find_ns_list(QueryState *qstate, NS_t *ns, int numns);static int find_ns_address(const char *name, DnsRRec *rlist, NS_t *ns, BOOL cache);static DnsRRec *find_ns_record(QueryState *qstate, Dcmp *domain, DnsMsg *dmsg);static int dns_negative_replycheck(QueryState *query);static void dns_ns_reply(QueryState *query);static void dns_cname_reply(QueryState *query);static void dns_cname6_reply(QueryState *query);static void cname6_reply(QueryState *query);#ifdef DNS_HAVE_IP6STACKstatic void ns6_reply(QueryState *query);#endifstatic int dns_XX_negative_replycheck(QueryState *query); /* * verify_dns_response() * * this routine will do some verification on the DNS message. there's no * need to check the message id since we used that to match the message to * the query. the DNS reply should have the question we sent to the name * server. * * return: 0 successful verification, * -1 indicates discard the message, */static intverify_dns_response(QueryState *qstate){ DnsHdr *dnshdr; DnsQuestion *quest;#ifdef DNS_DEBUG U16 error;#endif DNSC_DEBUG("entry\n"); /* RFC 1035 suggests verifing the header. we verify that this is a * reply and not a query. (RFC 1035 section 7.3) */ dnshdr = &qstate->dnsreply.m_dnshdr; if ((dnshdr->flags & DNS_QR) != DNS_QR) { dprintf("%C DNS message not a reply, discarding (flags = 0x%X)\n", dnshdr->flags); DNSC_DEBUG("exit - failure - DNS message has no question\n"); return -1; } /* the DNS reply should have the question we asked. we check that there * is a question and the domain of the question is correct. */ if ( 0 == dnshdr->nquest ) { dprintf("%C DNS message has no question, discarding\n");#ifdef DNS_DEBUG print_dns_header(dnshdr);#endif DNSC_DEBUG("exit - failure - DNS message has no question\n"); return -1; } quest = qstate->dnsreply.m_questions; if ( strcmp(quest->q_name, qstate->domain) != 0 ) { dprintf("%C domain mismatch for DNS reply\n");#ifdef DNS_DEBUG dprintf("expected %s received %s\n", qstate->domain, quest->q_name);#endif DNSC_DEBUG("exit - failure - domain mismatch for DNS reply\n"); return -1; } /* okay it seems to be a valid reply. check to see if there were * errors. we don't check for the name error here. */ if ((dnshdr->flags & DNS_RCODE) != DNS_NOERROR) { /* don't handle name errors here */ if ((dnshdr->flags & DNS_RCODE) == DNS_NAMEERR) { DNSC_DEBUG("exit - success - nameerr\n"); return 0; } /* If the name server replies with the response code as 'refuse' then we do not want to retry that name server for this query. So in this case we set the number of tries to RES_RETRY to avoid any retries. */ if ((dnshdr->flags & DNS_RCODE) == DNS_REFUSEERR) { int index = qstate->addr_index; qstate->ns_addrs[index].ns_try = RES_RETRY; }#ifdef DNS_DEBUG error = dnshdr->flags & DNS_RCODE; switch(error) { case DNS_FMTERR: dprintf("Name server returned - format error\n"); break; case DNS_SVRFAILERR: dprintf("Name server returned - server failure (internal error)\n"); break; case DNS_NOTIMPERR: dprintf("Name server returned - not implemented\n"); break; case DNS_REFUSEERR: dprintf("Name server returned - refused (server refused to perform query\n"); break; } print_dns_header(dnshdr);#endif DNSC_DEBUG("exit - failure\n"); return -1; } DNSC_DEBUG("exit - success\n"); return 0;} /* end verify_dns_response() *//* * dns_negative_replycheck() * * this routine will check for a negative reply. we expect the message has * already been verified. we check to see if the flag field indicates a * name error, and we also check for the no data case. * * return: 0 indicates alls well, no error and the no data is not implied, * -1 indicates that no data is implied, and * -2 indicates the no name error. */static intdns_negative_replycheck(QueryState *query){ DnsMsg *dnsmsg; DnsRRec *rr; DNSC_DEBUG("entry\n"); /* name error replies will have the rcode section of the flags field * set to the name error. */ dnsmsg = &query->dnsreply; if ((dnsmsg->m_dnshdr.flags & DNS_RCODE) == DNS_NAMEERR) { DNSC_DEBUG("exit - failure - no name error\n"); return -2; /* name error reply */ } /* * no data replies have the rcode section set to no error. the reply * will have "no relevant answers in the answer section. the authority * section will contain an SOA record, or there will be no NS records * there", see section 2.2 of RFC 2308. * * we assume the DNS message has already been verified so if there is * no name error then there is no errors given in the flag field. first * see if there are any "relevant" answer records. relevant in this * case means no A records for our domain. then see if there is an * SOA record or there are no NS records in the authority section. */ if ( find_num_match(query->domain, dnsmsg->m_answers, QT_A) == 0 && find_num_match(query->domain, dnsmsg->m_answers, QT_PTR) == 0 && find_num_match(query->domain, dnsmsg->m_answers, QT_A6) == 0 && find_num_match(query->domain, dnsmsg->m_answers, QT_AAAA) == 0 && find_num_match(query->domain, dnsmsg->m_answers, QT_CNAME) == 0) { rr = dnsmsg->m_authoritys; if ( query->qtype == aaaa_type || /* We need to send A6 */ find_num_record(rr, QT_SOA) > 0 || find_num_record(rr, QT_NS) == 0 ) { DNSC_DEBUG("exit - failure - no data is implied\n"); return -1; } } DNSC_DEBUG("exit - success\n"); return 0;} /* end dns_negative_replycheck() *//* dns_response_ptr6_query -- * This routine handles DNS responses for PTR queries. * * PARAMETERS * query - query state structure * * RETURNS * N/A */voiddns_response_ptr6_query(IN QueryState *query){ DnsMsg *dnsmsg; DnsRRec *ansRRs, *nsRR; int nrc; /* verify return code */ DNSC_DEBUG("entry\n"); /* * First verify we have a valid message. This means we have an answer of * some type. If the message isn't valid handle it like it was a timeout. * note verify_dns_response() checks the flags in the DNS header. The flag * indicates if there was an error. We use dns_negative_replycheck() to * check for no name error flags and the no data case. */ if (verify_dns_response(query) < 0) { DNSC_TRACE("DNS reply not valid for pointer query\n");#ifdef DNS_DEBUG print_dns_message(&query->dnsreply); #endif dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query); DNSC_DEBUG("exit - verify_dns_response failed\n"); return; } if ((nrc = dns_negative_replycheck(query)) < 0) { MSG_D_DNS_GET_HOSTBYADDR6(dmsg, query->reply); if ( nrc == -2 ) { dns_update_resolver_cache(&query->dnsreply); dmsg->error = EHOSTNOTFOUND; } else dmsg->error = ENODATA; /* we don't cache no data */ sendreply(query->reply); dnsmsg_cleanup(&query->dnsreply); free_querystate(query);#ifdef DNS_DEBUG print_responding_server(query); print_dns_message(&query->dnsreply); #endif DNSC_DEBUG("exit - dns_negative_replycheck failed\n"); return; } /* * See if there is an answer record that has the host name we're looking * for. If there is then we've got our answer. We format the results * and send them back. We want to update the cache with any useful stuff * and to clean up the DNS response and to free the query state. */ dnsmsg = &query->dnsreply; ansRRs = dnsmsg->m_answers; if (find_match_rrecord(query->domain, ansRRs, QT_PTR) != NULL) { dns_fmt_ptr6_answer(query, &query->dnsreply); dns_update_resolver_cache(&query->dnsreply); sendreply(query->reply);#ifdef DNS_DEBUG if ( dns_verbosity > 0 ) { print_responding_server(query); print_dns_message(&query->dnsreply); }#endif dnsmsg_cleanup(&query->dnsreply); free_querystate(query); DNSC_DEBUG("exit - find_match_rrecord success\n"); return; } /* * Didn't have the answer. So, if we find an NS record in the answer section * the reply is telling us to try another name server. Let dns_ns_reply() * handle this case, other wise we clean up the parsed message and handle * things like we had a time out. Note that if there are no answers and there * are authority records we consider that a referral, see the examples in * section 2.2 of RFC 2308. */ nsRR = dnsmsg->m_authoritys; if (find_num_record(ansRRs, QT_NS) > 0 || find_num_record(nsRR, QT_NS) > 0) { DNSC_DEBUG("exit - call dns_ns_reply\n"); return dns_ns_reply(query); } dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query); DNSC_DEBUG("exit\n"); } /* dns_response_ptr6_query *//* * dns_response_ptr_query() * * this routine handles DNS responses for PTR queries, that is we have the IP * address and are looking for the host name. we first verify the response using * verify_dns_response(). this will indicate there is at least one answer record. * we expect either the PTR record or a NS record telling us to try another name * server. anything else is considered an error. * * NOTE: in the case of PTR queries we know ahead of time that this query will * not be helping another query. that is trying to get the address of a name * server or a CNAME or something like that. so we don't have to deal with that * case and can just format our response to the user if we get the answer. */voiddns_response_ptr_query(QueryState *query){ DnsMsg *dnsmsg; DnsRRec *ansRRs, *nsRR; int nrc; /* verify return code */ DNSC_DEBUG("entry\n"); /* * first verify we have a valid message. this means we have an answer of * some type. if the message isn't valid handle it like it was a timeout. * note verify_dns_response() checks the flags in the DNS header. the flag * indicates if there was an error. we use dns_negative_replycheck() to * check for no name error flags and the no data case. */ if ( verify_dns_response(query) < 0 ) { dprintf("%C DNS reply not valid for pointer query\n");#ifdef DNS_DEBUG print_dns_message(&query->dnsreply); #endif dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query); return; } if ((nrc = dns_negative_replycheck(query)) < 0) { MSG_D_DNS_GET_HOSTBYADDR(dmsg, query->reply); if ( nrc == -2 ) { dns_update_resolver_cache(&query->dnsreply); dmsg->error = EHOSTNOTFOUND; } else dmsg->error = ENODATA; /* we don't cache no data */ sendreply(query->reply); dnsmsg_cleanup(&query->dnsreply); free_querystate(query);#ifdef DNS_DEBUG print_responding_server(query); print_dns_message(&query->dnsreply); #endif return; } /* * see if there is an answer record that has the host name we're looking * for. if there is then we've got our answer. we format the results * and send them back. we want to update the cache with any useful stuff * and to clean up the DNS response and to free the query state. */ dnsmsg = &query->dnsreply; ansRRs = dnsmsg->m_answers; if ( find_match_rrecord(query->domain, ansRRs, QT_PTR) != NULL) { fmt_ptr_answer(query, &query->dnsreply); dns_update_resolver_cache(&query->dnsreply); sendreply(query->reply);#ifdef DNS_DEBUG if ( dns_verbosity > 0 ) { print_responding_server(query); print_dns_message(&query->dnsreply); }#endif dnsmsg_cleanup(&query->dnsreply); free_querystate(query); return; } /* * didn't have the answer. so, if we find an NS record in the answer section * the reply is telling us to try another name server. let dns_ns_reply() * handle this case, other wise we clean up the parsed message and handle * things like we had a time out. note that if there are no answers and there * are authority records we consider that a referral, see the examples in * section 2.2 of RFC 2308. */ nsRR = dnsmsg->m_authoritys; if ( find_num_record(ansRRs, QT_NS) > 0 || find_num_record(nsRR, QT_NS) > 0 ) return dns_ns_reply(query); dnsmsg_cleanup(&query->dnsreply); dns_response_timeout(query);} /* end dns_response_ptr_query() *//* dns_response_std6_query -- * This routine handles responses for standard AAAA or A6 queries. * * PARAMETERS * query - query state structure * * RETURNS * N/A */voiddns_response_std6_query(IN QueryState *query){ DnsMsg *dnsmsg; DnsRRec *rr, *ns; int nrc; DNSC_DEBUG("entry\n"); if (verify_dns_response(query) < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -