shithub: riscv

Download patch

ref: 3720b5ab9c4cb485c64e83d8af740aea3680123b
parent: 94fd92cb6903b7b1319d8f87bf6cdfd86c43b125
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Nov 24 06:55:26 EST 2013

ndb/dns: add support for internationalized domain names

--- a/sys/src/cmd/ndb/dblookup.c
+++ b/sys/src/cmd/ndb/dblookup.c
@@ -103,8 +103,7 @@
 dblookup(char *name, int class, int type, int auth, int ttl)
 {
 	int err;
-	char *wild;
-	char buf[256];
+	char buf[Domlen], *wild;
 	RR *rp, *tp;
 	DN *dp, *ndp;
 
@@ -124,7 +123,7 @@
 	}
 
 	lock(&dblock);
-	dp = dnlookup(name, class, 1);
+	dp = idnlookup(name, class, 1);
 
 	if(opendatabase() < 0)
 		goto out;
@@ -142,7 +141,7 @@
 	/* walk the domain name trying the wildcard '*' at each position */
 	for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){
 		snprint(buf, sizeof buf, "*%s", wild);
-		ndp = dnlookup(buf, class, 1);
+		ndp = idnlookup(buf, class, 1);
 		if(ndp->rr)
 			err = 0;
 		if(cfg.cachedb)
@@ -162,7 +161,7 @@
 		 * don't call it non-existent if it's not ours
 		 * (unless we're a resolver).
 		 */
-		if(err == Rname && (!inmyarea(name) || cfg.resolver))
+		if(err == Rname && (!inmyarea(dp->name) || cfg.resolver))
 			err = Rserver;
 		dp->respcode = err;
 	}
@@ -179,6 +178,18 @@
 	return (t? strtoul(t->val, 0, 10): def);
 }
 
+static void
+mklowcase(char *cp)
+{
+	Rune r;
+
+	while(*cp != 0){
+		chartorune(&r, cp);
+		r = tolowerrune(r);
+		cp += runetochar(cp, &r);
+	}
+}
+
 /*
  *  lookup an RR in the network database
  */
@@ -236,7 +247,7 @@
 	case Tixfr:
 		return doaxfr(db, name);
 	default:
-//		dnslog("dnlookup1(%s) bad type", name);
+//		dnslog("dblookup1(%s) bad type", name);
 		return nil;
 	}
 
@@ -243,27 +254,36 @@
 	/*
 	 *  find a matching entry in the database
 	 */
-	t = nil;
 	nstrcpy(dname, name, sizeof dname);
-	free(ndbgetvalue(db, &s, "dom", dname, attr, &t));
-	if(t == nil && strchr(dname, '.') == nil)
-		free(ndbgetvalue(db, &s, "sys", dname, attr, &t));
-	if(t == nil) {
-		char *cp;
-
-		/* try lower case */
-		for(cp = dname; *cp; cp++)
-			if(isupper(*cp)) {
-				for(; *cp; cp++)
-					*cp = tolower(*cp);
-				free(ndbgetvalue(db, &s, "dom", dname, attr, &t));
-				if(t == nil && strchr(dname, '.') == nil)
-					free(ndbgetvalue(db, &s, "sys", dname, attr, &t));
-				break;
+	for(x=0; x<4; x++){
+		switch(x){
+		case 1:	/* try unicode */
+			if(idn2utf(name, dname, sizeof dname) == nil){
+				nstrcpy(dname, name, sizeof dname);
+				continue;
 			}
+			if(strcmp(name, dname) == 0)
+				continue;
+			break;
+		case 3:	/* try ascii (lower case) */
+			if(utf2idn(name, dname, sizeof dname) == nil)
+				continue;
+		case 2:
+			mklowcase(dname);
+			if(strcmp(name, dname) == 0)
+				continue;
+			break;
+		}
+		t = nil;
+		free(ndbgetvalue(db, &s, "dom", dname, attr, &t));
+		if(t == nil && strchr(dname, '.') == nil)
+			free(ndbgetvalue(db, &s, "sys", dname, attr, &t));
+		if(t != nil)
+			break;
 	}
+
 	if(t == nil) {
-//		dnslog("dnlookup1(%s) name not found", name);
+//		dnslog("dblookup1(%s) name not found", name);
 		return nil;
 	}
 
@@ -303,7 +323,7 @@
 			if(ttl)
 				rp->ttl = ttl;
 			if(dp == nil)
-				dp = dnlookup(dname, Cin, 1);
+				dp = idnlookup(dname, Cin, 1);
 			rp->owner = dp;
 			*l = rp;
 			l = &rp->next;
@@ -323,7 +343,7 @@
 				rp->ttl = ttl;
 			rp->auth = auth;
 			if(dp == nil)
-				dp = dnlookup(dname, Cin, 1);
+				dp = idnlookup(dname, Cin, 1);
 			rp->owner = dp;
 			*l = rp;
 			l = &rp->next;
@@ -330,7 +350,7 @@
 		}
 	ndbfree(t);
 
-//	dnslog("dnlookup1(%s) -> %#p", name, list);
+//	dnslog("dblookup1(%s) -> %#p", name, list);
 	return list;
 }
 
