Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 53 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
53
Dung lượng
807,95 KB
Nội dung
datalen The size of the data buffer. If data is NULL, then datalen is zero. newrr A buffer used for the dynamic update code (covered in Chapter 10, Advanced Features and Security). Unless you are playing with this feature, it is always NULL. buf A buffer in which res_mkquery makes the query packet. It should be PACKETSZ or larger, just like the answer buffer in res_search and res_query. buflen The size of the buf buffer (e.g., PACKETSZ). res_mkquery returns the size of the query packet, or −1 if there was an error. int res_send(const u_char *msg, int msglen, u_char *answer, int anslen) res_send implements the retry algorithm. It sends the query packet, msg, in a UDP packet, but it can also send it over a TCP stream. The response packet is stored in answer. This routine, of all the resolver routines, is the only one to use black magic (unless you know all about connected datagram sockets). You've seen these arguments before in the other resolver routines: msg The buffer containing the DNS query packet msglen The size of the msg answer The buffer in which to store the DNS response packet anslen The size of the answer buffer res_send returns the size of the response, or −1 if there was an error. If this routine returns −1 and errno is ECONNREFUSED, then there is no name server running on the target name server host. You can look at errno to see if it is ECONNREFUSED after calling res_search or res_ query. (res_search calls res_query, which calls res_send.) If you want to check errno after calling res_query, then clear errno first. That way, you know the current call to res_send was the one that set errno. However, you DNS & BIND 14.2.4 The Resolver Library Routines 425 don't have to clear errno before calling res_search. res_search clears errno itself before calling res_query. int res_init(void) res_init reads resolv.conf and initializes a data structure called _res (more about that later). All of the previously discussed routines will call res_init if they detect that it hasn't been called previously. Or you can call it on your own; this is useful if you want to change some of the defaults before calling the first resolver library routine. If there are any lines in resolv.conf that res_init doesn't understand, it ignores them. res_init always returns zero, even if the manpage reserves the right to return −1. extern int h_errno; int herror(const char *s) herror is a routine like perror, except that it prints out a string based on the value of the external variable h_errno instead of errno. The only argument is: s A string used to identify the error message. If a string s is supplied, it is printed first, followed by ": " and then a string based on the value of h_errno. Here are the possible values of h_errno: HOST_NOT_FOUND The domain name does not exist. The return code in the name server response was NXDOMAIN. TRY_AGAIN Either the name server is not running, or the name server returned SERVFAIL. NO_RECOVERY Either the domain name could not be compressed because it was an invalid domain name (e.g., a name missing a label − movie.edu) or the name server returned FORMERR, NOTIMP, or REFUSED. NO_DATA The domain name exists, but there are no data of the requested type. NETDB_INTERNAL There was a library error unrelated to the network or name service. Instead, see errno for the problem description. 14.2.5 The _res Structure Each of the resolver routines (i.e., each routine whose name starts with res_) makes use of a common data structure called _res. You can change the behavior of the resolver routines by changing _res. If you want to change the number of times res_send retries a query, you can change the value of the retry field. If you want DNS & BIND 14.2.5 The _res Structure 426 to turn off the resolver search algorithm, you turn off the RES_DNSRCH bit from the options mask. You'll find the all−important _res structure in resolv.h: struct __res_state { int retrans; /* retransmission time interval */ int retry; /* number of times to retransmit */ u_long options; /* option flags − see below. */ int nscount; /* number of name servers */ struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */ #define nsaddr nsaddr_list[0] /* for backward compatibility */ u_short id; /* current packet id */ char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */ char defdname[MAXDNAME]; /* default domain */ u_long pfcode; /* RES_PRF_ flags − see below. */ unsigned ndots:4; /* threshold for initial abs. query */ unsigned nsort:4; /* number of elements in sort_list[] */ char unused[3]; struct { struct in_addr addr; /* address to sort on */ u_int32_t mask; } sort_list[MAXRESOLVSORT]; }; The options field is a simple bit mask of the enabled options. To turn on a feature, turn on the corresponding bit in the options field. Bit masks for each of the options are defined in resolv.h; the options are: RES_INIT If this bit is on, then res_init has been called. RES_DEBUG This bit causes resolver debugging messages to be printed, if the resolver routines were compiled with DEBUG, that is. Off is the default. RES_AAONLY Requires the answer to be authoritative, not from a name server's cache. It's too bad this isn't implemented; it would be a useful feature. With the BIND resolver's design, this feature would have to be implemented in the name server, and it's not. RES_PRIMARY Query the primary server only − again, it's not implemented. RES_USEVC Turn this bit on if you'd like the resolver to make its queries over a virtual circuit (TCP) connection instead of with UDP packets. As you might guess, there is a performance penalty for setting up and tearing down a TCP connection. Off is the default. RES_STAYOPEN DNS & BIND 14.2.5 The _res Structure 427 If you are making your queries over a TCP connection, turning this bit on causes the connection to be left open. Otherwise, the connection is torn down after the query has been answered. Off is the default. RES_IGNTC If the name server response has the truncation bit set, then the default resolver behavior is to retry the query using TCP. If this bit is turned on, then the truncation bit in the response packet is ignored and the query is not retried using TCP. Off is the default. RES_RECURSE The default behavior for the BIND resolver is to make recursive queries. Turning this bit off turns off the "recursion desired" bit in the query packet. On is the default. RES_DEFNAMES The default behavior for the BIND resolver is to append the default domain to names that do not have a dot in them. Turning this bit off turns off appending the default domain. On is the default. RES_DNSRCH The default behavior for the BIND resolver is to append each entry in the search list to a name that does not end in a dot. Turning this bit off turns off the search list function. On is the default. RES_INSECURE1 The default behavior for a 4.9.3 or later BIND resolver is to ignore answers from servers that were not queried. Turning this bit on disables this security check. Off (i.e., security check on) is the default. RES_INSECURE2 The default behavior for a 4.9.3 or later BIND resolver is to ignore answers where the question section of the response does not match the question section of the original query. Turning this bit on disables this security check. Off (i.e., security check on) is the default. RES_NOALIASES The default behavior for the BIND resolver is to use aliases defined in the file specified by the user's HOSTALIASES environment variable. Turning this bit on disables the HOSTALIASES feature for 4.9.3 and later BIND resolvers. Previous resolvers did not allow this feature to be disabled. Off is the default. 14.2.6 The Name Server Library Routines The name server library contains routines you need to parse response packets. Here are the header files you must include: #include <sys/types.h> #include <netinet/in.h> DNS & BIND 14.2.6 The Name Server Library Routines 428 #include <netdb.h> #include <arpa/nameser.h> #include <resolv.h> Here are the name server library routines: int ns_init_parse(const u_char *msg, int msglen, ns_msg *handle) ns_init_parse is the first routine you must call before you use the other name server library routines. ns_init_parse fills in the data structure pointed to by handle, which is a parameter passed to other routines. The arguments are: msg A pointer to the beginning of the response buffer msglen The size of the response buffer handle A pointer to a data structure filled in by ns_init_parse ns_init_parse returns zero on success and −1 when it fails to parse the response buffer. const u_char *ns_msg_base(ns_msg handle) const u_char *ns_msg_end(ns_msg handle) int ns_msg_size(ns_msg handle) These routines return a pointer to the start of the response, a pointer to the end of the response, and the size of the response. They are returning data you passed into ns_init_parse. The only argument is: handle A data structure filled in by ns_init_parse u_int16_t ns_msg_id(ns_msg handle) ns_msg_id returns the identification from the header section (described earlier) of the response packet. The only argument is: handle A data structure filled in by ns_init_parse. u_int16_t ns_msg_get_flag(ns_msg handle, ns_flag flag) ns_msg_get_flag returns the "flag" fields from the header section of the response packet. Its arguments are: handle DNS & BIND 14.2.6 The Name Server Library Routines 429 A data structure filled in by ns_init_parse. flag An enumerated type that can have the following values: ns_f_qr /* Question/Response */ ns_f_opcode /* Operation Code */ ns_f_aa /* Authoritative Answer */ ns_f_tc /* Truncation Occurred */ ns_f_rd /* Recursion Desired */ ns_f_ra /* recursion Available */ ns_f_rcode /* Response Code */ u_int16_t ns_msg_count(ns_msg handle, ns_sect section) ns_msg_count returns a counter from the header section of the response packet. Its arguments are: handle A data structure filled in by ns_init_parse. section An enumerated type that can have the following values: ns_s_qd /* Question section */ ns_s_an /* Answer section */ ns_s_ns /* Name Server section */ ns_s_ar /* Additional records sectiona */ int ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) ns_parserr extracts information about a response record and stores it in rr. rr is a parameter passed to other name server libarary routines. The arguments are: handle A pointer to a data structure filled in by ns_init_parse. section The same parameter described in ns_msg_count. rrnum A resource record number for the resource records in this section. Resource records start numbering at 0. ns_msg_count tells you how many resource records are in this section. rr DNS & BIND 14.2.6 The Name Server Library Routines 430 A pointer to a data structure to be initialized. ns_parserr returns zero on success and −1 when it fails to parse the response buffer. char *ns_rr_name(ns_rr rr) u_int16_t ns_rr_type(ns_rr rr) u_int16_t ns_rr_class(ns_rr rr) u_int32_t ns_rr_ttl(ns_rr rr) u_int16_t ns_rr_rdlen(ns_rr rr) const u_char *ns_rr_rdata(ns_rr rr) These routines return individual fields from a response record. Their only argument is: rr A data structure filled in by ns_parserr. int ns_name_compress(const char *exp_dn, u_char *comp_dn, size_t length, const u_char **dnptrs, const u_char **lastdnptr) ns_name_compress compresses a domain name. You won't normally call this routine yourself − you'll let res_mkquery do it for you. However, if you need to compress a name for some reason, this is the tool to do it. The arguments are: exp_dn The "expanded" domain name that you supply; i.e., a normal null−terminated string containing a fully qualified domain name. comp_dn The place where ns_name_compress will store the compressed domain name. length The size of the comp_dn buffer. dnptrs An array of pointers to previously compressed domain names. dnptrs[0] points to the beginning of the message; the list ends with a NULL pointer. After you've initialized dnptrs[0] to the beginning of the message and dnptrs[1] to NULL, dn_comp updates the list each time you call it. lastdnptr A pointer to the end of the dnptrs array. ns_name_compress needs to know where the end of the array is, so it doesn't overrun it. If you want to use this routine, look at how it is used in res/res_mkquery.c from the BIND source. It's often easier to see how to use a routine from an example than from an explanation. ns_name_compress returns the DNS & BIND 14.2.6 The Name Server Library Routines 431 size of the compressed name, or −1 if there was an error. int ns_name_uncompress(const u_char *msg, const u_char *eomorig, const u_char *comp_dn, char *exp_dn, size_t length) ns_name_uncompress expands a "compressed" domain name. You will use this routine if you parse a name server response, as we do in the example that follows. The arguments are: msg A pointer to the beginning of your response packet (message). eomorig A pointer to the first byte after the message. It is used to make sure that ns_name_uncompress doesn't go past the end of the message. comp_dn A pointer to the compressed domain name within the message. exp_dn The place where ns_name_uncompress will store the expanded name. You should always allocate an array of MAXDNAME characters for the expanded name. length The size of the exp_dn buffer. ns_name_uncompress returns the size of the compressed name, or −1 if there was an error. You might wonder why ns_name_uncompress returns the size of the compressed name, not the size of the expanded name. It does this because when you call ns_name_uncompress, you are parsing a DNS packet and need to know how much space the compressed name took in the packet so that you can skip over it. int ns_name_skip(const u_char **ptrptr, const u_char *eom) ns_name_skip is like ns_name_uncompress, but instead of uncompressing the name, it just skips over it. The arguments are: ptrptr A pointer to a pointer to the name to skip over. The original pointer is advanced past the name. eom A pointer to the first byte after the message. It is used to make sure that ns_name_skip doesn't go past the end of the message. DNS & BIND 14.2.6 The Name Server Library Routines 432 ns_name_skip returns zero if successful. It returns −1 when it fails to uncompress the name. u_int ns_get16(const u_char *cp) void ns_put16(u_int s, u_char *cp) The DNS packets have fields that are unsigned short integer (type, class, and data length, to name a few). ns_get16 returns a 16−bit integer pointed to by cp. ns_put16 assigns the 16−bit value of s to the location pointed to by cp. u_long ns_get32(const u_char *cp) void ns_put32(u_long l, u_char *cp) These routines are like their 16−bit counterparts, except that they deal with a 32−bit integer instead of a 16−bit integer. The TTL field (time to live) of a resource record is a 32−bit integer. 14.2.7 Parsing DNS Responses The easiest way to learn how to parse a DNS packet is to look at code that already does it. Assuming that you have the DNS source code, the best file to look through is res/res_debug.c (BIND 4) or src/lib/resolv/res_debug.c (BIND 8). This file has fp_query, the routine that prints out the DNS packets in the name server debugging output. Our sample program traces its parentage to code from this file. You won't always want to parse the DNS response manually. An "intermediate" way to parse the response is to call p_query, which calls fp_query, to print out the DNS packet. Then use basic UNIX tools, like Perl or awk, to grab what you need. Cricket has been known to wimp out this way. 14.2.8 A Sample Program: check_soa Here is a C program to solve the same problem that we wrote a shell script for earlier: /**************************************************************** * check_soa −− Retrieve the SOA record from each name server * * for a given domain and print out the serial number. * * * * usage: check_soa domain * * * * The following errors are reported: * * o There is no address for a server. * * o There is no server running on this host. * * o There was no response from a server. * * o The server is not authoritative for the domain. * * o The response had an error response code. * * o The response had more than one answer. * * o The response answer did not contain an SOA record. * * o The expansion of a compressed domain name failed. * ****************************************************************/ /* Various header files */ #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <errno.h> #include <arpa/nameser.h> DNS & BIND 14.2.7 Parsing DNS Responses 433 #include <resolv.h> /* Error variables */ extern int h_errno; /* for resolver errors */ extern int errno; /* general system errors */ /* Our own routines; code included later in this chapter */ void nsError(); /* report resolver errors */ void findNameServers(); /* find a domain's name servers */ void addNameServers(); /* add name servers to our list */ void queryNameServers(); /* grab SOA records from servers */ void returnCodeError(); /* report response packet errors */ /* Maximum number of name servers we will check */ #define MAX_NS 20 Here are the header files that are needed, the declarations for external variables, and the declarations of functions. Notice that we use both h_errno (for the resolver routines) and errno. We've set a limit of 20 name servers that this program will check. You will rarely see a zone with more than ten name servers, so an upper limit of 20 should suffice: main(argc, argv) int argc; char *argv[]; { char *nsList[MAX_NS]; /* list of name servers */ int nsNum = 0; /* number of name servers in list */ /* sanity check: one (and only one) argument? */ if(argc != 2){ (void) fprintf(stderr, "usage: %s domain\n", argv[0]); exit(1); } (void) res_init(); /* * Find the name servers for the domain. * The name servers are written into nsList. */ findNameServers(argv[1], nsList, &nsNum); /* * Query each name server for the domain's SOA record. * The name servers are read from nsList. */ queryNameServers(argv[1], nsList, nsNum); exit(0); } The main body of the program is small. We have an array of string pointers, nsList, to store the names of the name servers for the zone. We call the resolver function res_init to initialize the _res structure. It wasn't necessary for this program to call res_init explicitly, since it would have been called by the first resolver routine that used the _res structure. However, if we had wanted to modify the value of any of the _res fields before calling the first resolver routine, we would have made the modifications right after calling res_init. Next, the program calls findNameServers to find all the name servers for the zone referenced in argv[1] and to store them in nsList. Last, the program calls queryNameServers to query each of the name servers in DNS & BIND 14.2.7 Parsing DNS Responses 434 [...]... Records DNSand Internet Firewalls Dialup Connections Network Names and Numbers Additional Resource Records DNS Versus X.500 DNS and WINS "The time has come," the Walrus said, "To talk of many things: Of shoes − and ships − and sealing−wax − Of cabbages − and kings − And why the sea is boiling hot − And whether pigs have wings." It's time we tied up loose ends We've already covered the mainstream of DNS and. .. faster 14.2.7 Parsing DNS Responses 442 DNS & BIND 14.1 Shell Script Programming with nslookup 14.3 Perl Programming with Net: :DNS [ Library Home | DNS & BIND | TCP/IP | sendmail | sendmail Reference | Firewalls | Practical Security ] 14.2.7 Parsing DNS Responses 443 DNS & BIND Chapter 14 Programming with the Resolver and Name Server Library Routines 14.3 Perl Programming with Net: :DNS If using the shell... it will travel across the Atlantic to the U.S and back, because there is nothing in the MX list to indicate that the French relay is closer to that sender 15.2 Wildcards 15.4 DNS and Internet Firewalls [ Library Home | DNS & BIND | TCP/IP | sendmail | sendmail Reference | Firewalls | Practical Security ] 455 DNS & BIND Chapter 15 Miscellaneous 15.4 DNS and Internet Firewalls The Domain Name System... to work with Internet firewalls It's a testimony to the flexibility of DNS and of the BIND implementation that you can configure DNS to work with, or even through, an Internet firewall That said, configuring BIND to work in a firewalled environment, although not difficult, takes a good, complete understanding of DNS and a few of BIND' s more obscure features Describing it also requires a large portion... Parsing DNS Responses 439 DNS & BIND } /* * Initialize a handle to this response The handle will * be used later to extract information from the response */ if (ns_initparse(response.buf, responseLen, &handle) < 0) { fprintf(stderr, "ns_initparse: %s\n", strerror(errno)); return; } /* * If the response reports an error, issue a message * and proceed to the next server in the list */ if(ns_msg_getflag(handle,... section of a DNS packet is returned as a list of Net: :DNS: :Question objects You can find the name, type, and class of a question object with the following methods: $question−>qname $question−>qtype $question−>qclass 14.3.5 Resource Record Objects The answer, authority, and additional sections of a DNS packet are returned as lists of Net: :DNS: :RR objects You can find the name, type, class, and TTL of... program using the resolver and name server routines in libc: % cc −o check_soa check_soa.c Or, if you've newly ported the BIND code as we describe in Appendix B, Compiling and Installing BIND on a Sun, and want to use the latest header files and resolver library: % cc −o check_soa \ −I/tmp/src/include \ −I/tmp/src/include/port/solaris/include \ check_soa.c \ /tmp/src/lib/libbind.a Here is what the output... programmer should, we test malloc's return value, * and quit if it fails */ nsList[*nsNum] = (char *) malloc (MAXDNAME); if(nsList[*nsNum] == NULL){ (void) fprintf(stderr, "malloc failed\n"); exit(1); } /* Expand the name server's name */ if (ns_name_uncompress( ns_msg_base(handle),/* Start of the packet 14.2.7 Parsing DNS Responses */ 436 DNS & BIND ns_msg_end(handle), /* End of the packet */ ns_rr_rdata(rr),... parse nslookup's output seems too awkward and writing a C program seems too complicated, consider writing your program in Perl using the Net: :DNS module written by Michael Fuhr You'll find the package at http://www.perl.com/CPAN−local/modules/by−module/Net/ Net: :DNS treats resolvers, DNS packets, sections of DNS packets, and individual resource records as objects and provides methods for setting or querying... whether it's TCP or UDP), the source and destination IP address, and the destination port (see Figure 15.1) 456 DNS & BIND Figure 15.1: Packet filters operate at the network and transport layers of the stack What's most important to us about packet filtering firewalls is that you can typically configure them to allow DNS traffic selectively between hosts on the Internet and your internal hosts That is, . nsList[nsNum]); } continue; /* nsNum for−loop */ DNS & BIND 14.2.7 Parsing DNS Responses 4 39 } /* * Initialize a handle to this response. The handle will * be used later to extract information. ran much faster. DNS & BIND 14.2.7 Parsing DNS Responses 442 14.1 Shell Script Programming with nslookup 14.3 Perl Programming with Net: :DNS [ Library Home | DNS & BIND | TCP/IP | sendmail. saved from _res */ DNS & BIND 14.2.7 Parsing DNS Responses 437 struct hostent *host; /* structure for looking up ns addr */ int i; /* counter variable */ ns_msg handle; /* handle for response