1 /* combined dataset, one file is a collection of various datasets
2 * and subzones. Special case.
3 */
4
5 #include <string.h>
6 #include <syslog.h>
7 #include <stdlib.h>
8 #include "rbldnsd.h"
9
10 /* Special "dataset", which does NOT contain any data itself,
11 * but contains several datasets of other types instead.
12 * Operations:
13 * we have a list of datasets in dslist, that contains
14 * our data (subdataset)
15 * when loading, we have current dataset in dssub, which
16 * will be called to parse a line
17 * we have a list of subzones in zlist for query
18 */
19
20 struct dsdata {
21 struct dataset *dslist; /* list of subzone datasets */
22 struct dataset **dslastp; /* where to connect next dataset */
23 unsigned nds; /* number of datasets in dslist */
24 struct dataset *sdslist; /* saved datasets list */
25 struct zone *zlist; /* list of subzones */
26 };
27
28 definedstype(combined, DSTF_SPECIAL, "several datasets/subzones combined");
29
ds_combined_reset(struct dsdata * dsd,int freeall)30 static void ds_combined_reset(struct dsdata *dsd, int freeall) {
31 struct dataset *dslist = dsd->dslist;
32 while(dslist) {
33 struct dataset *ds = dslist;
34 dslist = dslist->ds_next;
35 ds->ds_type->dst_resetfn(ds->ds_dsd, freeall);
36 if (freeall) free(ds);
37 }
38 dslist = dsd->sdslist;
39 while(dslist) {
40 struct dataset *ds = dslist;
41 dslist = dslist->ds_next;
42 ds->ds_type->dst_resetfn(ds->ds_dsd, 1);
43 free(ds);
44 }
45 dslist = dsd->dslist;
46 memset(dsd, 0, sizeof(*dsd));
47 if (!freeall) dsd->sdslist = dslist;
48 dsd->dslastp = &dsd->dslist;
49 }
50
51 static int
ds_combined_line(struct dataset UNUSED * unused_ds,char UNUSED * unused_s,struct dsctx * dsc)52 ds_combined_line(struct dataset UNUSED *unused_ds,
53 char UNUSED *unused_s, struct dsctx *dsc) {
54 dslog(LOG_ERR, dsc, "invalid/unrecognized entry - specify $DATASET line");
55 return 0;
56 }
57
ds_combined_finishlast(struct dsctx * dsc)58 static void ds_combined_finishlast(struct dsctx *dsc) {
59 struct dataset *dssub = dsc->dsc_subset;
60 if (dssub) {
61 const char *fname = dsc->dsc_fname;
62 dsc->dsc_fname = NULL;
63 dssub->ds_type->dst_finishfn(dssub, dsc);
64 dsc->dsc_subset = NULL;
65 dsc->dsc_fname = fname;
66 }
67 }
68
ds_combined_start(struct dataset UNUSED * ds)69 static void ds_combined_start(struct dataset UNUSED *ds) {
70 }
71
ds_combined_finish(struct dataset * ds,struct dsctx * dsc)72 static void ds_combined_finish(struct dataset *ds, struct dsctx *dsc) {
73 struct dsdata *dsd = ds->ds_dsd;
74 struct zone *zone;
75 unsigned nzones;
76 ds_combined_finishlast(dsc);
77 for(nzones = 0, zone = dsd->zlist; zone; zone = zone->z_next)
78 ++nzones;
79 dsloaded(dsc, "subzones=%u datasets=%u", nzones, dsd->nds);
80 }
81
ds_combined_newset(struct dataset * ds,char * line,struct dsctx * dsc)82 int ds_combined_newset(struct dataset *ds, char *line, struct dsctx *dsc) {
83 char *p;
84 const char *const space = " \t";
85 struct dsdata *dsd = ds->ds_dsd;
86 struct dataset *dssub;
87 struct dslist *dsl;
88 struct zone *zone;
89 unsigned char dn[DNS_MAXDN];
90 unsigned dnlen;
91 char *name;
92
93 ds_combined_finishlast(dsc);
94
95 /* remove comment. only recognize # after a space. */
96 for (p = line; *p; ++p)
97 if (ISCOMMENT(*p) && (p == line || ISSPACE(p[-1]))) {
98 *p = '\0';
99 break;
100 }
101 p = strtok(line, space); /* dataset type */
102 if (!p) return 0;
103 if ((name = strchr(p, ':')) != NULL)
104 *name++ = '\0';
105
106 for(;;) { /* search appropriate dataset */
107
108 if (!(dssub = dsd->sdslist)) {
109 /* end of the saved list, allocate new dataset */
110 const struct dstype **dstp = ds_types, *dst;
111 dstp = ds_types;
112 while(strcmp(p, (*dstp)->dst_name))
113 if (!*++dstp) {
114 dslog(LOG_ERR, dsc, "unknown dataset type `%.60s'", p);
115 return -1;
116 }
117 dst = *dstp;
118 if (dst->dst_flags & DSTF_SPECIAL) {
119 dslog(LOG_ERR, dsc,
120 "dataset type `%s' cannot be used inside `combined'",
121 dst->dst_name);
122 return -1;
123 }
124 dssub = (struct dataset *)
125 ezalloc(sizeof(struct dataset) + dst->dst_size);
126 if (!dssub)
127 return -1;
128 dssub->ds_type = dst;
129 dssub->ds_dsd = (struct dsdata *)(dssub + 1);
130 dssub->ds_mp = ds->ds_mp; /* use parent memory pool */
131 break;
132 }
133
134 else if (strcmp(dssub->ds_type->dst_name, p) == 0) {
135 /* reuse existing one */
136 dsd->sdslist = dssub->ds_next;
137 break;
138 }
139
140 else {
141 /* entry is of different type, free it and try next one */
142 dsd->sdslist = dssub->ds_next;
143 dssub->ds_type->dst_resetfn(dssub->ds_dsd, 1);
144 free(dssub);
145 }
146
147 }
148
149 dssub->ds_next = NULL;
150 *dsd->dslastp = dssub;
151 dsd->dslastp = &dssub->ds_next;
152 if (name && *name) {
153 if (strlen(name) > 20) name[20] = '\0';
154 if (!(dssub->ds_spec = mp_strdup(ds->ds_mp, name)))
155 return -1;
156 }
157
158 if (!(p = strtok(NULL, space)))
159 dswarn(dsc, "no subzone(s) specified for dataset, data will be ignored");
160 else do {
161 if (p[0] == '@' && p[1] == '\0') {
162 dn[0] = '\0';
163 dnlen = 1;
164 }
165 else if (!(dnlen = dns_ptodn(p, dn, sizeof(dn)))) {
166 dswarn(dsc, "invalid domain name `%.60s'", p);
167 continue;
168 }
169 else
170 dns_dntol(dn, dn);
171 zone = newzone(&dsd->zlist, dn, dnlen, ds->ds_mp);
172 dsl = mp_talloc(ds->ds_mp, struct dslist);
173 if (!zone || !dsl) return -1;
174 connectdataset(zone, dssub, dsl);
175 } while((p = strtok(NULL, space)) != NULL);
176
177 ++dsd->nds;
178 dsc->dsc_subset = dssub;
179 dssub->ds_type->dst_resetfn(dssub->ds_dsd, 0);
180 dssub->ds_ttl = ds->ds_ttl;
181 memcpy(dssub->ds_subst, ds->ds_subst, sizeof(ds->ds_subst));
182 dssub->ds_type->dst_startfn(dssub);
183
184 return 1;
185 }
186
187 static int
ds_combined_query(const struct dataset * ds,const struct dnsqinfo * qi,struct dnspacket * pkt)188 ds_combined_query(const struct dataset *ds, const struct dnsqinfo *qi,
189 struct dnspacket *pkt) {
190 struct dnsqinfo sqi;
191 const struct dslist *dsl;
192 int found = 0;
193 const struct zone *zone =
194 findqzone(ds->ds_dsd->zlist,
195 qi->qi_dnlen0 + 1, qi->qi_dnlab, qi->qi_dnlptr,
196 &sqi);
197 if (!zone) return 0;
198 sqi.qi_tflag = qi->qi_tflag;
199 for (dsl = zone->z_dsl; dsl; dsl = dsl->dsl_next)
200 found |= dsl->dsl_queryfn(dsl->dsl_ds, &sqi, pkt);
201 /* if it was a query for our base subzone, always return `found' */
202 return found | (sqi.qi_dnlab ? 0 : NSQUERY_FOUND);
203 }
204
205 #ifndef NO_MASTER_DUMP
206
207 static void
ds_combined_dump(const struct dataset * ds,const unsigned char * odn,FILE * f)208 ds_combined_dump(const struct dataset *ds, const unsigned char *odn, FILE *f) {
209 char bname[DNS_MAXDOMAIN+1], sname[DNS_MAXDOMAIN+1];
210 const struct zone *zone;
211 const struct dslist *dsl;
212 dns_dntop(odn, bname, DNS_MAXDOMAIN + 1);
213 for(zone = ds->ds_dsd->zlist; zone; zone = zone->z_next) {
214 if (zone->z_dnlen == 1)
215 fprintf(f, "$ORIGIN %s.\n", bname);
216 else {
217 dns_dntop(zone->z_dn, sname, DNS_MAXDOMAIN + 1);
218 fprintf(f, "$ORIGIN %s.%s.\n", sname, bname);
219 }
220 for(dsl = zone->z_dsl; dsl; dsl = dsl->dsl_next)
221 dsl->dsl_ds->ds_type->dst_dumpfn(dsl->dsl_ds, NULL/*XXX*/, f);
222 }
223 }
224
225 #endif
226