VirtualBox

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

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