VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/hostres.c@ 59313

Last change on this file since 59313 was 59313, checked in by vboxsync, 9 years ago

NAT: When host resolver has a DNS mapping for a name, don't bother
actually looking up the name (and failing if the name doesn't resolve).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.8 KB
Line 
1/* $Id: hostres.c 59313 2016-01-12 03:00:48Z vboxsync $ */
2/** @file
3 * Host resolver
4 */
5
6/*
7 * Copyright (C) 2009-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef RT_OS_WINDOWS
19# include <netdb.h>
20#endif
21#include <iprt/ctype.h>
22#include <iprt/assert.h>
23#include <slirp.h>
24
25#define isdigit(ch) RT_C_IS_DIGIT(ch)
26#define isalpha(ch) RT_C_IS_ALPHA(ch)
27
28#define DNS_CONTROL_PORT_NUMBER 53
29/* see RFC 1035(4.1.1) */
30struct dnsmsg_header
31{
32 uint16_t id;
33
34#ifdef RT_OS_WINDOWS
35 /* size of the type forces alignment */
36# define U16_BIT_FIELD_T uint16_t
37#else
38 /* gcc -pedantic complains about implementaion-defined types */
39# define U16_BIT_FIELD_T unsigned int
40#endif
41
42 /* XXX: endianness */
43 U16_BIT_FIELD_T rd:1;
44 U16_BIT_FIELD_T tc:1;
45 U16_BIT_FIELD_T aa:1;
46 U16_BIT_FIELD_T opcode:4;
47 U16_BIT_FIELD_T qr:1;
48 U16_BIT_FIELD_T rcode:4;
49 U16_BIT_FIELD_T Z:3;
50 U16_BIT_FIELD_T ra:1;
51
52 uint16_t qdcount;
53 uint16_t ancount;
54 uint16_t nscount;
55 uint16_t arcount;
56};
57AssertCompileSize(struct dnsmsg_header, 12);
58
59#define QR_Query 0
60#define QR_Response 1
61
62#define OpCode_Query 0
63
64#define RCode_NoError 0
65#define RCode_FormErr 1
66#define RCode_ServFail 2
67#define RCode_NXDomain 3
68#define RCode_NotImp 4
69#define RCode_Refused 5
70
71#define Type_A 1
72#define Type_CNAME 5
73#define Type_PTR 12
74#define Type_ANY 255
75
76#define Class_IN 1
77#define Class_ANY 255
78
79/* compressed label encoding */
80#define DNS_LABEL_PTR 0xc0
81
82#define DNS_MAX_UDP_LEN 512
83#define DNS_MAX_LABEL_LEN 63
84#define DNS_MAX_NAME_LEN 255
85
86
87/*
88 * A tree of labels.
89 *
90 * rfc1035#section-3.1
91 * rfc1035#section-4.1.4
92 */
93struct label
94{
95 const uint8_t *buf;
96 ssize_t off;
97 struct label *children;
98 struct label *sibling;
99};
100
101
102/*
103 * A structure to build DNS response.
104 */
105struct response
106{
107 struct label *labels; /* already encoded in buf */
108 size_t qlen; /* original question */
109 size_t end; /* of data in buf */
110
111 /* continuous buffer to build the response */
112 uint8_t buf[DNS_MAX_UDP_LEN];
113};
114
115
116static int verify_header(PNATState pData, struct mbuf **pMBuf);
117static struct mbuf *respond(PNATState pData, struct mbuf *m, struct response *res);
118struct mbuf *resolve(PNATState pData, struct mbuf *m, struct response *res,
119 uint16_t qtype, size_t qname);
120struct mbuf *resolve_reverse(PNATState pData, struct mbuf *m, struct response *res,
121 uint16_t qtype, size_t qname, struct in_addr addr);
122struct mbuf *refuse(PNATState pData, struct mbuf *m, unsigned int rcode);
123static ssize_t append_a(struct response *res, const char *name, struct in_addr addr);
124static ssize_t append_cname(struct response *res, const char *name, const char *cname);
125static ssize_t append_ptr(struct response *res, const char *inaddrname, const char *name);
126static ssize_t append_name_rr(struct response *res, const char *question, int type, const char *answer);
127static ssize_t append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl);
128static ssize_t append_name(struct response *res, const char *name);
129static ssize_t append_u32(struct response *res, uint32_t value);
130static ssize_t append_u16(struct response *res, uint16_t value);
131static ssize_t append_u8(struct response *res, uint8_t value);
132static ssize_t append_bytes(struct response *res, uint8_t *p, size_t size);
133static ssize_t check_space(struct response *res, size_t size);
134
135static int get_in_addr_arpa(struct in_addr *paddr, struct label *root);
136static int labelstrcmp(struct label *l, const char *s);
137static void strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off);
138
139static void LogLabelsTree(const char *before, struct label *l, const char *after);
140static void free_labels(struct label *root);
141
142#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
143static void alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h);
144static PDNSMAPPINGENTRY getDNSMapByName(PNATState pData, const char *name);
145#endif
146
147#if 1 /* XXX */
148# define LogErr(args) Log2(args)
149# define LogDbg(args) Log3(args)
150#else
151# define LogErr(args) LogRel(args)
152# define LogDbg(args) LogRel(args)
153#endif
154
155
156struct mbuf *
157hostresolver(PNATState pData, struct mbuf *m)
158{
159 int error;
160
161 struct response res;
162
163 error = verify_header(pData, &m);
164 if (error != 0)
165 return m;
166
167 RT_ZERO(res);
168
169 /*
170 * Do the real work
171 */
172 m = respond(pData, m, &res);
173
174 free_labels(res.labels);
175 return m;
176}
177
178
179struct mbuf *
180refuse(PNATState pData, struct mbuf *m, unsigned int rcode)
181{
182 struct dnsmsg_header *pHdr;
183
184 pHdr = mtod(m, struct dnsmsg_header *);
185 pHdr->qr = QR_Response;
186 pHdr->rcode = rcode;
187 pHdr->ra = 1;
188 pHdr->aa = 0;
189
190 return m;
191}
192
193
194static int
195verify_header(PNATState pData, struct mbuf **pMBuf)
196{
197 struct mbuf *m;
198 struct dnsmsg_header *pHdr;
199 size_t mlen;
200
201 m = *pMBuf;
202
203 /*
204 * In theory we should have called
205 *
206 * m = m_pullup(m, sizeof(struct dnsmsg_header));
207 *
208 * here first (which should have been a nop), but the way mbufs
209 * are used in NAT will always cause a copy that will have no
210 * leading space. We can use m_copyup() instead, but if we are
211 * peeking under the hood anyway, we might as well just rely on
212 * the fact that this header will be contiguous.
213 */
214 pHdr = mtod(m, struct dnsmsg_header *);
215
216 if (RT_UNLIKELY(pHdr->qr != QR_Query))
217 {
218 LogErr(("NAT: hostres: unexpected response\n"));
219 goto drop;
220 }
221
222 mlen = m_length(m, NULL);
223 if (RT_UNLIKELY(mlen > DNS_MAX_UDP_LEN))
224 {
225 LogErr(("NAT: hostres: packet too large\n"));
226 refuse(pData, m, RCode_FormErr); /* or drop? */
227 return 1;
228 }
229
230 if (RT_UNLIKELY(pHdr->opcode != OpCode_Query))
231 {
232 LogErr(("NAT: hostres: unsupported opcode\n"));
233 refuse(pData, m, RCode_NotImp);
234 return 1;
235 }
236
237 if (RT_UNLIKELY(pHdr->qdcount != RT_H2N_U16_C(1)))
238 {
239 LogErr(("NAT: hostres: multiple questions\n"));
240 refuse(pData, m, RCode_NotImp);
241 return 1;
242 }
243
244 if (RT_UNLIKELY(pHdr->ancount != 0))
245 {
246 LogErr(("NAT: hostres: answers in query\n"));
247 refuse(pData, m, RCode_NotImp);
248 return 1;
249 }
250
251 if (RT_UNLIKELY(mlen < sizeof(*pHdr)
252 + /* qname */ 1
253 + /* qtype */ 2
254 + /* qclass */ 2))
255 {
256 LogErr(("NAT: hostres: packet too small\n"));
257 refuse(pData, m, RCode_FormErr);
258 return 1;
259 }
260
261 return 0;
262
263 drop:
264 if (m != NULL)
265 m_freem(pData, m);
266 *pMBuf = NULL;
267 return 1;
268}
269
270
271static struct mbuf *
272respond(PNATState pData, struct mbuf *m, struct response *res)
273{
274 struct dnsmsg_header *pHdr;
275 size_t mlen;
276 size_t off;
277 size_t qname;
278 uint16_t qtype, qclass;
279 struct in_addr in_addr_arpa;
280 struct label *l;
281
282 /**
283 * Copy the request into the contiguous buffer for the response
284 * and parse the question.
285 */
286
287 mlen = m_length(m, NULL);
288 m_copydata(m, 0, mlen, (char *)res->buf);
289 res->end = res->qlen = mlen;
290
291 /* convert header to response */
292 pHdr = (struct dnsmsg_header *)res->buf;
293 pHdr->qr = QR_Response;
294 pHdr->rcode = RCode_NoError;
295 pHdr->ra = 1; /* the host provides recursion */
296 pHdr->aa = 0; /* we are not authoritative */
297 pHdr->Z = 0; /* clear rfc2535 dnssec bits */
298
299 off = sizeof(*pHdr);
300 qname = off;
301
302 /*
303 * Parse/verify QNAME and collect the suffixes to be used for
304 * compression in the answer.
305 */
306 while (off < mlen) {
307 size_t loff, llen;
308 uint8_t c;
309
310 c = res->buf[off];
311
312 /*
313 * There's just one question with just one name, so there are
314 * no other labels it can point to. Thus all well-formed
315 * names with a pointer can only be infinite loops.
316 */
317 if ((c & DNS_LABEL_PTR) == DNS_LABEL_PTR)
318 {
319 LogErr(("NAT: hostres: label pointer in the qname\n"));
320 return refuse(pData, m, RCode_FormErr);
321 }
322
323 if ((c & DNS_LABEL_PTR) != 0)
324 {
325 LogErr(("NAT: hostres: unexpected high bits\n"));
326 return refuse(pData, m, RCode_FormErr);
327 }
328
329 /*
330 * label of "llen" chars starts at offset "loff".
331 */
332 loff = off;
333 llen = c;
334 ++off;
335
336 if (loff + 1 + llen > mlen)
337 {
338 LogErr(("NAT: hostres: length byte points beyound packet boundary\n"));
339 return refuse(pData, m, RCode_FormErr);
340 }
341
342 if (llen == 0) /* end of the label list */
343 {
344 break;
345 }
346
347 /* do only minimal verification of the label */
348 while (off < loff + 1 + llen)
349 {
350 c = res->buf[off];
351 ++off;
352
353 if (c == '.')
354 {
355 LogErr(("NAT: hostres: dot inside label\n"));
356 return refuse(pData, m, RCode_FormErr);
357 }
358
359 if (c == '\0')
360 {
361 LogErr(("NAT: hostres: nul byte inside label\n"));
362 return refuse(pData, m, RCode_FormErr);
363 }
364 }
365
366 l = RTMemAllocZ(sizeof(*l));
367 l->buf = res->buf;
368 l->off = loff;
369 l->children = res->labels;
370 res->labels = l;
371 }
372
373 /*
374 * QTYPE and QCLASS
375 */
376 if (RT_UNLIKELY(off + 4 > mlen))
377 {
378 LogErr(("NAT: hostres: question too short\n"));
379 return refuse(pData, m, RCode_FormErr);
380 }
381
382 memcpy(&qtype, &res->buf[off], sizeof(qtype));
383 qtype = RT_N2H_U16(qtype);
384 off += sizeof(qtype);
385
386 memcpy(&qclass, &res->buf[off], sizeof(qclass));
387 qclass = RT_N2H_U16(qclass);
388 off += sizeof(qclass);
389
390 if ( qclass != Class_IN
391 && qclass != Class_ANY)
392 {
393 LogErr(("NAT: hostres: unsupported qclass %d\n", qclass));
394 return refuse(pData, m, RCode_NotImp);
395 }
396
397 if ( qtype != Type_A
398 && qtype != Type_CNAME
399 && qtype != Type_PTR
400 && qtype != Type_ANY)
401 {
402 LogErr(("NAT: hostres: unsupported qtype %d\n", qtype));
403 return refuse(pData, m, RCode_NotImp);
404 }
405
406
407 /**
408 * Check if there's anything after the question. If query says it
409 * has authority or additional records, ignore and drop them
410 * without parsing.
411 *
412 * We have already rejected queries with answer(s) before. We
413 * have ensured that qname in the question doesn't contain
414 * pointers, so truncating the buffer is safe.
415 */
416 if (off < mlen)
417 {
418 int trailer = mlen - off;
419
420 LogDbg(("NAT: hostres: question %zu < mlen %zu\n", off, mlen));
421
422 if (pHdr->nscount == 0 && pHdr->arcount == 0)
423 {
424 LogErr(("NAT: hostres: unexpected %d bytes after the question\n", trailer));
425 return refuse(pData, m, RCode_FormErr);
426 }
427
428 LogDbg(("NAT: hostres: ignoring %d bytes of %s%s%s records\n",
429 trailer,
430 pHdr->nscount != 0 ? "authority" : "",
431 pHdr->nscount != 0 && pHdr->arcount != 0 ? " and " : "",
432 pHdr->arcount != 0 ? "additional" : ""));
433
434 m_adj(m, -trailer);
435 mlen -= trailer;
436 res->end = res->qlen = mlen;
437
438 pHdr->nscount = 0;
439 pHdr->arcount = 0;
440 }
441
442
443 /*
444 * Check for IN-ADDR.ARPA. Use the fact that res->labels at this
445 * point contains only the qname, so we have easy top-down access
446 * to its components.
447 */
448 if (get_in_addr_arpa(&in_addr_arpa, res->labels))
449 return resolve_reverse(pData, m, res, qtype, qname, in_addr_arpa);
450 else
451 return resolve(pData, m, res, qtype, qname);
452}
453
454
455struct mbuf *
456resolve(PNATState pData, struct mbuf *m, struct response *res,
457 uint16_t qtype, size_t qname)
458{
459 struct dnsmsg_header *pHdr;
460 struct hostent *h;
461 struct hostent hostent;
462 char *h_aliases[1];
463 char *h_addr_list[2];
464 size_t oend;
465 size_t nanswers;
466 ssize_t nbytes;
467 int i;
468
469 char name[DNS_MAX_NAME_LEN+1];
470
471 pHdr = (struct dnsmsg_header *)res->buf;
472 nanswers = 0;
473 oend = res->end;
474
475 strnlabels(name, sizeof(name), res->buf, qname);
476 LogDbg(("NAT: hostres: qname=\"%s\"\n", name));
477
478 if (qtype != Type_A && qtype != Type_CNAME && qtype != Type_ANY)
479 {
480 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
481 }
482
483 h = NULL;
484#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
485 {
486 PDNSMAPPINGENTRY pDNSMapingEntry = getDNSMapByName(pData, name);
487 if (pDNSMapingEntry != NULL)
488 {
489 LogDbg(("NAT: hostres: %s resolved from %s%s\n",
490 name,
491 pDNSMapingEntry->fPattern ? "pattern " : "mapping",
492 pDNSMapingEntry->fPattern ? pDNSMapingEntry->pszName : ""));
493
494 if (qtype == Type_CNAME)
495 {
496 goto out;
497 }
498
499 hostent.h_name = name;
500 hostent.h_aliases = h_aliases;
501 h_aliases[0] = NULL;
502 hostent.h_addrtype = AF_INET;
503 hostent.h_length = sizeof(RTNETADDRIPV4);
504 hostent.h_addr_list = h_addr_list;
505 h_addr_list[0] = (char *)&pDNSMapingEntry->u32IpAddress;
506 h_addr_list[1] = NULL;
507
508 h = &hostent;
509 }
510 }
511#endif
512
513 if (h == NULL)
514 {
515 h = gethostbyname(name);
516 }
517
518 if (h == NULL)
519 {
520 /* LogErr: h_errno */
521 return refuse(pData, m, RCode_NXDomain);
522 }
523
524 if (h->h_length != sizeof(RTNETADDRIPV4))
525 {
526 /* Log: what kind of address did we get?! */
527 goto out;
528 }
529
530 if ( h->h_addr_list == NULL
531 || h->h_addr_list[0] == NULL)
532 {
533 /* Log: shouldn't happen */
534 goto out;
535 }
536
537#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
538 alterHostentWithDataFromDNSMap(pData, h);
539#endif
540
541 /*
542 * Emit CNAME record if canonical name differs from the qname.
543 */
544 if ( h->h_name != NULL
545 && RTStrICmp(h->h_name, name) != 0)
546 {
547 LogDbg(("NAT: hostres: %s CNAME %s\n", name, h->h_name));
548 nbytes = append_cname(res, name, h->h_name);
549 if (nbytes > 0)
550 {
551 ++nanswers;
552 }
553 else
554 {
555 LogErr(("NAT: hostres: failed to add %s CNAME %s\n",
556 name, h->h_name));
557 if (nbytes < 0)
558 return refuse(pData, m, RCode_ServFail);
559 else
560 {
561 pHdr->tc = 1;
562 goto out;
563 }
564 }
565
566 /*
567 * rfc1034#section-3.6.2 - ... a type CNAME or * query should
568 * return just the CNAME.
569 */
570 if (qtype == Type_CNAME || qtype == Type_ANY)
571 goto out;
572 }
573 else if (qtype == Type_CNAME)
574 {
575 LogDbg(("NAT: hostres: %s is already canonical\n", name));
576 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
577 }
578
579 /*
580 * Emit A records.
581 */
582 for (i = 0; h->h_addr_list[i] != NULL; ++i)
583 {
584 const char *cname = h->h_name ? h->h_name : name;
585 struct in_addr addr;
586
587 addr.s_addr = *(uint32_t *)h->h_addr_list[i];
588 nbytes = append_a(res, cname, addr);
589
590 if (nbytes > 0)
591 {
592 ++nanswers;
593 }
594 else
595 {
596 LogErr(("NAT: hostres: failed to add %s A %RTnaipv4\n",
597 cname, addr.s_addr));
598 if (nbytes < 0)
599 return refuse(pData, m, RCode_ServFail);
600 else
601 {
602 pHdr->tc = 1;
603 goto out;
604 }
605 }
606 }
607
608#if 0
609 /*
610 * It's not clear what to do with h_aliases.
611 *
612 * For names from the DNS it seems to contain the chain of CNAMEs,
613 * starting with the original qname from the question. So for
614 * them we'd need to reply with a chain of:
615 *
616 * h_aliases[i] CNAME h_aliases[i+1]
617 *
618 * OTOH, for the names from the hosts file it seems to contain all
619 * the names except the first one (which is considered primary and
620 * is reported as h_name). In which case the reply should be:
621 *
622 * h_aliases[i] CNAME h_name
623 *
624 * Obviously, we have no idea how the name was resolved, so we
625 * generate at most one CNAME for h_host (if differs) and ignore
626 * aliases altogehter.
627 */
628 for (i = 0; h->h_aliases[i] != NULL; ++i)
629 {
630 LogDbg(("NAT: hostres: ... %s\n", h->h_aliases[i]));
631 }
632#endif
633
634 out:
635 if (nanswers > 0)
636 {
637 int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
638 if (!ok)
639 {
640 /* XXX: this may fail part way: restore old lenght, clear TC? */
641 return refuse(pData, m, RCode_ServFail);
642 }
643 pHdr->ancount = RT_H2N_U16(nanswers);
644 }
645 memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
646 return m;
647}
648
649
650struct mbuf *
651resolve_reverse(PNATState pData, struct mbuf *m, struct response *res,
652 uint16_t qtype, size_t qname, struct in_addr in_addr_arpa)
653{
654 struct dnsmsg_header *pHdr;
655 struct hostent *h;
656 size_t oend;
657 size_t nanswers;
658 ssize_t nbytes;
659 int i;
660
661 pHdr = (struct dnsmsg_header *)res->buf;
662 nanswers = 0;
663 oend = res->end;
664
665 LogDbg(("NAT: hostres: %RTnaipv4\n", in_addr_arpa.s_addr));
666
667 if (qtype != Type_PTR && qtype != Type_ANY)
668 {
669 /* can't answer CNAME to PTR queries using gethostby* */
670 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
671 }
672
673 /* XXX: TODO: apply HostResolverMappings */
674 h = gethostbyaddr(&in_addr_arpa, sizeof(struct in_addr), AF_INET);
675 if (h == NULL)
676 {
677 /* LogErr: h_errno */
678 return refuse(pData, m, RCode_NXDomain);
679 }
680
681 if (h->h_name != NULL)
682 {
683 char name[DNS_MAX_NAME_LEN+1];
684 strnlabels(name, sizeof(name), res->buf, qname);
685
686 LogDbg(("NAT: hostres: %s PTR %s\n", name, h->h_name));
687 nbytes = append_ptr(res, name, h->h_name);
688 if (nbytes > 0)
689 {
690 ++nanswers;
691 }
692 else
693 {
694 LogErr(("NAT: hostres: failed to add %s PTR %s\n",
695 name, h->h_name));
696 if (nbytes < 0)
697 return refuse(pData, m, RCode_ServFail);
698 else
699 {
700 pHdr->tc = 1;
701 goto out;
702 }
703 }
704 }
705
706 out:
707 if (nanswers > 0)
708 {
709 int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
710 if (!ok)
711 {
712 /* XXX: this may fail part way: restore old lenght, clear TC? */
713 return refuse(pData, m, RCode_ServFail);
714 }
715 pHdr->ancount = RT_H2N_U16(nanswers);
716 }
717 memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
718 return m;
719}
720
721
722
723#define APPEND_PROLOGUE() \
724 ssize_t size = -1; \
725 size_t oend = res->end; \
726 ssize_t nbytes; \
727 do {} while (0)
728
729#define CHECKED(_append) \
730 do { \
731 nbytes = (_append); \
732 if (RT_UNLIKELY(nbytes <= 0)) \
733 { \
734 if (nbytes == 0) \
735 size = 0; \
736 goto out; \
737 } \
738 } while (0)
739
740#define APPEND_EPILOGUE() \
741 do { \
742 size = res->end - oend; \
743 out: \
744 if (RT_UNLIKELY(size <= 0)) \
745 res->end = oend; \
746 return size; \
747 } while (0)
748
749
750/*
751 * A RR - rfc1035#section-3.4.1
752 */
753static ssize_t
754append_a(struct response *res, const char *name, struct in_addr addr)
755{
756 APPEND_PROLOGUE();
757
758 CHECKED( append_rrhdr(res, name, Type_A, 3600) );
759 CHECKED( append_u16(res, RT_H2N_U16_C(sizeof(addr))) );
760 CHECKED( append_u32(res, addr.s_addr) );
761
762 APPEND_EPILOGUE();
763}
764
765
766/*
767 * CNAME RR - rfc1035#section-3.3.1
768 */
769static ssize_t
770append_cname(struct response *res, const char *name, const char *cname)
771{
772 return append_name_rr(res, name, Type_CNAME, cname);
773}
774
775
776/*
777 * PTR RR - rfc1035#section-3.3.12
778 */
779static ssize_t
780append_ptr(struct response *res, const char *inaddrname, const char *name)
781{
782 return append_name_rr(res, inaddrname, Type_PTR, name);
783}
784
785
786static ssize_t
787append_name_rr(struct response *res, const char *question,
788 int type, const char *answer)
789{
790 size_t rdlpos;
791 uint16_t rdlength;
792
793 APPEND_PROLOGUE();
794
795 CHECKED( append_rrhdr(res, question, type, 3600) );
796
797 rdlpos = res->end;
798 CHECKED( append_u16(res, 0) ); /* RDLENGTH placeholder */
799
800 CHECKED( append_name(res, answer) );
801
802 rdlength = RT_H2N_U16(nbytes);
803 memcpy(&res->buf[rdlpos], &rdlength, sizeof(rdlength));
804
805 APPEND_EPILOGUE();
806}
807
808
809/*
810 * Append common RR header, up to but not including RDLENGTH and RDATA
811 * proper (rfc1035#section-3.2.1).
812 */
813static ssize_t
814append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl)
815{
816 APPEND_PROLOGUE();
817
818 CHECKED( append_name(res, name) );
819 CHECKED( append_u16(res, RT_H2N_U16(type)) );
820 CHECKED( append_u16(res, RT_H2N_U16_C(Class_IN)) );
821 CHECKED( append_u32(res, RT_H2N_U32(ttl)) );
822
823 APPEND_EPILOGUE();
824}
825
826
827static ssize_t
828append_name(struct response *res, const char *name)
829{
830 ssize_t size, nbytes;
831 struct label *root;
832 struct label *haystack, *needle;
833 struct label *head, **neck;
834 struct label *tail, **graft;
835 uint8_t *buf;
836 size_t wr, oend;
837 const char *s;
838
839 size = -1;
840 oend = res->end;
841
842 /**
843 * Split new name into a list of labels encoding it into the
844 * temporary buffer.
845 */
846 root = NULL;
847
848 buf = RTMemAllocZ(strlen(name) + 1);
849 if (buf == NULL)
850 return -1;
851 wr = 0;
852
853 s = name;
854 while (*s != '\0') {
855 const char *part;
856 size_t poff, plen;
857 struct label *l;
858
859 part = s;
860 while (*s != '\0' && *s != '.')
861 ++s;
862
863 plen = s - part;
864
865 if (plen > DNS_MAX_LABEL_LEN)
866 {
867 LogErr(("NAT: hostres: name component too long\n"));
868 goto out;
869 }
870
871 if (*s == '.')
872 {
873 if (plen == 0)
874 {
875 LogErr(("NAT: hostres: empty name component\n"));
876 goto out;
877 }
878
879 ++s;
880 }
881
882 poff = wr;
883
884 buf[poff] = (uint8_t)plen; /* length byte */
885 ++wr;
886
887 memcpy(&buf[wr], part, plen); /* label text */
888 wr += plen;
889
890 l = RTMemAllocZ(sizeof(*l));
891 if (l == NULL)
892 goto out;
893
894 l->buf = buf;
895 l->off = poff;
896 l->children = root;
897 root = l;
898 }
899
900
901 /**
902 * Search for a tail that is already encoded in the message.
903 */
904 neck = &root; /* where needle head is connected */
905 needle = root;
906
907 tail = NULL; /* tail in the haystack */
908 graft = &res->labels;
909 haystack = res->labels;
910
911 while (needle != NULL && haystack != NULL)
912 {
913 size_t nlen, hlen;
914
915 nlen = needle->buf[needle->off];
916 Assert((nlen & DNS_LABEL_PTR) == 0);
917
918 hlen = haystack->buf[haystack->off];
919 Assert((hlen & DNS_LABEL_PTR) == 0);
920
921 if ( nlen == hlen
922 && RTStrNICmp((char *)&needle->buf[needle->off+1],
923 (char *)&haystack->buf[haystack->off+1],
924 nlen) == 0)
925 {
926 neck = &needle->children;
927 needle = needle->children;
928
929 tail = haystack;
930 graft = &haystack->children;
931 haystack = haystack->children;
932 }
933 else
934 {
935 haystack = haystack->sibling;
936 }
937 }
938
939
940 /**
941 * Head contains (in reverse) the prefix that needs to be encoded
942 * and added to the haystack. Tail points to existing suffix that
943 * can be compressed to a pointer into the haystack.
944 */
945 head = *neck;
946 if (head != NULL)
947 {
948 struct label *l;
949 size_t nlen, pfxlen, pfxdst;
950
951 nlen = needle->buf[head->off]; /* last component */
952 pfxlen = head->off + 1 + nlen; /* all prefix */
953 pfxdst = res->end; /* in response buffer */
954
955 /* copy new prefix into response buffer */
956 nbytes = append_bytes(res, buf, pfxlen);
957 if (nbytes <= 0)
958 {
959 if (nbytes == 0)
960 size = 0;
961 goto out;
962 }
963
964 /* adjust labels to point to the response */
965 for (l = head; l != NULL; l = l->children)
966 {
967 l->buf = res->buf;
968 l->off += pfxdst;
969 }
970
971 *neck = NULL; /* decapitate */
972
973 l = *graft; /* graft to the labels tree */
974 *graft = head;
975 head->sibling = l;
976 }
977
978 if (tail == NULL)
979 nbytes = append_u8(res, 0);
980 else
981 nbytes = append_u16(res, RT_H2N_U16((DNS_LABEL_PTR << 8) | tail->off));
982 if (nbytes <= 0)
983 {
984 if (nbytes == 0)
985 size = 0;
986 goto out;
987 }
988
989 size = res->end - oend;
990 out:
991 if (RT_UNLIKELY(size <= 0))
992 res->end = oend;
993 free_labels(root);
994 RTMemFree(buf);
995 return size;
996}
997
998
999static ssize_t
1000append_u32(struct response *res, uint32_t value)
1001{
1002 return append_bytes(res, (uint8_t *)&value, sizeof(value));
1003}
1004
1005
1006static ssize_t
1007append_u16(struct response *res, uint16_t value)
1008{
1009 return append_bytes(res, (uint8_t *)&value, sizeof(value));
1010}
1011
1012
1013static ssize_t
1014append_u8(struct response *res, uint8_t value)
1015{
1016 return append_bytes(res, &value, sizeof(value));
1017}
1018
1019
1020static ssize_t
1021append_bytes(struct response *res, uint8_t *p, size_t size)
1022{
1023 if (check_space(res, size) == 0)
1024 return 0;
1025
1026 memcpy(&res->buf[res->end], p, size);
1027 res->end += size;
1028 return size;
1029}
1030
1031
1032static ssize_t
1033check_space(struct response *res, size_t size)
1034{
1035 if ( size > sizeof(res->buf)
1036 || res->end > sizeof(res->buf) - size)
1037 return 0;
1038
1039 return size;
1040}
1041
1042
1043static int
1044get_in_addr_arpa(struct in_addr *paddr, struct label *root)
1045{
1046 RTNETADDRIPV4 addr;
1047 struct label *l;
1048 int i;
1049
1050 l = root;
1051 if (l == NULL || labelstrcmp(l, "arpa") != 0)
1052 return 0;
1053
1054 l = l->children;
1055 if (l == NULL || labelstrcmp(l, "in-addr") != 0)
1056 return 0;
1057
1058 for (i = 0; i < 4; ++i)
1059 {
1060 char buf[4];
1061 size_t llen;
1062 int rc;
1063 uint8_t octet;
1064
1065 l = l->children;
1066 if (l == NULL)
1067 return 0;
1068
1069 llen = l->buf[l->off];
1070 Assert((llen & DNS_LABEL_PTR) == 0);
1071
1072 /* valid octet values are at most 3 digits */
1073 if (llen > 3)
1074 return 0;
1075
1076 /* copy to avoid dealing with trailing bytes */
1077 memcpy(buf, &l->buf[l->off + 1], llen);
1078 buf[llen] = '\0';
1079
1080 rc = RTStrToUInt8Full(buf, 10, &octet);
1081 if (rc != VINF_SUCCESS)
1082 return 0;
1083
1084 addr.au8[i] = octet;
1085 }
1086
1087 if (l->children != NULL)
1088 return 0; /* too many components */
1089
1090 if (paddr != NULL)
1091 paddr->s_addr = addr.u;
1092
1093 return 1;
1094}
1095
1096
1097/*
1098 * Compare label with string.
1099 */
1100static int
1101labelstrcmp(struct label *l, const char *s)
1102{
1103 size_t llen;
1104
1105 llen = l->buf[l->off];
1106 Assert((llen & DNS_LABEL_PTR) == 0);
1107
1108 return RTStrNICmp((char *)&l->buf[l->off + 1], s, llen);
1109}
1110
1111
1112/*
1113 * Convert a chain of labels to a C string.
1114 *
1115 * I'd rather use a custom formatter for e.g. %R[label] , but it needs
1116 * two arguments and microsoft VC doesn't support compound literals.
1117 */
1118static void
1119strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off)
1120{
1121 size_t cb;
1122 size_t llen;
1123
1124 namebuf[0] = '\0';
1125 cb = 0;
1126
1127 llen = 0;
1128
1129 while (cb < nbuflen - 1) {
1130 llen = msg[off];
1131 if ((llen & DNS_LABEL_PTR) == DNS_LABEL_PTR)
1132 {
1133 off = ((llen & ~DNS_LABEL_PTR) << 8) | msg[off + 1];
1134 llen = msg[off];
1135 }
1136
1137 /* pointers to pointers should not happen */
1138 if ((llen & DNS_LABEL_PTR) != 0)
1139 {
1140 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, "[???]");
1141 return;
1142 }
1143
1144 if (llen == 0)
1145 {
1146 if (namebuf[0] == '\0')
1147 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1148 break;
1149 }
1150
1151 if (namebuf[0] != '\0')
1152 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1153
1154 cb += RTStrPrintf(namebuf + cb, nbuflen - cb,
1155 "%.*s", llen, (char *)&msg[off+1]);
1156 off = off + 1 + llen;
1157 }
1158}
1159
1160
1161static void
1162LogLabelsTree(const char *before, struct label *l, const char *after)
1163{
1164 size_t llen;
1165
1166 if (before != NULL)
1167 LogDbg(("%s", before));
1168
1169 if (l == NULL)
1170 {
1171 LogDbg(("NULL%s", after ? after : ""));
1172 return;
1173 }
1174
1175 if (l->children)
1176 LogDbg(("("));
1177
1178 if (l->buf != NULL)
1179 {
1180 llen = l->buf[l->off];
1181 if ((llen & DNS_LABEL_PTR) == 0)
1182 {
1183 LogDbg(("\"%.*s\"@%zu", llen, &l->buf[l->off+1], l->off));
1184 }
1185 else
1186 {
1187 LogDbg(("<invalid byte 0t%zu/0x%zf at offset %zd>",
1188 llen, llen, l->off));
1189 }
1190 }
1191 else
1192 {
1193 LogDbg(("<*>"));
1194 }
1195
1196 if (l->children)
1197 LogLabelsTree(" ", l->children, ")");
1198
1199 if (l->sibling)
1200 LogLabelsTree(" ", l->sibling, NULL);
1201
1202 if (after != NULL)
1203 LogDbg(("%s", after));
1204}
1205
1206
1207static void
1208free_labels(struct label *root)
1209{
1210 struct label TOP; /* traverse the tree with pointer reversal */
1211 struct label *b, *f;
1212
1213 if (root == NULL)
1214 return;
1215
1216 RT_ZERO(TOP);
1217
1218 b = &TOP;
1219 f = root;
1220
1221 while (f != &TOP) {
1222 if (f->children) { /* recurse left */
1223 struct label *oldf = f;
1224 struct label *newf = f->children;
1225 oldf->children = b; /* reverse the pointer */
1226 b = oldf;
1227 f = newf;
1228 }
1229 else if (f->sibling) { /* turn right */
1230 f->children = f->sibling;
1231 f->sibling = NULL;
1232 }
1233 else { /* backtrack */
1234 struct label *oldf = f; /* garbage */
1235 struct label *oldb = b;
1236 b = oldb->children;
1237 oldb->children = NULL; /* oldf, but we are g/c'ing it */
1238 f = oldb;
1239
1240 RTMemFree(oldf);
1241 }
1242 }
1243}
1244
1245#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1246void
1247slirp_add_host_resolver_mapping(PNATState pData,
1248 const char *pszHostName, bool fPattern,
1249 uint32_t u32HostIP)
1250{
1251 LogRel(("ENTER: pszHostName:%s%s, u32HostIP:%RTnaipv4\n",
1252 pszHostName ? pszHostName : "(null)",
1253 fPattern ? " (pattern)" : "",
1254 u32HostIP));
1255
1256 if ( pszHostName != NULL
1257 && u32HostIP != INADDR_ANY
1258 && u32HostIP != INADDR_BROADCAST)
1259 {
1260 PDNSMAPPINGENTRY pDnsMapping = RTMemAllocZ(sizeof(DNSMAPPINGENTRY));
1261 if (!pDnsMapping)
1262 {
1263 LogFunc(("Can't allocate DNSMAPPINGENTRY\n"));
1264 LogFlowFuncLeave();
1265 return;
1266 }
1267
1268 pDnsMapping->u32IpAddress = u32HostIP;
1269 pDnsMapping->fPattern = fPattern;
1270 pDnsMapping->pszName = RTStrDup(pszHostName);
1271
1272 if (pDnsMapping->pszName == NULL)
1273 {
1274 LogFunc(("Can't allocate enough room for host name\n"));
1275 RTMemFree(pDnsMapping);
1276 LogFlowFuncLeave();
1277 return;
1278 }
1279
1280 if (fPattern) /* there's no case-insensitive pattern-match function */
1281 RTStrToLower(pDnsMapping->pszName);
1282
1283 STAILQ_INSERT_TAIL(fPattern ? &pData->DNSMapPatterns : &pData->DNSMapNames,
1284 pDnsMapping, MapList);
1285
1286 LogRel(("NAT: User-defined mapping %s%s = %RTnaipv4 is registered\n",
1287 pDnsMapping->pszName,
1288 pDnsMapping->fPattern ? " (pattern)" : "",
1289 pDnsMapping->u32IpAddress));
1290 }
1291 LogFlowFuncLeave();
1292}
1293
1294
1295static PDNSMAPPINGENTRY
1296getDNSMapByName(PNATState pData, const char *pszName)
1297{
1298 PDNSMAPPINGENTRY pDNSMapingEntry;
1299 const char *pszNameLower;
1300
1301 pszNameLower = RTStrDup(pszName);
1302 if (RT_UNLIKELY(pszNameLower == NULL))
1303 return NULL;
1304 RTStrToLower(pszNameLower);
1305
1306 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapNames, MapList)
1307 {
1308 if (RTStrICmp(pDNSMapingEntry->pszName, pszNameLower) == 0)
1309 goto done;
1310 }
1311
1312 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapPatterns, MapList)
1313 {
1314 if (RTStrSimplePatternMultiMatch(pDNSMapingEntry->pszName, RTSTR_MAX,
1315 pszNameLower, RTSTR_MAX, NULL))
1316 goto done;
1317 }
1318
1319 done:
1320 RTStrFree(pszNameLower);
1321 return pDNSMapingEntry;
1322}
1323
1324
1325static void
1326alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h)
1327{
1328 PDNSMAPPINGENTRY pDNSMapingEntry = NULL;
1329 char **ppszAlias;
1330
1331 if (h->h_name != NULL)
1332 {
1333 pDNSMapingEntry = getDNSMapByName(pData, h->h_name);
1334 if (pDNSMapingEntry != NULL)
1335 goto done;
1336 }
1337
1338 for (ppszAlias = h->h_aliases; *ppszAlias != NULL; ++ppszAlias)
1339 {
1340 pDNSMapingEntry = getDNSMapByName(pData, *ppszAlias);
1341 if (pDNSMapingEntry != NULL)
1342 goto done;
1343 }
1344
1345 done:
1346 if (pDNSMapingEntry != NULL)
1347 {
1348 *(uint32_t *)h->h_addr_list[0] = pDNSMapingEntry->u32IpAddress;
1349 h->h_addr_list[1] = NULL;
1350 }
1351}
1352#endif /* VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER */
Note: See TracBrowser for help on using the repository browser.

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