]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind/bin/named/ns_sort.c
This commit was generated by cvs2svn to compensate for changes in r56639,
[FreeBSD/FreeBSD.git] / contrib / bind / bin / named / ns_sort.c
1 #if !defined(lint) && !defined(SABER)
2 static const char sccsid[] = "@(#)ns_sort.c     4.10 (Berkeley) 3/3/91";
3 static const char rcsid[] = "$Id: ns_sort.c,v 8.5 1999/10/13 16:39:12 vixie Exp $";
4 #endif /* not lint */
5
6 /*
7  * Copyright (c) 1986, 1990
8  *    The Regents of the University of California.  All rights reserved.
9  * 
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  * 
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38
39 /*
40  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
41  * 
42  * Permission to use, copy, modify, and distribute this software for any
43  * purpose with or without fee is hereby granted, provided that the above
44  * copyright notice and this permission notice appear in all copies, and that
45  * the name of Digital Equipment Corporation not be used in advertising or
46  * publicity pertaining to distribution of the document or software without
47  * specific, written prior permission.
48  * 
49  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
50  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
51  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
52  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
53  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
54  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
55  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56  * SOFTWARE.
57  */
58
59 /*
60  * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
61  *
62  * Permission to use, copy, modify, and distribute this software for any
63  * purpose with or without fee is hereby granted, provided that the above
64  * copyright notice and this permission notice appear in all copies.
65  *
66  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
67  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
68  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
69  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
70  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
71  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
72  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
73  * SOFTWARE.
74  */
75
76 /*
77  * Sorting should really be handled by the resolver, but:
78  *  1) There are too many brain dead resolvers out there that can't be replaced.
79  *  2) It would be a pain to individually configure all those resolvers anyway.
80  *
81  * Here's the scoop:
82  *
83  * To enable address sorting in responses, you need to supply the sortlist
84  * statement in the config file.  The sortlist statement takes an
85  * address match list and interprets it even more specially than the
86  * topology statement does.
87  *
88  * Each top level statement in the sortlist must itself be an explicit
89  * address match list with one or two elements.  The first element
90  * (which may be an IP address, an IP prefix, an ACL name or nested
91  * address match list) of each top level list is checked against the
92  * source address of the query until a match is found.
93  *
94  * Once the source address of the query has been matched, if the top level
95  * statement contains only one element, the actual primitive element that
96  * matched the source address is used to select the address in the response
97  * to move to the beginning of the response.  If the statement is a list
98  * of two elements, then the second element is treated like the address
99  * match list in a topology statement.  Each top level element is assigned
100  * a distance and the address in the response with the minimum distance is
101  * moved to the beginning of the response.
102  * 
103  * In the following example, any queries received from any of the addresses
104  * of the host itself will get responses preferring addresses on any of
105  * the locally connected networks.  Next most preferred are addresses on
106  * the 192.168.1/24 network, and after that either the 192.168.2/24 or
107  * 192.168.3/24 network with no preference shown between these two networks.
108  * Queries received from a host on the 192.168.1/24 network will prefer
109  * other addresses on that network to the 192.168.2/24 and 192.168.3/24
110  * networks.  Queries received from a host on the 192.168.4/24 or the
111  * 192.168.5/24 network will only prefer other addresses on their
112  * directly connected networks.
113  *
114  * sortlist {
115  *            {
116  *              localhost;
117  *              {
118  *                localnets;
119  *                192.168.1/24;
120  *                { 192,168.2/24; 192.168.3/24; };
121  *              };
122  *            };
123  *            {
124  *              192.168.1/24;
125  *              {
126  *                192.168.1/24;
127  *                { 192.168.2/24; 192.168.3/24; };
128  *              };
129  *            };
130  *            {
131  *              192.168.2/24;
132  *              {
133  *                192.168.2/24;
134  *                { 192.168.1/24; 192.168.3/24; };
135  *              };
136  *            };
137  *            {
138  *              192.168.3/24;
139  *              {
140  *                192.168.3/24;
141  *                { 192.168.1/24; 192.168.2/24; };
142  *              };
143  *            };
144  *            {
145  *              { 192.168.4/24; 192.168.5/24; };
146  *            };
147  * };
148  *
149  * 
150  * The following example will give reasonable behaviour for the local host
151  * and hosts on directly connected networks.  It is similar to the behavior
152  * of the address sort in BIND 4.9.x.  Responses sent to queries from the
153  * local host will favor any of the directly connected networks.  Responses
154  * sent to queries from any other hosts on a directly connected network will
155  * prefer addresses on that same network.  Responses to other queries will
156  * not be sorted.
157  *
158  * sortlist {
159  *            { localhost; localnets; };
160  *            { localnets; };
161  * };
162  *
163  * XXX - it wouldb e nice to have an ACL called "source" that matched the
164  *       source address of a query so that a host could be configured to
165  *       automatically prefer itself, and an ACL called "sourcenet", that
166  *       would return the primitive IP match element that matched the source
167  *       address so that you could do:
168  *          { localnets; { sourcenet; { other stuff ...}; };
169  *       and automatically get similar behaviour to what you get with:
170  *          { localnets; };
171  *
172  */
173
174 #include "port_before.h"
175
176 #include <sys/param.h>
177 #include <sys/types.h>
178 #include <sys/socket.h>
179 #include <sys/file.h>
180 #include <netinet/in.h>
181 #include <arpa/nameser.h>
182 #include <arpa/inet.h>
183 #include <stdio.h>
184 #include <syslog.h>
185 #include <resolv.h>
186
187 #include <isc/eventlib.h>
188 #include <isc/logging.h>
189
190 #include "port_after.h"
191
192 #include "named.h"
193
194 static int sort_rr(u_char *cp, u_char *eom, int ancount, ip_match_list iml);
195
196 static int ip_match_address_elt(ip_match_list, struct in_addr,
197                                 ip_match_element *);
198
199 void
200 sort_response(u_char *cp, u_char *eom, int ancount, struct sockaddr_in *from) {
201         struct in_addr address;
202         struct ip_match_element imelement;
203         ip_match_element imetl, imematch, imeprimitive;
204         struct ip_match_list imlist;
205         ip_match_list iml;
206         int indirect, matched;
207
208         if (server_options->sortlist == NULL)
209                 return;
210
211         if (from->sin_family != AF_INET)
212                 return;
213
214         address = from->sin_addr;
215
216         for (imetl = server_options->sortlist->first; imetl != NULL;
217              imetl = imetl->next) {
218                 if (imetl->type == ip_match_indirect)
219                         imematch = imetl->u.indirect.list->first;
220                 else
221                         /*
222                          * allow a bare pattern as a top level statement
223                          * and treat it like {pattern;};
224                          */
225                         imematch = imetl;
226
227                 switch (imematch->type) {
228                 case ip_match_pattern:
229                         indirect = 0;
230                         break;
231                 case ip_match_indirect:
232                         indirect = 1;
233                         break;
234                 case ip_match_localhost:
235                         imematch->u.indirect.list = local_addresses;
236                         indirect = 1;
237                         break;
238                 case ip_match_localnets:
239                         imematch->u.indirect.list = local_networks;
240                         indirect = 1;
241                         break;
242                 default:
243                         panic("unexpected ime type in ip_match_address()",
244                               NULL);
245                 }
246                 if (indirect) {
247                         imeprimitive = NULL;
248                         matched = ip_match_address_elt(imematch->u.indirect.list,
249                                                        address, &imeprimitive);
250                         if (matched >= 0) {
251                                 if (imematch->flags & IP_MATCH_NEGATE)
252                                         /* Don't sort */
253                                         return;
254                         } else 
255                                 continue;
256                 } else {
257                         if (ina_onnet(address, imematch->u.direct.address,
258                                       imematch->u.direct.mask)) {
259                                 if (imematch->flags & IP_MATCH_NEGATE)
260                                         /* Don't sort */
261                                         return;
262                                 else
263                                         imeprimitive = imematch;
264                         } else
265                                 continue;
266                 }
267                 if (imetl != imematch && imematch->next != NULL) {
268                         /*
269                          * Not a bare pattern at the top level, but a two
270                          * element list
271                          */
272                         switch (imematch->next->type) {
273                         case ip_match_pattern:
274                         case ip_match_localhost:
275                         case ip_match_localnets:
276                                 imelement = *(imematch->next);
277                                 imelement.next = NULL;
278                                 iml = &imlist;
279                                 iml->first = iml->last = &imelement;
280                                 break;
281                         case ip_match_indirect:
282                                 iml = imematch->next->u.indirect.list;
283                                 break;
284                         default:
285                                 panic("unexpected ime type in ip_match_address()",
286                                       NULL);
287                         }
288                 } else if (imeprimitive) {
289                         imelement = *imeprimitive;
290                         imelement.next = NULL;
291                         iml = &imlist;
292                         iml->first = iml->last = &imelement;
293                 } else {
294                         /* Don't sort because we'd just use "any" */
295                         return;
296                 }
297                 sort_rr(cp, eom, ancount, iml);
298                 break;
299         }
300
301         return;
302 }
303
304 static int
305 sort_rr(u_char *cp, u_char *eom, int ancount, ip_match_list iml) {
306         int type, class, dlen, n, c, distance, closest;
307         struct in_addr inaddr;
308         u_char *rr1 = NULL, *rrbest, *cpstart;
309
310         rr1 = NULL;
311         cpstart = cp;
312         for (c = ancount; c > 0; --c) {
313             n = dn_skipname(cp, eom);
314             if (n < 0)
315                 return (1);             /* bogus, stop processing */
316             cp += n;
317             if (cp + QFIXEDSZ > eom)
318                 return (1);
319             GETSHORT(type, cp);
320             GETSHORT(class, cp);
321             cp += INT32SZ;
322             GETSHORT(dlen, cp);
323             if (dlen > eom - cp)
324                 return (1);             /* bogus, stop processing */
325             switch (type) {
326             case T_A:
327                 switch (class) {
328                 case C_IN:
329                 case C_HS:
330                         memcpy((char *)&inaddr, cp, INADDRSZ);
331                         /* Find the address with the minimum distance */
332                         if (rr1 == NULL) {
333                                 rr1 = cp;
334                                 rrbest = cp;
335                                 closest = distance_of_address(iml, inaddr);
336                         } else {
337                                 distance = distance_of_address(iml, inaddr);
338                                 if (distance < closest) {
339                                         rrbest = cp;
340                                         closest = distance;
341                                 }
342                         }
343                         break;
344                 }
345                 break;
346             }
347             cp += dlen;
348         }
349         if (rr1 != rrbest && rr1 != NULL) {
350                 memcpy((char *)&inaddr, rrbest, INADDRSZ);
351                 memcpy(rrbest, rr1, INADDRSZ);
352                 memcpy(rr1, (char *)&inaddr, INADDRSZ);
353         }
354         return (0);
355 }
356
357 /*
358  * Just like ip_match_address(), but also returns a pointer to the primitive
359  * element that matched.
360  */
361
362 static int
363 ip_match_address_elt(ip_match_list iml, struct in_addr address,
364                      ip_match_element *imep) {
365         ip_match_element ime;
366         int ret;
367         int indirect;
368
369         INSIST(iml != NULL);
370         for (ime = iml->first; ime != NULL; ime = ime->next) {
371                 switch (ime->type) {
372                 case ip_match_pattern:
373                         indirect = 0;
374                         break;
375                 case ip_match_indirect:
376                         indirect = 1;
377                         break;
378                 case ip_match_localhost:
379                         ime->u.indirect.list = local_addresses;
380                         indirect = 1;
381                         break;
382                 case ip_match_localnets:
383                         ime->u.indirect.list = local_networks;
384                         indirect = 1;
385                         break;
386                 default:
387                         panic("unexpected ime type in ip_match_address()",
388                               NULL);
389                 }
390                 if (indirect) {
391                         ret = ip_match_address_elt(ime->u.indirect.list,
392                                                    address, imep);
393                         if (ret >= 0) {
394                                 if (ime->flags & IP_MATCH_NEGATE)
395                                         ret = (ret) ? 0 : 1;
396                                 return (ret);
397                         }
398                 } else {
399                         if (ina_onnet(address, ime->u.direct.address,
400                                       ime->u.direct.mask)) {
401                                 *imep = ime;
402                                 if (ime->flags & IP_MATCH_NEGATE)
403                                         return (0);
404                                 else
405                                         return (1);
406                         }
407                 }
408         }
409         return (-1);
410 }