VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use