b1948521977fbf2bc03e2b5e5ef26a9f17052e73
[kopensolaris-gnu/glibc.git] / resolv / nss_dns / dns-network.c
1 /* Copyright (C) 1996, 1997, 1998, 1999 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  * 4. Neither the name of the University nor the names of its contributors
43  *    may be used to endorse or promote products derived from this software
44  *    without specific prior written permission.
45  *
46  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
47  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
50  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56  * SUCH DAMAGE.
57  */
58
59 #include <ctype.h>
60 #include <errno.h>
61 #include <netdb.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65
66 #include "nsswitch.h"
67 #include <arpa/inet.h>
68
69 /* Maximum number of aliases we allow.  */
70 #define MAX_NR_ALIASES  48
71
72
73 #if PACKETSZ > 1024
74 #define MAXPACKET       PACKETSZ
75 #else
76 #define MAXPACKET       1024
77 #endif
78
79
80 typedef enum
81 {
82   BYADDR,
83   BYNAME
84 } lookup_method;
85
86
87 /* We need this time later.  */
88 typedef union querybuf
89 {
90   HEADER hdr;
91   u_char buf[MAXPACKET];
92 } querybuf;
93
94 /* These functions are defined in res_comp.c.  */
95 #define NS_MAXCDNAME    255     /* maximum compressed domain name */
96 extern int __ns_name_ntop __P ((const u_char *, char *, size_t));
97 extern int __ns_name_unpack __P ((const u_char *, const u_char *,
98                                   const u_char *, u_char *, size_t));
99
100
101 /* Prototypes for local functions.  */
102 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
103                                     struct netent *result, char *buffer,
104                                     size_t buflen, lookup_method net_i);
105
106
107 enum nss_status
108 _nss_dns_getnetbyname_r (const char *name, struct netent *result,
109                          char *buffer, size_t buflen, int *errnop)
110 {
111   /* Return entry for network with NAME.  */
112   querybuf net_buffer;
113   int anslen;
114   char *qbuf;
115
116   if ((_res.options & RES_INIT) == 0 && __res_ninit (&_res) == -1)
117     return NSS_STATUS_UNAVAIL;
118
119   qbuf = strdupa (name);
120   anslen = res_nsearch (&_res, qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
121                         sizeof (querybuf));
122   if (anslen < 0)
123     {
124       /* Nothing found.  */
125       *errnop = errno;
126       return (errno == ECONNREFUSED
127               || errno == EPFNOSUPPORT
128               || errno == EAFNOSUPPORT)
129         ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
130     }
131
132   return getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYNAME);
133 }
134
135
136 enum nss_status
137 _nss_dns_getnetbyaddr_r (long net, int type, struct netent *result,
138                          char *buffer, size_t buflen, int *errnop)
139 {
140   /* Return entry for network with NAME.  */
141   enum nss_status status;
142   querybuf net_buffer;
143   unsigned int net_bytes[4];
144   char qbuf[MAXDNAME];
145   int cnt, anslen;
146   u_int32_t net2;
147
148   /* No net address lookup for IPv6 yet.  */
149   if (type != AF_INET)
150     return NSS_STATUS_UNAVAIL;
151
152   if ((_res.options & RES_INIT) == 0 && __res_ninit (&_res) == -1)
153     return NSS_STATUS_UNAVAIL;
154   
155   net2 = (u_int32_t) net;
156   for (cnt = 4; net2 != 0; net2 >>= 8)
157     net_bytes[--cnt] = net2 & 0xff;
158
159   switch (cnt)
160     {
161     case 3:
162       /* Class A network.  */
163       sprintf (qbuf, "0.0.0.%u.in-addr.arpa", net_bytes[3]);
164       break;
165     case 2:
166       /* Class B network.  */
167       sprintf (qbuf, "0.0.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2]);
168       break;
169     case 1:
170       /* Class C network.  */
171       sprintf (qbuf, "0.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
172                net_bytes[1]);
173       break;
174     case 0:
175       /* Class D - E network.  */
176       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
177                net_bytes[1], net_bytes[0]);
178       break;
179     }
180
181   anslen = res_nquery (&_res, qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
182                        sizeof (querybuf));
183   if (anslen < 0)
184     {
185       /* Nothing found.  */
186       *errnop = errno;
187       return (errno == ECONNREFUSED
188               || errno == EPFNOSUPPORT
189               || errno == EAFNOSUPPORT)
190         ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
191     }
192
193   status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR);
194   if (status == NSS_STATUS_SUCCESS)
195     {
196       /* Strip trailing zeros.  */
197       unsigned int u_net = net; /* Maybe net should be unsigned?  */
198
199       while ((u_net & 0xff) == 0 && u_net != 0)
200         u_net >>= 8;
201       result->n_net = u_net;
202     }
203
204   return status;
205 }
206
207
208 #undef offsetof
209 #define offsetof(Type, Member) ((size_t) &((Type *) NULL)->Member)
210
211 static enum nss_status
212 getanswer_r (const querybuf *answer, int anslen, struct netent *result,
213              char *buffer, size_t buflen, lookup_method net_i)
214 {
215   /*
216    * Find first satisfactory answer
217    *
218    *      answer --> +------------+  ( MESSAGE )
219    *                 |   Header   |
220    *                 +------------+
221    *                 |  Question  | the question for the name server
222    *                 +------------+
223    *                 |   Answer   | RRs answering the question
224    *                 +------------+
225    *                 | Authority  | RRs pointing toward an authority
226    *                 | Additional | RRs holding additional information
227    *                 +------------+
228    */
229   struct net_data
230   {
231     char *aliases[MAX_NR_ALIASES];
232     char linebuffer[0];
233   } *net_data = (struct net_data *) buffer;
234   int linebuflen = buflen - offsetof (struct net_data, linebuffer);
235   const char *end_of_message = &answer->buf[anslen];
236   const HEADER *header_pointer = &answer->hdr;
237   /* #/records in the answer section.  */
238   int answer_count =  ntohs (header_pointer->ancount);
239   /* #/entries in the question section.  */
240   int question_count = ntohs (header_pointer->qdcount);
241   char *bp = net_data->linebuffer;
242   const char *cp = &answer->buf[HFIXEDSZ];
243   char **alias_pointer;
244   int have_answer;
245   char *ans;
246   u_char packtmp[NS_MAXCDNAME];
247
248   if (question_count == 0)
249     {
250       /* FIXME: the Sun version uses for host name lookup an additional
251          parameter for pointing to h_errno.  this is missing here.
252          OSF/1 has a per-thread h_errno variable.  */
253       if (header_pointer->aa != 0)
254         {
255           __set_h_errno (HOST_NOT_FOUND);
256           return NSS_STATUS_NOTFOUND;
257         }
258       else
259         {
260           __set_h_errno (TRY_AGAIN);
261           return NSS_STATUS_TRYAGAIN;
262         }
263     }
264
265   /* Skip the question part.  */
266   while (question_count-- > 0)
267     cp += __dn_skipname (cp, end_of_message) + QFIXEDSZ;
268
269   alias_pointer = result->n_aliases = &net_data->aliases[0];
270   *alias_pointer = NULL;
271   have_answer = 0;
272   ans = NULL;
273
274   while (--answer_count >= 0 && cp < end_of_message)
275     {
276       int n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
277       int type, class;
278
279       n = __ns_name_unpack (answer->buf, end_of_message, cp,
280                             packtmp, sizeof packtmp);
281       if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
282         {
283           if (errno == EMSGSIZE)
284             {
285               errno = ERANGE;
286               return NSS_STATUS_TRYAGAIN;
287             }
288
289           n = -1;
290         }
291
292       if (n > 0 && bp[0] == '.')
293         bp[0] = '\0';
294
295       if (n < 0 || res_dnok (bp) == 0)
296         break;
297       cp += n;
298       ans = strdupa (bp);
299       GETSHORT (type, cp);
300       GETSHORT (class, cp);
301       cp += INT32SZ;            /* TTL */
302       GETSHORT (n, cp);
303
304       if (class == C_IN && type == T_PTR)
305         {
306           n = __ns_name_unpack (answer->buf, end_of_message, cp,
307                                 packtmp, sizeof packtmp);
308           if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
309             {
310               if (errno == EMSGSIZE)
311                 {
312                   errno = ERANGE;
313                   return NSS_STATUS_TRYAGAIN;
314                 }
315
316               n = -1;
317             }
318
319           if (n < 0 || !res_hnok (bp))
320             {
321               /* XXX What does this mean?  The original form from bind
322                  returns NULL. Incrementing cp has no effect in any case.
323                  What should I return here. ??? */
324               cp += n;
325               return NSS_STATUS_UNAVAIL;
326             }
327           cp += n;
328           *alias_pointer++ = bp;
329           bp += strlen (bp) + 1;
330           result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
331           ++have_answer;
332         }
333     }
334
335   if (have_answer)
336     {
337       char *tmp;
338       int len;
339       char *in, *cp, *rp, *wp;
340       int cnt, first_flag;
341
342       *alias_pointer = NULL;
343       switch (net_i)
344         {
345         case BYADDR:
346           result->n_name = result->n_aliases[0];
347           result->n_net = 0L;
348           break;
349         case BYNAME:
350           len = strlen (result->n_aliases[0]);
351           tmp = (char *) alloca (len + 1);
352           tmp[len] = 0;
353           wp = &tmp[len - 1];
354
355           rp = in = result->n_aliases[0];
356           result->n_name = ans;
357
358           first_flag = 1;
359           for (cnt = 0; cnt < 4; ++cnt)
360             {
361               char *startp;
362
363               startp = rp;
364               while (*rp != '.')
365                 ++rp;
366               if (rp - startp > 1 || *startp != '0' || !first_flag)
367                 {
368                   first_flag = 0;
369                   if (cnt > 0)
370                     *wp-- = '.';
371                   cp = rp;
372                   while (cp > startp)
373                     *wp-- = *--cp;
374                 }
375               in = rp + 1;
376             }
377
378           result->n_net = inet_network (wp);
379           break;
380         }
381
382       ++result->n_aliases;
383       return NSS_STATUS_SUCCESS;
384     }
385
386   __set_h_errno (TRY_AGAIN);
387   return NSS_STATUS_TRYAGAIN;
388 }