@@ -406,7 +426,7 @@
 
 	USED(entry);
 	rp = rralloc(Tcname);
-	rp->host = dnlookup(pair->val, Cin, 1);
+	rp->host = idnlookup(pair->val, Cin, 1);
 	return rp;
 }
 static RR*
@@ -415,7 +435,7 @@
 	RR *rp;
 
 	rp = rralloc(Tmx);
-	rp->host = dnlookup(pair->val, Cin, 1);
+	rp->host = idnlookup(pair->val, Cin, 1);
 	rp->pref = intval(entry, pair, "pref", 1);
 	return rp;
 }
@@ -426,7 +446,7 @@
 	Ndbtuple *t;
 
 	rp = rralloc(Tns);
-	rp->host = dnlookup(pair->val, Cin, 1);
+	rp->host = idnlookup(pair->val, Cin, 1);
 	t = look(entry, pair, "soa");
 	if(t && t->val[0] == 0)
 		rp->local = 1;
@@ -466,7 +486,7 @@
 	ns = look(entry, pair, "ns");
 	if(ns == nil)
 		ns = look(entry, pair, "dom");
-	rp->host = dnlookup(ns->val, Cin, 1);
+	rp->host = idnlookup(ns->val, Cin, 1);
 
 	/* accept all of:
 	 *  mbox=person
@@ -481,15 +501,15 @@
 			p = strchr(mb->val, '@');
 			if(p != nil)
 				*p = '.';
-			rp->rmb = dnlookup(mb->val, Cin, 1);
+			rp->rmb = idnlookup(mb->val, Cin, 1);
 		} else {
 			snprint(mailbox, sizeof mailbox, "%s.%s",
 				mb->val, ns->val);
-			rp->rmb = dnlookup(mailbox, Cin, 1);
+			rp->rmb = idnlookup(mailbox, Cin, 1);
 		}
 	else {
 		snprint(mailbox, sizeof mailbox, "postmaster.%s", ns->val);
-		rp->rmb = dnlookup(mailbox, Cin, 1);
+		rp->rmb = idnlookup(mailbox, Cin, 1);
 	}
 
 	/*
@@ -509,7 +529,7 @@
 	RR *rp;
 
 	rp = rralloc(Tsrv);
-	rp->host = dnlookup(pair->val, Cin, 1);
+	rp->host = idnlookup(pair->val, Cin, 1);
 	rp->srv->pri = intval(entry, pair, "pri", 0);
 	rp->srv->weight = intval(entry, pair, "weight", 0);
 	/* TODO: translate service name to port # */
