Commit | Line | Data |
---|---|---|
dc0d77d7 CE |
1 | #include <stdio.h> |
2 | #include <unistd.h> | |
3 | #include <sys/types.h> | |
4 | #include <sys/stat.h> | |
5 | #include "uint16.h" | |
6 | #include "uint32.h" | |
7 | #include "str.h" | |
8 | #include "byte.h" | |
9 | #include "fmt.h" | |
10 | #include "ip4.h" | |
11 | #include "exit.h" | |
12 | #include "case.h" | |
13 | #include "scan.h" | |
14 | #include "buffer.h" | |
15 | #include "strerr.h" | |
16 | #include "getln.h" | |
17 | #include "cdb_make.h" | |
18 | #include "stralloc.h" | |
19 | #include "open.h" | |
20 | #include "dns.h" | |
21 | ||
22 | #define TTL_NS 259200 | |
23 | #define TTL_POSITIVE 86400 | |
24 | #define TTL_NEGATIVE 2560 | |
25 | ||
26 | #define FATAL "tinydns-data: fatal: " | |
27 | ||
28 | void die_datatmp(void) | |
29 | { | |
30 | strerr_die2sys(111,FATAL,"unable to create data.tmp: "); | |
31 | } | |
32 | void nomem(void) | |
33 | { | |
34 | strerr_die1sys(111,FATAL); | |
35 | } | |
36 | ||
37 | void ttdparse(stralloc *sa,char ttd[8]) | |
38 | { | |
39 | unsigned int i; | |
40 | char ch; | |
41 | ||
42 | byte_zero(ttd,8); | |
43 | for (i = 0;(i < 16) && (i < sa->len);++i) { | |
44 | ch = sa->s[i]; | |
45 | if ((ch >= '0') && (ch <= '9')) | |
46 | ch -= '0'; | |
47 | else if ((ch >= 'a') && (ch <= 'f')) | |
48 | ch -= 'a' - 10; | |
49 | else | |
50 | ch = 0; | |
51 | if (!(i & 1)) ch <<= 4; | |
52 | ttd[i >> 1] |= ch; | |
53 | } | |
54 | } | |
55 | ||
56 | void locparse(stralloc *sa,char loc[2]) | |
57 | { | |
58 | loc[0] = (sa->len > 0) ? sa->s[0] : 0; | |
59 | loc[1] = (sa->len > 1) ? sa->s[1] : 0; | |
60 | } | |
61 | ||
62 | void ipprefix_cat(stralloc *out,char *s) | |
63 | { | |
64 | unsigned long u; | |
65 | char ch; | |
66 | unsigned int j; | |
67 | ||
68 | for (;;) | |
69 | if (*s == '.') | |
70 | ++s; | |
71 | else { | |
72 | j = scan_ulong(s,&u); | |
73 | if (!j) return; | |
74 | s += j; | |
75 | ch = u; | |
76 | if (!stralloc_catb(out,&ch,1)) nomem(); | |
77 | } | |
78 | } | |
79 | ||
80 | void txtparse(stralloc *sa) | |
81 | { | |
82 | char ch; | |
83 | unsigned int i; | |
84 | unsigned int j; | |
85 | ||
86 | j = 0; | |
87 | i = 0; | |
88 | while (i < sa->len) { | |
89 | ch = sa->s[i++]; | |
90 | if (ch == '\\') { | |
91 | if (i >= sa->len) break; | |
92 | ch = sa->s[i++]; | |
93 | if ((ch >= '0') && (ch <= '7')) { | |
94 | ch -= '0'; | |
95 | if ((i < sa->len) && (sa->s[i] >= '0') && (sa->s[i] <= '7')) { | |
96 | ch <<= 3; | |
97 | ch += sa->s[i++] - '0'; | |
98 | if ((i < sa->len) && (sa->s[i] >= '0') && (sa->s[i] <= '7')) { | |
99 | ch <<= 3; | |
100 | ch += sa->s[i++] - '0'; | |
101 | } | |
102 | } | |
103 | } | |
104 | } | |
105 | sa->s[j++] = ch; | |
106 | } | |
107 | sa->len = j; | |
108 | } | |
109 | ||
110 | char defaultsoa[20]; | |
111 | ||
112 | void defaultsoa_init(int fd) | |
113 | { | |
114 | struct stat st; | |
115 | if (fstat(fd,&st) == -1) | |
116 | strerr_die2sys(111,FATAL,"unable to stat data: "); | |
117 | uint32_pack_big(defaultsoa,st.st_mtime); | |
118 | if (byte_equal(defaultsoa,4,"\0\0\0\0")) | |
119 | defaultsoa[3] = 1; | |
120 | byte_copy(defaultsoa + 4,16,"\0\0\100\000\0\0\010\000\0\020\000\000\0\0\012\000"); | |
121 | } | |
122 | ||
123 | int fdcdb; | |
124 | struct cdb_make cdb; | |
125 | static stralloc key; | |
126 | static stralloc result; | |
127 | ||
128 | void rr_add(const char *buf,unsigned int len) | |
129 | { | |
130 | if (!stralloc_catb(&result,buf,len)) nomem(); | |
131 | } | |
132 | void rr_addname(const char *d) | |
133 | { | |
134 | rr_add(d,dns_domain_length(d)); | |
135 | } | |
136 | void rr_start(const char type[2],unsigned long ttl,const char ttd[8],const char loc[2]) | |
137 | { | |
138 | char buf[4]; | |
139 | if (!stralloc_copyb(&result,type,2)) nomem(); | |
140 | if (byte_equal(loc,2,"\0\0")) | |
141 | rr_add("=",1); | |
142 | else { | |
143 | rr_add(">",1); | |
144 | rr_add(loc,2); | |
145 | } | |
146 | uint32_pack_big(buf,ttl); | |
147 | rr_add(buf,4); | |
148 | rr_add(ttd,8); | |
149 | } | |
150 | void rr_finish(const char *owner) | |
151 | { | |
152 | if (byte_equal(owner,2,"\1*")) { | |
153 | owner += 2; | |
154 | result.s[2] -= 19; | |
155 | } | |
156 | if (!stralloc_copyb(&key,owner,dns_domain_length(owner))) nomem(); | |
157 | case_lowerb(key.s,key.len); | |
158 | if (cdb_make_add(&cdb,key.s,key.len,result.s,result.len) == -1) | |
159 | die_datatmp(); | |
160 | } | |
161 | ||
162 | buffer b; | |
163 | char bspace[1024]; | |
164 | ||
165 | static stralloc line; | |
166 | int match = 1; | |
167 | unsigned long linenum = 0; | |
168 | ||
169 | #define NUMFIELDS 15 | |
170 | static stralloc f[NUMFIELDS]; | |
171 | ||
172 | static char *d1; | |
173 | static char *d2; | |
174 | char dptr[DNS_NAME4_DOMAIN]; | |
175 | ||
176 | char strnum[FMT_ULONG]; | |
177 | ||
178 | void syntaxerror(const char *why) | |
179 | { | |
180 | strnum[fmt_ulong(strnum,linenum)] = 0; | |
181 | strerr_die4x(111,FATAL,"unable to parse data line ",strnum,why); | |
182 | } | |
183 | ||
184 | int main() | |
185 | { | |
186 | int fddata; | |
187 | int i; | |
188 | int j; | |
189 | int k; | |
190 | char ch; | |
191 | unsigned long ttl; | |
192 | char ttd[8]; | |
193 | char loc[2]; | |
194 | unsigned long u; | |
195 | char ip[4]; | |
196 | char type[2]; | |
197 | char soa[20]; | |
198 | char buf[4]; | |
199 | ||
200 | umask(022); | |
201 | ||
202 | fddata = open_read("data"); | |
203 | if (fddata == -1) | |
204 | strerr_die2sys(111,FATAL,"unable to open data: "); | |
205 | defaultsoa_init(fddata); | |
206 | ||
207 | buffer_init(&b,buffer_unixread,fddata,bspace,sizeof bspace); | |
208 | ||
209 | fdcdb = open_trunc("data.tmp"); | |
210 | if (fdcdb == -1) die_datatmp(); | |
211 | if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp(); | |
212 | ||
213 | while (match) { | |
214 | ++linenum; | |
215 | if (getln(&b,&line,&match,'\n') == -1) | |
216 | strerr_die2sys(111,FATAL,"unable to read line: "); | |
217 | ||
218 | while (line.len) { | |
219 | ch = line.s[line.len - 1]; | |
220 | if ((ch != ' ') && (ch != '\t') && (ch != '\n')) break; | |
221 | --line.len; | |
222 | } | |
223 | if (!line.len) continue; | |
224 | if (line.s[0] == '#') continue; | |
225 | if (line.s[0] == '-') continue; | |
226 | ||
227 | j = 1; | |
228 | for (i = 0;i < NUMFIELDS;++i) { | |
229 | if (j >= line.len) { | |
230 | if (!stralloc_copys(&f[i],"")) nomem(); | |
231 | } | |
232 | else { | |
233 | k = byte_chr(line.s + j,line.len - j,':'); | |
234 | if (!stralloc_copyb(&f[i],line.s + j,k)) nomem(); | |
235 | j += k + 1; | |
236 | } | |
237 | } | |
238 | ||
239 | switch(line.s[0]) { | |
240 | ||
241 | case '%': | |
242 | locparse(&f[0],loc); | |
243 | if (!stralloc_copyb(&key,"\0%",2)) nomem(); | |
244 | if (!stralloc_0(&f[1])) nomem(); | |
245 | ipprefix_cat(&key,f[1].s); | |
246 | if (cdb_make_add(&cdb,key.s,key.len,loc,2) == -1) | |
247 | die_datatmp(); | |
248 | break; | |
249 | ||
250 | case 'Z': | |
251 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
252 | ||
253 | if (!stralloc_0(&f[3])) nomem(); | |
254 | if (!scan_ulong(f[3].s,&u)) uint32_unpack_big(defaultsoa,&u); | |
255 | uint32_pack_big(soa,u); | |
256 | if (!stralloc_0(&f[4])) nomem(); | |
257 | if (!scan_ulong(f[4].s,&u)) uint32_unpack_big(defaultsoa + 4,&u); | |
258 | uint32_pack_big(soa + 4,u); | |
259 | if (!stralloc_0(&f[5])) nomem(); | |
260 | if (!scan_ulong(f[5].s,&u)) uint32_unpack_big(defaultsoa + 8,&u); | |
261 | uint32_pack_big(soa + 8,u); | |
262 | if (!stralloc_0(&f[6])) nomem(); | |
263 | if (!scan_ulong(f[6].s,&u)) uint32_unpack_big(defaultsoa + 12,&u); | |
264 | uint32_pack_big(soa + 12,u); | |
265 | if (!stralloc_0(&f[7])) nomem(); | |
266 | if (!scan_ulong(f[7].s,&u)) uint32_unpack_big(defaultsoa + 16,&u); | |
267 | uint32_pack_big(soa + 16,u); | |
268 | ||
269 | if (!stralloc_0(&f[8])) nomem(); | |
270 | if (!scan_ulong(f[8].s,&ttl)) ttl = TTL_NEGATIVE; | |
271 | ttdparse(&f[9],ttd); | |
272 | locparse(&f[10],loc); | |
273 | ||
274 | rr_start(DNS_T_SOA,ttl,ttd,loc); | |
275 | if (!dns_domain_fromdot(&d2,f[1].s,f[1].len)) nomem(); | |
276 | rr_addname(d2); | |
277 | if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem(); | |
278 | rr_addname(d2); | |
279 | rr_add(soa,20); | |
280 | rr_finish(d1); | |
281 | break; | |
282 | ||
283 | case '.': case '&': | |
284 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
285 | if (!stralloc_0(&f[3])) nomem(); | |
286 | if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_NS; | |
287 | ttdparse(&f[4],ttd); | |
288 | locparse(&f[5],loc); | |
289 | ||
290 | if (!stralloc_0(&f[1])) nomem(); | |
291 | ||
292 | if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) { | |
293 | if (!stralloc_cats(&f[2],".ns.")) nomem(); | |
294 | if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem(); | |
295 | } | |
296 | if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem(); | |
297 | ||
298 | if (line.s[0] == '.') { | |
299 | rr_start(DNS_T_SOA,ttl ? TTL_NEGATIVE : 0,ttd,loc); | |
300 | rr_addname(d2); | |
301 | rr_add("\12hostmaster",11); | |
302 | rr_addname(d1); | |
303 | rr_add(defaultsoa,20); | |
304 | rr_finish(d1); | |
305 | } | |
306 | ||
307 | rr_start(DNS_T_NS,ttl,ttd,loc); | |
308 | rr_addname(d2); | |
309 | rr_finish(d1); | |
310 | ||
311 | if (ip4_scan(f[1].s,ip)) { | |
312 | rr_start(DNS_T_A,ttl,ttd,loc); | |
313 | rr_add(ip,4); | |
314 | rr_finish(d2); | |
315 | } | |
316 | ||
317 | break; | |
318 | ||
319 | case '+': case '=': | |
320 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
321 | if (!stralloc_0(&f[2])) nomem(); | |
322 | if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; | |
323 | ttdparse(&f[3],ttd); | |
324 | locparse(&f[4],loc); | |
325 | ||
326 | if (!stralloc_0(&f[1])) nomem(); | |
327 | ||
328 | if (ip4_scan(f[1].s,ip)) { | |
329 | rr_start(DNS_T_A,ttl,ttd,loc); | |
330 | rr_add(ip,4); | |
331 | rr_finish(d1); | |
332 | ||
333 | if (line.s[0] == '=') { | |
334 | dns_name4_domain(dptr,ip); | |
335 | rr_start(DNS_T_PTR,ttl,ttd,loc); | |
336 | rr_addname(d1); | |
337 | rr_finish(dptr); | |
338 | } | |
339 | } | |
340 | break; | |
341 | ||
342 | case '@': | |
343 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
344 | if (!stralloc_0(&f[4])) nomem(); | |
345 | if (!scan_ulong(f[4].s,&ttl)) ttl = TTL_POSITIVE; | |
346 | ttdparse(&f[5],ttd); | |
347 | locparse(&f[6],loc); | |
348 | ||
349 | if (!stralloc_0(&f[1])) nomem(); | |
350 | ||
351 | if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) { | |
352 | if (!stralloc_cats(&f[2],".mx.")) nomem(); | |
353 | if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem(); | |
354 | } | |
355 | if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem(); | |
356 | ||
357 | if (!stralloc_0(&f[3])) nomem(); | |
358 | if (!scan_ulong(f[3].s,&u)) u = 0; | |
359 | ||
360 | rr_start(DNS_T_MX,ttl,ttd,loc); | |
361 | uint16_pack_big(buf,u); | |
362 | rr_add(buf,2); | |
363 | rr_addname(d2); | |
364 | rr_finish(d1); | |
365 | ||
366 | if (ip4_scan(f[1].s,ip)) { | |
367 | rr_start(DNS_T_A,ttl,ttd,loc); | |
368 | rr_add(ip,4); | |
369 | rr_finish(d2); | |
370 | } | |
371 | break; | |
372 | ||
373 | case '^': case 'C': | |
374 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
375 | if (!dns_domain_fromdot(&d2,f[1].s,f[1].len)) nomem(); | |
376 | if (!stralloc_0(&f[2])) nomem(); | |
377 | if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; | |
378 | ttdparse(&f[3],ttd); | |
379 | locparse(&f[4],loc); | |
380 | ||
381 | if (line.s[0] == 'C') | |
382 | rr_start(DNS_T_CNAME,ttl,ttd,loc); | |
383 | else | |
384 | rr_start(DNS_T_PTR,ttl,ttd,loc); | |
385 | rr_addname(d2); | |
386 | rr_finish(d1); | |
387 | break; | |
388 | ||
389 | case '\'': | |
390 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
391 | if (!stralloc_0(&f[2])) nomem(); | |
392 | if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE; | |
393 | ttdparse(&f[3],ttd); | |
394 | locparse(&f[4],loc); | |
395 | ||
396 | rr_start(DNS_T_TXT,ttl,ttd,loc); | |
397 | ||
398 | txtparse(&f[1]); | |
399 | i = 0; | |
400 | while (i < f[1].len) { | |
401 | k = f[1].len - i; | |
402 | if (k > 127) k = 127; | |
403 | ch = k; | |
404 | rr_add(&ch,1); | |
405 | rr_add(f[1].s + i,k); | |
406 | i += k; | |
407 | } | |
408 | ||
409 | rr_finish(d1); | |
410 | break; | |
411 | ||
412 | case ':': | |
413 | if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem(); | |
414 | if (!stralloc_0(&f[3])) nomem(); | |
415 | if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_POSITIVE; | |
416 | ttdparse(&f[4],ttd); | |
417 | locparse(&f[5],loc); | |
418 | ||
419 | if (!stralloc_0(&f[1])) nomem(); | |
420 | scan_ulong(f[1].s,&u); | |
421 | uint16_pack_big(type,u); | |
422 | if (byte_equal(type,2,DNS_T_AXFR)) | |
423 | syntaxerror(": type AXFR prohibited"); | |
424 | if (byte_equal(type,2,"\0\0")) | |
425 | syntaxerror(": type 0 prohibited"); | |
426 | if (byte_equal(type,2,DNS_T_SOA)) | |
427 | syntaxerror(": type SOA prohibited"); | |
428 | if (byte_equal(type,2,DNS_T_NS)) | |
429 | syntaxerror(": type NS prohibited"); | |
430 | if (byte_equal(type,2,DNS_T_CNAME)) | |
431 | syntaxerror(": type CNAME prohibited"); | |
432 | if (byte_equal(type,2,DNS_T_PTR)) | |
433 | syntaxerror(": type PTR prohibited"); | |
434 | if (byte_equal(type,2,DNS_T_MX)) | |
435 | syntaxerror(": type MX prohibited"); | |
436 | ||
437 | txtparse(&f[2]); | |
438 | ||
439 | rr_start(type,ttl,ttd,loc); | |
440 | rr_add(f[2].s,f[2].len); | |
441 | rr_finish(d1); | |
442 | break; | |
443 | ||
444 | default: | |
445 | syntaxerror(": unrecognized leading character"); | |
446 | } | |
447 | } | |
448 | ||
449 | if (cdb_make_finish(&cdb) == -1) die_datatmp(); | |
450 | if (fsync(fdcdb) == -1) die_datatmp(); | |
451 | if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */ | |
452 | if (rename("data.tmp","data.cdb") == -1) | |
453 | strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: "); | |
454 | ||
455 | _exit(0); | |
456 | } |