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