VirtualBox

Ignore:
Timestamp:
Dec 15, 2015 11:12:29 PM (9 years ago)
Author:
vboxsync
Message:

NAT: Start rewriting host resolver to clean up the code to be able to
add reverse lookups eventually. Name compression (label pointers) are
supported in replies. Code for user-defined mapping is left as is for
now.

Location:
trunk/src/VBox/Devices/Network/slirp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/slirp/hostres.c

    r59063 r59142  
    2828#define DNS_CONTROL_PORT_NUMBER 53
    2929/* see RFC 1035(4.1.1) */
    30 union dnsmsg_header
    31 {
    32     struct
    33     {
    34         unsigned id:16;
    35         unsigned rd:1;
    36         unsigned tc:1;
    37         unsigned aa:1;
    38         unsigned opcode:4;
    39         unsigned qr:1;
    40         unsigned rcode:4;
    41         unsigned Z:3;
    42         unsigned ra:1;
    43         uint16_t qdcount;
    44         uint16_t ancount;
    45         uint16_t nscount;
    46         uint16_t arcount;
    47     } X;
    48     uint16_t raw[6];
     30struct dnsmsg_header
     31{
     32    uint16_t id;
     33
     34    /* XXX: endianness */
     35    unsigned rd:1;
     36    unsigned tc:1;
     37    unsigned aa:1;
     38    unsigned opcode:4;
     39    unsigned qr:1;
     40    unsigned rcode:4;
     41    unsigned Z:3;
     42    unsigned ra:1;
     43
     44    uint16_t qdcount;
     45    uint16_t ancount;
     46    uint16_t nscount;
     47    uint16_t arcount;
    4948};
    50 AssertCompileSize(union dnsmsg_header, 12);
    51 
    52 struct dns_meta_data
    53 {
    54     uint16_t type;
    55     uint16_t class;
     49AssertCompileSize(struct dnsmsg_header, 12);
     50
     51#define QR_Query                0
     52#define QR_Response             1
     53
     54#define OpCode_Query            0
     55
     56#define RCode_NoError           0
     57#define RCode_FormErr           1
     58#define RCode_ServFail          2
     59#define RCode_NXDomain          3
     60#define RCode_NotImp            4
     61#define RCode_Refused           5
     62
     63#define Type_A                  1
     64#define Type_CNAME              5
     65#define Type_PTR                12
     66#define Type_ANY                255
     67
     68#define Class_IN                1
     69#define Class_ANY               255
     70
     71/* compressed label encoding */
     72#define DNS_LABEL_PTR           0xc0
     73
     74#define DNS_MAX_UDP_LEN         512
     75#define DNS_MAX_LABEL_LEN       63
     76#define DNS_MAX_NAME_LEN        255
     77
     78
     79/*
     80 * A tree of labels.
     81 *
     82 * rfc1035#section-3.1
     83 * rfc1035#section-4.1.4
     84 */
     85struct label
     86{
     87    const uint8_t *buf;
     88    ssize_t off;
     89    struct label *children;
     90    struct label *sibling;
    5691};
    5792
    58 struct dnsmsg_answer
    59 {
    60     uint16_t name;
    61     struct dns_meta_data meta;
    62     uint16_t ttl[2];
    63     uint16_t rdata_len;
    64     uint8_t  rdata[1];  /* depends on value at rdata_len */
     93
     94/*
     95 * A structure to build DNS response.
     96 */
     97struct response
     98{
     99    struct label *labels;       /* already encoded in buf */
     100    size_t qlen;                /* original question */
     101    size_t end;                 /* of data in buf */
     102
     103    /* continuous buffer to build the response */
     104    uint8_t buf[DNS_MAX_UDP_LEN];
    65105};
    66106
    67 /* see RFC 1035(4.1) */
    68 static void CStr2QStr(const char *pcszStr, char *pszQStr, size_t cQStr);
    69 static void QStr2CStr(const char *pcszQStr, char *pszStr, size_t cStr);
     107
     108static int verify_header(PNATState pData, struct mbuf **pMBuf);
     109static struct mbuf *respond(PNATState pData, struct mbuf *m, struct response *res);
     110struct mbuf *resolve(PNATState pData, struct mbuf *m, struct response *res, size_t qname, uint16_t qtype);
     111struct mbuf *refuse(PNATState pData, struct mbuf *m, unsigned int rcode);
     112static ssize_t append_a(struct response *res, const char *name, struct in_addr addr);
     113static ssize_t append_cname(struct response *res, const char *name, const char *cname);
     114static ssize_t append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl);
     115static ssize_t append_name(struct response *res, const char *name);
     116static ssize_t append_u32(struct response *res, uint32_t value);
     117static ssize_t append_u16(struct response *res, uint16_t value);
     118static ssize_t append_u8(struct response *res, uint8_t value);
     119static ssize_t append_bytes(struct response *res, uint8_t *p, size_t size);
     120static ssize_t check_space(struct response *res, size_t size);
     121
     122static void strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off);
     123
     124static void LogLabelsTree(const char *before, struct label *l, const char *after);
     125static void free_labels(struct label *root);
     126
    70127#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
    71128static void alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *pHostent);
    72129#endif
    73130
    74 
     131#if 1 /* XXX */
     132# define LogErr(args) Log2(args)
     133# define LogDbg(args) Log3(args)
     134#else
     135# define LogErr(args) LogRel(args)
     136# define LogDbg(args) LogRel(args)
     137#endif
     138
     139
     140struct mbuf *
     141hostresolver(PNATState pData, struct mbuf *m)
     142{
     143    struct label *l;
     144    int error;
     145
     146    struct response res;
     147
     148    error = verify_header(pData, &m);
     149    if (error != 0)
     150        return m;
     151
     152    RT_ZERO(res);
     153
     154    /*
     155     * Do the real work
     156     */
     157    m = respond(pData, m, &res);
     158
     159    free_labels(res.labels);
     160    return m;
     161}
     162
     163
     164struct mbuf *
     165refuse(PNATState pData, struct mbuf *m, unsigned int rcode)
     166{
     167    struct dnsmsg_header *pHdr;
     168
     169    pHdr = mtod(m, struct dnsmsg_header *);
     170    pHdr->qr = QR_Response;
     171    pHdr->rcode = rcode;
     172    pHdr->ra = 1;
     173    pHdr->aa = 0;
     174
     175    return m;
     176}
     177
     178
     179static int
     180verify_header(PNATState pData, struct mbuf **pMBuf)
     181{
     182    struct mbuf *m;
     183    struct dnsmsg_header *pHdr;
     184    size_t mlen;
     185
     186    m = *pMBuf;
     187
     188    /*
     189     * In theory we should have called
     190     *
     191     *   m = m_pullup(m, sizeof(struct dnsmsg_header));
     192     *
     193     * here first (which should have been a nop), but the way mbufs
     194     * are used in NAT will always cause a copy that will have no
     195     * leading space.  We can use m_copyup() instead, but if we are
     196     * peeking under the hood anyway, we might as well just rely on
     197     * the fact that this header will be contiguous.
     198     */
     199    pHdr = mtod(m, struct dnsmsg_header *);
     200
     201    if (RT_UNLIKELY(pHdr->qr != QR_Query))
     202    {
     203        LogErr(("NAT: hostres: unexpected response\n"));
     204        goto drop;
     205    }
     206
     207    mlen = m_length(m, NULL);
     208    if (RT_UNLIKELY(mlen > DNS_MAX_UDP_LEN))
     209    {
     210        LogErr(("NAT: hostres: packet too large\n"));
     211        refuse(pData, m, RCode_FormErr); /* or drop? */
     212        return 1;
     213    }
     214
     215    if (RT_UNLIKELY(pHdr->opcode != OpCode_Query))
     216    {
     217        LogErr(("NAT: hostres: unsupported opcode\n"));
     218        refuse(pData, m, RCode_NotImp);
     219        return 1;
     220    }
     221
     222    if (RT_UNLIKELY(pHdr->Z != 0))
     223    {
     224        LogErr(("NAT: hostres: MBZ bits are not\n"));
     225        refuse(pData, m, RCode_FormErr);
     226        return 1;
     227    }
     228
     229    if (RT_UNLIKELY(pHdr->qdcount != RT_H2N_U16_C(1)))
     230    {
     231        LogErr(("NAT: hostres: multiple questions\n"));
     232        refuse(pData, m, RCode_NotImp);
     233        return 1;
     234    }
     235
     236    if (RT_UNLIKELY(pHdr->ancount != 0))
     237    {
     238        LogErr(("NAT: hostres: answers in query\n"));
     239        refuse(pData, m, RCode_FormErr);
     240        return 1;
     241    }
     242
     243    if (RT_UNLIKELY(pHdr->nscount != 0))
     244    {
     245        LogErr(("NAT: hostres: authority RRs in query\n"));
     246        refuse(pData, m, RCode_FormErr);
     247        return 1;
     248    }
     249
     250    if (RT_UNLIKELY(pHdr->arcount != 0))
     251    {
     252        LogErr(("NAT: hostres: additional info RRs in query\n"));
     253        refuse(pData, m, RCode_FormErr);
     254        return 1;
     255    }
     256
     257    if (RT_UNLIKELY(mlen < sizeof(*pHdr)
     258                             + /* qname  */ 1
     259                             + /* qtype  */ 2
     260                             + /* qclass */ 2))
     261    {
     262        LogErr(("NAT: hostres: packet too small\n"));
     263        refuse(pData, m, RCode_FormErr);
     264        return 1;
     265    }
     266
     267    return 0;
     268
     269  drop:
     270    if (m != NULL)
     271        m_freem(pData, m);
     272    *pMBuf = NULL;
     273    return 1;
     274}
     275
     276
     277static struct mbuf *
     278respond(PNATState pData, struct mbuf *m, struct response *res)
     279{
     280    struct dnsmsg_header *pHdr;
     281    size_t mlen;
     282    size_t off;
     283    size_t qname;
     284    uint16_t qtype, qclass;
     285    struct label *l, **pparent;
     286
     287    /**
     288     * Copy the request into the contiguous buffer for the response
     289     * and parse the question.
     290     */
     291
     292    mlen = m_length(m, NULL);
     293    m_copydata(m, 0, mlen, (char *)res->buf);
     294    res->end = res->qlen = mlen;
     295
     296    /* convert header to response */
     297    pHdr = (struct dnsmsg_header *)res->buf;
     298    pHdr->qr = QR_Response;
     299    pHdr->rcode = RCode_NoError;
     300    pHdr->ra = 1;               /* the host provides recursion */
     301    pHdr->aa = 0;               /* we are not authoritative */
     302
     303    off = sizeof(*pHdr);
     304    qname = off;
     305
     306    /*
     307     * Parse/verify QNAME and collect the suffixes to be used for
     308     * compression in the answer.
     309     */
     310    while (off < mlen) {
     311        size_t loff, llen;
     312        uint8_t c;
     313
     314        c = res->buf[off];
     315
     316        /*
     317         * There's just one question with just one name, so there are
     318         * no other labels it can point to.  Thus all well-formed
     319         * names with a pointer can only be infinite loops.
     320         */
     321        if ((c & DNS_LABEL_PTR) == DNS_LABEL_PTR)
     322        {
     323            LogErr(("NAT: hostres: label pointer in the qname\n"));
     324            return refuse(pData, m, RCode_FormErr);
     325        }
     326
     327        if ((c & DNS_LABEL_PTR) != 0)
     328        {
     329            LogErr(("NAT: hostres: unexpected high bits\n"));
     330            return refuse(pData, m, RCode_FormErr);
     331        }
     332
     333        /*
     334         * label of "llen" chars starts at offset "loff".
     335         */
     336        loff = off;
     337        llen = c;
     338        ++off;
     339
     340        if (loff + 1 + llen > mlen)
     341        {
     342            LogErr(("NAT: hostres: length byte points beyound packet boundary\n"));
     343            return refuse(pData, m, RCode_FormErr);
     344        }
     345
     346        if (llen == 0)             /* end of the label list */
     347        {
     348            break;
     349        }
     350
     351        /* do only minimal verification of the label */
     352        while (off < loff + 1 + llen)
     353        {
     354            c = res->buf[off];
     355            ++off;
     356
     357            if (c == '.')
     358            {
     359                LogErr(("NAT: hostres: dot inside label\n"));
     360                return refuse(pData, m, RCode_FormErr);
     361            }
     362
     363            if (c == '\0')
     364            {
     365                LogErr(("NAT: hostres: nul byte inside label\n"));
     366                return refuse(pData, m, RCode_FormErr);
     367            }
     368        }
     369
     370        l = RTMemAllocZ(sizeof(*l));
     371        l->buf = res->buf;
     372        l->off = loff;
     373        l->children = res->labels;
     374        res->labels = l;
     375    }
     376
     377    /*
     378     * QTYPE and QCLASS
     379     */
     380    if (RT_UNLIKELY(off + 4 != mlen))
     381    {
     382        LogErr(("NAT: hostres: question too short / too long\n"));
     383        return refuse(pData, m, RCode_FormErr);
     384    }
     385
     386    memcpy(&qtype, &res->buf[off], sizeof(qtype));
     387    qtype = RT_N2H_U16(qtype);
     388    off += sizeof(qtype);
     389
     390    memcpy(&qclass, &res->buf[off], sizeof(qclass));
     391    qclass = RT_N2H_U16(qclass);
     392    off += sizeof(qclass);
     393
     394    if (   qclass != Class_IN
     395        && qclass != Class_ANY)
     396    {
     397        LogErr(("NAT: hostres: unsupported qclass %d\n", qclass));
     398        return refuse(pData, m, RCode_NotImp);
     399    }
     400
     401    if (   qtype != Type_A
     402        && qtype != Type_ANY)
     403    {
     404        LogErr(("NAT: hostres: unsupported qtype %d\n", qtype));
     405        return refuse(pData, m, RCode_NotImp);
     406    }
     407
     408    return resolve(pData, m, res, qname, qtype);
     409}
     410
     411
     412struct mbuf *
     413resolve(PNATState pData, struct mbuf *m, struct response *res,
     414        size_t qname, uint16_t qtype)
     415{
     416    struct dnsmsg_header *pHdr;
     417    struct hostent *h;
     418    size_t oend;
     419    size_t nanswers;
     420    ssize_t nbytes;
     421    int i;
     422
     423    char name[DNS_MAX_NAME_LEN+1];
     424
     425    pHdr = (struct dnsmsg_header *)res->buf;
     426    nanswers = 0;
     427    oend = res->end;
     428
     429    strnlabels(name, sizeof(name), res->buf, qname);
     430    LogDbg(("NAT: hostres: qname=\"%s\"\n", name));
     431
     432    h = gethostbyname(name);
     433    if (h == NULL)
     434    {
     435        /* LogErr: h_errno */
     436        return refuse(pData, m, RCode_NXDomain);
     437    }
     438
     439#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
     440    if (!LIST_EMPTY(&pData->DNSMapHead))
     441        alterHostentWithDataFromDNSMap(pData, h);
     442#endif
     443
     444    if (   h->h_name != NULL
     445        && RTStrICmp(h->h_name, name) != 0)
     446    {
     447        LogDbg(("NAT: hostres: %s CNAME %s\n", name, h->h_name));
     448        nbytes = append_cname(res, name, h->h_name);
     449        if (nbytes > 0)
     450        {
     451            ++nanswers;
     452        }
     453        else
     454        {
     455            LogErr(("NAT: hostres: failed to add %s CNAME %s\n",
     456                    name, h->h_name));
     457            if (nbytes < 0)
     458                return refuse(pData, m, RCode_ServFail);
     459            else
     460            {
     461                pHdr->tc = 1;
     462                goto out;
     463            }
     464        }
     465    }
     466
     467    /*
     468     * XXX: TODO: rfc1034#section-3.6.2
     469     *
     470     * If the query is Type_ANY and the name is CNAME, we should only
     471     * return the CNAME, but not the A RRs.
     472     */
     473    for (i = 0; h->h_addr_list[i] != NULL; ++i)
     474    {
     475        const char *cname = h->h_name ? h->h_name : name;
     476        struct in_addr addr;
     477
     478        addr.s_addr = *(uint32_t *)h->h_addr_list[i];
     479        nbytes = append_a(res, cname, addr);
     480
     481        if (nbytes > 0)
     482        {
     483            ++nanswers;
     484        }
     485        else
     486        {
     487            LogErr(("NAT: hostres: failed to add %s A %RTnaipv4\n",
     488                    cname, addr.s_addr));
     489            if (nbytes < 0)
     490                return refuse(pData, m, RCode_ServFail);
     491            else
     492            {
     493                pHdr->tc = 1;
     494                goto out;
     495            }
     496        }
     497    }
     498
     499#if 0
     500    /*
     501     * It's not clear what to do with h_aliases.
     502     *
     503     * For names from the DNS it seems to contain the chain of CNAMEs,
     504     * starting with the original qname from the question.  So for
     505     * them we'd need to reply with a chain of:
     506     *
     507     *     h_aliases[i] CNAME h_aliases[i+1]
     508     *
     509     * OTOH, for the names from the hosts file it seems to contain all
     510     * the names except the first one (which is considered primary and
     511     * is reported as h_name).  In which case the reply should be:
     512     *
     513     *     h_aliases[i] CNAME h_name
     514     *
     515     * Obviously, we have no idea how the name was resolved, so we
     516     * generate at most one CNAME for h_host (if differs) and ignore
     517     * aliases altogehter.
     518     */
     519    for (i = 0; h->h_aliases[i] != NULL; ++i)
     520    {
     521        LogDbg(("NAT: hostres: ... %s\n", h->h_aliases[i]));
     522    }
     523#endif
     524
     525  out:
     526    if (nanswers > 0)
     527    {
     528        int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
     529        if (!ok)
     530        {
     531            /* XXX: this may fail part way: restore old lenght, clear TC? */
     532            return refuse(pData, m, RCode_ServFail);
     533        }
     534        pHdr->ancount = RT_H2N_U16(nanswers);
     535    }
     536    memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
     537    return m;
     538}
     539
     540
     541
     542#define APPEND_PROLOGUE()                       \
     543    ssize_t size = -1;                          \
     544    size_t oend = res->end;                     \
     545    ssize_t nbytes;                             \
     546    do {} while (0)
     547
     548#define CHECKED(_append)                        \
     549    do {                                        \
     550        nbytes = (_append);                     \
     551        if (RT_UNLIKELY(nbytes <= 0))           \
     552        {                                       \
     553            if (nbytes == 0)                    \
     554                size = 0;                       \
     555            goto out;                           \
     556        }                                       \
     557    } while (0)
     558
     559#define APPEND_EPILOGUE()                       \
     560    do {                                        \
     561        size = res->end - oend;                 \
     562      out:                                      \
     563        if (RT_UNLIKELY(size <= 0))             \
     564            res->end = oend;                    \
     565        return size;                            \
     566    } while (0)
     567
     568
     569/*
     570 * A RR - rfc1035#section-3.4.1
     571 */
     572static ssize_t
     573append_a(struct response *res, const char *name, struct in_addr addr)
     574{
     575    APPEND_PROLOGUE();
     576
     577    CHECKED( append_rrhdr(res, name, Type_A, 3600) );
     578    CHECKED( append_u16(res, RT_H2N_U16_C(sizeof(addr))) );
     579    CHECKED( append_u32(res, addr.s_addr) );
     580 
     581    APPEND_EPILOGUE();
     582}
     583
     584
     585/*
     586 * CNAME RR - rfc1035#section-3.3.1
     587 */
     588static ssize_t
     589append_cname(struct response *res, const char *name, const char *cname)
     590{
     591    size_t rdlpos;
     592    uint16_t rdlength;
     593
     594    APPEND_PROLOGUE();
     595
     596    CHECKED( append_rrhdr(res, name, Type_CNAME, 3600) );
     597
     598    rdlpos = res->end;
     599    CHECKED( append_u16(res, 0) ); /* RDLENGTH placeholder */
     600
     601    CHECKED( append_name(res, cname) );
     602
     603    rdlength = RT_H2N_U16(nbytes);
     604    memcpy(&res->buf[rdlpos], &rdlength, sizeof(rdlength));
     605
     606    APPEND_EPILOGUE();
     607}
     608
     609
     610/*
     611 * Append common RR header, up to but not including RDLENGTH and RDATA
     612 * proper (rfc1035#section-3.2.1).
     613 */
     614static ssize_t
     615append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl)
     616{
     617    APPEND_PROLOGUE();
     618
     619    CHECKED( append_name(res, name) );
     620    CHECKED( append_u16(res, RT_H2N_U16(type)) );
     621    CHECKED( append_u16(res, RT_H2N_U16_C(Class_IN)) );
     622    CHECKED( append_u32(res, RT_H2N_U32(ttl)) );
     623
     624    APPEND_EPILOGUE();
     625}
     626
     627
     628static ssize_t
     629append_name(struct response *res, const char *name)
     630{
     631    ssize_t size, nbytes;
     632    struct label *root;
     633    struct label *haystack, *needle;
     634    struct label *head, **neck;
     635    struct label *tail, **graft;
     636    uint8_t *buf;
     637    size_t wr, oend;
     638    uint8_t terminator;
     639    const char *s;
     640
     641    char namestr[DNS_MAX_NAME_LEN + 1];
     642
     643    size = -1;
     644    oend = res->end;
     645
     646    /**
     647     * Split new name into a list of labels encoding it into the
     648     * temporary buffer.
     649     */
     650    root = NULL;
     651
     652    buf = RTMemAllocZ(strlen(name) + 1);
     653    if (buf == NULL)
     654        return -1;
     655    wr = 0;
     656
     657    s = name;
     658    while (*s != '\0') {
     659        const char *part;
     660        size_t poff, plen;
     661        struct label *l;
     662
     663        part = s;
     664        while (*s != '\0' && *s != '.')
     665            ++s;
     666
     667        plen = s - part;
     668
     669        if (plen > DNS_MAX_LABEL_LEN)
     670        {
     671            LogErr(("NAT: hostres: name component too long\n"));
     672            goto out;
     673        }
     674
     675        if (*s == '.')
     676        {
     677            if (plen == 0)
     678            {
     679                LogErr(("NAT: hostres: empty name component\n"));
     680                goto out;
     681            }
     682
     683            ++s;
     684        }
     685       
     686        poff = wr;
     687
     688        buf[poff] = (uint8_t)plen; /* length byte */
     689        ++wr;
     690
     691        memcpy(&buf[wr], part, plen); /* label text */
     692        wr += plen;
     693
     694        l = RTMemAllocZ(sizeof(*l));
     695        if (l == NULL)
     696            goto out;
     697
     698        l->buf = buf;
     699        l->off = poff;
     700        l->children = root;
     701        root = l;
     702    }
     703
     704
     705    /**
     706     * Search for a tail that is already encoded in the message.
     707     */
     708    neck = &root;               /* where needle head is connected */
     709    needle = root;
     710
     711    tail = NULL;                /* tail in the haystack */
     712    graft = &res->labels;
     713    haystack = res->labels;
     714
     715    while (needle != NULL && haystack != NULL)
     716    {
     717        size_t nlen, hlen;
     718
     719        nlen = needle->buf[needle->off];
     720        Assert((nlen & DNS_LABEL_PTR) == 0);
     721
     722        hlen = haystack->buf[haystack->off];
     723        Assert((hlen & DNS_LABEL_PTR) == 0);
     724
     725        if (   nlen == hlen
     726            && RTStrNICmp((char *)&needle->buf[needle->off+1],
     727                          (char *)&haystack->buf[haystack->off+1],
     728                          nlen) == 0)
     729        {
     730            neck = &needle->children;
     731            needle = needle->children;
     732
     733            tail = haystack;
     734            graft = &haystack->children;
     735            haystack = haystack->children;
     736        }
     737        else
     738        {
     739            haystack = haystack->sibling;
     740        }
     741    }
     742
     743
     744    /**
     745     * Head contains (in reverse) the prefix that needs to be encoded
     746     * and added to the haystack.  Tail points to existing suffix that
     747     * can be compressed to a pointer into the haystack.
     748     */
     749    head = *neck;
     750    if (head != NULL)
     751    {
     752        struct label *l;
     753        size_t nlen, pfxlen, pfxdst;
     754
     755        nlen = needle->buf[head->off]; /* last component */
     756        pfxlen = head->off + 1 + nlen; /* all prefix */
     757        pfxdst = res->end;             /* in response buffer */
     758
     759        /* copy new prefix into response buffer */
     760        nbytes = append_bytes(res, buf, pfxlen);
     761        if (nbytes <= 0)
     762        {
     763            if (nbytes == 0)
     764                size = 0;
     765            goto out;
     766        }
     767
     768        /* adjust labels to point to the response */
     769        for (l = head; l != NULL; l = l->children)
     770        {
     771            l->buf = res->buf;
     772            l->off += pfxdst;
     773        }
     774
     775        *neck = NULL;           /* decapitate */
     776
     777        l = *graft;             /* graft to the labels tree */
     778        *graft = head;
     779        head->sibling = l;
     780    }
     781
     782    if (tail == NULL)
     783        nbytes = append_u8(res, 0);
     784    else
     785        nbytes = append_u16(res, RT_H2N_U16((DNS_LABEL_PTR << 8) | tail->off));
     786    if (nbytes <= 0)
     787    {
     788        if (nbytes == 0)
     789            size = 0;
     790        goto out;
     791    }
     792
     793    size = res->end - oend;
     794  out:
     795    if (RT_UNLIKELY(size <= 0))
     796        res->end = oend;
     797    free_labels(root);
     798    RTMemFree(buf);
     799    return size;
     800}
     801
     802
     803static ssize_t
     804append_u32(struct response *res, uint32_t value)
     805{
     806    return append_bytes(res, (uint8_t *)&value, sizeof(value));
     807}
     808
     809
     810static ssize_t
     811append_u16(struct response *res, uint16_t value)
     812{
     813    return append_bytes(res, (uint8_t *)&value, sizeof(value));
     814}
     815
     816
     817static ssize_t
     818append_u8(struct response *res, uint8_t value)
     819{
     820    return append_bytes(res, &value, sizeof(value));
     821}
     822
     823
     824static ssize_t
     825append_bytes(struct response *res, uint8_t *p, size_t size)
     826{
     827    if (check_space(res, size) == 0)
     828        return 0;
     829
     830    memcpy(&res->buf[res->end], p, size);
     831    res->end += size;
     832    return size;
     833}
     834
     835
     836static ssize_t
     837check_space(struct response *res, size_t size)
     838{
     839    if (   size > sizeof(res->buf)
     840        || res->end > sizeof(res->buf) - size)
     841        return 0;
     842
     843    return size;
     844}
     845
     846
     847/*
     848 * Convert a chain of labels to a C string.
     849 *
     850 * I'd rather use a custom formatter for e.g. %R[label] , but it needs
     851 * two arguments and microsoft VC doesn't support compound literals.
     852 */
    75853static void
    76 doanswer(struct mbuf *m, struct hostent *pHostent)
    77 {
    78     union dnsmsg_header *pHdr;
    79     int i;
    80 
    81     pHdr = mtod(m, union dnsmsg_header *);
    82 
    83     if (!pHostent)
    84     {
    85         pHdr->X.qr = 1; /* response */
    86         pHdr->X.aa = 1;
    87         pHdr->X.rd = 1;
    88         pHdr->X.rcode = 3;
     854strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off)
     855{
     856    size_t cb;
     857    size_t llen;
     858
     859    namebuf[0] = '\0';
     860    cb = 0;
     861
     862    llen = 0;
     863
     864    while (cb < nbuflen - 1) {
     865        llen = msg[off];
     866        if ((llen & DNS_LABEL_PTR) == DNS_LABEL_PTR)
     867        {
     868            off = ((llen & ~DNS_LABEL_PTR) << 8) | msg[off + 1];
     869            llen = msg[off];
     870        }
     871
     872        /* pointers to pointers should not happen */
     873        if ((llen & DNS_LABEL_PTR) != 0)
     874        {
     875            cb += RTStrPrintf(namebuf + cb, nbuflen - cb, "[???]");
     876            return;
     877        }
     878
     879        if (llen == 0)
     880        {
     881            if (namebuf[0] == '\0')
     882                cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
     883            break;
     884        }
     885
     886        if (namebuf[0] != '\0')
     887            cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
     888
     889        cb += RTStrPrintf(namebuf + cb, nbuflen - cb,
     890                          "%.*s", llen, (char *)&msg[off+1]);
     891        off = off + 1 + llen;
     892    }
     893}
     894
     895
     896static void
     897LogLabelsTree(const char *before, struct label *l, const char *after)
     898{
     899    size_t llen;
     900
     901    if (before != NULL)
     902        LogDbg(("%s", before));
     903
     904    if (l == NULL)
     905    {
     906        LogDbg(("NULL%s", after ? after : ""));
     907        return;
     908    }
     909
     910    if (l->children)
     911        LogDbg(("("));
     912
     913    if (l->buf != NULL)
     914    {
     915        llen = l->buf[l->off];
     916        if ((llen & DNS_LABEL_PTR) == 0)
     917        {
     918            LogDbg(("\"%.*s\"@%zu", llen, &l->buf[l->off+1], l->off));
     919        }
     920        else
     921        {
     922            LogDbg(("<invalid byte 0t%zu/0x%zf at offset %zd>",
     923                    llen, llen, l->off));
     924        }
    89925    }
    90926    else
    91927    {
    92         char *answers;
    93         uint16_t off;
    94         char **cstr;
    95         char *c;
    96         size_t anslen = 0;
    97         uint16_t addr_off = (uint16_t)~0;
    98         struct dns_meta_data *meta;
    99 
    100         /* answers zone lays after query in response packet */
    101         answers = (char *)pHdr + m->m_len;
    102 
    103         off = (char *)&pHdr[1] - (char *)pHdr;
    104         off |= (0x3 << 14);
    105 
    106         /* add aliases */
    107         for (cstr = pHostent->h_aliases; cstr && *cstr; cstr++)
    108         {
    109             uint16_t len;
    110             struct dnsmsg_answer *ans = (struct dnsmsg_answer *)answers;
    111             ans->name = htons(off);
    112             ans->meta.type = htons(5); /* CNAME */
    113             ans->meta.class = htons(1);
    114             *(uint32_t *)ans->ttl = htonl(3600); /* 1h */
    115             c = (addr_off == (uint16_t)~0 ? pHostent->h_name : *cstr);
    116             len = strlen(c) + 2;
    117             ans->rdata_len = htons(len);
    118             ans->rdata[len - 1] = 0;
    119             CStr2QStr(c, (char *)ans->rdata, len);
    120             off = (char *)&ans->rdata - (char *)pHdr;
    121             off |= (0x3 << 14);
    122             if (addr_off == (uint16_t)~0)
    123                 addr_off = off;
    124             answers = (char *)&ans[1] + len - 2;  /* note: 1 symbol already counted */
    125             anslen += sizeof(struct dnsmsg_answer) + len - 2;
    126             pHdr->X.ancount++;
    127         }
    128 
    129         /* add addresses */
    130         for(i = 0; i < pHostent->h_length && pHostent->h_addr_list[i] != NULL; ++i)
    131         {
    132             struct dnsmsg_answer *ans = (struct dnsmsg_answer *)answers;
    133 
    134             ans->name = htons(off);
    135             ans->meta.type = htons(1);
    136             ans->meta.class = htons(1);
    137             *(uint32_t *)ans->ttl = htonl(3600); /* 1h */
    138             ans->rdata_len = htons(4); /* IPv4 */
    139             *(uint32_t *)ans->rdata = *(uint32_t *)pHostent->h_addr_list[i];
    140             answers = (char *)&ans[1] + 2;
    141             anslen += sizeof(struct dnsmsg_answer) + 3;
    142             pHdr->X.ancount++;
    143         }
    144         pHdr->X.qr = 1; /* response */
    145         pHdr->X.aa = 1;
    146         pHdr->X.rd = 1;
    147         pHdr->X.ra = 1;
    148         pHdr->X.rcode = 0;
    149         HTONS(pHdr->X.ancount);
    150 
    151         m->m_len += anslen;
    152     }
    153 }
    154 
    155 int
    156 hostresolver(PNATState pData, struct mbuf *m)
    157 {
    158     int i;
    159     /* Parse dns request */
    160     char *qw_qname = NULL;
    161     struct hostent *pHostent = NULL;
    162     char pszCname[255];
    163     int cname_len = 0;
    164     struct dns_meta_data *meta;
    165 
    166     union dnsmsg_header *pHdr = NULL;
    167 
    168     pHdr = mtod(m, union dnsmsg_header *);
    169 
    170     if (pHdr->X.qr == 1)
    171         return 1; /* this is respose */
    172 
    173     memset(pszCname, 0, sizeof(pszCname));
    174     qw_qname = (char *)&pHdr[1];
    175 
    176     if ((ntohs(pHdr->X.qdcount) != 1))
    177     {
    178         static bool fMultiWarn;
    179         if (!fMultiWarn)
    180         {
    181             LogRel(("NAT: alias_dns: Multiple queries isn't supported\n"));
    182             fMultiWarn = true;
    183         }
    184         return 1;
    185     }
    186 
    187     {
    188         meta = (struct dns_meta_data *)(qw_qname + strlen(qw_qname) + 1);
    189         Log(("pszQname:%s qtype:%hd qclass:%hd\n",
    190             qw_qname, ntohs(meta->type), ntohs(meta->class)));
    191 
    192         QStr2CStr(qw_qname, pszCname, sizeof(pszCname));
    193         cname_len = RTStrNLen(pszCname, sizeof(pszCname));
    194         /* Some guests like win-xp adds _dot_ after host name
    195          * and after domain name (not passed with host resolver)
    196          * that confuses host resolver.
    197          */
    198         if (   cname_len > 2
    199             && pszCname[cname_len - 1] == '.'
    200             && pszCname[cname_len - 2] == '.')
    201         {
    202             pszCname[cname_len - 1] = 0;
    203             pszCname[cname_len - 2] = 0;
    204         }
    205         pHostent = gethostbyname(pszCname);
    206 #ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
    207         if (   pHostent
    208             && !LIST_EMPTY(&pData->DNSMapHead))
    209             alterHostentWithDataFromDNSMap(pData, pHostent);
    210 #endif
    211         doanswer(m, pHostent);
    212     }
    213 
    214     return 0;
    215 }
    216 
    217 /*
    218  * qstr is z-string with -dot- replaced with \count to next -dot-
    219  * e.g. ya.ru is \02ya\02ru
    220  * Note: it's assumed that caller allocates buffer for cstr
    221  */
    222 static void QStr2CStr(const char *pcszQStr, char *pszStr, size_t cStr)
    223 {
    224     const char *q;
    225     char *c;
    226     size_t cLen = 0;
    227 
    228     Assert(cStr > 0);
    229     for (q = pcszQStr, c = pszStr; *q != '\0' && cLen < cStr-1; q++, cLen++)
    230     {
    231         if (   isalpha(*q)
    232             || isdigit(*q)
    233             || *q == '-'
    234             || *q == '_')
    235         {
    236            *c = *q;
    237             c++;
    238         }
    239         else if (c != &pszStr[0])
    240         {
    241             *c = '.';
    242             c++;
    243         }
    244     }
    245     *c = '\0';
    246 }
    247 
    248 /*
    249  *
    250  */
    251 static void CStr2QStr(const char *pcszStr, char *pszQStr, size_t cQStr)
    252 {
    253     const char *c;
    254     const char *pc;
    255     char *q;
    256     size_t cLen = 0;
    257 
    258     Assert(cQStr > 0);
    259     for (c = pcszStr, q = pszQStr; *c != '\0' && cLen < cQStr-1; q++, cLen++)
    260     {
    261         /* at the begining or at -dot- position */
    262         if (*c == '.' || (c == pcszStr && q == pszQStr))
    263         {
    264             if (c != pcszStr)
    265                 c++;
    266             pc = strchr(c, '.');
    267             *q = pc ? (pc - c) : strlen(c);
    268         }
    269         else
    270         {
    271             *q = *c;
    272             c++;
    273         }
    274     }
    275     *q = '\0';
     928        LogDbg(("<*>"));
     929    }
     930
     931    if (l->children)
     932        LogLabelsTree(" ", l->children, ")");
     933
     934    if (l->sibling)
     935        LogLabelsTree(" ", l->sibling, NULL);
     936
     937    if (after != NULL)
     938        LogDbg(("%s", after));
     939}
     940
     941
     942static void
     943free_labels(struct label *root)
     944{
     945    struct label TOP;    /* traverse the tree with pointer reversal */
     946    struct label *b, *f;
     947
     948    if (root == NULL)
     949        return;
     950
     951    RT_ZERO(TOP);
     952
     953    b = &TOP;
     954    f = root;
     955
     956    while (f != &TOP) {
     957        if (f->children) {      /* recurse left */
     958            struct label *oldf = f;
     959            struct label *newf = f->children;
     960            oldf->children = b; /* reverse the pointer */
     961            b = oldf;
     962            f = newf;
     963        }
     964        else if (f->sibling) { /* turn right */
     965            f->children = f->sibling;
     966            f->sibling = NULL;
     967        }
     968        else {                  /* backtrack */
     969            struct label *oldf = f; /* garbage */
     970            struct label *oldb = b;
     971            b = oldb->children;
     972            oldb->children = NULL; /* oldf, but we are g/c'ing it */
     973            f = oldb;
     974
     975            RTMemFree(oldf);
     976        }
     977    }
    276978}
    277979
  • trunk/src/VBox/Devices/Network/slirp/slirp.h

    r59063 r59142  
    352352
    353353/* hostres.c */
    354 int hostresolver(PNATState, struct mbuf *);
     354struct mbuf *hostresolver(PNATState, struct mbuf *);
    355355
    356356/*slirp.c*/
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette