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