diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/network/dn_comp.c | 103 |
1 files changed, 102 insertions, 1 deletions
diff --git a/src/network/dn_comp.c b/src/network/dn_comp.c index 4f4452aa..a17d434d 100644 --- a/src/network/dn_comp.c +++ b/src/network/dn_comp.c @@ -1,9 +1,110 @@ +#include <string.h> #include <resolv.h> #include "libc.h" +/* RFC 1035 message compression */ + +/* label start offsets of a compressed domain name s */ +static int getoffs(short *offs, const unsigned char *base, const unsigned char *s) +{ + int i=0; + for (;;) { + while (*s & 0xc0) { + if ((*s & 0xc0) != 0xc0) return 0; + s = base + ((s[0]&0x3f)<<8 | s[1]); + } + if (!*s) return i; + if (s-base >= 0x4000) return 0; + offs[i++] = s-base; + s += *s + 1; + } +} + +/* label lengths of an ascii domain name s */ +static int getlens(unsigned char *lens, const char *s, int l) +{ + int i=0,j=0,k=0; + for (;;) { + for (; j<l && s[j]!='.'; j++); + if (j-k-1u > 62) return 0; + lens[i++] = j-k; + if (j==l) return i; + k = ++j; + } +} + +/* longest suffix match of an ascii domain with a compressed domain name dn */ +static int match(int *offset, const unsigned char *base, const unsigned char *dn, + const char *end, const unsigned char *lens, int nlen) +{ + int l, o, m=0; + short offs[128]; + int noff = getoffs(offs, base, dn); + if (!noff) return 0; + for (;;) { + l = lens[--nlen]; + o = offs[--noff]; + end -= l; + if (l != base[o] || memcmp(base+o+1, end, l)) + return m; + *offset = o; + m += l; + if (nlen) m++; + if (!nlen || !noff) return m; + end--; + } +} + int __dn_comp(const char *src, unsigned char *dst, int space, unsigned char **dnptrs, unsigned char **lastdnptr) { - return -1; + int i, j, n, m=0, offset, bestlen=0, bestoff; + unsigned char lens[127]; + unsigned char **p; + const char *end; + size_t l = strnlen(src, 255); + if (l && src[l-1] == '.') l--; + if (l>253 || space<=0) return -1; + if (!l) { + *dst = 0; + return 1; + } + end = src+l; + n = getlens(lens, src, l); + if (!n) return -1; + + p = dnptrs; + if (p && *p) for (p++; *p; p++) { + m = match(&offset, *dnptrs, *p, end, lens, n); + if (m > bestlen) { + bestlen = m; + bestoff = offset; + if (m == l) + break; + } + } + + /* encode unmatched part */ + if (space < l-bestlen+2+(bestlen-1 < l-1)) return -1; + memcpy(dst+1, src, l-bestlen); + for (i=j=0; i<l-bestlen; i+=lens[j++]+1) + dst[i] = lens[j]; + + /* add tail */ + if (bestlen) { + dst[i++] = 0xc0 | bestoff>>8; + dst[i++] = bestoff; + } else + dst[i++] = 0; + + /* save dst pointer */ + if (i>2 && lastdnptr && dnptrs && *dnptrs) { + while (*p) p++; + if (p+1 < lastdnptr) { + *p++ = dst; + *p=0; + } + } + return i; } weak_alias(__dn_comp, dn_comp); |