1
2 /* Copyright 1998 by the Massachusetts Institute of Technology.
3 * Copyright (C) 2012 Marko Kreen <markokr@gmail.com>
4 *
5 * Permission to use, copy, modify, and distribute this
6 * software and its documentation for any purpose and without
7 * fee is hereby granted, provided that the above copyright
8 * notice appear in all copies and that both that copyright
9 * notice and this permission notice appear in supporting
10 * documentation, and that the name of M.I.T. not be used in
11 * advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission.
13 * M.I.T. makes no representations about the suitability of
14 * this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
16 */
17
18 #include "ares_setup.h"
19
20 #ifdef HAVE_NETINET_IN_H
21 # include <netinet/in.h>
22 #endif
23 #ifdef HAVE_NETDB_H
24 # include <netdb.h>
25 #endif
26 #ifdef HAVE_ARPA_INET_H
27 # include <arpa/inet.h>
28 #endif
29
30 #include "ares_nameser.h"
31
32 #include "ares.h"
33 #include "ares_dns.h"
34 #include "ares_data.h"
35 #include "ares_private.h"
36
37 int
ares_parse_soa_reply(const unsigned char * abuf,int alen,struct ares_soa_reply ** soa_out)38 ares_parse_soa_reply(const unsigned char *abuf, int alen,
39 struct ares_soa_reply **soa_out)
40 {
41 const unsigned char *aptr;
42 long len;
43 char *qname = NULL, *rr_name = NULL;
44 struct ares_soa_reply *soa = NULL;
45 int qdcount, ancount, qclass;
46 int status, i, rr_type, rr_class, rr_len;
47
48 if (alen < HFIXEDSZ)
49 return ARES_EBADRESP;
50
51 /* parse message header */
52 qdcount = DNS_HEADER_QDCOUNT(abuf);
53 ancount = DNS_HEADER_ANCOUNT(abuf);
54
55 if (qdcount != 1)
56 return ARES_EBADRESP;
57 if (ancount == 0)
58 return ARES_EBADRESP;
59
60 aptr = abuf + HFIXEDSZ;
61
62 /* query name */
63 status = ares__expand_name_for_response(aptr, abuf, alen, &qname, &len, 0);
64 if (status != ARES_SUCCESS)
65 goto failed_stat;
66
67 if (alen <= len + HFIXEDSZ + 1)
68 goto failed;
69 aptr += len;
70
71 qclass = DNS_QUESTION_TYPE(aptr);
72
73 /* skip qtype & qclass */
74 if (aptr + QFIXEDSZ > abuf + alen)
75 goto failed;
76 aptr += QFIXEDSZ;
77
78 /* qclass of SOA with multiple answers */
79 if (qclass == T_SOA && ancount > 1)
80 goto failed;
81
82 /* examine all the records, break and return if found soa */
83 for (i = 0; i < ancount; i++)
84 {
85 rr_name = NULL;
86 status = ares__expand_name_for_response (aptr, abuf, alen, &rr_name, &len, 0);
87 if (status != ARES_SUCCESS)
88 {
89 ares_free(rr_name);
90 goto failed_stat;
91 }
92
93 aptr += len;
94 if ( aptr + RRFIXEDSZ > abuf + alen )
95 {
96 ares_free(rr_name);
97 status = ARES_EBADRESP;
98 goto failed_stat;
99 }
100 rr_type = DNS_RR_TYPE( aptr );
101 rr_class = DNS_RR_CLASS( aptr );
102 rr_len = DNS_RR_LEN( aptr );
103 aptr += RRFIXEDSZ;
104 if (aptr + rr_len > abuf + alen)
105 {
106 ares_free(rr_name);
107 status = ARES_EBADRESP;
108 goto failed_stat;
109 }
110 if ( rr_class == C_IN && rr_type == T_SOA )
111 {
112 /* allocate result struct */
113 soa = ares_malloc_data(ARES_DATATYPE_SOA_REPLY);
114 if (!soa)
115 {
116 ares_free(rr_name);
117 status = ARES_ENOMEM;
118 goto failed_stat;
119 }
120
121 /* nsname */
122 status = ares__expand_name_for_response(aptr, abuf, alen, &soa->nsname,
123 &len, 0);
124 if (status != ARES_SUCCESS)
125 {
126 ares_free(rr_name);
127 goto failed_stat;
128 }
129 aptr += len;
130
131 /* hostmaster */
132 status = ares__expand_name_for_response(aptr, abuf, alen,
133 &soa->hostmaster, &len, 0);
134 if (status != ARES_SUCCESS)
135 {
136 ares_free(rr_name);
137 goto failed_stat;
138 }
139 aptr += len;
140
141 /* integer fields */
142 if (aptr + 5 * 4 > abuf + alen)
143 {
144 ares_free(rr_name);
145 goto failed;
146 }
147 soa->serial = DNS__32BIT(aptr + 0 * 4);
148 soa->refresh = DNS__32BIT(aptr + 1 * 4);
149 soa->retry = DNS__32BIT(aptr + 2 * 4);
150 soa->expire = DNS__32BIT(aptr + 3 * 4);
151 soa->minttl = DNS__32BIT(aptr + 4 * 4);
152
153 ares_free(qname);
154 ares_free(rr_name);
155
156 *soa_out = soa;
157
158 return ARES_SUCCESS;
159 }
160 aptr += rr_len;
161
162 ares_free(rr_name);
163
164 if (aptr > abuf + alen)
165 goto failed_stat;
166 }
167 /* no SOA record found */
168 status = ARES_EBADRESP;
169 goto failed_stat;
170 failed:
171 status = ARES_EBADRESP;
172
173 failed_stat:
174 if (soa)
175 ares_free_data(soa);
176 if (qname)
177 ares_free(qname);
178 return status;
179 }
180