VirtualBox

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

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

NAT: Make host resolver asynchronous so that a blocked synchronous
lookup doesn't stop the whole tcp/ip stack.

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