+
+char *adns__sockaddr_ntoa(const struct sockaddr *sa, char *buf) {
+ int err;
+ int len= ADNS_ADDR2TEXT_BUFLEN;
+
+ err= adns_addr2text(sa, 0, buf, &len, 0);
+ if (err == EIO)
+ err= adns_addr2text(sa, adns_qf_addrlit_scope_numeric, buf, &len, 0);
+ assert(!err);
+ return buf;
+}
+
+/*
+ * Reverse-domain parsing and construction.
+ */
+
+int adns__make_reverse_domain(const struct sockaddr *sa, const char *zone,
+ char **buf_io, size_t bufsz,
+ char **buf_free_r) {
+ size_t req;
+ char *p;
+ unsigned c, y;
+ unsigned long aa;
+ const unsigned char *ap;
+ int i, j;
+
+ AF_IN_IN6_OTHER(sa->sa_family, {
+ req= 4 * 4;
+ if (!zone) zone= "in-addr.arpa";
+ }, {
+ req = 2 * 32;
+ if (!zone) zone= "ip6.arpa";
+ }, {
+ return ENOSYS;
+ });
+
+ req += strlen(zone) + 1;
+ if (req <= bufsz)
+ p= *buf_io;
+ else {
+ p= malloc(req); if (!p) return errno;
+ *buf_free_r = p;
+ }
+
+ *buf_io= p;
+ SOCKADDR_IN_IN6(const, sa, sin, {
+ aa= ntohl(sin->sin_addr.s_addr);
+ for (i=0; i<4; i++) {
+ p += sprintf(p, "%d", (int)(aa & 0xff));
+ *p++= '.';
+ aa >>= 8;
+ }
+ }, {
+ ap= sin6->sin6_addr.s6_addr + 16;
+ for (i=0; i<16; i++) {
+ c= *--ap;
+ for (j=0; j<2; j++) {
+ y= c & 0xf;
+ *p++= (y < 10) ? y + '0' : y - 10 + 'a';
+ c >>= 4;
+ *p++= '.';
+ }
+ }
+ });
+
+ strcpy(p, zone);
+ return 0;
+}
+
+
+#define REVPARSE_P_L(labnum) \
+ const char *p= dgram + rps->labstart[labnum]; \
+ int l= rps->lablen[labnum]
+ /*
+ * REVPARSE_P_L(int labnum);
+ * expects:
+ * const char *dgram;
+ * const struct revparse_state *rps;
+ * produces:
+ * const char *p; // start of label labnum in dgram
+ * int l; // length of label in dgram
+ */
+
+static bool revparse_check_tail(struct revparse_state *rps,
+ const char *dgram, int nlabels,
+ int bodylen, const char *inarpa) {
+ int i;
+
+ if (nlabels != bodylen+2) return 0;
+ for (i=0; i<2; i++) {
+ REVPARSE_P_L(bodylen+i);
+ const char *want= !i ? inarpa : "arpa";
+ if (!adns__labels_equal(p,l, want,strlen(want))) return 0;
+ }
+ return 1;
+}
+
+static bool revparse_atoi(const char *p, int l, int base,
+ unsigned max, unsigned *v_r) {
+ if (l>3) return 0;
+ if (l>1 && p[0]=='0') return 0;
+ unsigned v=0;
+ while (l-- > 0) {
+ int tv;
+ int c= ctype_toupper(*p++);
+ if ('0'<=c && c<='9') tv = c-'0';
+ else if ('A'<=c && c<='Z') tv = c-'A'+10;
+ else return 0;
+ if (tv >= base) return 0;
+ v *= base;
+ v += tv;
+ }
+ if (v>max) return 0;
+ *v_r= v;
+ return 1;
+}
+
+static bool revparse_inet(struct revparse_state *rps,
+ const char *dgram, int nlabels,
+ adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
+ if (!revparse_check_tail(rps,dgram,nlabels,4,"in-addr")) return 0;
+
+ uint32_t a=0;
+ int i;
+ for (i=3; i>=0; i--) {
+ REVPARSE_P_L(i);
+ unsigned v;
+ if (!revparse_atoi(p,l,10,255,&v)) return 0;
+ a <<= 8;
+ a |= v;
+ }
+ *rrtype_r= adns_r_a;
+ addr_r->inet.sin_family= AF_INET;
+ addr_r->inet.sin_addr.s_addr= htonl(a);
+ return 1;
+}
+
+static bool revparse_inet6(struct revparse_state *rps,
+ const char *dgram, int nlabels,
+ adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
+ if (!revparse_check_tail(rps,dgram,nlabels,32,"ip6")) return 0;
+
+ int i, j;
+ memset(addr_r,0,sizeof(*addr_r));
+ unsigned char *a= addr_r->inet6.sin6_addr.s6_addr+16;
+ for (i=0; i<32; ) { /* i incremented in inner loop */
+ unsigned b=0;
+ for (j=0; j<2; j++, i++) {
+ REVPARSE_P_L(i);
+ unsigned v;
+ if (!revparse_atoi(p,l,16,15,&v)) return 0;
+ b >>= 4;
+ b |= v << 4;
+ }
+ *--a= b;
+ }
+ *rrtype_r= adns_r_aaaa;
+ addr_r->inet.sin_family= AF_INET6;
+ return 1;
+}
+
+bool adns__revparse_label(struct revparse_state *rps, int labnum,
+ const char *dgram, int labstart, int lablen) {
+ if (labnum >= MAXREVLABELS)
+ return 0;
+
+ assert(labstart <= 65535);
+ assert(lablen <= 255);
+ rps->labstart[labnum] = labstart;
+ rps->lablen[labnum] = lablen;
+ return 1;
+}
+
+bool adns__revparse_done(struct revparse_state *rps,
+ const char *dgram, int nlabels,
+ adns_rrtype *rrtype_r, adns_sockaddr *addr_r) {
+ return
+ revparse_inet(rps,dgram,nlabels,rrtype_r,addr_r) ||
+ revparse_inet6(rps,dgram,nlabels,rrtype_r,addr_r);
+}