ndb/dns: add support for internationalized domain names

This commit is contained in:
cinap_lenrek 2013-11-24 11:55:26 +01:00
parent 94fd92cb69
commit 3720b5ab9c
7 changed files with 370 additions and 60 deletions

View file

@ -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;

View file

@ -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: "<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 @@ 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)

View file

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

View file

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

View file

@ -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;
}

262
sys/src/cmd/ndb/idn.c Normal file
View file

@ -0,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;
}

View file

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