Fix declarations to match prototype.
[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                          int *herrnop)
111 {
112   /* Return entry for network with NAME.  */
113   querybuf net_buffer;
114   int anslen;
115   char *qbuf;
116
117   if ((_res.options & RES_INIT) == 0 && __res_ninit (&_res) == -1)
118     return NSS_STATUS_UNAVAIL;
119
120   qbuf = strdupa (name);
121   anslen = res_nsearch (&_res, qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
122                         sizeof (querybuf));
123   if (anslen < 0)
124     {
125       /* Nothing found.  */
126       *errnop = errno;
127       return (errno == ECONNREFUSED
128               || errno == EPFNOSUPPORT
129               || errno == EAFNOSUPPORT)
130         ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
131     }
132
133   return getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYNAME);
134 }
135
136
137 enum nss_status
138 _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
139                          char *buffer, size_t buflen, int *errnop,
140                          int *herrnop)
141 {
142   /* Return entry for network with NAME.  */
143   enum nss_status status;
144   querybuf net_buffer;
145   unsigned int net_bytes[4];
146   char qbuf[MAXDNAME];
147   int cnt, anslen;
148   u_int32_t net2;
149
150   /* No net address lookup for IPv6 yet.  */
151   if (type != AF_INET)
152     return NSS_STATUS_UNAVAIL;
153
154   if ((_res.options & RES_INIT) == 0 && __res_ninit (&_res) == -1)
155     return NSS_STATUS_UNAVAIL;
156
157   net2 = (u_int32_t) net;
158   for (cnt = 4; net2 != 0; net2 >>= 8)
159     net_bytes[--cnt] = net2 & 0xff;
160
161   switch (cnt)
162     {
163     case 3:
164       /* Class A network.  */
165       sprintf (qbuf, "0.0.0.%u.in-addr.arpa", net_bytes[3]);
166       break;
167     case 2:
168       /* Class B network.  */
169       sprintf (qbuf, "0.0.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2]);
170       break;
171     case 1:
172       /* Class C network.  */
173       sprintf (qbuf, "0.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
174                net_bytes[1]);
175       break;
176     case 0:
177       /* Class D - E network.  */
178       sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
179                net_bytes[1], net_bytes[0]);
180       break;
181     }
182
183   anslen = res_nquery (&_res, qbuf, C_IN, T_PTR, (u_char *) &net_buffer,
184                        sizeof (querybuf));
185   if (anslen < 0)
186     {
187       /* Nothing found.  */
188       *errnop = errno;
189       return (errno == ECONNREFUSED
190               || errno == EPFNOSUPPORT
191               || errno == EAFNOSUPPORT)
192         ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
193     }
194
195   status = getanswer_r (&net_buffer, anslen, result, buffer, buflen, BYADDR);
196   if (status == NSS_STATUS_SUCCESS)
197     {
198       /* Strip trailing zeros.  */
199       unsigned int u_net = net; /* Maybe net should be unsigned?  */
200
201       while ((u_net & 0xff) == 0 && u_net != 0)
202         u_net >>= 8;
203       result->n_net = u_net;
204     }
205
206   return status;
207 }
208
209
210 #undef offsetof
211 #define offsetof(Type, Member) ((size_t) &((Type *) NULL)->Member)
212
213 static enum nss_status
214 getanswer_r (const querybuf *answer, int anslen, struct netent *result,
215              char *buffer, size_t buflen, lookup_method net_i)
216 {
217   /*
218    * Find first satisfactory answer
219    *
220    *      answer --> +------------+  ( MESSAGE )
221    *                 |   Header   |
222    *                 +------------+
223    *                 |  Question  | the question for the name server
224    *                 +------------+
225    *                 |   Answer   | RRs answering the question
226    *                 +------------+
227    *                 | Authority  | RRs pointing toward an authority
228    *                 | Additional | RRs holding additional information
229    *                 +------------+
230    */
231   struct net_data
232   {
233     char *aliases[MAX_NR_ALIASES];
234     char linebuffer[0];
235   } *net_data = (struct net_data *) buffer;
236   int linebuflen = buflen - offsetof (struct net_data, linebuffer);
237   const char *end_of_message = &answer->buf[anslen];
238   const HEADER *header_pointer = &answer->hdr;
239   /* #/records in the answer section.  */
240   int answer_count =  ntohs (header_pointer->ancount);
241   /* #/entries in the question section.  */
242   int question_count = ntohs (header_pointer->qdcount);
243   char *bp = net_data->linebuffer;
244   const char *cp = &answer->buf[HFIXEDSZ];
245   char **alias_pointer;
246   int have_answer;
247   char *ans;
248   u_char packtmp[NS_MAXCDNAME];
249
250   if (question_count == 0)
251     {
252       /* FIXME: the Sun version uses for host name lookup an additional
253          parameter for pointing to h_errno.  this is missing here.
254          OSF/1 has a per-thread h_errno variable.  */
255       if (header_pointer->aa != 0)
256         {
257           __set_h_errno (HOST_NOT_FOUND);
258           return NSS_STATUS_NOTFOUND;
259         }
260       else
261         {
262           __set_h_errno (TRY_AGAIN);
263           return NSS_STATUS_TRYAGAIN;
264         }
265     }
266
267   /* Skip the question part.  */
268   while (question_count-- > 0)
269     cp += __dn_skipname (cp, end_of_message) + QFIXEDSZ;
270
271   alias_pointer = result->n_aliases = &net_data->aliases[0];
272   *alias_pointer = NULL;
273   have_answer = 0;
274   ans = NULL;
275
276   while (--answer_count >= 0 && cp < end_of_message)
277     {
278       int n = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
279       int type, class;
280
281       n = __ns_name_unpack (answer->buf, end_of_message, cp,
282                             packtmp, sizeof packtmp);
283       if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
284         {
285           if (errno == EMSGSIZE)
286             {
287               errno = ERANGE;
288               return NSS_STATUS_TRYAGAIN;
289             }
290
291           n = -1;
292         }
293
294       if (n > 0 && bp[0] == '.')
295         bp[0] = '\0';
296
297       if (n < 0 || res_dnok (bp) == 0)
298         break;
299       cp += n;
300       ans = strdupa (bp);
301       GETSHORT (type, cp);
302       GETSHORT (class, cp);
303       cp += INT32SZ;            /* TTL */
304       GETSHORT (n, cp);
305
306       if (class == C_IN && type == T_PTR)
307         {
308           n = __ns_name_unpack (answer->buf, end_of_message, cp,
309                                 packtmp, sizeof packtmp);
310           if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
311             {
312               if (errno == EMSGSIZE)
313                 {
314                   errno = ERANGE;
315                   return NSS_STATUS_TRYAGAIN;
316                 }
317
318               n = -1;
319             }
320
321           if (n < 0 || !res_hnok (bp))
322             {
323               /* XXX What does this mean?  The original form from bind
324                  returns NULL. Incrementing cp has no effect in any case.
325                  What should I return here. ??? */
326               cp += n;
327               return NSS_STATUS_UNAVAIL;
328             }
329           cp += n;
330           *alias_pointer++ = bp;
331           bp += strlen (bp) + 1;
332           result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
333           ++have_answer;
334         }
335     }
336
337   if (have_answer)
338     {
339       char *tmp;
340       int len;
341       char *in, *cp, *rp, *wp;
342       int cnt, first_flag;
343
344       *alias_pointer = NULL;
345       switch (net_i)
346         {
347         case BYADDR:
348           result->n_name = result->n_aliases[0];
349           result->n_net = 0L;
350           break;
351         case BYNAME:
352           len = strlen (result->n_aliases[0]);
353           tmp = (char *) alloca (len + 1);
354           tmp[len] = 0;
355           wp = &tmp[len - 1];
356
357           rp = in = result->n_aliases[0];
358           result->n_name = ans;
359
360           first_flag = 1;
361           for (cnt = 0; cnt < 4; ++cnt)
362             {
363               char *startp;
364
365               startp = rp;
366               while (*rp != '.')
367                 ++rp;
368               if (rp - startp > 1 || *startp != '0' || !first_flag)
369                 {
370                   first_flag = 0;
371                   if (cnt > 0)
372                     *wp-- = '.';
373                   cp = rp;
374                   while (cp > startp)
375                     *wp-- = *--cp;
376                 }
377               in = rp + 1;
378             }
379
380           result->n_net = inet_network (wp);
381           break;
382         }
383
384       ++result->n_aliases;
385       return NSS_STATUS_SUCCESS;
386     }
387
388   __set_h_errno (TRY_AGAIN);
389   return NSS_STATUS_TRYAGAIN;
390 }