From 3720b5ab9c4cb485c64e83d8af740aea3680123b Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sun, 24 Nov 2013 11:55:26 +0100 Subject: [PATCH] ndb/dns: add support for internationalized domain names --- sys/src/cmd/ndb/dblookup.c | 97 +++++++------ sys/src/cmd/ndb/dn.c | 52 +++++-- sys/src/cmd/ndb/dnresolve.c | 6 +- sys/src/cmd/ndb/dns.h | 5 + sys/src/cmd/ndb/dnsdebug.c | 2 +- sys/src/cmd/ndb/idn.c | 262 ++++++++++++++++++++++++++++++++++++ sys/src/cmd/ndb/mkfile | 6 +- 7 files changed, 370 insertions(+), 60 deletions(-) create mode 100644 sys/src/cmd/ndb/idn.c diff --git a/sys/src/cmd/ndb/dblookup.c b/sys/src/cmd/ndb/dblookup.c index 5260f47af..50d9e367d 100644 --- a/sys/src/cmd/ndb/dblookup.c +++ b/sys/src/cmd/ndb/dblookup.c @@ -103,8 +103,7 @@ RR* 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 @@ dblookup(char *name, int class, int type, int auth, int ttl) } lock(&dblock); - dp = dnlookup(name, class, 1); + dp = idnlookup(name, class, 1); if(opendatabase() < 0) goto out; @@ -142,7 +141,7 @@ dblookup(char *name, int class, int type, int auth, int ttl) /* 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 @@ out: * 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 @@ intval(Ndbtuple *entry, Ndbtuple *pair, char *attr, ulong def) 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,34 +247,43 @@ dblookup1(char *name, int type, int auth, int ttl) case Tixfr: return doaxfr(db, name); default: -// dnslog("dnlookup1(%s) bad type", name); +// dnslog("dblookup1(%s) bad type", name); return nil; } /* * 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 @@ dblookup1(char *name, int type, int auth, int ttl) 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,14 +343,14 @@ dblookup1(char *name, int type, int auth, int ttl) 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; } ndbfree(t); -// dnslog("dnlookup1(%s) -> %#p", name, list); +// dnslog("dblookup1(%s) -> %#p", name, list); return list; } @@ -406,7 +426,7 @@ cnamerr(Ndbtuple *entry, Ndbtuple *pair) 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 @@ mxrr(Ndbtuple *entry, Ndbtuple *pair) 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 @@ nsrr(Ndbtuple *entry, Ndbtuple *pair) 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 @@ soarr(Ndbtuple *entry, Ndbtuple *pair) 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 @@ soarr(Ndbtuple *entry, Ndbtuple *pair) 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 @@ srvrr(Ndbtuple *entry, Ndbtuple *pair) 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 @@ dbtuple2cache(Ndbtuple *t) 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 @@ lookupinfo(char *attr) 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 @@ addlocaldnsserver(DN *dp, int class, char *ipaddr, int i) /* 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 @@ dnsservers(int class) 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; diff --git a/sys/src/cmd/ndb/dn.c b/sys/src/cmd/ndb/dn.c index 0628f8ee5..08b86369f 100644 --- a/sys/src/cmd/ndb/dn.c +++ b/sys/src/cmd/ndb/dn.c @@ -224,6 +224,16 @@ dnlookup(char *name, int class, int enter) 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 @@ dnname(DN *dn) return dn? dn->name: ""; } +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 @@ int rravfmt(Fmt *f) { int rv, quote; - char *strp; + char buf[Domlen], *strp; Fmt fstr; RR *rp; Server *s; @@ -1306,34 +1327,37 @@ rravfmt(Fmt *f) 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 @@ rravfmt(Fmt *f) 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 @@ rravfmt(Fmt *f) 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 @@ rravfmt(Fmt *f) 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 @@ rravfmt(Fmt *f) " 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) diff --git a/sys/src/cmd/ndb/dnresolve.c b/sys/src/cmd/ndb/dnresolve.c index 7ca39619b..6d7be2291 100644 --- a/sys/src/cmd/ndb/dnresolve.c +++ b/sys/src/cmd/ndb/dnresolve.c @@ -177,7 +177,7 @@ dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, * 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 @@ issuequery(Query *qp, char *name, int class, int depth, int recurse) } /* 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 @@ dnresolve1(char *name, int class, int type, Request *req, int depth, if(class != Cin) return nil; - dp = dnlookup(name, class, 1); + dp = idnlookup(name, class, 1); /* * Try the cache first diff --git a/sys/src/cmd/ndb/dns.h b/sys/src/cmd/ndb/dns.h index 6f938c781..51feb5739 100644 --- a/sys/src/cmd/ndb/dns.h +++ b/sys/src/cmd/ndb/dns.h @@ -453,6 +453,7 @@ void dndump(char*); 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); @@ -534,4 +535,8 @@ int convDNS2M(DNSmsg*, uchar*, int); /* 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 diff --git a/sys/src/cmd/ndb/dnsdebug.c b/sys/src/cmd/ndb/dnsdebug.c index ee047a653..4ab24a85f 100644 --- a/sys/src/cmd/ndb/dnsdebug.c +++ b/sys/src/cmd/ndb/dnsdebug.c @@ -292,7 +292,7 @@ getdnsservers(int class) 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; } diff --git a/sys/src/cmd/ndb/idn.c b/sys/src/cmd/ndb/idn.c new file mode 100644 index 000000000..46d421a45 --- /dev/null +++ b/sys/src/cmd/ndb/idn.c @@ -0,0 +1,262 @@ +#include +#include +#include +#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; +} + diff --git a/sys/src/cmd/ndb/mkfile b/sys/src/cmd/ndb/mkfile index 1ec0dbdb8..2d00b81ae 100644 --- a/sys/src/cmd/ndb/mkfile +++ b/sys/src/cmd/ndb/mkfile @@ -17,13 +17,13 @@ TARG = \ 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