@@ -624,7 +644,7 @@
 
 	for(et = t; et; et = et->entry)
 		if(strcmp(et->attr, "dom") == 0){
-			dp = dnlookup(et->val, Cin, 1);
+			dp = idnlookup(et->val, Cin, 1);
 
 			/* first same line */
 			for(nt = et->line; nt != et; nt = nt->line){
@@ -787,9 +807,6 @@
 	return t;
 }
 
-char *localservers =	  "local#dns#servers";
-char *localserverprefix = "local#dns#server";
-
 /*
  *  return non-zero if this is a bad delegation
  */
@@ -892,7 +909,7 @@
 
 	/* ns record for name server, make up an impossible name */
 	rp = rralloc(Tns);
-	snprint(buf, sizeof buf, "%s%d", localserverprefix, i);
+	snprint(buf, sizeof buf, "local#dns#server%d", i);
 	nsdp = dnlookup(buf, class, 1);
 	rp->host = nsdp;
 	rp->owner = dp;			/* e.g., local#dns#servers */
@@ -932,7 +949,7 @@
 	RR *nsrp;
 	DN *dp;
 
-	dp = dnlookup(localservers, class, 1);
+	dp = dnlookup("local#dns#servers", class, 1);
 	nsrp = rrlookup(dp, Tns, NOneg);
 	if(nsrp != nil)
 		return nsrp;
--- a/sys/src/cmd/ndb/dn.c
+++ b/sys/src/cmd/ndb/dn.c
@@ -224,6 +224,16 @@
 	return dp;
 }
 
+DN*
+idnlookup(char *name, int class, int enter)
+{
+	char dom[Domlen];
+
+	if(utf2idn(name, dom, sizeof dom) != nil)
+		name = dom;
+	return dnlookup(name, class, enter);
+}
+
 static int
 rrsame(RR *rr1, RR *rr2)
 {
@@ -1156,6 +1166,17 @@
 	return dn? dn->name: "<null>";
 }
 
+static char *
+idnname(DN *dn, char *buf, int nbuf)
+{
+	char *name;
+
+	name = dnname(dn);
+	if(idn2utf(name, buf, nbuf) != nil)
+		return buf;
+	return name;
+}
+
 /*
  *  print conversion for rr records
  */
@@ -1287,7 +1308,7 @@
 rravfmt(Fmt *f)
 {
 	int rv, quote;
-	char *strp;
+	char buf[Domlen], *strp;
 	Fmt fstr;
 	RR *rp;
 	Server *s;
@@ -1306,34 +1327,37 @@
 	if(rp->type == Tptr)
 		fmtprint(&fstr, "ptr=%s", dnname(rp->owner));
 	else
-		fmtprint(&fstr, "dom=%s", dnname(rp->owner));
+		fmtprint(&fstr, "dom=%s", idnname(rp->owner, buf, sizeof(buf)));
 
 	switch(rp->type){
 	case Thinfo:
 		fmtprint(&fstr, " cpu=%s os=%s",
-			dnname(rp->cpu), dnname(rp->os));
+			idnname(rp->cpu, buf, sizeof(buf)),
+			idnname(rp->os, buf, sizeof(buf)));
 		break;
 	case Tcname:
-		fmtprint(&fstr, " cname=%s", dnname(rp->host));
+		fmtprint(&fstr, " cname=%s", idnname(rp->host, buf, sizeof(buf)));
 		break;
 	case Tmb:
 	case Tmd:
 	case Tmf:
-		fmtprint(&fstr, " mbox=%s", dnname(rp->host));
+		fmtprint(&fstr, " mbox=%s", idnname(rp->host, buf, sizeof(buf)));
 		break;
 	case Tns:
-		fmtprint(&fstr,  " ns=%s", dnname(rp->host));
+		fmtprint(&fstr,  " ns=%s", idnname(rp->host, buf, sizeof(buf)));
 		break;
 	case Tmg:
 	case Tmr:
-		fmtprint(&fstr, " mbox=%s", dnname(rp->mb));
+		fmtprint(&fstr, " mbox=%s", idnname(rp->mb, buf, sizeof(buf)));
 		break;
 	case Tminfo:
 		fmtprint(&fstr, " mbox=%s mbox=%s",
-			dnname(rp->mb), dnname(rp->rmb));
+			idnname(rp->mb, buf, sizeof(buf)),
+			idnname(rp->rmb, buf, sizeof(buf)));
 		break;
 	case Tmx:
-		fmtprint(&fstr, " pref=%lud mx=%s", rp->pref, dnname(rp->host));
+		fmtprint(&fstr, " pref=%lud mx=%s", rp->pref,
+			idnname(rp->host, buf, sizeof(buf)));
 		break;
 	case Ta:
 	case Taaaa:
