Commit | Line | Data |
---|---|---|
dc0d77d7 CE |
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | } |