+
+static const char out_table[32]="0123456789abcdefghijklmnopqrstuv";
+
+static inline int enoughinput(struct dnsbitenc *be) {
+ return be->npending >= 5;
+}
+static inline void startlabel(struct dnsbitenc *be) {
+ be->labremain=63;
+}
+static inline void outputchar(struct dnsbitenc *be) {
+ /* there must be enough input and enough space */
+ if (!be->labremain) {
+ *--(be->out) = 63;
+ startlabel(be);
+ }
+ *--(be->out) = out_table[be->pending & 0x1f];
+ be->pending >>= 5;
+ be->npending -= 5;
+}
+
+void dnsbitenc_start(struct dnsbitenc *be, uint8_t *buf, int buflen) {
+ assert(buflen>0);
+ be->pending=0;
+ be->npending=0;
+ be->bufstop=buf+1;
+ be->out=buf+buflen;
+ startlabel(be);
+}
+
+void dnsbitenc_addbits(struct dnsbitenc *be, uint32_t val, int nbits) {
+ assert(nbits<=28); /* otherwise pending might overflow */
+ assert(!(val & ~(((uint32_t)1 << nbits)-1)));
+ be->pending |= val << be->npending;
+ be->npending += nbits;
+ while (enoughinput(be)) {
+ assert(be->out > be->bufstop);
+ outputchar(be);
+ }
+}
+
+void dnsbitenc_addu32(struct dnsbitenc *be, uint32_t val) {
+ dnsbitenc_addbits(be, val & 0xffff, 16);
+ dnsbitenc_addbits(be, val >> 16, 16);
+}
+
+int dnsbitenc_restbytes(struct dnsbitenc *be, uint8_t *bytes, int qty) {
+ for (;;) {
+ if (buf==bufstop) {
+ return qty + ((be->npending + 7) / 8);
+ }
+ if (be->npending<5) {
+ if (qty) {
+ be->pending |= (*bytes++) << be->npending;
+ be->npending += 8;
+ } else if (be->npending <= 0) {
+ return 0;
+ }
+ }
+ outputchar(be);
+ }
+}
+
+uint8_t *dnsbitenc_getresult(struct dnsbitenc *be) {
+ *--(be->out) = 63 - be->labremain; /* finish the last label */
+ return be->out;
+}
+
+
+
+
+#define MAX_DATA_LABELS
+
+struct labelinpacket {
+ const uint8_t *bytes;
+ int len;
+};
+
+struct dnsbitdec {
+ /* private for dnsbitdec_... functions; do not access direcctly */
+ const databuf[MAX_DOMAIN_BYTES];
+ const uint8_t *in;
+};
+
+static void setafter(const uint8_t *domain, const uint8_t **domain_end_r) {
+ if (domain_end_r && !*domain_end_r)
+ *domain_end_r=domain;
+}
+
+int dnsdomaindec_start(struct dnsbitdec *bd, const uint8_t *packet,
+ const uint8_t *endpacket, const uint8_t *domain,
+ int mydompathlen, const char *mydompath[],
+ const uint8_t **domain_end_r) {
+ int maxlabels=mydompath+MAX_DATA_LABELS;
+ const strut labelinpacket labels[maxlabels];
+ int nlabels=0, totallen=0;
+
+ *domain_end_r=0;
+ for (;;) {
+ if (domain==endpacket)
+ return FORMERR;
+ unsigned b=*domain++;
+ if (!b) {
+ totallen++;
+ setafter(domain,domain_end_r);
+ break;
+ }
+ if ((b & 0xc0) == 0xc0) {
+ if (domain==endpacket)
+ return FORMERR;
+ unsigned b2=*domain++;
+ setafter(domain,domain_end_r);
+ unsigned off= ((b & 0x3f)<<8) | b2;
+ if (off >= endpacket - packet)
+ return False;
+ domain=packet+off;
+ b=*domain++;
+ if (!b) return FORMERR
+ /* now fall through to code expecting a literal */
+ }
+ if ((b & 0xc0) != 0x00)
+ return FORMERR;
+ if (nlabels>=maxlabels)
+ return NOTAUTH;
+ totallen+=b+1;
+ if (totallen >= MAX_DOMAIN_BYTES) /* we still need a nul label */
+ return FORMERR;
+ labels[nlabels].bytes=domain;
+ labels[nlabels].len=b;
+ nlabels++;
+ domain += b;
+ }
+ if (nlabels <= mydompathlen)
+ return NOTAUTH;
+ for (i=0; i<mydompathlen; i++) {
+ int l=strlen(mydompath[i]);
+ const struct labelinpacket *got=&labels[nlabels-mydompathlen];
+ if (got->len != l || memcmp(got->bytes,mydompath[i]))
+ return NOTAUTH;
+ }
+ /* OK, it's our domain and it has some data and a good
+ * number of labels. Wow. */
+ uint8_t *copyto=&bd->databuf;
+ for (i=0; i<nlabels-mydompathlen; i++) {
+ const uint8_t *p=labels[i].bytes;
+ for (j=0; j<labels[i].len; j++) {
+ int val=in_table[*p++];
+ if (val & 0x80)
+ return NXDOMAIN;
+ *copyto++=val;
+ }
+ }
+ assert(copyto <= bd->databuf+sizeof(bd->databuf));
+ bd->in=copyto;
+ return 0;
+}
+
+static uint8_t in_table[256];
+
+static void dnsdomaindec_globalinit(void) {
+ memset(in_table,0xff,sizeof(in_table));
+ for (i=0; i<32; i++) {
+ in_table[out_table[i]]=i;
+ in_table[toupper((unsigned char)out_table[i])]=i;
+ }
+}
+
+static void inputchar(struct dnsbitdec *bd) {
+ /* must be enough input and enough space in pending */
+ int ch=*--(bd->in);
+ bd->pending <<= 8;
+ bd->pending |= ch; /* already decoded */
+}
+uint32_t dnsdomaindec_getbits(struct dnsbitdec *bd, int nbits)
+{
+ /* must be enough */
+ assert(nbits<=28);
+ while (bd->pending < nbits)
+ inputchar();
+ int newnpending = bd->pending - nbits;
+ uint32_t rv = bd->pending >> newnpending;
+ bd->pending &= ((uint32_t)1 << newnpending)-1;
+ bd->npending = newnpending;
+}
+uint32_t dnsdomaindec_getu32(struct dnsbitdec *bd)
+{
+ uint32_t lsw=dnsdomaindec_getbits(bd,16);
+ uint32_t msw=dnsdomaindec_getbits(bd,16);
+ return (msw << 16) | lsw;
+}
+int dnsbitdec_restbytes(struct dnsbitdec *bd, uint8_t outbuf[MAX_DOMAIN_BYTES])
+{
+ for (;;) {
+