1 #include <unistd.h>
2 #include "uint16.h"
3 #include "open.h"
4 #include "tai.h"
5 #include "cdb.h"
6 #include "byte.h"
7 #include "case.h"
8 #include "dns.h"
9 #include "seek.h"
10 #include "response.h"
11 
want(const char * owner,const char type[2])12 static int want(const char *owner,const char type[2])
13 {
14   unsigned int pos;
15   static char *d;
16   char x[10];
17   uint16 datalen;
18 
19   pos = dns_packet_skipname(response,response_len,12); if (!pos) return 0;
20   pos += 4;
21 
22   while (pos < response_len) {
23     pos = dns_packet_getname(response,response_len,pos,&d); if (!pos) return 0;
24     pos = dns_packet_copy(response,response_len,pos,x,10); if (!pos) return 0;
25     if (dns_domain_equal(d,owner))
26       if (byte_equal(type,2,x))
27         return 0;
28     uint16_unpack_big(x + 8,&datalen);
29     pos += datalen;
30   }
31   return 1;
32 }
33 
34 static char *d1;
35 
36 static char clientloc[2];
37 static struct tai now;
38 static struct cdb c;
39 
40 static char data[32767];
41 static uint32 dlen;
42 static unsigned int dpos;
43 static char type[2];
44 static uint32 ttl;
45 
find(char * d,int flagwild)46 static int find(char *d,int flagwild)
47 {
48   int r;
49   char ch;
50   struct tai cutoff;
51   char ttd[8];
52   char ttlstr[4];
53   char recordloc[2];
54   double newttl;
55 
56   for (;;) {
57     r = cdb_findnext(&c,d,dns_domain_length(d));
58     if (r <= 0) return r;
59     dlen = cdb_datalen(&c);
60     if (dlen > sizeof data) return -1;
61     if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return -1;
62     dpos = dns_packet_copy(data,dlen,0,type,2); if (!dpos) return -1;
63     dpos = dns_packet_copy(data,dlen,dpos,&ch,1); if (!dpos) return -1;
64     if ((ch == '=' + 1) || (ch == '*' + 1)) {
65       --ch;
66       dpos = dns_packet_copy(data,dlen,dpos,recordloc,2); if (!dpos) return -1;
67       if (byte_diff(recordloc,2,clientloc)) continue;
68     }
69     if (flagwild != (ch == '*')) continue;
70     dpos = dns_packet_copy(data,dlen,dpos,ttlstr,4); if (!dpos) return -1;
71     uint32_unpack_big(ttlstr,&ttl);
72     dpos = dns_packet_copy(data,dlen,dpos,ttd,8); if (!dpos) return -1;
73     if (byte_diff(ttd,8,"\0\0\0\0\0\0\0\0")) {
74       tai_unpack(ttd,&cutoff);
75       if (ttl == 0) {
76 	if (tai_less(&cutoff,&now)) continue;
77 	tai_sub(&cutoff,&cutoff,&now);
78 	newttl = tai_approx(&cutoff);
79 	if (newttl <= 2.0) newttl = 2.0;
80 	if (newttl >= 3600.0) newttl = 3600.0;
81 	ttl = newttl;
82       }
83       else
84 	if (!tai_less(&cutoff,&now)) continue;
85     }
86     return 1;
87   }
88 }
89 
dobytes(unsigned int len)90 static int dobytes(unsigned int len)
91 {
92   char buf[20];
93   if (len > 20) return 0;
94   dpos = dns_packet_copy(data,dlen,dpos,buf,len);
95   if (!dpos) return 0;
96   return response_addbytes(buf,len);
97 }
98 
doname(void)99 static int doname(void)
100 {
101   dpos = dns_packet_getname(data,dlen,dpos,&d1);
102   if (!dpos) return 0;
103   return response_addname(d1);
104 }
105 
doit(char * q,char qtype[2])106 static int doit(char *q,char qtype[2])
107 {
108   unsigned int bpos;
109   unsigned int anpos;
110   unsigned int aupos;
111   unsigned int arpos;
112   char *control;
113   char *wild;
114   int flaggavesoa;
115   int flagfound;
116   int r;
117   int flagns;
118   int flagauthoritative;
119   char x[20];
120   uint16 u16;
121   char addr[8][4];
122   int addrnum;
123   uint32 addrttl;
124   int i;
125 
126   anpos = response_len;
127 
128   control = q;
129   for (;;) {
130     flagns = 0;
131     flagauthoritative = 0;
132     cdb_findstart(&c);
133     while (r = find(control,0)) {
134       if (r == -1) return 0;
135       if (byte_equal(type,2,DNS_T_SOA)) flagauthoritative = 1;
136       if (byte_equal(type,2,DNS_T_NS)) flagns = 1;
137     }
138     if (flagns) break;
139     if (!*control) return 0; /* q is not within our bailiwick */
140     control += *control;
141     control += 1;
142   }
143 
144   if (!flagauthoritative) {
145     response[2] &= ~4;
146     goto AUTHORITY; /* q is in a child zone */
147   }
148 
149 
150   flaggavesoa = 0;
151   flagfound = 0;
152   wild = q;
153 
154   for (;;) {
155     addrnum = 0;
156     addrttl = 0;
157     cdb_findstart(&c);
158     while (r = find(wild,wild != q)) {
159       if (r == -1) return 0;
160       flagfound = 1;
161       if (flaggavesoa && byte_equal(type,2,DNS_T_SOA)) continue;
162       if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME)) continue;
163       if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4)) {
164 	addrttl = ttl;
165 	i = dns_random(addrnum + 1);
166 	if (i < 8) {
167 	  if ((i < addrnum) && (addrnum < 8))
168 	    byte_copy(addr[addrnum],4,addr[i]);
169 	  byte_copy(addr[i],4,data + dpos);
170 	}
171 	if (addrnum < 1000000) ++addrnum;
172 	continue;
173       }
174       if (!response_rstart(q,type,ttl)) return 0;
175       if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_CNAME) || byte_equal(type,2,DNS_T_PTR)) {
176 	if (!doname()) return 0;
177       }
178       else if (byte_equal(type,2,DNS_T_MX)) {
179 	if (!dobytes(2)) return 0;
180 	if (!doname()) return 0;
181       }
182       else if (byte_equal(type,2,DNS_T_SOA)) {
183 	if (!doname()) return 0;
184 	if (!doname()) return 0;
185 	if (!dobytes(20)) return 0;
186         flaggavesoa = 1;
187       }
188       else
189         if (!response_addbytes(data + dpos,dlen - dpos)) return 0;
190       response_rfinish(RESPONSE_ANSWER);
191     }
192     for (i = 0;i < addrnum;++i)
193       if (i < 8) {
194 	if (!response_rstart(q,DNS_T_A,addrttl)) return 0;
195 	if (!response_addbytes(addr[i],4)) return 0;
196 	response_rfinish(RESPONSE_ANSWER);
197       }
198 
199     if (flagfound) break;
200     if (wild == control) break;
201     if (!*wild) break; /* impossible */
202     wild += *wild;
203     wild += 1;
204   }
205 
206   if (!flagfound)
207     response_nxdomain();
208 
209 
210   AUTHORITY:
211   aupos = response_len;
212 
213   if (flagauthoritative && (aupos == anpos)) {
214     cdb_findstart(&c);
215     while (r = find(control,0)) {
216       if (r == -1) return 0;
217       if (byte_equal(type,2,DNS_T_SOA)) {
218         if (!response_rstart(control,DNS_T_SOA,ttl)) return 0;
219 	if (!doname()) return 0;
220 	if (!doname()) return 0;
221 	if (!dobytes(20)) return 0;
222         response_rfinish(RESPONSE_AUTHORITY);
223         break;
224       }
225     }
226   }
227   else
228     if (want(control,DNS_T_NS)) {
229       cdb_findstart(&c);
230       while (r = find(control,0)) {
231         if (r == -1) return 0;
232         if (byte_equal(type,2,DNS_T_NS)) {
233           if (!response_rstart(control,DNS_T_NS,ttl)) return 0;
234 	  if (!doname()) return 0;
235           response_rfinish(RESPONSE_AUTHORITY);
236         }
237       }
238     }
239 
240   arpos = response_len;
241 
242   bpos = anpos;
243   while (bpos < arpos) {
244     bpos = dns_packet_skipname(response,arpos,bpos); if (!bpos) return 0;
245     bpos = dns_packet_copy(response,arpos,bpos,x,10); if (!bpos) return 0;
246     if (byte_equal(x,2,DNS_T_NS) || byte_equal(x,2,DNS_T_MX)) {
247       if (byte_equal(x,2,DNS_T_NS)) {
248         if (!dns_packet_getname(response,arpos,bpos,&d1)) return 0;
249       }
250       else
251         if (!dns_packet_getname(response,arpos,bpos + 2,&d1)) return 0;
252       case_lowerb(d1,dns_domain_length(d1));
253       if (want(d1,DNS_T_A)) {
254 	cdb_findstart(&c);
255 	while (r = find(d1,0)) {
256           if (r == -1) return 0;
257 	  if (byte_equal(type,2,DNS_T_A)) {
258             if (!response_rstart(d1,DNS_T_A,ttl)) return 0;
259 	    if (!dobytes(4)) return 0;
260             response_rfinish(RESPONSE_ADDITIONAL);
261 	  }
262         }
263       }
264     }
265     uint16_unpack_big(x + 8,&u16);
266     bpos += u16;
267   }
268 
269   if (flagauthoritative && (response_len > 512)) {
270     byte_zero(response + RESPONSE_ADDITIONAL,2);
271     response_len = arpos;
272     if (response_len > 512) {
273       byte_zero(response + RESPONSE_AUTHORITY,2);
274       response_len = aupos;
275     }
276   }
277 
278   return 1;
279 }
280 
respond(char * q,char qtype[2],char ip[4])281 int respond(char *q,char qtype[2],char ip[4])
282 {
283   int fd;
284   int r;
285   char key[6];
286 
287   tai_now(&now);
288   fd = open_read("data.cdb");
289   if (fd == -1) return 0;
290   cdb_init(&c,fd);
291 
292   byte_zero(clientloc,2);
293   key[0] = 0;
294   key[1] = '%';
295   byte_copy(key + 2,4,ip);
296   r = cdb_find(&c,key,6);
297   if (!r) r = cdb_find(&c,key,5);
298   if (!r) r = cdb_find(&c,key,4);
299   if (!r) r = cdb_find(&c,key,3);
300   if (!r) r = cdb_find(&c,key,2);
301   if (r == -1) return 0;
302   if (r && (cdb_datalen(&c) == 2))
303     if (cdb_read(&c,clientloc,2,cdb_datapos(&c)) == -1) return 0;
304 
305   r = doit(q,qtype);
306 
307   cdb_free(&c);
308   close(fd);
309   return r;
310 }
311