14 #define GETIL_B(cb) (dgram[*(cb)++])
15 #define GET_B(cb,tv) ((tv)= GETIL_B((cb)))
16 #define GET_W(cb,tv) ((tv)=0, (tv)|=(GETIL_B((cb))<<8), (tv)|=GETIL_B(cb), (tv))
18 static void vbuf__append_quoted1035(vbuf
*vb
, const byte
*buf
, int len
) {
24 for (i
=0; i
<len
; i
++) {
26 if (ch
== '.' || ch
== '"' || ch
== '(' || ch
== ')' ||
27 ch
== '@' || ch
== ';' || ch
== '$') {
28 sprintf(qbuf
,"\\%c",ch
);
30 } else if (ch
<= ' ' || ch
>= 127) {
31 sprintf(qbuf
,"\\%03o",ch
);
35 if (!adns__vbuf_append(vb
,buf
,i
) || !adns__vbuf_append(vb
,qbuf
,strlen(qbuf
)))
36 return adns_s_nolocalmem
;
41 static adns_status
get_label(const byte
*dgram
, int dglen
, int *max_io
,
42 int *cbyte_io
, int *lablen_r
, int *labstart_r
,
44 /* If succeeds, *lablen_r may be set to -1 to indicate truncation/overrun */
45 int max
, cbyte
, lablen
, namelen
;
51 if (cbyte
+2 > max
) goto x_truncated
;
53 if (!(lablen
& 0x0c000)) break;
54 if ((lablen
& 0x0c000) != 0x0c000) return adns_s_unknownreply
;
55 if (cbyte_io
) { *cbyte_io
= cbyte
; cbyte_io
= 0; }
56 cbyte
= dgram
+DNS_HDR_SIZE
+(lablen
&0x3fff);
59 if (labstart_r
) *labstart_r
= cbyte
;
62 if (namelen
) namelen
++;
64 if (namelen
> DNS_MAXDOMAIN
) return adns_s_domaintoolong
;
67 if (cbyte
> max
) goto x_truncated
;
69 if (cbyte_io
) *cbyte_io
= cbyte
;
78 static adns_status
get_domain_perm(adns_state ads
, adns_query qu
, int serv
,
79 const byte
*dgram
, int dglen
,
80 int *cbyte_io
, int max
, char **domain_r
) {
81 /* Returns 0 for OK (*domain_r set) or truncated (*domain_r null)
82 * or any other adns_s_* value.
84 int cbyte
, sused
, lablen
, namelen
;
86 /* If we follow a pointer we set cbyte_io to 0 to indicate that
87 * we've lost our original starting and ending points; we don't
88 * put the end of the pointed-to thing into the original *cbyte_io.
95 st
= get_label(dgram
,dglen
,&max
, &cbyte
,&lablen
,&labstart
,&namelen
);
97 if (lablen
<0) goto x_truncated
;
99 if (qu
->ans
.used
!= sused
)
100 if (!adns__vbuf_append(&qu
->ans
,".",1)) return adns_s_nolocalmem
;
101 if (qu
->flags
& adns_qf_anyquote
) {
102 if (!vbuf__append_quoted1035(&qu
->ans
,dgram
+labstart
,lablen
))
103 return adns_s_nolocalmem
;
105 if (!ctype_isalpha(dgram
[labstart
])) return adns_s_invaliddomain
;
106 for (i
= cbyte
+1; i
<cbyte
+lablen
; i
++) {
108 if (ch
!= '-' && !ctype_isalpha(ch
) && !ctype_isdigit(ch
))
109 return adns_s_invaliddomain
;
111 if (!adns__vbuf_append(&qu
->ans
,dgram
+labstart
,lablen
))
112 return adns_s_nolocalmem
;
115 if (cbyte_io
) *cbyte_io
= cbyte
;
116 if (!adns__vbuf_append(&qu
->ans
,"",1)) return adns_s_nolocalmem
;
117 *domain_r
= qu
->ans
.buf
+sused
;
121 return cbyte_io ?
-1 : adns_s_serverfaulty
;
124 static adns_status
get_domain_temp(adns_state ads
, adns_query qu
, int serv
,
125 const byte
*dgram
, int dglen
,
126 int *cbyte_io
, int max
, char **domain_r
) {
131 st
= get_domain_perm(ads
,qu
,serv
,dgram
,dglen
,cbyte_io
,max
,domain_r
);
136 static adns_status
get_rr_temp(adns_state ads
, adns_query qu
, int serv
,
137 const byte
*dgram
, int dglen
, int *cbyte_io
,
138 int *type_r
, int *class_r
, int *rdlen_r
, int *rdstart_r
,
139 const byte
*eo_dgram
, int eo_dglen
, int eo_cbyte
,
141 /* _s_ok can have *type_r == -1 and other output invalid, for truncation
142 * type_r and class_r must be !0, other _r may be 0.
143 * eo_dgram==0 for no comparison, otherwise all eo_ must be valid.
145 int cbyte
, tmp
, rdlen
, mismatch
;
146 int max
, lablen
, labstart
, namelen
;
147 int eo_max
, eo_lablen
, eo_labstart
, eo_namelen
;
150 mismatch
= eo_dgram ?
1 : 0;
152 namelen
= 0; eo_namelen
= 0;
153 max
= dglen
; eo_max
= eo_dglen
;
155 st
= get_label(dgram
,dglen
,&max
,
156 &cbyte
,&lablen
,&labstart
,&namelen
);
158 if (lablen
<0) goto x_truncated
;
161 st
= get_label(eo_dgram
,eo_dglen
,&eo_max
,
162 &eo_cbyte
,&eo_lablen
,&eo_labstart
,&eo_namelen
);
164 assert(eo_lablen
>=0);
165 if (lablen
!= eo_lablen
) mismatch
= 1;
166 while (!mismatch
&& lablen
-- > 0) {
167 ch
= dgram
[labstart
++]; if (ctype_isalpha(ch
)) ch
&= ~32;
168 eo_ch
= eo_dgram
[eo_labstart
++]; if (ctype_isalpha(eo_ch
)) eo_ch
&= ~32;
169 if (ch
!= eo_ch
) mismatch
= 1
173 if (eo_matched_r
) *eo_matched_r
= !mismatch
;
175 if (cbyte
+10>len
) goto x_truncated
;
176 GET_W(cbyte
,tmp
); *type_r
= tmp
;
177 GET_W(cbyte
,tmp
); *class_r
= tmp
;
178 cbyte
+= 4; /* we skip the TTL */
179 GET_W(cbyte
,rdlen
); if (rdlen_r
) *rdlen_r
= tmp
;
180 if (rdstart_r
) *rdstart_r
= cbyte
;
182 if (cbyte
>dglen
) goto x_truncated
;
191 void adns__procdgram(adns_state ads
, const byte
*dgram
, int dglen
, int serv
) {
192 int cbyte
, anstart
, rrstart
, lablen
, wantedrrs
, get_t
, cnamestart
;
196 if (dglen
<DNS_HDR_SIZE
) {
197 adns__diag(ads
,serv
,"received datagram too short for message header (%d)",len
);
203 GET_W(cbyte
,qdcount
);
204 GET_W(cbyte
,ancount
);
205 GET_W(cbyte
,nscount
);
206 GET_W(cbyte
,arcount
);
207 assert(cbyte
== DNS_HDR_SIZE
);
212 adns__diag(ads
,serv
,"server sent us a query, not a response");
216 adns__diag(ads
,serv
,"server sent us unknown opcode %d (wanted 0=QUERY)",
221 adns__diag(ads
,serv
,"server sent reply without quoting our question");
223 } else if (qdcount
>1) {
224 adns__diag(ads
,serv
,"server claimed to answer %d questions with one message",
228 for (qu
= ads
->timew
; qu
= nqu
; qu
++) {
230 if (qu
->id
!= id
) continue;
231 if (len
< qu
->querylen
) continue;
232 if (memcmp(qu
->querymsg
+DNSHDRSIZE
,dgram
+DNSHDRSIZE
,qu
->querylen
-DNSHDRSIZE
))
236 anstart
= qu
->querylen
;
238 adns__debug(ads
,serv
,"reply not found (id=%02x)",id
);
242 adns__diag(ads
,serv
,"server thinks we didn't ask for recursive lookup");
243 adns__query_fail(ads
,qu
,adns_s_serverfaulty
);
252 case rcode_formaterror
:
253 adns__warn(ads
,serv
,"server cannot understand our query (Format Error)");
254 adns__query_fail(ads
,qu
,adns_s_serverfaulty
);
257 adns__query_fail(ads
,qu
,adns_s_serverfailure
);
260 adns__warn(ads
,serv
,"server claims not to implement our query");
261 adns__query_fail(ads
,qu
,adns_s_notimplemented
);
264 adns__warn(ads
,serv
,"server refused our query");
265 adns__query_fail(ads
,qu
,adns_s_refused
);
268 adns__warn(ads
,serv
,"server gave unknown response code %d",rcode
);
269 adns__query_fail(ads
,qu
,adns_s_reasonunknown
);
273 /* Now, take a look at the answer section, and see if it is complete.
274 * If it has any CNAMEs we stuff them in the answer.
277 for (rri
= 0; rri
<ancount
; rri
++) {
280 st
= get_rr_temp(ads
,qu
,serv
, dgram
,dglen
,&cbyte
,
281 &rrtype
,&rrclass
,&rdlength
,&rdstart
,
282 dgram
,dglen
,cnamestart
, &ownermatched
);
284 st
= get_rr_temp(ads
,qu
,serv
, dgram
,dglen
,&cbyte
,
285 &rrtype
,&rrclass
,&rdlength
,&rdstart
,
286 qu
->querymsg
,qu
->querylen
,DNS_HDR_SIZE
, &ownermatched
);
288 if (st
) adns__query_fail(ads
,qu
,st
);
289 if (rrtype
== -1) goto x_truncated
;
291 if (rrclass
!= DNS_CLASS_IN
) {
292 adns__diag(ads
,serv
,"ignoring answer RR with wrong class %d (expected IN=%d)",
293 rrclass
,DNS_CLASS_IN
);
297 if (ads
->iflag
& adns_if_debug
) {
298 st
= get_domain_temp(ads
,qu
,serv
, dgram
,dglen
,&rrstart
,dglen
, &cowner
);
299 if (st
) adns__debug(ads
,serv
,"ignoring RR with an irrelevant owner, code %d",st
);
300 else adns__debug(ads
,serv
,"ignoring RR with an irrelevant owner \"%s\"",cowner
);
305 (qu
->type
& adns__rrt_typemask
) != adns_cname
&&
306 rrtype
== adns_cname
) { /* Ignore second and subsequent CNAMEs */
307 st
= get_domain_perm(ads
,qu
,serv
, dgram
,dglen
,
308 &rdstart
,rdstart
+rdlength
,&qu
->cname
);
310 if (!qu
->cname
) goto x_truncated
;
311 /* If we find the answer section truncated after this point we restart
312 * the query at the CNAME; if beforehand then we obviously have to use
313 * TCP. If there is no truncation we can use the whole answer if
314 * it contains the relevant info.
316 } else if (rrtype
== (qu
->type
& adns__rrt_typemask
)) {
319 adns__debug(ads
,serv
,"ignoring answer RR with irrelevant type %d",rrtype
);
323 /* If we got here then the answer section is intact. */
327 /* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */
329 if (rcode
== rcode_nxdomain
) {
330 adns__query_finish(ads
,qu
,adns_s_nxdomain
);
334 /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */
335 foundsoa
= 0; foundns
= 0;
336 for (rri
= 0; rri
<nscount
; rri
++) {
338 st
= get_rr_temp(ads
,qu
,serv
, dgram
,dglen
,&cbyte
,
339 &rrtype
,&rrclass
, &rdlength
,&rdstart
, 0,0,0,0);
341 if (rrtype
==-1) goto x_truncated
;
342 if (rrclass
!= DNS_CLASS_IN
) {
343 adns__diag(ads
,serv
,"ignoring authority RR with wrong class %d (expected IN=%d)",
344 rrclass
,DNS_CLASS_IN
);
347 if (rrtype
== adns_r_soa_raw
) { foundsoa
= 1; break; }
348 else if (rrtype
== adns_r_ns_raw
) { foundns
= 1; }
351 if (foundsoa
|| !foundns
) {
352 /* Aha ! A NODATA response, good. */
353 adns__query_finish(ads
,qu
,adns_s_nodata
);
357 /* Now what ? No relevant answers, no SOA, and at least some NS's.
358 * Looks like a referral. Just one last chance ... if we came across
359 * a CNAME then perhaps we should do our own CNAME lookup.
362 cname_recurse(ads
,qu
);
366 /* Bloody hell, I thought we asked for recursion ? */
368 adns__diag(ads
,serv
,"server is not willing to do recursive lookups for us");
369 adns__query_fail(ads
,qu
,adns_s_norecurse
);
372 adns__diag(ads
,serv
,"server claims to do recursion, but gave us a referral");
373 adns__query_fail(ads
,qu
,adns_s_serverfault
);
377 /* Now, we have some RRs which we wanted. */
383 { truncated(ads
,qu
,flg_ra
); return; }
388 if (anstart
> dgend
) { truncated(ads
,qu
,f1
); return; }
392 /* Look for CNAMEs in the answer section */
397 adns__diag(ads
,serv
,"server refused our query");
403 case 1: /* Format error */
404 case 3: /* Name Error */
409 adns__diag(ads
,serv
,"received datagram size %d",len
);
416 adns_r_a_mf
= adns_r_a
|adns__qtf_masterfmt
,
419 adns_r_ns
= adns_r_ns_raw
|adns__qtf_deref
,
420 adns_r_ns_mf
= adns_r_ns_raw
|adns__qtf_masterfmt
,
423 adns_r_cname_mf
= adns_r_cname
|adns__qtf_masterfmt
,
426 adns_r_soa
= adns_r_soa_raw
|adns__qtf_mail822
,
427 adns_r_soa_mf
= adns_r_soa_raw
|adns__qtf_masterfmt
,
430 adns_r_null_mf
= adns_r_null
|adns__qtf_masterfmt
,
433 adns_r_ptr
= adns_r_ptr_raw
|adns__qtf_deref
,
434 adns_r_ptr_mf
= adns_r_ptr_raw
|adns__qtf_masterfmt
,
437 adns_r_hinfo_mf
= adns_r_hinfo
|adns__qtf_masterfmt
,
440 adns_r_mx
= adns_r_mx_raw
|adns__qtf_deref
,
441 adns_r_mx_mf
= adns_r_mx_raw
|adns__qtf_masterfmt
,
444 adns_r_txt_mf
= adns_r_txt
|adns__qtf_masterfmt
,
447 adns_r_rp
= adns_r_rp_raw
|adns__qtf_mail822
,
448 adns_r_rp_mf
= adns_r_rp_raw
|adns__qtf_masterfmt