@@ -1346,7 +1370,8 @@
 		soa = rp->soa;
 		fmtprint(&fstr,
 " ns=%s mbox=%s serial=%lud refresh=%lud retry=%lud expire=%lud ttl=%lud",
-			dnname(rp->host), dnname(rp->rmb),
+			idnname(rp->host, buf, sizeof(buf)),
+			idnname(rp->rmb, buf, sizeof(buf)),
 			(soa? soa->serial: 0),
 			(soa? soa->refresh: 0), (soa? soa->retry: 0),
 			(soa? soa->expire: 0), (soa? soa->minttl: 0));
@@ -1357,7 +1382,7 @@
 		srv = rp->srv;
 		fmtprint(&fstr, " pri=%ud weight=%ud port=%ud target=%s",
 			(srv? srv->pri: 0), (srv? srv->weight: 0),
-			rp->port, dnname(rp->host));
+			rp->port, idnname(rp->host, buf, sizeof(buf)));
 		break;
 	case Tnull:
 		if (rp->null == nil)
@@ -1381,7 +1406,8 @@
 		break;
 	case Trp:
 		fmtprint(&fstr, " rp=%s txt=%s",
-			dnname(rp->rmb), dnname(rp->rp));
+			idnname(rp->rmb, buf, sizeof(buf)),
+			idnname(rp->rp, buf, sizeof(buf)));
 		break;
 	case Tkey:
 		if (rp->key == nil)
@@ -1399,7 +1425,7 @@
 " type=%d alg=%d labels=%d ttl=%lud exp=%lud incep=%lud tag=%d signer=%s",
 				rp->sig->type, rp->sig->alg, rp->sig->labels,
 				rp->sig->ttl, rp->sig->exp, rp->sig->incep,
-				rp->sig->tag, dnname(rp->sig->signer));
+				rp->sig->tag, idnname(rp->sig->signer, buf, sizeof(buf)));
 		break;
 	case Tcert:
 		if (rp->cert == nil)
--- a/sys/src/cmd/ndb/dnresolve.c
+++ b/sys/src/cmd/ndb/dnresolve.c
@@ -177,7 +177,7 @@
 	 *  try the name directly
 	 */
 	rp = dnresolve1(name, class, type, req, depth, recurse);
