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