]> andersk Git - openssh.git/blob - openbsd-compat/getrrsetbyname.c
- (dtucker) [openbsd-compat/port-irix.c] Add errno.h, found by Iain Morgan.
[openssh.git] / openbsd-compat / getrrsetbyname.c
1 /* $OpenBSD: getrrsetbyname.c,v 1.10 2005/03/30 02:58:28 tedu Exp $ */
2
3 /*
4  * Copyright (c) 2001 Jakob Schlyter. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /*
30  * Portions Copyright (c) 1999-2001 Internet Software Consortium.
31  *
32  * Permission to use, copy, modify, and distribute this software for any
33  * purpose with or without fee is hereby granted, provided that the above
34  * copyright notice and this permission notice appear in all copies.
35  *
36  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
37  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
38  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
39  * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
40  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
41  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
42  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
43  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44  */
45
46 /* OPENBSD ORIGINAL: lib/libc/net/getrrsetbyname.c */
47
48 #include "includes.h"
49
50 #ifndef HAVE_GETRRSETBYNAME
51
52 #include <stdlib.h>
53 #include <string.h>
54
55 #include "getrrsetbyname.h"
56
57 #if defined(HAVE_DECL_H_ERRNO) && !HAVE_DECL_H_ERRNO
58 extern int h_errno;
59 #endif
60
61 /* We don't need multithread support here */
62 #ifdef _THREAD_PRIVATE
63 # undef _THREAD_PRIVATE
64 #endif
65 #define _THREAD_PRIVATE(a,b,c) (c)
66
67 /* to avoid conflicts where a platform already has _res */
68 #ifdef _res
69 # undef _res
70 #endif
71 #define _res    _compat_res
72
73 struct __res_state _res;
74
75 /* Necessary functions and macros */
76
77 /*
78  * Inline versions of get/put short/long.  Pointer is advanced.
79  *
80  * These macros demonstrate the property of C whereby it can be
81  * portable or it can be elegant but rarely both.
82  */
83
84 #ifndef INT32SZ
85 # define INT32SZ        4
86 #endif
87 #ifndef INT16SZ
88 # define INT16SZ        2
89 #endif
90
91 #ifndef GETSHORT
92 #define GETSHORT(s, cp) { \
93         register u_char *t_cp = (u_char *)(cp); \
94         (s) = ((u_int16_t)t_cp[0] << 8) \
95             | ((u_int16_t)t_cp[1]) \
96             ; \
97         (cp) += INT16SZ; \
98 }
99 #endif
100
101 #ifndef GETLONG
102 #define GETLONG(l, cp) { \
103         register u_char *t_cp = (u_char *)(cp); \
104         (l) = ((u_int32_t)t_cp[0] << 24) \
105             | ((u_int32_t)t_cp[1] << 16) \
106             | ((u_int32_t)t_cp[2] << 8) \
107             | ((u_int32_t)t_cp[3]) \
108             ; \
109         (cp) += INT32SZ; \
110 }
111 #endif
112
113 /*
114  * Routines to insert/extract short/long's.
115  */
116
117 #ifndef HAVE__GETSHORT
118 static u_int16_t
119 _getshort(msgp)
120         register const u_char *msgp;
121 {
122         register u_int16_t u;
123
124         GETSHORT(u, msgp);
125         return (u);
126 }
127 #elif defined(HAVE_DECL__GETSHORT) && (HAVE_DECL__GETSHORT == 0)
128 u_int16_t _getshort(register const u_char *);
129 #endif
130
131 #ifndef HAVE__GETLONG
132 static u_int32_t
133 _getlong(msgp)
134         register const u_char *msgp;
135 {
136         register u_int32_t u;
137
138         GETLONG(u, msgp);
139         return (u);
140 }
141 #elif defined(HAVE_DECL__GETLONG) && (HAVE_DECL__GETLONG == 0)
142 u_int32_t _getlong(register const u_char *);
143 #endif
144
145 /* ************** */
146
147 #define ANSWER_BUFFER_SIZE 1024*64
148
149 struct dns_query {
150         char                    *name;
151         u_int16_t               type;
152         u_int16_t               class;
153         struct dns_query        *next;
154 };
155
156 struct dns_rr {
157         char                    *name;
158         u_int16_t               type;
159         u_int16_t               class;
160         u_int16_t               ttl;
161         u_int16_t               size;
162         void                    *rdata;
163         struct dns_rr           *next;
164 };
165
166 struct dns_response {
167         HEADER                  header;
168         struct dns_query        *query;
169         struct dns_rr           *answer;
170         struct dns_rr           *authority;
171         struct dns_rr           *additional;
172 };
173
174 static struct dns_response *parse_dns_response(const u_char *, int);
175 static struct dns_query *parse_dns_qsection(const u_char *, int,
176     const u_char **, int);
177 static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **,
178     int);
179
180 static void free_dns_query(struct dns_query *);
181 static void free_dns_rr(struct dns_rr *);
182 static void free_dns_response(struct dns_response *);
183
184 static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t);
185
186 int
187 getrrsetbyname(const char *hostname, unsigned int rdclass,
188     unsigned int rdtype, unsigned int flags,
189     struct rrsetinfo **res)
190 {
191         struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
192         int result;
193         struct rrsetinfo *rrset = NULL;
194         struct dns_response *response = NULL;
195         struct dns_rr *rr;
196         struct rdatainfo *rdata;
197         int length;
198         unsigned int index_ans, index_sig;
199         u_char answer[ANSWER_BUFFER_SIZE];
200
201         /* check for invalid class and type */
202         if (rdclass > 0xffff || rdtype > 0xffff) {
203                 result = ERRSET_INVAL;
204                 goto fail;
205         }
206
207         /* don't allow queries of class or type ANY */
208         if (rdclass == 0xff || rdtype == 0xff) {
209                 result = ERRSET_INVAL;
210                 goto fail;
211         }
212
213         /* don't allow flags yet, unimplemented */
214         if (flags) {
215                 result = ERRSET_INVAL;
216                 goto fail;
217         }
218
219         /* initialize resolver */
220         if ((_resp->options & RES_INIT) == 0 && res_init() == -1) {
221                 result = ERRSET_FAIL;
222                 goto fail;
223         }
224
225 #ifdef DEBUG
226         _resp->options |= RES_DEBUG;
227 #endif /* DEBUG */
228
229 #ifdef RES_USE_DNSSEC
230         /* turn on DNSSEC if EDNS0 is configured */
231         if (_resp->options & RES_USE_EDNS0)
232                 _resp->options |= RES_USE_DNSSEC;
233 #endif /* RES_USE_DNSEC */
234
235         /* make query */
236         length = res_query(hostname, (signed int) rdclass, (signed int) rdtype,
237             answer, sizeof(answer));
238         if (length < 0) {
239                 switch(h_errno) {
240                 case HOST_NOT_FOUND:
241                         result = ERRSET_NONAME;
242                         goto fail;
243                 case NO_DATA:
244                         result = ERRSET_NODATA;
245                         goto fail;
246                 default:
247                         result = ERRSET_FAIL;
248                         goto fail;
249                 }
250         }
251
252         /* parse result */
253         response = parse_dns_response(answer, length);
254         if (response == NULL) {
255                 result = ERRSET_FAIL;
256                 goto fail;
257         }
258
259         if (response->header.qdcount != 1) {
260                 result = ERRSET_FAIL;
261                 goto fail;
262         }
263
264         /* initialize rrset */
265         rrset = calloc(1, sizeof(struct rrsetinfo));
266         if (rrset == NULL) {
267                 result = ERRSET_NOMEMORY;
268                 goto fail;
269         }
270         rrset->rri_rdclass = response->query->class;
271         rrset->rri_rdtype = response->query->type;
272         rrset->rri_ttl = response->answer->ttl;
273         rrset->rri_nrdatas = response->header.ancount;
274
275 #ifdef HAVE_HEADER_AD
276         /* check for authenticated data */
277         if (response->header.ad == 1)
278                 rrset->rri_flags |= RRSET_VALIDATED;
279 #endif
280
281         /* copy name from answer section */
282         rrset->rri_name = strdup(response->answer->name);
283         if (rrset->rri_name == NULL) {
284                 result = ERRSET_NOMEMORY;
285                 goto fail;
286         }
287
288         /* count answers */
289         rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass,
290             rrset->rri_rdtype);
291         rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass,
292             T_SIG);
293
294         /* allocate memory for answers */
295         rrset->rri_rdatas = calloc(rrset->rri_nrdatas,
296             sizeof(struct rdatainfo));
297         if (rrset->rri_rdatas == NULL) {
298                 result = ERRSET_NOMEMORY;
299                 goto fail;
300         }
301
302         /* allocate memory for signatures */
303         rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo));
304         if (rrset->rri_sigs == NULL) {
305                 result = ERRSET_NOMEMORY;
306                 goto fail;
307         }
308
309         /* copy answers & signatures */
310         for (rr = response->answer, index_ans = 0, index_sig = 0;
311             rr; rr = rr->next) {
312
313                 rdata = NULL;
314
315                 if (rr->class == rrset->rri_rdclass &&
316                     rr->type  == rrset->rri_rdtype)
317                         rdata = &rrset->rri_rdatas[index_ans++];
318
319                 if (rr->class == rrset->rri_rdclass &&
320                     rr->type  == T_SIG)
321                         rdata = &rrset->rri_sigs[index_sig++];
322
323                 if (rdata) {
324                         rdata->rdi_length = rr->size;
325                         rdata->rdi_data   = malloc(rr->size);
326
327                         if (rdata->rdi_data == NULL) {
328                                 result = ERRSET_NOMEMORY;
329                                 goto fail;
330                         }
331                         memcpy(rdata->rdi_data, rr->rdata, rr->size);
332                 }
333         }
334         free_dns_response(response);
335
336         *res = rrset;
337         return (ERRSET_SUCCESS);
338
339 fail:
340         if (rrset != NULL)
341                 freerrset(rrset);
342         if (response != NULL)
343                 free_dns_response(response);
344         return (result);
345 }
346
347 void
348 freerrset(struct rrsetinfo *rrset)
349 {
350         u_int16_t i;
351
352         if (rrset == NULL)
353                 return;
354
355         if (rrset->rri_rdatas) {
356                 for (i = 0; i < rrset->rri_nrdatas; i++) {
357                         if (rrset->rri_rdatas[i].rdi_data == NULL)
358                                 break;
359                         free(rrset->rri_rdatas[i].rdi_data);
360                 }
361                 free(rrset->rri_rdatas);
362         }
363
364         if (rrset->rri_sigs) {
365                 for (i = 0; i < rrset->rri_nsigs; i++) {
366                         if (rrset->rri_sigs[i].rdi_data == NULL)
367                                 break;
368                         free(rrset->rri_sigs[i].rdi_data);
369                 }
370                 free(rrset->rri_sigs);
371         }
372
373         if (rrset->rri_name)
374                 free(rrset->rri_name);
375         free(rrset);
376 }
377
378 /*
379  * DNS response parsing routines
380  */
381 static struct dns_response *
382 parse_dns_response(const u_char *answer, int size)
383 {
384         struct dns_response *resp;
385         const u_char *cp;
386
387         /* allocate memory for the response */
388         resp = calloc(1, sizeof(*resp));
389         if (resp == NULL)
390                 return (NULL);
391
392         /* initialize current pointer */
393         cp = answer;
394
395         /* copy header */
396         memcpy(&resp->header, cp, HFIXEDSZ);
397         cp += HFIXEDSZ;
398
399         /* fix header byte order */
400         resp->header.qdcount = ntohs(resp->header.qdcount);
401         resp->header.ancount = ntohs(resp->header.ancount);
402         resp->header.nscount = ntohs(resp->header.nscount);
403         resp->header.arcount = ntohs(resp->header.arcount);
404
405         /* there must be at least one query */
406         if (resp->header.qdcount < 1) {
407                 free_dns_response(resp);
408                 return (NULL);
409         }
410
411         /* parse query section */
412         resp->query = parse_dns_qsection(answer, size, &cp,
413             resp->header.qdcount);
414         if (resp->header.qdcount && resp->query == NULL) {
415                 free_dns_response(resp);
416                 return (NULL);
417         }
418
419         /* parse answer section */
420         resp->answer = parse_dns_rrsection(answer, size, &cp,
421             resp->header.ancount);
422         if (resp->header.ancount && resp->answer == NULL) {
423                 free_dns_response(resp);
424                 return (NULL);
425         }
426
427         /* parse authority section */
428         resp->authority = parse_dns_rrsection(answer, size, &cp,
429             resp->header.nscount);
430         if (resp->header.nscount && resp->authority == NULL) {
431                 free_dns_response(resp);
432                 return (NULL);
433         }
434
435         /* parse additional section */
436         resp->additional = parse_dns_rrsection(answer, size, &cp,
437             resp->header.arcount);
438         if (resp->header.arcount && resp->additional == NULL) {
439                 free_dns_response(resp);
440                 return (NULL);
441         }
442
443         return (resp);
444 }
445
446 static struct dns_query *
447 parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
448 {
449         struct dns_query *head, *curr, *prev;
450         int i, length;
451         char name[MAXDNAME];
452
453         for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
454
455                 /* allocate and initialize struct */
456                 curr = calloc(1, sizeof(struct dns_query));
457                 if (curr == NULL) {
458                         free_dns_query(head);
459                         return (NULL);
460                 }
461                 if (head == NULL)
462                         head = curr;
463                 if (prev != NULL)
464                         prev->next = curr;
465
466                 /* name */
467                 length = dn_expand(answer, answer + size, *cp, name,
468                     sizeof(name));
469                 if (length < 0) {
470                         free_dns_query(head);
471                         return (NULL);
472                 }
473                 curr->name = strdup(name);
474                 if (curr->name == NULL) {
475                         free_dns_query(head);
476                         return (NULL);
477                 }
478                 *cp += length;
479
480                 /* type */
481                 curr->type = _getshort(*cp);
482                 *cp += INT16SZ;
483
484                 /* class */
485                 curr->class = _getshort(*cp);
486                 *cp += INT16SZ;
487         }
488
489         return (head);
490 }
491
492 static struct dns_rr *
493 parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
494     int count)
495 {
496         struct dns_rr *head, *curr, *prev;
497         int i, length;
498         char name[MAXDNAME];
499
500         for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
501
502                 /* allocate and initialize struct */
503                 curr = calloc(1, sizeof(struct dns_rr));
504                 if (curr == NULL) {
505                         free_dns_rr(head);
506                         return (NULL);
507                 }
508                 if (head == NULL)
509                         head = curr;
510                 if (prev != NULL)
511                         prev->next = curr;
512
513                 /* name */
514                 length = dn_expand(answer, answer + size, *cp, name,
515                     sizeof(name));
516                 if (length < 0) {
517                         free_dns_rr(head);
518                         return (NULL);
519                 }
520                 curr->name = strdup(name);
521                 if (curr->name == NULL) {
522                         free_dns_rr(head);
523                         return (NULL);
524                 }
525                 *cp += length;
526
527                 /* type */
528                 curr->type = _getshort(*cp);
529                 *cp += INT16SZ;
530
531                 /* class */
532                 curr->class = _getshort(*cp);
533                 *cp += INT16SZ;
534
535                 /* ttl */
536                 curr->ttl = _getlong(*cp);
537                 *cp += INT32SZ;
538
539                 /* rdata size */
540                 curr->size = _getshort(*cp);
541                 *cp += INT16SZ;
542
543                 /* rdata itself */
544                 curr->rdata = malloc(curr->size);
545                 if (curr->rdata == NULL) {
546                         free_dns_rr(head);
547                         return (NULL);
548                 }
549                 memcpy(curr->rdata, *cp, curr->size);
550                 *cp += curr->size;
551         }
552
553         return (head);
554 }
555
556 static void
557 free_dns_query(struct dns_query *p)
558 {
559         if (p == NULL)
560                 return;
561
562         if (p->name)
563                 free(p->name);
564         free_dns_query(p->next);
565         free(p);
566 }
567
568 static void
569 free_dns_rr(struct dns_rr *p)
570 {
571         if (p == NULL)
572                 return;
573
574         if (p->name)
575                 free(p->name);
576         if (p->rdata)
577                 free(p->rdata);
578         free_dns_rr(p->next);
579         free(p);
580 }
581
582 static void
583 free_dns_response(struct dns_response *p)
584 {
585         if (p == NULL)
586                 return;
587
588         free_dns_query(p->query);
589         free_dns_rr(p->answer);
590         free_dns_rr(p->authority);
591         free_dns_rr(p->additional);
592         free(p);
593 }
594
595 static int
596 count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type)
597 {
598         int n = 0;
599
600         while(p) {
601                 if (p->class == class && p->type == type)
602                         n++;
603                 p = p->next;
604         }
605
606         return (n);
607 }
608
609 #endif /* !defined(HAVE_GETRRSETBYNAME) */
This page took 0.117873 seconds and 5 git commands to generate.