release
[hcoop/zz_old/debian/djbdns.git] / tinydns-edit.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include "stralloc.h"
6 #include "buffer.h"
7 #include "exit.h"
8 #include "open.h"
9 #include "getln.h"
10 #include "strerr.h"
11 #include "scan.h"
12 #include "byte.h"
13 #include "str.h"
14 #include "fmt.h"
15 #include "ip4.h"
16 #include "dns.h"
17
18 #define FATAL "tinydns-edit: fatal: "
19
20 #define TTL_NS 259200
21 #define TTL_POSITIVE 86400
22
23 char *fn;
24 char *fnnew;
25
26 void die_usage()
27 {
28 strerr_die1x(100,"tinydns-edit: usage: tinydns-edit data data.new add [ns|childns|host|alias|mx] domain a.b.c.d");
29 }
30 void nomem()
31 {
32 strerr_die2x(111,FATAL,"out of memory");
33 }
34 void die_read()
35 {
36 strerr_die4sys(100,FATAL,"tinydns-edit: fatal: unable to read ",fn,": ");
37 }
38 void die_write()
39 {
40 strerr_die4sys(100,FATAL,"tinydns-edit: fatal: unable to write ",fnnew,": ");
41 }
42
43 char mode;
44 static char *target;
45 char targetip[4];
46
47 int fd;
48 buffer b;
49 char bspace[1024];
50
51 int fdnew;
52 buffer bnew;
53 char bnewspace[1024];
54
55 static stralloc line;
56 int match = 1;
57
58 #define NUMFIELDS 10
59 static stralloc f[NUMFIELDS];
60
61 static char *d1;
62 static char *d2;
63 char ip[4];
64 char ipstr[IP4_FMT];
65 char strnum[FMT_ULONG];
66
67 static char *names[26];
68 static int used[26];
69
70 void put(const char *buf,unsigned int len)
71 {
72 if (buffer_putalign(&bnew,buf,len) == -1) die_write();
73 }
74
75 int main(int argc,char **argv)
76 {
77 unsigned long ttl;
78 struct stat st;
79 int i;
80 int j;
81 int k;
82 char ch;
83
84 if (!*argv) die_usage();
85
86 if (!*++argv) die_usage();
87 fn = *argv;
88
89 if (!*++argv) die_usage();
90 fnnew = *argv;
91
92 if (!*++argv) die_usage();
93 if (str_diff(*argv,"add")) die_usage();
94
95 if (!*++argv) die_usage();
96 if (str_equal(*argv,"ns")) mode = '.';
97 else if (str_equal(*argv,"childns")) mode = '&';
98 else if (str_equal(*argv,"host")) mode = '=';
99 else if (str_equal(*argv,"alias")) mode = '+';
100 else if (str_equal(*argv,"mx")) mode = '@';
101 else die_usage();
102
103 if (!*++argv) die_usage();
104 if (!dns_domain_fromdot(&target,*argv,str_len(*argv))) nomem();
105
106 if (!*++argv) die_usage();
107 if (!ip4_scan(*argv,targetip)) die_usage();
108
109 umask(077);
110
111 fd = open_read(fn);
112 if (fd == -1) die_read();
113 if (fstat(fd,&st) == -1) die_read();
114 buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace);
115
116 fdnew = open_trunc(fnnew);
117 if (fdnew == -1) die_write();
118 if (fchmod(fdnew,st.st_mode & 0644) == -1) die_write();
119 buffer_init(&bnew,buffer_unixwrite,fdnew,bnewspace,sizeof bnewspace);
120
121 switch(mode) {
122 case '.': case '&':
123 ttl = TTL_NS;
124 for (i = 0;i < 26;++i) {
125 ch = 'a' + i;
126 if (!stralloc_copyb(&f[0],&ch,1)) nomem();
127 if (!stralloc_cats(&f[0],".ns.")) nomem();
128 if (!dns_domain_todot_cat(&f[0],target)) nomem();
129 if (!dns_domain_fromdot(&names[i],f[0].s,f[0].len)) nomem();
130 }
131 break;
132 case '+': case '=':
133 ttl = TTL_POSITIVE;
134 break;
135 case '@':
136 ttl = TTL_POSITIVE;
137 for (i = 0;i < 26;++i) {
138 ch = 'a' + i;
139 if (!stralloc_copyb(&f[0],&ch,1)) nomem();
140 if (!stralloc_cats(&f[0],".mx.")) nomem();
141 if (!dns_domain_todot_cat(&f[0],target)) nomem();
142 if (!dns_domain_fromdot(&names[i],f[0].s,f[0].len)) nomem();
143 }
144 break;
145 }
146
147 while (match) {
148 if (getln(&b,&line,&match,'\n') == -1) die_read();
149
150 put(line.s,line.len);
151 if (line.len && !match) put("\n",1);
152
153 while (line.len) {
154 ch = line.s[line.len - 1];
155 if ((ch != ' ') && (ch != '\t') && (ch != '\n')) break;
156 --line.len;
157 }
158 if (!line.len) continue;
159 if (line.s[0] == '#') continue;
160
161 j = 1;
162 for (i = 0;i < NUMFIELDS;++i) {
163 if (j >= line.len) {
164 if (!stralloc_copys(&f[i],"")) nomem();
165 }
166 else {
167 k = byte_chr(line.s + j,line.len - j,':');
168 if (!stralloc_copyb(&f[i],line.s + j,k)) nomem();
169 j += k + 1;
170 }
171 }
172
173 switch(mode) {
174 case '.': case '&':
175 if (line.s[0] == mode) {
176 if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
177 if (dns_domain_equal(d1,target)) {
178 if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) {
179 if (!stralloc_cats(&f[2],".ns.")) nomem();
180 if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem();
181 }
182 if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
183 if (!stralloc_0(&f[3])) nomem();
184 if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_NS;
185 for (i = 0;i < 26;++i)
186 if (dns_domain_equal(d2,names[i])) {
187 used[i] = 1;
188 break;
189 }
190 }
191 }
192 break;
193
194 case '=':
195 if (line.s[0] == '=') {
196 if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
197 if (dns_domain_equal(d1,target))
198 strerr_die2x(100,FATAL,"host name already used");
199 if (!stralloc_0(&f[1])) nomem();
200 if (ip4_scan(f[1].s,ip))
201 if (byte_equal(ip,4,targetip))
202 strerr_die2x(100,FATAL,"IP address already used");
203 }
204 break;
205
206 case '@':
207 if (line.s[0] == '@') {
208 if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
209 if (dns_domain_equal(d1,target)) {
210 if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) {
211 if (!stralloc_cats(&f[2],".mx.")) nomem();
212 if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem();
213 }
214 if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
215 if (!stralloc_0(&f[4])) nomem();
216 if (!scan_ulong(f[4].s,&ttl)) ttl = TTL_POSITIVE;
217 for (i = 0;i < 26;++i)
218 if (dns_domain_equal(d2,names[i])) {
219 used[i] = 1;
220 break;
221 }
222 }
223 }
224 break;
225 }
226 }
227
228 if (!stralloc_copyb(&f[0],&mode,1)) nomem();
229 if (!dns_domain_todot_cat(&f[0],target)) nomem();
230 if (!stralloc_cats(&f[0],":")) nomem();
231 if (!stralloc_catb(&f[0],ipstr,ip4_fmt(ipstr,targetip))) nomem();
232 switch(mode) {
233 case '.': case '&': case '@':
234 for (i = 0;i < 26;++i)
235 if (!used[i])
236 break;
237 if (i >= 26)
238 strerr_die2x(100,FATAL,"too many records for that domain");
239 ch = 'a' + i;
240 if (!stralloc_cats(&f[0],":")) nomem();
241 if (!stralloc_catb(&f[0],&ch,1)) nomem();
242 if (mode == '@')
243 if (!stralloc_cats(&f[0],":")) nomem();
244 break;
245 }
246 if (!stralloc_cats(&f[0],":")) nomem();
247 if (!stralloc_catb(&f[0],strnum,fmt_ulong(strnum,ttl))) nomem();
248 if (!stralloc_cats(&f[0],"\n")) nomem();
249 put(f[0].s,f[0].len);
250
251 if (buffer_flush(&bnew) == -1) die_write();
252 if (fsync(fdnew) == -1) die_write();
253 if (close(fdnew) == -1) die_write(); /* NFS dorks */
254 if (rename(fnnew,fn) == -1)
255 strerr_die6sys(111,FATAL,"unable to move ",fnnew," to ",fn,": ");
256 _exit(0);
257 }