-	if(rp == nil && (dp = dnlookup(name, class, 0)) != nil) {
+	if(rp == nil && (dp = idnlookup(name, class, 0)) != nil) {
 		/*
 		 * try it as a canonical name if we weren't told
 		 * that the name didn't exist
@@ -348,7 +348,7 @@
 		}
 
 		/* look for ns in cache */
-		nsdp = dnlookup(cp, class, 0);
+		nsdp = idnlookup(cp, class, 0);
 		nsrp = nil;
 		if(nsdp)
 			nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
@@ -387,7 +387,7 @@
 	if(class != Cin)
 		return nil;
 
-	dp = dnlookup(name, class, 1);
+	dp = idnlookup(name, class, 1);
 
 	/*
 	 *  Try the cache first
--- a/sys/src/cmd/ndb/dns.h
+++ b/sys/src/cmd/ndb/dns.h
@@ -453,6 +453,7 @@
 void	dnget(void);
 void	dninit(void);
 DN*	dnlookup(char*, int, int);
+DN*	idnlookup(char*, int, int);
 void	dnptr(uchar*, uchar*, char*, int, int, int);
 void	dnpurge(void);
 void	dnput(void);
@@ -533,5 +534,9 @@
 
 /* convM2DNS.c */
 char*	convM2DNS(uchar*, int, DNSmsg*, int*);
+
+/* idn.c */
+char*	utf2idn(char *, char *, int);
+char*	idn2utf(char *, char *, int);
 
 #pragma varargck argpos dnslog 1
--- a/sys/src/cmd/ndb/dnsdebug.c
+++ b/sys/src/cmd/ndb/dnsdebug.c
@@ -292,7 +292,7 @@
 
 	rr = rralloc(Tns);
 	rr->owner = dnlookup("local#dns#servers", class, 1);
-	rr->host = dnlookup(servername, class, 1);
+	rr->host = idnlookup(servername, class, 1);
 
 	return rr;
 }
--- /dev/null
+++ b/sys/src/cmd/ndb/idn.c
@@ -1,0 +1,262 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include "dns.h"
+
+enum {
+	base = 36,
+	tmin = 1,
+	tmax = 26,
+	skew = 38,
+	damp = 700,
+	initial_bias = 72,
+	initial_n = 0x80,
+};
+
+static uint maxint = ~0;
+
+static uint
+decode_digit(uint cp)
+{
+	if((cp - '0') < 10)
+		return cp - ('0' - 26);
+	if((cp - 'A') < 26)
+		return cp - 'A';
+	if((cp - 'a') < 26)
+		return cp - 'a';
+	return base;
+}
+
+static char
+encode_digit(uint d, int flag)
+{
+	if(d < 26)
+		return d + (flag ? 'A' : 'a');
+	return d + ('0' - 26);
+}
+
+static uint
+adapt(uint delta, uint numpoints, int firsttime)
+{
+	uint k;
+
+	delta = firsttime ? delta / damp : delta >> 1;
+	delta += delta / numpoints;
+	for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base)
+		delta /= base - tmin;
+	return k + (base - tmin + 1) * delta / (delta + skew);
+}
+
+static int
+punyencode(uint input_length, Rune input[], uint max_out, char output[])
+{
+	uint n, delta, h, b, out, bias, j, m, q, k, t;
+
+	n = initial_n;
+	delta = out = 0;
+	bias = initial_bias;
+
+	for (j = 0;  j < input_length;  ++j) {
+		if ((uint)input[j] < 0x80) {
+			if (max_out - out < 2)
+				return -1;
+			output[out++] = input[j];
+		}
+	}
+
+	h = b = out;
+
+	if (b > 0)
+		output[out++] = '-';
+
+	while (h < input_length) {
+		for (m = maxint, j = 0; j < input_length; ++j) {
+			if (input[j] >= n && input[j] < m)
+				m = input[j];
+		}
+
+		if (m - n > (maxint - delta) / (h + 1))
+			return -1;
+
+		delta += (m - n) * (h + 1);
+		n = m;
+
+		for (j = 0;  j < input_length;  ++j) {
+			if (input[j] < n) {
+				if (++delta == 0)
+					return -1;
+			}
+
+			if (input[j] == n) {
+				for (q = delta, k = base;; k += base) {
+					if (out >= max_out)
+						return -1;
+					if (k <= bias)
+						t = tmin;
+					else if (k >= bias + tmax)
+						t = tmax;
+					else
+						t = k - bias;
+					if (q < t)
+						break;
+					output[out++] = encode_digit(t + (q - t) % (base - t), 0);
+					q = (q - t) / (base - t);
+				}
+				output[out++] = encode_digit(q, isupperrune(input[j]));
+				bias = adapt(delta, h + 1, h == b);
+				delta = 0;
+				++h;
+			}
+		}
+
+		++delta, ++n;
+	}
+
+	return (int)out;
+}
+
+static int
+punydecode(uint input_length, char input[], uint max_out, Rune output[])
+{
+	uint n, out, i, bias, b, j, in, oldi, w, k, digit, t;
+
+	n = initial_n;
+	out = i = 0;
+	bias = initial_bias;
+
+	for (b = j = 0; j < input_length; ++j)
+		if (input[j] == '-')
+			b = j;
+
+	if (b > max_out)
+		return -1;
+
+	for (j = 0;  j < b;  ++j) {
+		if (input[j] & 0x80)
+			return -1;
+		output[out++] = input[j];
+	}
+
+	for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) {
+		for (oldi = i, w = 1, k = base;; k += base) {
+			if (in >= input_length)
+				return -1;
+			digit = decode_digit(input[in++]);
+			if (digit >= base)
+				return -1;
+			if (digit > (maxint - i) / w)
+				return -1;
+			i += digit * w;
+			if (k <= bias)
+				t = tmin;
+			else if (k >= bias + tmax)
+				t = tmax;
+			else
+				t = k - bias;
+			if (digit < t)
+				break;
+			if (w > maxint / (base - t))
+				return -1;
+			w *= (base - t);
+		}
+
+		bias = adapt(i - oldi, out + 1, oldi == 0);
+
+		if (i / (out + 1) > maxint - n)
+			return -1;
+		n += i / (out + 1);
+		i %= (out + 1);
+
+		if (out >= max_out)
+			return -1;
+
+		memmove(output + i + 1, output + i, (out - i) * sizeof *output);
+		if(((uint)input[in-1] - 'A') < 26)
+			output[i++] = toupperrune(n);
+		else
+			output[i++] = tolowerrune(n);
+	}
+
+	return (int)out;
+}
+
+/*
+ * convert punycode encoded internationalized
+ * domain name to unicode string
+ */
+char*
+idn2utf(char *name, char *buf, int nbuf)
+{
+	char *dp, *de, *cp;
+	Rune rb[Domlen], r;
+	int nc, nr, n;
+
+	cp = name;
+	dp = buf;
+	de = dp+nbuf-1;
+	for(;;){
+		nc = nr = 0;
+		while(cp[nc] != 0){
+			n = chartorune(&r, cp+nc);
+			if(r == '.')
+				break;
+			rb[nr++] = r;
+			nc += n;
+		}
+		if(cistrncmp(cp, "xn--", 4) == 0)
+			if((nr = punydecode(nc-4, cp+4, nelem(rb), rb)) < 0)
+				return nil;
+		dp = seprint(dp, de, "%.*S", nr, rb);
+		if(dp >= de)
+			return nil;
+		if(cp[nc] == 0)
+			break;
+		*dp++ = '.';
+		cp += nc+1;
+	}
+	*dp = 0;
+	return buf;
+}
+
+/*
+ * convert unicode string to punycode
+ * encoded internationalized domain name
+ */
+char*
+utf2idn(char *name, char *buf, int nbuf)
+{
+	char *dp, *de, *cp;
+	Rune rb[Domlen], r;
+	int nc, nr, n;
+
+	dp = buf;
+	de = dp+nbuf-1;
+	cp = name;
+	for(;;){
+		nc = nr = 0;
+		while(cp[nc] != 0 && nr < nelem(rb)){
+			n = chartorune(&r, cp+nc);
+			if(r == '.')
+				break;
+			rb[nr++] = r;
+			nc += n;
+		}
+		if(nc == nr)
+			dp = seprint(dp, de, "%.*s", nc, cp);
+		else {
+			dp = seprint(dp, de, "xn--");
+			if((n = punyencode(nr, rb, de - dp, dp)) < 0)
+				return nil;
+			dp += n;
+		}
+		if(dp >= de)
+			return nil;
+		if(cp[nc] == 0)
+			break;
+		*dp++ = '.';
+		cp += nc+1;
+	}
+	*dp = 0;
+	return buf;
+}
+
--- a/sys/src/cmd/ndb/mkfile
+++ b/sys/src/cmd/ndb/mkfile
@@ -17,13 +17,13 @@
 
 
 DNSOBJ = dns.$O dnudpserver.$O dn.$O dnresolve.$O dblookup.$O dnserver.$O dnnotify.$O\
-	 dnarea.$O convM2DNS.$O convDNS2M.$O # lock.$O coherence.$O
+	 dnarea.$O convM2DNS.$O convDNS2M.$O idn.$O
 
 DNSTCPOBJ = dnstcp.$O dn.$O dnresolve.$O dblookup.$O dnserver.$O\
-	 dnarea.$O convM2DNS.$O convDNS2M.$O
+	 dnarea.$O convM2DNS.$O convDNS2M.$O idn.$O
 
 DNSDEBUGOBJ = dnsdebug.$O dn.$O dnresolve.$O dblookup.$O dnserver.$O\
-	 dnarea.$O convM2DNS.$O convDNS2M.$O
+	 dnarea.$O convM2DNS.$O convDNS2M.$O idn.$O
 
 HFILES = dns.h /$objtype/lib/libndb.a
 
--