Rewritten to use generic cache handling functions in cache.c.
[kopensolaris-gnu/glibc.git] / resolv / nss_dns / dns-network.c
1 /* Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Extended from original form by Ulrich Drepper <drepper@cygnus.com>, 1996.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 /* Parts of this file are plain copies of the file `getnetnamadr.c' from
21    the bind package and it has the following copyright.  */
22
23 /* Copyright (c) 1993 Carlos Leandro and Rui Salgueiro
24  *      Dep. Matematica Universidade de Coimbra, Portugal, Europe
25  *
26  * Permission to use, copy, modify, and distribute this software for any
27  * purpose with or without fee is hereby granted, provided that the above
28  * copyright notice and this permission notice appear in all copies.
29  */
30 /*
31  * Copyright (c) 1983, 1993
32  *      The Regents of the University of California.  All rights reserved.
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions and the following disclaimer.
39  * 2. Redistributions in binary form must reproduce the above copyright
40  *    notice, this list of conditions and the following disclaimer in the
41  *    documentation and/or other materials provided with the distribution.
42  * 3. All advertising materials mentioning features or use of this software
43  *    must display the following acknowledgement:
44  *      This product includes software developed by the University of
45  *      California, Berkeley and its contributors.
46  * 4. Neither the name of the University nor the names of its contributors
47  *    may be used to endorse or promote products derived from this software
48  *    without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60  * SUCH DAMAGE.
61  */
62
63 #include <ctype.h>
64 #include <errno.h>
65 #include <netdb.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69
70 #include "nsswitch.h"
71 #include <arpa/inet.h>
72
73 /* Maximum number of aliases we allow.  */
74 #define MAX_NR_ALIASES  48
75
76
77 #if PACKETSZ > 1024
78 #define MAXPACKET       PACKETSZ
79 #else
80 #define MAXPACKET       1024
81 #endif
82
83
84 typedef enum
85 {
86   BYADDR,
87   BYNAME
88 } lookup_method;
89
90
91 /* We need this time later.  */
92 typedef union querybuf
93 {
94   HEADER hdr;
95   u_char buf[MAXPACKET];
96 } querybuf;
97
98
99 /* Prototypes for local functions.  */
100 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
101                                     struct netent *result, char *buffer,
102                                     size_t buflen, lookup_method net_i);
103
104
105 enum nss_status
106 _nss_dns_getnetbyname_r (const char *name, struct netent *result,
107                          char *buffer, size_t buflen, int *errnop)
108 {
109   /* Return entry for network with NAME.  */
110   querybuf net_buffer;
111   int anslen;
112   char *qbuf;
113
114   qbuf = strdupa (name);
115   anslen = res_search (qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
116                        sizeof (querybuf));
117   if (anslen < 0)
118     {
119       /* Nothing found.  */
120       *errnop = errno;
121       return (errno == ECONNREFUSED
122               || errno == EPFNOSUPPORT
123               || errno == EAFNOSUPPORT)
124         ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
125     }
126
127   return getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYNAME);
128 }
129
130
131 enum nss_status
132 _nss_dns_getnetbyaddr_r (long net, int type, struct netent *result,
133                          char *buffer, size_t buflen, int *errnop)
134 {
135   /* Return entry for network with NAME.  */
136   enum nss_status status;
137   querybuf net_buffer;
138   unsigned int net_bytes[4];
139   char qbuf[MAXDNAME];
140   int cnt, anslen;
141   u_int32_t net2;
142
143   /* No net address lookup for IPv6 yet.  */
144   if (type != AF_INET)
145     return NSS_STATUS_UNAVAIL;
146
147   net2 = (u_int32_t) net;
148   for (cnt = 4; net2 != 0; net2 >>= 8)
149     net_bytes[--cnt] = net2 & 0xff;
150
151   switch (cnt)
152     {
153     case 3:
154       /* Class A network.  */
155       sprintf (qbuf, "0.0.0.%u.in-addr.arpa", net_bytes[3]);
156       break;
157     case 2:
158       /* Class B network.  */
159       sprintf (qbuf, "0.0.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2]);
160       break;
161     case 1:
162       /* Class C network.  */
163       sprintf (qbuf, "0.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
164                net_bytes[1]);
165       break;
166     case 0:
167       /* Class D - E network.  */
168       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
169                net_bytes[1], net_bytes[0]);
170       break;
171     }
172
173   anslen = res_query (qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
174                       sizeof (querybuf));
175   if (anslen < 0)
176     {
177       /* Nothing found.  */
178       *errnop = errno;
179       return (errno == ECONNREFUSED
180               || errno == EPFNOSUPPORT
181               || errno == EAFNOSUPPORT)
182         ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
183     }
184
185   status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR);
186   if (status == NSS_STATUS_SUCCESS)
187     {
188       /* Strip trailing zeros.  */
189       unsigned int u_net = net; /* Maybe net should be unsigned?  */
190
191       while ((u_net & 0xff) == 0 && u_net != 0)
192         u_net >>= 8;
193       result->n_net = u_net;
194     }
195
196   return status;
197 }
198
199
200 #undef offsetof
201 #define offsetof(Type, Member) ((size_t) &((Type *) NULL)->Member)
202
203 static enum nss_status
204 getanswer_r (const querybuf *answer, int anslen, struct netent *result,
205              char *buffer, size_t buflen, lookup_method net_i)
206 {
207   /*
208    * Find first satisfactory answer
209    *
210    *      answer --> +------------+  ( MESSAGE )
211    *                 |   Header   |
212    *                 +------------+
213    *                 |  Question  | the question for the name server
214    *                 +------------+
215    *                 |   Answer   | RRs answering the question
216    *                 +------------+
217    *                 | Authority  | RRs pointing toward an authority
218    *                 | Additional | RRs holding additional information
219    *                 +------------+
220    */
221   struct net_data
222   {
223     char *aliases[MAX_NR_ALIASES];
224     char linebuffer[0];
225   } *net_data = (struct net_data *) buffer;
226   int linebuflen = buflen - offsetof (struct net_data, linebuffer);
227   const char *end_of_message = &answer->buf[anslen];
228   const HEADER *header_pointer = &answer->hdr;
229   /* #/records in the answer section.  */
230   int answer_count =  ntohs (header_pointer->ancount);
231   /* #/entries in the question section.  */
232   int question_count = ntohs (header_pointer->qdcount);
233   char *bp = net_data->linebuffer;
234   const char *cp = &answer->buf[HFIXEDSZ];
235   char **alias_pointer;
236   int have_answer;
237   char *ans;
238
239   if (question_count == 0)
240     {
241       /* FIXME: the Sun version uses for host name lookup an additional
242          parameter for pointing to h_errno.  this is missing here.
243          OSF/1 has a per-thread h_errno variable.  */
244       if (header_pointer->aa != 0)
245         {
246           __set_h_errno (HOST_NOT_FOUND);
247           return NSS_STATUS_NOTFOUND;
248         }
249       else
250         {
251           __set_h_errno (TRY_AGAIN);
252           return NSS_STATUS_TRYAGAIN;
253         }
254     }
255
256   /* Skip the question part.  */
257   while (question_count-- > 0)
258     cp += __dn_skipname (cp, end_of_message) + QFIXEDSZ;
259
260   alias_pointer = result->n_aliases = &net_data->aliases[0];
261   *alias_pointer = NULL;
262   have_answer = 0;
263   ans = NULL;
264
265   while (--answer_count >= 0 && cp < end_of_message)
266     {
267       int n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
268       int type, class;
269
270       if (n < 0 || res_dnok (bp) == 0)
271         break;
272       cp += n;
273       ans = strdupa (bp);
274       GETSHORT (type, cp);
275       GETSHORT (class, cp);
276       cp += INT32SZ;            /* TTL */
277       GETSHORT (n, cp);
278
279       if (class == C_IN && type == T_PTR)
280         {
281           n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
282           if (n < 0 || !res_hnok (bp))
283             {
284               /* XXX What does this mean?  The original form from bind
285                  returns NULL. Incrementing cp has no effect in any case.
286                  What should I return here. ??? */
287               cp += n;
288               return NSS_STATUS_UNAVAIL;
289             }
290           cp += n;
291           *alias_pointer++ = bp;
292           bp += strlen (bp) + 1;
293           result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
294           ++have_answer;
295         }
296     }
297
298   if (have_answer)
299     {
300       char *tmp;
301       int len;
302       char *in, *cp, *rp, *wp;
303       int cnt, first_flag;
304
305       *alias_pointer = NULL;
306       switch (net_i)
307         {
308         case BYADDR:
309           result->n_name = result->n_aliases[0];
310           result->n_net = 0L;
311           break;
312         case BYNAME:
313           len = strlen (result->n_aliases[0]);
314           tmp = (char *) alloca (len + 1);
315           tmp[len] = 0;
316           wp = &tmp[len - 1];
317
318           rp = in = result->n_aliases[0];
319           result->n_name = ans;
320
321           first_flag = 1;
322           for (cnt = 0; cnt < 4; ++cnt)
323             {
324               char *startp;
325
326               startp = rp;
327               while (*rp != '.')
328                 ++rp;
329               if (rp - startp > 1 || *startp != '0' || !first_flag)
330                 {
331                   first_flag = 0;
332                   if (cnt > 0)
333                     *wp-- = '.';
334                   cp = rp;
335                   while (cp > startp)
336                     *wp-- = *--cp;
337                 }
338               in = rp + 1;
339             }
340
341           result->n_net = inet_network (wp);
342           break;
343         }
344
345       ++result->n_aliases;
346       return NSS_STATUS_SUCCESS;
347     }
348
349   __set_h_errno (TRY_AGAIN);
350   return NSS_STATUS_TRYAGAIN;
351 }