13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <ip.h>
43e12c5d1SDavid du Colombier #include "dns.h"
53e12c5d1SDavid du Colombier
63e12c5d1SDavid du Colombier /*
73e12c5d1SDavid du Colombier * a dictionary of domain names for packing messages
83e12c5d1SDavid du Colombier */
93e12c5d1SDavid du Colombier enum
103e12c5d1SDavid du Colombier {
113e12c5d1SDavid du Colombier Ndict= 64,
123e12c5d1SDavid du Colombier };
133e12c5d1SDavid du Colombier typedef struct Dict Dict;
143e12c5d1SDavid du Colombier struct Dict
153e12c5d1SDavid du Colombier {
163e12c5d1SDavid du Colombier struct {
173e12c5d1SDavid du Colombier ushort offset; /* pointer to packed name in message */
183e12c5d1SDavid du Colombier char *name; /* pointer to unpacked name in buf */
193e12c5d1SDavid du Colombier } x[Ndict];
203e12c5d1SDavid du Colombier int n; /* size of dictionary */
213e12c5d1SDavid du Colombier uchar *start; /* start of packed message */
2276783259SDavid du Colombier char buf[16*1024]; /* buffer for unpacked names (was 4k) */
233e12c5d1SDavid du Colombier char *ep; /* first free char in buf */
243e12c5d1SDavid du Colombier };
253e12c5d1SDavid du Colombier
267dd7cddfSDavid du Colombier #define NAME(x) p = pname(p, ep, x, dp)
2760845620SDavid du Colombier #define SYMBOL(x) p = psym(p, ep, x)
2860845620SDavid du Colombier #define STRING(x) p = pstr(p, ep, x)
297dd7cddfSDavid du Colombier #define BYTES(x, n) p = pbytes(p, ep, x, n)
307dd7cddfSDavid du Colombier #define USHORT(x) p = pushort(p, ep, x)
317dd7cddfSDavid du Colombier #define UCHAR(x) p = puchar(p, ep, x)
327dd7cddfSDavid du Colombier #define ULONG(x) p = pulong(p, ep, x)
335d459b5aSDavid du Colombier #define V4ADDR(x) p = pv4addr(p, ep, x)
345d459b5aSDavid du Colombier #define V6ADDR(x) p = pv6addr(p, ep, x)
353e12c5d1SDavid du Colombier
363e12c5d1SDavid du Colombier static uchar*
psym(uchar * p,uchar * ep,char * np)3760845620SDavid du Colombier psym(uchar *p, uchar *ep, char *np)
3860845620SDavid du Colombier {
3960845620SDavid du Colombier int n;
4060845620SDavid du Colombier
4160845620SDavid du Colombier n = strlen(np);
4260845620SDavid du Colombier if(n >= Strlen) /* DNS maximum length string */
4360845620SDavid du Colombier n = Strlen - 1;
4460845620SDavid du Colombier if(ep - p < n+1) /* see if it fits in the buffer */
4560845620SDavid du Colombier return ep+1;
4660845620SDavid du Colombier *p++ = n;
47e8ece30eSDavid du Colombier memmove(p, np, n);
4860845620SDavid du Colombier return p + n;
4960845620SDavid du Colombier }
5060845620SDavid du Colombier
5160845620SDavid du Colombier static uchar*
pstr(uchar * p,uchar * ep,char * np)5260845620SDavid du Colombier pstr(uchar *p, uchar *ep, char *np)
533e12c5d1SDavid du Colombier {
544f8f669cSDavid du Colombier return psym(p, ep, np);
553e12c5d1SDavid du Colombier }
563e12c5d1SDavid du Colombier
573e12c5d1SDavid du Colombier static uchar*
pbytes(uchar * p,uchar * ep,uchar * np,int n)587dd7cddfSDavid du Colombier pbytes(uchar *p, uchar *ep, uchar *np, int n)
593e12c5d1SDavid du Colombier {
607dd7cddfSDavid du Colombier if(ep - p < n)
617dd7cddfSDavid du Colombier return ep+1;
62e8ece30eSDavid du Colombier memmove(p, np, n);
637dd7cddfSDavid du Colombier return p + n;
643e12c5d1SDavid du Colombier }
657dd7cddfSDavid du Colombier
667dd7cddfSDavid du Colombier static uchar*
puchar(uchar * p,uchar * ep,int val)677dd7cddfSDavid du Colombier puchar(uchar *p, uchar *ep, int val)
687dd7cddfSDavid du Colombier {
697dd7cddfSDavid du Colombier if(ep - p < 1)
707dd7cddfSDavid du Colombier return ep+1;
717dd7cddfSDavid du Colombier *p++ = val;
727dd7cddfSDavid du Colombier return p;
737dd7cddfSDavid du Colombier }
747dd7cddfSDavid du Colombier
757dd7cddfSDavid du Colombier static uchar*
pushort(uchar * p,uchar * ep,int val)767dd7cddfSDavid du Colombier pushort(uchar *p, uchar *ep, int val)
777dd7cddfSDavid du Colombier {
787dd7cddfSDavid du Colombier if(ep - p < 2)
797dd7cddfSDavid du Colombier return ep+1;
803e12c5d1SDavid du Colombier *p++ = val>>8;
813e12c5d1SDavid du Colombier *p++ = val;
823e12c5d1SDavid du Colombier return p;
833e12c5d1SDavid du Colombier }
843e12c5d1SDavid du Colombier
853e12c5d1SDavid du Colombier static uchar*
pulong(uchar * p,uchar * ep,int val)867dd7cddfSDavid du Colombier pulong(uchar *p, uchar *ep, int val)
873e12c5d1SDavid du Colombier {
887dd7cddfSDavid du Colombier if(ep - p < 4)
897dd7cddfSDavid du Colombier return ep+1;
903e12c5d1SDavid du Colombier *p++ = val>>24;
913e12c5d1SDavid du Colombier *p++ = val>>16;
923e12c5d1SDavid du Colombier *p++ = val>>8;
933e12c5d1SDavid du Colombier *p++ = val;
943e12c5d1SDavid du Colombier return p;
953e12c5d1SDavid du Colombier }
963e12c5d1SDavid du Colombier
973e12c5d1SDavid du Colombier static uchar*
pv4addr(uchar * p,uchar * ep,char * name)985d459b5aSDavid du Colombier pv4addr(uchar *p, uchar *ep, char *name)
993e12c5d1SDavid du Colombier {
1007dd7cddfSDavid du Colombier uchar ip[IPaddrlen];
1017dd7cddfSDavid du Colombier
1027dd7cddfSDavid du Colombier if(ep - p < 4)
1037dd7cddfSDavid du Colombier return ep+1;
1047dd7cddfSDavid du Colombier parseip(ip, name);
1057dd7cddfSDavid du Colombier v6tov4(p, ip);
1063e12c5d1SDavid du Colombier return p + 4;
1073e12c5d1SDavid du Colombier }
1083e12c5d1SDavid du Colombier
1093e12c5d1SDavid du Colombier static uchar*
pv6addr(uchar * p,uchar * ep,char * name)1105d459b5aSDavid du Colombier pv6addr(uchar *p, uchar *ep, char *name)
1115d459b5aSDavid du Colombier {
1125d459b5aSDavid du Colombier if(ep - p < IPaddrlen)
1135d459b5aSDavid du Colombier return ep+1;
1145d459b5aSDavid du Colombier parseip(p, name);
1155d459b5aSDavid du Colombier return p + IPaddrlen;
1165d459b5aSDavid du Colombier }
1175d459b5aSDavid du Colombier
1185d459b5aSDavid du Colombier static uchar*
pname(uchar * p,uchar * ep,char * np,Dict * dp)1197dd7cddfSDavid du Colombier pname(uchar *p, uchar *ep, char *np, Dict *dp)
1203e12c5d1SDavid du Colombier {
1216b0d5c8bSDavid du Colombier int i;
1224f8f669cSDavid du Colombier char *cp;
1233e12c5d1SDavid du Colombier char *last; /* last component packed */
1243e12c5d1SDavid du Colombier
1257dd7cddfSDavid du Colombier if(strlen(np) >= Domlen) /* make sure we don't exceed DNS limits */
1267dd7cddfSDavid du Colombier return ep+1;
1273e12c5d1SDavid du Colombier
1283e12c5d1SDavid du Colombier last = 0;
1293e12c5d1SDavid du Colombier while(*np){
1303e12c5d1SDavid du Colombier /* look through every component in the dictionary for a match */
1314f8f669cSDavid du Colombier for(i = 0; i < dp->n; i++)
1323e12c5d1SDavid du Colombier if(strcmp(np, dp->x[i].name) == 0){
1337dd7cddfSDavid du Colombier if(ep - p < 2)
1347dd7cddfSDavid du Colombier return ep+1;
1355d34ce99SDavid du Colombier if ((dp->x[i].offset>>8) & 0xc0)
1365d34ce99SDavid du Colombier dnslog("convDNS2M: offset too big for "
1375d34ce99SDavid du Colombier "DNS packet format");
1385d34ce99SDavid du Colombier *p++ = dp->x[i].offset>>8 | 0xc0;
1393e12c5d1SDavid du Colombier *p++ = dp->x[i].offset;
1403e12c5d1SDavid du Colombier return p;
1413e12c5d1SDavid du Colombier }
1423e12c5d1SDavid du Colombier
1433e12c5d1SDavid du Colombier /* if there's room, enter this name in dictionary */
1444f8f669cSDavid du Colombier if(dp->n < Ndict)
1453e12c5d1SDavid du Colombier if(last){
1463e12c5d1SDavid du Colombier /* the whole name is already in dp->buf */
1473e12c5d1SDavid du Colombier last = strchr(last, '.') + 1;
1483e12c5d1SDavid du Colombier dp->x[dp->n].name = last;
1493e12c5d1SDavid du Colombier dp->x[dp->n].offset = p - dp->start;
1503e12c5d1SDavid du Colombier dp->n++;
1513e12c5d1SDavid du Colombier } else {
1523e12c5d1SDavid du Colombier /* add to dp->buf */
1533e12c5d1SDavid du Colombier i = strlen(np);
1544f8f669cSDavid du Colombier if(dp->ep + i + 1 < &dp->buf[sizeof dp->buf]){
1553e12c5d1SDavid du Colombier strcpy(dp->ep, np);
1563e12c5d1SDavid du Colombier dp->x[dp->n].name = dp->ep;
1573e12c5d1SDavid du Colombier last = dp->ep;
1583e12c5d1SDavid du Colombier dp->x[dp->n].offset = p - dp->start;
1593e12c5d1SDavid du Colombier dp->ep += i + 1;
1603e12c5d1SDavid du Colombier dp->n++;
1613e12c5d1SDavid du Colombier }
1623e12c5d1SDavid du Colombier }
1633e12c5d1SDavid du Colombier
1643e12c5d1SDavid du Colombier /* put next component into message */
1653e12c5d1SDavid du Colombier cp = strchr(np, '.');
1664f8f669cSDavid du Colombier if(cp == nil){
1673e12c5d1SDavid du Colombier i = strlen(np);
1683e12c5d1SDavid du Colombier cp = np + i; /* point to null terminator */
1693e12c5d1SDavid du Colombier } else {
1703e12c5d1SDavid du Colombier i = cp - np;
1713e12c5d1SDavid du Colombier cp++; /* point past '.' */
1723e12c5d1SDavid du Colombier }
1737dd7cddfSDavid du Colombier if(ep-p < i+1)
1747dd7cddfSDavid du Colombier return ep+1;
17576783259SDavid du Colombier if (i > Labellen)
17676783259SDavid du Colombier return ep+1;
1773e12c5d1SDavid du Colombier *p++ = i; /* count of chars in label */
178e8ece30eSDavid du Colombier memmove(p, np, i);
1793e12c5d1SDavid du Colombier np = cp;
1803e12c5d1SDavid du Colombier p += i;
1813e12c5d1SDavid du Colombier }
1827dd7cddfSDavid du Colombier
1837dd7cddfSDavid du Colombier if(p >= ep)
1847dd7cddfSDavid du Colombier return ep+1;
1853e12c5d1SDavid du Colombier *p++ = 0; /* add top level domain */
1863e12c5d1SDavid du Colombier
1873e12c5d1SDavid du Colombier return p;
1883e12c5d1SDavid du Colombier }
1893e12c5d1SDavid du Colombier
1903e12c5d1SDavid du Colombier static uchar*
convRR2M(RR * rp,uchar * p,uchar * ep,Dict * dp)1917dd7cddfSDavid du Colombier convRR2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
1923e12c5d1SDavid du Colombier {
1933e12c5d1SDavid du Colombier uchar *lp, *data;
1947dd7cddfSDavid du Colombier int len, ttl;
19560845620SDavid du Colombier Txt *t;
1963e12c5d1SDavid du Colombier
1973e12c5d1SDavid du Colombier NAME(rp->owner->name);
1983e12c5d1SDavid du Colombier USHORT(rp->type);
1993e12c5d1SDavid du Colombier USHORT(rp->owner->class);
2007dd7cddfSDavid du Colombier
2017dd7cddfSDavid du Colombier /* egregious overuse of ttl (it's absolute time in the cache) */
2027dd7cddfSDavid du Colombier if(rp->db)
2037dd7cddfSDavid du Colombier ttl = rp->ttl;
2047dd7cddfSDavid du Colombier else
2057dd7cddfSDavid du Colombier ttl = rp->ttl - now;
2067dd7cddfSDavid du Colombier if(ttl < 0)
2077dd7cddfSDavid du Colombier ttl = 0;
2087dd7cddfSDavid du Colombier ULONG(ttl);
2097dd7cddfSDavid du Colombier
2103e12c5d1SDavid du Colombier lp = p; /* leave room for the rdata length */
2113e12c5d1SDavid du Colombier p += 2;
2123e12c5d1SDavid du Colombier data = p;
2137dd7cddfSDavid du Colombier
2147dd7cddfSDavid du Colombier if(data >= ep)
2157dd7cddfSDavid du Colombier return p+1;
2167dd7cddfSDavid du Colombier
2173e12c5d1SDavid du Colombier switch(rp->type){
2183e12c5d1SDavid du Colombier case Thinfo:
21960845620SDavid du Colombier SYMBOL(rp->cpu->name);
22060845620SDavid du Colombier SYMBOL(rp->os->name);
2213e12c5d1SDavid du Colombier break;
2223e12c5d1SDavid du Colombier case Tcname:
2233e12c5d1SDavid du Colombier case Tmb:
2243e12c5d1SDavid du Colombier case Tmd:
2253e12c5d1SDavid du Colombier case Tmf:
2263e12c5d1SDavid du Colombier case Tns:
2273e12c5d1SDavid du Colombier NAME(rp->host->name);
2283e12c5d1SDavid du Colombier break;
2293e12c5d1SDavid du Colombier case Tmg:
2303e12c5d1SDavid du Colombier case Tmr:
2313e12c5d1SDavid du Colombier NAME(rp->mb->name);
2323e12c5d1SDavid du Colombier break;
2333e12c5d1SDavid du Colombier case Tminfo:
2343e12c5d1SDavid du Colombier NAME(rp->rmb->name);
2353e12c5d1SDavid du Colombier NAME(rp->mb->name);
2363e12c5d1SDavid du Colombier break;
2373e12c5d1SDavid du Colombier case Tmx:
2383e12c5d1SDavid du Colombier USHORT(rp->pref);
2393e12c5d1SDavid du Colombier NAME(rp->host->name);
2403e12c5d1SDavid du Colombier break;
2413e12c5d1SDavid du Colombier case Ta:
2425d459b5aSDavid du Colombier V4ADDR(rp->ip->name);
2435d459b5aSDavid du Colombier break;
2445d459b5aSDavid du Colombier case Taaaa:
2455d459b5aSDavid du Colombier V6ADDR(rp->ip->name);
2463e12c5d1SDavid du Colombier break;
2473e12c5d1SDavid du Colombier case Tptr:
2483e12c5d1SDavid du Colombier NAME(rp->ptr->name);
2493e12c5d1SDavid du Colombier break;
2503e12c5d1SDavid du Colombier case Tsoa:
2513e12c5d1SDavid du Colombier NAME(rp->host->name);
2523e12c5d1SDavid du Colombier NAME(rp->rmb->name);
2533e12c5d1SDavid du Colombier ULONG(rp->soa->serial);
2543e12c5d1SDavid du Colombier ULONG(rp->soa->refresh);
2553e12c5d1SDavid du Colombier ULONG(rp->soa->retry);
2563e12c5d1SDavid du Colombier ULONG(rp->soa->expire);
2573e12c5d1SDavid du Colombier ULONG(rp->soa->minttl);
2583e12c5d1SDavid du Colombier break;
2594f8f669cSDavid du Colombier case Tsrv:
2604f8f669cSDavid du Colombier USHORT(rp->srv->pri);
2614f8f669cSDavid du Colombier USHORT(rp->srv->weight);
262*225077b0SDavid du Colombier USHORT(rp->port);
263*225077b0SDavid du Colombier STRING(rp->host->name); /* rfc2782 sez no name compression */
2644f8f669cSDavid du Colombier break;
2657dd7cddfSDavid du Colombier case Ttxt:
26660845620SDavid du Colombier for(t = rp->txt; t != nil; t = t->next)
26760845620SDavid du Colombier STRING(t->p);
2687dd7cddfSDavid du Colombier break;
2699a747e4fSDavid du Colombier case Tnull:
2709a747e4fSDavid du Colombier BYTES(rp->null->data, rp->null->dlen);
2719a747e4fSDavid du Colombier break;
2727dd7cddfSDavid du Colombier case Trp:
2737dd7cddfSDavid du Colombier NAME(rp->rmb->name);
27460845620SDavid du Colombier NAME(rp->rp->name);
2757dd7cddfSDavid du Colombier break;
2767dd7cddfSDavid du Colombier case Tkey:
2777dd7cddfSDavid du Colombier USHORT(rp->key->flags);
2787dd7cddfSDavid du Colombier UCHAR(rp->key->proto);
2797dd7cddfSDavid du Colombier UCHAR(rp->key->alg);
2807dd7cddfSDavid du Colombier BYTES(rp->key->data, rp->key->dlen);
2817dd7cddfSDavid du Colombier break;
2827dd7cddfSDavid du Colombier case Tsig:
2837dd7cddfSDavid du Colombier USHORT(rp->sig->type);
2847dd7cddfSDavid du Colombier UCHAR(rp->sig->alg);
2857dd7cddfSDavid du Colombier UCHAR(rp->sig->labels);
2867dd7cddfSDavid du Colombier ULONG(rp->sig->ttl);
2877dd7cddfSDavid du Colombier ULONG(rp->sig->exp);
2887dd7cddfSDavid du Colombier ULONG(rp->sig->incep);
2897dd7cddfSDavid du Colombier USHORT(rp->sig->tag);
2907dd7cddfSDavid du Colombier NAME(rp->sig->signer->name);
2917dd7cddfSDavid du Colombier BYTES(rp->sig->data, rp->sig->dlen);
2927dd7cddfSDavid du Colombier break;
2937dd7cddfSDavid du Colombier case Tcert:
2947dd7cddfSDavid du Colombier USHORT(rp->cert->type);
2957dd7cddfSDavid du Colombier USHORT(rp->cert->tag);
2967dd7cddfSDavid du Colombier UCHAR(rp->cert->alg);
2977dd7cddfSDavid du Colombier BYTES(rp->cert->data, rp->cert->dlen);
2987dd7cddfSDavid du Colombier break;
2993e12c5d1SDavid du Colombier }
3003e12c5d1SDavid du Colombier
3013e12c5d1SDavid du Colombier /* stuff in the rdata section length */
3023e12c5d1SDavid du Colombier len = p - data;
3033e12c5d1SDavid du Colombier *lp++ = len >> 8;
3043e12c5d1SDavid du Colombier *lp = len;
3053e12c5d1SDavid du Colombier
3063e12c5d1SDavid du Colombier return p;
3073e12c5d1SDavid du Colombier }
3083e12c5d1SDavid du Colombier
3093e12c5d1SDavid du Colombier static uchar*
convQ2M(RR * rp,uchar * p,uchar * ep,Dict * dp)3107dd7cddfSDavid du Colombier convQ2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
3113e12c5d1SDavid du Colombier {
3123e12c5d1SDavid du Colombier NAME(rp->owner->name);
3133e12c5d1SDavid du Colombier USHORT(rp->type);
3143e12c5d1SDavid du Colombier USHORT(rp->owner->class);
3153e12c5d1SDavid du Colombier return p;
3163e12c5d1SDavid du Colombier }
3173e12c5d1SDavid du Colombier
3183e12c5d1SDavid du Colombier static uchar*
rrloop(RR * rp,int * countp,uchar * p,uchar * ep,Dict * dp,int quest)3197dd7cddfSDavid du Colombier rrloop(RR *rp, int *countp, uchar *p, uchar *ep, Dict *dp, int quest)
3203e12c5d1SDavid du Colombier {
3213e12c5d1SDavid du Colombier uchar *np;
3223e12c5d1SDavid du Colombier
3233e12c5d1SDavid du Colombier *countp = 0;
3247dd7cddfSDavid du Colombier for(; rp && p < ep; rp = rp->next){
3253e12c5d1SDavid du Colombier if(quest)
3267dd7cddfSDavid du Colombier np = convQ2M(rp, p, ep, dp);
3273e12c5d1SDavid du Colombier else
3287dd7cddfSDavid du Colombier np = convRR2M(rp, p, ep, dp);
3297dd7cddfSDavid du Colombier if(np > ep)
3303e12c5d1SDavid du Colombier break;
3313e12c5d1SDavid du Colombier p = np;
3323e12c5d1SDavid du Colombier (*countp)++;
3333e12c5d1SDavid du Colombier }
3343e12c5d1SDavid du Colombier return p;
3353e12c5d1SDavid du Colombier }
3363e12c5d1SDavid du Colombier
3373e12c5d1SDavid du Colombier /*
3383e12c5d1SDavid du Colombier * convert into a message
3393e12c5d1SDavid du Colombier */
3403e12c5d1SDavid du Colombier int
convDNS2M(DNSmsg * m,uchar * buf,int len)3413e12c5d1SDavid du Colombier convDNS2M(DNSmsg *m, uchar *buf, int len)
3423e12c5d1SDavid du Colombier {
343d6d99297SDavid du Colombier ulong trunc = 0;
3443e12c5d1SDavid du Colombier uchar *p, *ep, *np;
3453e12c5d1SDavid du Colombier Dict d;
3463e12c5d1SDavid du Colombier
3473e12c5d1SDavid du Colombier d.n = 0;
3483e12c5d1SDavid du Colombier d.start = buf;
3493e12c5d1SDavid du Colombier d.ep = d.buf;
3503e12c5d1SDavid du Colombier memset(buf, 0, len);
3517dd7cddfSDavid du Colombier m->qdcount = m->ancount = m->nscount = m->arcount = 0;
3523e12c5d1SDavid du Colombier
3533e12c5d1SDavid du Colombier /* first pack in the RR's so we can get real counts */
3543e12c5d1SDavid du Colombier p = buf + 12;
3553e12c5d1SDavid du Colombier ep = buf + len;
3567dd7cddfSDavid du Colombier p = rrloop(m->qd, &m->qdcount, p, ep, &d, 1);
3577dd7cddfSDavid du Colombier p = rrloop(m->an, &m->ancount, p, ep, &d, 0);
3587dd7cddfSDavid du Colombier p = rrloop(m->ns, &m->nscount, p, ep, &d, 0);
3597dd7cddfSDavid du Colombier p = rrloop(m->ar, &m->arcount, p, ep, &d, 0);
360d6d99297SDavid du Colombier if(p > ep) {
361d6d99297SDavid du Colombier trunc = Ftrunc;
362d6d99297SDavid du Colombier dnslog("udp packet full; truncating my reply");
363d6d99297SDavid du Colombier p = ep;
364d6d99297SDavid du Colombier }
3653e12c5d1SDavid du Colombier
3663e12c5d1SDavid du Colombier /* now pack the rest */
3673e12c5d1SDavid du Colombier np = p;
3683e12c5d1SDavid du Colombier p = buf;
3693e12c5d1SDavid du Colombier ep = buf + len;
3703e12c5d1SDavid du Colombier USHORT(m->id);
371d6d99297SDavid du Colombier USHORT(m->flags | trunc);
3723e12c5d1SDavid du Colombier USHORT(m->qdcount);
3733e12c5d1SDavid du Colombier USHORT(m->ancount);
3743e12c5d1SDavid du Colombier USHORT(m->nscount);
3753e12c5d1SDavid du Colombier USHORT(m->arcount);
376d6d99297SDavid du Colombier USED(p);
3773e12c5d1SDavid du Colombier return np - buf;
3783e12c5d1SDavid du Colombier }
379