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
--
⑨