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_domain_perm(adns_state ads
, adns_query qu
, int serv
,
42 const byte
*dgram
, int dglen
,
43 int *cbyte_io
, int max
, char **domain_r
) {
44 /* Returns 0 for OK (*domain_r set) or truncated (*domain_r null)
45 * or any other adns_s_* value.
47 int cbyte
, sused
, lablen
;
49 /* If we follow a pointer we set cbyte_io to 0 to indicate that
50 * we've lost our original starting and ending points; we don't
51 * put the end of the pointed-to thing into the original *cbyte_io.
57 if (cbyte
>=max
) goto x_truncated
;
61 if ((lablen
&0x0c000) != 0x0c0000) return adns_s_unknownreply
;
62 if (cbyte_io
) { *cbyte_io
= cbyte
; cbyte_io
= 0; }
63 cbyte
= (lablen
&0x3fff) + DNS_HDR_SIZE
;
67 if (cbyte
+lablen
>=max
) bgoto x_truncated
;
68 if (qu
->ans
.used
!= sused
)
69 if (!adns__vbuf_append(&qu
->ans
,".",1)) return adns_s_nolocalmem
;
70 if (qu
->flags
& adns_qf_anyquote
) {
71 if (!vbuf__append_quoted1035(&qu
->ans
,dgram
+cbyte
,lablen
))
72 return adns_s_nolocalmem
;
74 if (!ctype_isalpha(dgram
[cbyte
])) return adns_s_invaliddomain
;
75 for (i
= cbyte
+1; i
<cbyte
+lablen
; i
++) {
77 if (ch
!= '-' && !ctype_isalpha(ch
) && !ctype_isdigit(ch
))
78 return adns_s_invaliddomain
;
80 if (!adns__vbuf_append(&qu
->ans
,dgram
+cbyte
,lablen
))
81 return adns_s_nolocalmem
;
84 if (cbyte_io
) *cbyte_io
= cbyte
;
85 if (!adns__vbuf_append(&qu
->ans
,"",1)) return adns_s_nolocalmem
;
86 *domain_r
= qu
->ans
.buf
+sused
;
90 return cbyte_io ?
-1 : adns_s_serverfaulty
;
93 static adns_status
get_domain_temp(adns_state ads
, adns_query qu
, int serv
,
94 const byte
*dgram
, int dglen
,
95 int *cbyte_io
, int max
, char **domain_r
) {
100 st
= get_domain_perm(ads
,qu
,serv
,dgram
,dglen
,cbyte_io
,max
,domain_r
);
105 /* fixme: sensible comparison of owners */
107 static adns_status
get_rr_temp(adns_state ads
, adns_query qu
, int serv
,
108 const byte
*dgram
, int dglen
,
110 int *type_r
, int *class_r
, int *rdlen_r
, int *rdstart_r
,
112 int cbyte
, tmp
, rdlen
;
115 st
= get_domain_temp(ads
,qu
,serv
,dgram
,dglen
,&cbyte
,dglen
,owner_r
);
118 if (cbyte
+10>len
) goto x_truncated
;
119 GET_W(cbyte
,tmp
); if (type_r
) *type_r
= tmp
;
120 GET_W(cbyte
,tmp
); if (class_r
) *class_r
= tmp
;
121 cbyte
+= 4; /* we skip the TTL */
122 GET_W(cbyte
,rdlen
); if (rdlen_r
) *rdlen_r
= tmp
;
123 if (rdstart_r
) *rdstart_r
= cbyte
;
125 if (cbyte
>dglen
) goto x_truncated
;
130 *owner_r
= 0; return 0;;
133 void adns__procdgram(adns_state ads
, const byte
*dgram
, int dglen
, int serv
) {
134 int cbyte
, anstart
, rrstart
, lablen
, wantedrrs
, get_t
;
138 if (dglen
<DNS_HDR_SIZE
) {
139 adns__diag(ads
,serv
,"received datagram too short for message header (%d)",len
);
145 GET_W(cbyte
,qdcount
);
146 GET_W(cbyte
,ancount
);
147 GET_W(cbyte
,nscount
);
148 GET_W(cbyte
,arcount
);
149 assert(cbyte
== DNS_HDR_SIZE
);
152 adns__diag(ads
,serv
,"server sent us a query, not a response");
156 adns__diag(ads
,serv
,"server sent us unknown opcode %d (wanted 0=QUERY)",
161 adns__diag(ads
,serv
,"server sent reply without quoting our question");
163 } else if (qdcount
>1) {
164 adns__diag(ads
,serv
,"server claimed to answer %d questions with one message",
168 for (qu
= ads
->timew
; qu
= nqu
; qu
++) {
170 if (qu
->id
!= id
) continue;
171 if (len
< qu
->querylen
) continue;
172 if (memcmp(qu
->querymsg
+DNSHDRSIZE
,dgram
+DNSHDRSIZE
,qu
->querylen
-DNSHDRSIZE
))
176 anstart
= qu
->querylen
;
178 adns__debug(ads
,serv
,"reply not found (id=%02x)",id
);
182 adns__diag(ads
,serv
,"server thinks we didn't ask for recursive lookup");
183 adns__query_fail(ads
,qu
,adns_s_serverfaulty
);
192 case rcode_formaterror
:
193 adns__warn(ads
,serv
,"server cannot understand our query (Format Error)");
194 adns__query_fail(ads
,qu
,adns_s_serverfaulty
);
197 adns__query_fail(ads
,qu
,adns_s_serverfailure
);
200 adns__warn(ads
,serv
,"server claims not to implement our query");
201 adns__query_fail(ads
,qu
,adns_s_notimplemented
);
204 adns__warn(ads
,serv
,"server refused our query");
205 adns__query_fail(ads
,qu
,adns_s_refused
);
208 adns__warn(ads
,serv
,"server gave unknown response code %d",rcode
);
209 adns__query_fail(ads
,qu
,adns_s_reasonunknown
);
213 /* Now, take a look at the answer section, and see if it is complete.
214 * If it has any CNAMEs we stuff them in the answer.
217 for (rri
= 0; rri
<ancount
; rri
++) {
219 st
= get_rr_temp(ads
,qu
,serv
,
222 &rrtype
,&rrclass
,&rdlength
,&cowner
);
223 if (st
) adns__query_fail(ads
,qu
,st
);
224 if (rrclass
!= DNS_CLASS_IN
) {
225 adns__diag(ads
,serv
,"ignoring RR with wrong class %d (expected IN=%d)",
226 rrclass
,DNS_CLASS_IN
);
229 if (strcmp_quoted1035(cowner
, qu
->cname ? qu
->cname
: qu
->owner
)) {
230 adns__debug(ads
,serv
,"ignoring answer RR with irrelevant owner \"%s\"",cowner
);
234 (qu
->type
& adns__rrt_typemask
) != adns_cname
&&
235 rrtype
== adns_cname
) { /* Ignore second and subsequent CNAMEs */
236 qu
->cname
= get_domain_perm(ads
,qu
,dgram
,len
,rdstart
,rdstart
+rdlength
);
237 /* If we find the answer section truncated after this point we restart
238 * the query at the CNAME; otherwise we can use it as-is.
240 } else if (rrtype
== (qu
->type
& adns__rrt_typemask
)) {
243 adns__debug(ads
,serv
,"ignoring answer RR with irrelevant type %d",rrtype
);
247 /* If we got here then the answer section is intact. */
251 /* Oops, NODATA or NXDOMAIN or perhaps a referral (which would be a problem) */
253 if (rcode
== rcode_nxdomain
) {
254 adnns__query_finish(ads
,qu
,adns_s_nxdomain
);
258 /* RFC2308: NODATA has _either_ a SOA _or_ _no_ NS records in authority section */
259 for (rri
= 0; rri
<nscount
; rri
++) {
266 adns__diag(ads
,serv
,"server is not willing to do recursive lookups for us");
267 adns__query_fail(ads
,qu
,adns_s_norecurse
);
275 if (anstart
> dgend
) { truncated(ads
,qu
,f1
); return; }
279 /* Look for CNAMEs in the answer section */
284 adns__diag(ads
,serv
,"server refused our query");
290 case 1: /* Format error */
291 case 3: /* Name Error */
296 adns__diag(ads
,serv
,"received datagram size %d",len
);