VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.9 KB
Line 
1/* $Id: hostres.c 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * Host resolver
4 */
5
6/*
7 * Copyright (C) 2009-2020 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/assert.h>
22#include <iprt/ctype.h>
23#include <iprt/errcore.h>
24#include <slirp.h>
25
26#define isdigit(ch) RT_C_IS_DIGIT(ch)
27#define isalpha(ch) RT_C_IS_ALPHA(ch)
28
29#define DNS_CONTROL_PORT_NUMBER 53
30/* see RFC 1035(4.1.1) */
31struct dnsmsg_header
32{
33 uint16_t id;
34
35#ifdef RT_OS_WINDOWS
36 /* size of the type forces alignment */
37# define U16_BIT_FIELD_T uint16_t
38#else
39 /* gcc -pedantic complains about implementaion-defined types */
40# define U16_BIT_FIELD_T unsigned int
41#endif
42
43 /* XXX: endianness */
44 U16_BIT_FIELD_T rd:1;
45 U16_BIT_FIELD_T tc:1;
46 U16_BIT_FIELD_T aa:1;
47 U16_BIT_FIELD_T opcode:4;
48 U16_BIT_FIELD_T qr:1;
49 U16_BIT_FIELD_T rcode:4;
50 U16_BIT_FIELD_T Z:3;
51 U16_BIT_FIELD_T ra:1;
52
53 uint16_t qdcount;
54 uint16_t ancount;
55 uint16_t nscount;
56 uint16_t arcount;
57};
58AssertCompileSize(struct dnsmsg_header, 12);
59
60#define QR_Query 0
61#define QR_Response 1
62
63#define OpCode_Query 0
64
65#define RCode_NoError 0
66#define RCode_FormErr 1
67#define RCode_ServFail 2
68#define RCode_NXDomain 3
69#define RCode_NotImp 4
70#define RCode_Refused 5
71
72#define Type_A 1
73#define Type_CNAME 5
74#define Type_PTR 12
75#define Type_ANY 255
76
77#define Class_IN 1
78#define Class_ANY 255
79
80/* compressed label encoding */
81#define DNS_LABEL_PTR 0xc0
82
83#define DNS_MAX_UDP_LEN 512
84#define DNS_MAX_LABEL_LEN 63
85#define DNS_MAX_NAME_LEN 255
86
87
88/*
89 * A tree of labels.
90 *
91 * rfc1035#section-3.1
92 * rfc1035#section-4.1.4
93 */
94struct label
95{
96 const uint8_t *buf;
97 ssize_t off;
98 struct label *children;
99 struct label *sibling;
100};
101
102
103/*
104 * A structure to build DNS response.
105 */
106struct response
107{
108 PNATState pData;
109
110 uint32_t src;
111 uint16_t sport;
112
113 struct label *labels; /* already encoded in buf */
114 size_t qlen; /* original question */
115 size_t end; /* of data in buf */
116
117 /* continuous buffer to build the response */
118 uint8_t buf[DNS_MAX_UDP_LEN];
119};
120
121
122static int verify_header(PNATState pData, struct mbuf **pMBuf);
123static struct mbuf *refuse_mbuf(struct mbuf *m, unsigned int rcode);
124
125static int respond(struct response *res);
126static int resolve(struct response *res, uint16_t qtype, size_t qname);
127static int resolve_reverse(struct response *res, uint16_t qtype, size_t qname,
128 struct in_addr addr);
129
130static int refuse(struct response *res, unsigned int rcode);
131
132
133static ssize_t append_a(struct response *res, const char *name, struct in_addr addr);
134static ssize_t append_cname(struct response *res, const char *name, const char *cname);
135static ssize_t append_ptr(struct response *res, const char *inaddrname, const char *name);
136static ssize_t append_name_rr(struct response *res, const char *question, int type, const char *answer);
137static ssize_t append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl);
138static ssize_t append_name(struct response *res, const char *name);
139static ssize_t append_u32(struct response *res, uint32_t value);
140static ssize_t append_u16(struct response *res, uint16_t value);
141static ssize_t append_u8(struct response *res, uint8_t value);
142static ssize_t append_bytes(struct response *res, uint8_t *p, size_t size);
143static ssize_t check_space(struct response *res, size_t size);
144
145static int get_in_addr_arpa(struct in_addr *paddr, struct label *root);
146static int labelstrcmp(struct label *l, const char *s);
147static void strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off);
148
149/*static void LogLabelsTree(const char *before, struct label *l, const char *after); - unused */
150static void free_labels(struct label *root);
151
152#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
153static void alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h);
154static PDNSMAPPINGENTRY getDNSMapByName(PNATState pData, const char *name);
155static PDNSMAPPINGENTRY getDNSMapByAddr(PNATState pData, const uint32_t *pu32IpAddress);
156#endif
157
158#if 1 /* XXX */
159# define LogErr(args) Log2(args)
160# define LogDbg(args) Log3(args)
161#else
162# define LogErr(args) LogRel(args)
163# define LogDbg(args) LogRel(args)
164#endif
165
166
167static void hostres_async(struct response *res);
168static void hostres_slirp_reply(struct response *res);
169
170
171/*
172 * Host resolver is called on slirp thread from udp.c
173 */
174struct mbuf *
175hostresolver(PNATState pData, struct mbuf *m, uint32_t src, uint16_t sport)
176{
177 struct response *res;
178 u_int mlen;
179 int rc;
180
181 rc = verify_header(pData, &m);
182 if (RT_FAILURE(rc))
183 return m;
184
185 res = RTMemAllocZ(sizeof(*res));
186 if (res == NULL)
187 return refuse_mbuf(m, RCode_ServFail);
188
189 res->pData = pData;
190 res->src = src;
191 res->sport = sport;
192
193 mlen = m_length(m, NULL);
194 m_copydata(m, 0, mlen, (char *)res->buf);
195 res->end = res->qlen = mlen;
196
197 rc = slirp_call_hostres(pData->pvUser, NULL, 0,
198 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
199 (PFNRT)hostres_async, 1, res);
200
201 if (RT_FAILURE(rc))
202 {
203 LogErr(("NAT: hostres: failed to post async request: %Rrc\n", rc));
204 RTMemFree(res);
205 return refuse_mbuf(m, RCode_ServFail);
206 }
207
208 m_freem(pData, m);
209 return NULL;
210}
211
212
213/*
214 * Do quick sanity-checks on the request before doing async
215 * resolution. If we don't like it, immediately drop or convert to
216 * response in place and bounce back the mbuf.
217 */
218static int
219verify_header(PNATState pData, struct mbuf **pMBuf)
220{
221 struct mbuf *m;
222 struct dnsmsg_header *pHdr;
223 size_t mlen;
224
225 m = *pMBuf;
226 mlen = m_length(m, NULL);
227
228 /*
229 * In theory we should have called
230 *
231 * m = m_pullup(m, sizeof(struct dnsmsg_header));
232 *
233 * here first (which should have been a nop), but the way mbufs
234 * are used in NAT will always cause a copy that will have no
235 * leading space. We can use m_copyup() instead, but if we are
236 * peeking under the hood anyway, we might as well just rely on
237 * the fact that this header will be contiguous.
238 */
239 pHdr = mtod(m, struct dnsmsg_header *);
240
241 if (RT_UNLIKELY(mlen < sizeof(*pHdr)))
242 {
243 LogErr(("NAT: hostres: packet too small: %zu bytes\n", mlen));
244 goto drop; /* can't even refuse it */
245 }
246
247 if (RT_UNLIKELY(mlen > DNS_MAX_UDP_LEN))
248 {
249 LogErr(("NAT: hostres: packet too large: %zu bytes\n", mlen));
250 goto drop; /* don't echo back huge packets */
251 }
252
253 if (RT_UNLIKELY(pHdr->qr != QR_Query))
254 {
255 LogErr(("NAT: hostres: unexpected response\n"));
256 goto drop; /* ignore */
257 }
258
259 if (RT_UNLIKELY(pHdr->opcode != OpCode_Query))
260 {
261 LogErr(("NAT: hostres: unsupported opcode %d\n", pHdr->opcode));
262 refuse_mbuf(m, RCode_NotImp);
263 return VERR_PARSE_ERROR;
264 }
265
266 if (RT_UNLIKELY(pHdr->qdcount != RT_H2N_U16_C(1)))
267 {
268 LogErr(("NAT: hostres: multiple questions\n"));
269 refuse_mbuf(m, RCode_FormErr);
270 return VERR_PARSE_ERROR;
271 }
272
273 if (RT_UNLIKELY(pHdr->ancount != 0))
274 {
275 LogErr(("NAT: hostres: answers in query\n"));
276 refuse_mbuf(m, RCode_FormErr);
277 return VERR_PARSE_ERROR;
278 }
279
280 /* XXX: let it fail when we parse it? */
281 if (RT_UNLIKELY(mlen < sizeof(*pHdr)
282 + /* qname */ 1
283 + /* qtype */ 2
284 + /* qclass */ 2))
285 {
286 LogErr(("NAT: hostres: packet too small: %zu bytes\n", mlen));
287 refuse_mbuf(m, RCode_FormErr);
288 return VERR_PARSE_ERROR;
289 }
290
291 return VINF_SUCCESS;
292
293 drop:
294 if (m != NULL)
295 m_freem(pData, m);
296 *pMBuf = NULL;
297 return VERR_PARSE_ERROR;
298}
299
300
301/*
302 * Turn the request in mbuf into an error response. This is used on
303 * slirp thread for pre-checks before we do async resolution.
304 */
305static struct mbuf *
306refuse_mbuf(struct mbuf *m, unsigned int rcode)
307{
308 struct dnsmsg_header *pHdr;
309
310 pHdr = mtod(m, struct dnsmsg_header *);
311 pHdr->qr = QR_Response;
312 pHdr->rcode = rcode;
313 pHdr->ra = 1;
314 pHdr->aa = 0;
315
316 return m;
317}
318
319
320/*
321 * Actuall resolution runs on the dedicated host resolver thread.
322 */
323static void
324hostres_async(struct response *res)
325{
326 int rc;
327
328 /* build reply in res->buf[] */
329 respond(res);
330
331 free_labels(res->labels);
332
333 rc = slirp_call(res->pData->pvUser, NULL, 0,
334 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
335 (PFNRT)hostres_slirp_reply, 1, res);
336
337 if (RT_FAILURE(rc))
338 {
339 LogErr(("NAT: hostres: failed to post async reply: %Rrc\n", rc));
340 RTMemFree(res);
341 }
342}
343
344
345/*
346 * We are back to the slirp thread to send the reply.
347 */
348static void
349hostres_slirp_reply(struct response *res)
350{
351 PNATState pData = res->pData;
352 struct sockaddr_in src, dst;
353 struct mbuf *m = NULL;
354 size_t mlen;
355 int ok;
356
357 mlen = if_maxlinkhdr + sizeof(struct ip) + sizeof(struct udphdr);
358 mlen += res->end;
359
360 if (mlen <= MHLEN)
361 {
362 m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
363 }
364 else
365 {
366 void *pvBuf; /* ignored */
367 size_t cbBuf;
368
369 m = slirp_ext_m_get(pData, mlen, &pvBuf, &cbBuf);
370 }
371
372 if (m == NULL)
373 goto out;
374
375 /* reserve leading space for ethernet header */
376 m->m_data += if_maxlinkhdr;
377
378 /* reserve leading space for protocol headers */
379 m->m_pkthdr.header = mtod(m, void *);
380 m->m_data += sizeof(struct ip) + sizeof(struct udphdr);
381
382 m->m_len = 0;
383 ok = m_append(pData, m, (int)res->end, (c_caddr_t)res->buf);
384 if (!ok)
385 {
386 m_freem(pData, m);
387 goto out;
388 }
389
390 src.sin_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
391 src.sin_port = RT_H2N_U16_C(53);
392 dst.sin_addr.s_addr = res->src;
393 dst.sin_port = res->sport;
394
395 udp_output2(pData, NULL, m, &src, &dst, IPTOS_LOWDELAY);
396
397 out:
398 RTMemFree(res);
399}
400
401
402static int
403respond(struct response *res)
404{
405 struct dnsmsg_header *pHdr;
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_NXDomain);
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_NXDomain);
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 ssize_t 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((uint16_t)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
772 pHdr = (struct dnsmsg_header *)res->buf;
773 nanswers = 0;
774 oend = res->end;
775
776 LogDbg(("NAT: hostres: %RTnaipv4\n", in_addr_arpa.s_addr));
777
778 if (qtype != Type_PTR && qtype != Type_ANY)
779 {
780 /* can't answer CNAME to PTR queries using gethostby* */
781 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
782 }
783
784 h = NULL;
785#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
786 /*
787 * If the address in the question is unknown to the real resolver
788 * but has a mapping, and if we do the real lookup first, then the
789 * guest will time out before our lookup times out and even though
790 * we reply with the answer from the map, the answer will be lost.
791 */
792 {
793 PDNSMAPPINGENTRY pReverseMapping = getDNSMapByAddr(res->pData, (const uint32_t *)&in_addr_arpa.s_addr);
794 if (pReverseMapping != NULL)
795 {
796 LogDbg(("NAT: hostres: %RTnaipv4 resolved from mapping\n",
797 in_addr_arpa.s_addr));
798
799 hostent.h_name = pReverseMapping->pszName;
800 hostent.h_aliases = h_aliases;
801 h_aliases[0] = NULL;
802 hostent.h_addrtype = AF_INET;
803 hostent.h_length = sizeof(RTNETADDRIPV4);
804 hostent.h_addr_list = h_addr_list;
805 h_addr_list[0] = (char *)&in_addr_arpa.s_addr;
806 h_addr_list[1] = NULL;
807
808 h = &hostent;
809 }
810 }
811#endif
812
813 if (h == NULL)
814 {
815#ifdef RT_OS_WINDOWS
816 h = gethostbyaddr((const char *)&in_addr_arpa, sizeof(struct in_addr), AF_INET);
817#else
818 h = gethostbyaddr(&in_addr_arpa, sizeof(struct in_addr), AF_INET);
819#endif
820 }
821
822 if (h == NULL)
823 {
824 /* LogErr: h_errno */
825 return refuse(res, RCode_NXDomain);
826 }
827
828 if (h->h_name != NULL)
829 {
830 char name[DNS_MAX_NAME_LEN+1];
831 strnlabels(name, sizeof(name), res->buf, qname);
832
833 LogDbg(("NAT: hostres: %s PTR %s\n", name, h->h_name));
834 nbytes = append_ptr(res, name, h->h_name);
835 if (nbytes > 0)
836 {
837 ++nanswers;
838 }
839 else
840 {
841 LogErr(("NAT: hostres: failed to add %s PTR %s\n",
842 name, h->h_name));
843 if (nbytes < 0)
844 return refuse(res, RCode_ServFail);
845 else
846 {
847 pHdr->tc = 1;
848 goto out;
849 }
850 }
851 }
852
853 out:
854 pHdr->ancount = RT_H2N_U16((uint16_t)nanswers);
855 return VINF_SUCCESS;
856}
857
858
859static int
860refuse(struct response *res, unsigned int rcode)
861{
862 struct dnsmsg_header *pHdr = (struct dnsmsg_header *)res->buf;
863 pHdr->rcode = rcode;
864
865 return VINF_SUCCESS;
866}
867
868
869#define APPEND_PROLOGUE() \
870 ssize_t size = -1; \
871 size_t oend = res->end; \
872 ssize_t nbytes; \
873 do {} while (0)
874
875#define CHECKED(_append) \
876 do { \
877 nbytes = (_append); \
878 if (RT_UNLIKELY(nbytes <= 0)) \
879 { \
880 if (nbytes == 0) \
881 size = 0; \
882 goto out; \
883 } \
884 } while (0)
885
886#define APPEND_EPILOGUE() \
887 do { \
888 size = res->end - oend; \
889 out: \
890 if (RT_UNLIKELY(size <= 0)) \
891 res->end = oend; \
892 return size; \
893 } while (0)
894
895
896/*
897 * A RR - rfc1035#section-3.4.1
898 */
899static ssize_t
900append_a(struct response *res, const char *name, struct in_addr addr)
901{
902 APPEND_PROLOGUE();
903
904 CHECKED( append_rrhdr(res, name, Type_A, 3600) );
905 CHECKED( append_u16(res, RT_H2N_U16_C(sizeof(addr))) );
906 CHECKED( append_u32(res, addr.s_addr) );
907
908 APPEND_EPILOGUE();
909}
910
911
912/*
913 * CNAME RR - rfc1035#section-3.3.1
914 */
915static ssize_t
916append_cname(struct response *res, const char *name, const char *cname)
917{
918 return append_name_rr(res, name, Type_CNAME, cname);
919}
920
921
922/*
923 * PTR RR - rfc1035#section-3.3.12
924 */
925static ssize_t
926append_ptr(struct response *res, const char *inaddrname, const char *name)
927{
928 return append_name_rr(res, inaddrname, Type_PTR, name);
929}
930
931
932static ssize_t
933append_name_rr(struct response *res, const char *question,
934 int type, const char *answer)
935{
936 size_t rdlpos;
937 uint16_t rdlength;
938
939 APPEND_PROLOGUE();
940
941 CHECKED( append_rrhdr(res, question, type, 3600) );
942
943 rdlpos = res->end;
944 CHECKED( append_u16(res, 0) ); /* RDLENGTH placeholder */
945
946 CHECKED( append_name(res, answer) );
947
948 rdlength = RT_H2N_U16(nbytes);
949 memcpy(&res->buf[rdlpos], &rdlength, sizeof(rdlength));
950
951 APPEND_EPILOGUE();
952}
953
954
955/*
956 * Append common RR header, up to but not including RDLENGTH and RDATA
957 * proper (rfc1035#section-3.2.1).
958 */
959static ssize_t
960append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl)
961{
962 APPEND_PROLOGUE();
963
964 CHECKED( append_name(res, name) );
965 CHECKED( append_u16(res, RT_H2N_U16(type)) );
966 CHECKED( append_u16(res, RT_H2N_U16_C(Class_IN)) );
967 CHECKED( append_u32(res, RT_H2N_U32(ttl)) );
968
969 APPEND_EPILOGUE();
970}
971
972
973static ssize_t
974append_name(struct response *res, const char *name)
975{
976 ssize_t size, nbytes;
977 struct label *root;
978 struct label *haystack, *needle;
979 struct label *head, **neck;
980 struct label *tail, **graft;
981 uint8_t *buf;
982 size_t wr, oend;
983 const char *s;
984
985 size = -1;
986 oend = res->end;
987
988 /**
989 * Split new name into a list of labels encoding it into the
990 * temporary buffer.
991 */
992 root = NULL;
993
994 buf = RTMemAllocZ(strlen(name) + 1);
995 if (buf == NULL)
996 return -1;
997 wr = 0;
998
999 s = name;
1000 while (*s != '\0') {
1001 const char *part;
1002 size_t poff, plen;
1003 struct label *l;
1004
1005 part = s;
1006 while (*s != '\0' && *s != '.')
1007 ++s;
1008
1009 plen = s - part;
1010
1011 if (plen > DNS_MAX_LABEL_LEN)
1012 {
1013 LogErr(("NAT: hostres: name component too long\n"));
1014 goto out;
1015 }
1016
1017 if (*s == '.')
1018 {
1019 if (plen == 0)
1020 {
1021 LogErr(("NAT: hostres: empty name component\n"));
1022 goto out;
1023 }
1024
1025 ++s;
1026 }
1027
1028 poff = wr;
1029
1030 buf[poff] = (uint8_t)plen; /* length byte */
1031 ++wr;
1032
1033 memcpy(&buf[wr], part, plen); /* label text */
1034 wr += plen;
1035
1036 l = RTMemAllocZ(sizeof(*l));
1037 if (l == NULL)
1038 goto out;
1039
1040 l->buf = buf;
1041 l->off = poff;
1042 l->children = root;
1043 root = l;
1044 }
1045
1046
1047 /**
1048 * Search for a tail that is already encoded in the message.
1049 */
1050 neck = &root; /* where needle head is connected */
1051 needle = root;
1052
1053 tail = NULL; /* tail in the haystack */
1054 graft = &res->labels;
1055 haystack = res->labels;
1056
1057 while (needle != NULL && haystack != NULL)
1058 {
1059 size_t nlen, hlen;
1060
1061 nlen = needle->buf[needle->off];
1062 Assert((nlen & DNS_LABEL_PTR) == 0);
1063
1064 hlen = haystack->buf[haystack->off];
1065 Assert((hlen & DNS_LABEL_PTR) == 0);
1066
1067 if ( nlen == hlen
1068 && RTStrNICmp((char *)&needle->buf[needle->off+1],
1069 (char *)&haystack->buf[haystack->off+1],
1070 nlen) == 0)
1071 {
1072 neck = &needle->children;
1073 needle = needle->children;
1074
1075 tail = haystack;
1076 graft = &haystack->children;
1077 haystack = haystack->children;
1078 }
1079 else
1080 {
1081 haystack = haystack->sibling;
1082 }
1083 }
1084
1085
1086 /**
1087 * Head contains (in reverse) the prefix that needs to be encoded
1088 * and added to the haystack. Tail points to existing suffix that
1089 * can be compressed to a pointer into the haystack.
1090 */
1091 head = *neck;
1092 if (head != NULL)
1093 {
1094 struct label *l;
1095 size_t nlen, pfxlen, pfxdst;
1096
1097 nlen = needle->buf[head->off]; /* last component */
1098 pfxlen = head->off + 1 + nlen; /* all prefix */
1099 pfxdst = res->end; /* in response buffer */
1100
1101 /* copy new prefix into response buffer */
1102 nbytes = append_bytes(res, buf, pfxlen);
1103 if (nbytes <= 0)
1104 {
1105 if (nbytes == 0)
1106 size = 0;
1107 goto out;
1108 }
1109
1110 /* adjust labels to point to the response */
1111 for (l = head; l != NULL; l = l->children)
1112 {
1113 l->buf = res->buf;
1114 l->off += pfxdst;
1115 }
1116
1117 *neck = NULL; /* decapitate */
1118
1119 l = *graft; /* graft to the labels tree */
1120 *graft = head;
1121 head->sibling = l;
1122 }
1123
1124 if (tail == NULL)
1125 nbytes = append_u8(res, 0);
1126 else
1127 nbytes = append_u16(res, RT_H2N_U16((DNS_LABEL_PTR << 8) | tail->off));
1128 if (nbytes <= 0)
1129 {
1130 if (nbytes == 0)
1131 size = 0;
1132 goto out;
1133 }
1134
1135 size = res->end - oend;
1136 out:
1137 if (RT_UNLIKELY(size <= 0))
1138 res->end = oend;
1139 free_labels(root);
1140 RTMemFree(buf);
1141 return size;
1142}
1143
1144
1145static ssize_t
1146append_u32(struct response *res, uint32_t value)
1147{
1148 return append_bytes(res, (uint8_t *)&value, sizeof(value));
1149}
1150
1151
1152static ssize_t
1153append_u16(struct response *res, uint16_t value)
1154{
1155 return append_bytes(res, (uint8_t *)&value, sizeof(value));
1156}
1157
1158
1159static ssize_t
1160append_u8(struct response *res, uint8_t value)
1161{
1162 return append_bytes(res, &value, sizeof(value));
1163}
1164
1165
1166static ssize_t
1167append_bytes(struct response *res, uint8_t *p, size_t size)
1168{
1169 if (check_space(res, size) == 0)
1170 return 0;
1171
1172 memcpy(&res->buf[res->end], p, size);
1173 res->end += size;
1174 return size;
1175}
1176
1177
1178static ssize_t
1179check_space(struct response *res, size_t size)
1180{
1181 if ( size > sizeof(res->buf)
1182 || res->end > sizeof(res->buf) - size)
1183 return 0;
1184
1185 return size;
1186}
1187
1188
1189static int
1190get_in_addr_arpa(struct in_addr *paddr, struct label *root)
1191{
1192 RTNETADDRIPV4 addr;
1193 struct label *l;
1194 int i;
1195 RT_ZERO(addr); /* makes MSC happy*/
1196
1197 l = root;
1198 if (l == NULL || labelstrcmp(l, "arpa") != 0)
1199 return 0;
1200
1201 l = l->children;
1202 if (l == NULL || labelstrcmp(l, "in-addr") != 0)
1203 return 0;
1204
1205 for (i = 0; i < 4; ++i)
1206 {
1207 char buf[4];
1208 size_t llen;
1209 int rc;
1210 uint8_t octet;
1211
1212 l = l->children;
1213 if (l == NULL)
1214 return 0;
1215
1216 llen = l->buf[l->off];
1217 Assert((llen & DNS_LABEL_PTR) == 0);
1218
1219 /* valid octet values are at most 3 digits */
1220 if (llen > 3)
1221 return 0;
1222
1223 /* copy to avoid dealing with trailing bytes */
1224 memcpy(buf, &l->buf[l->off + 1], llen);
1225 buf[llen] = '\0';
1226
1227 rc = RTStrToUInt8Full(buf, 10, &octet);
1228 if (rc != VINF_SUCCESS)
1229 return 0;
1230
1231 addr.au8[i] = octet;
1232 }
1233
1234 if (l->children != NULL)
1235 return 0; /* too many components */
1236
1237 if (paddr != NULL)
1238 paddr->s_addr = addr.u;
1239
1240 return 1;
1241}
1242
1243
1244/*
1245 * Compare label with string.
1246 */
1247static int
1248labelstrcmp(struct label *l, const char *s)
1249{
1250 size_t llen;
1251
1252 llen = l->buf[l->off];
1253 Assert((llen & DNS_LABEL_PTR) == 0);
1254
1255 return RTStrNICmp((char *)&l->buf[l->off + 1], s, llen);
1256}
1257
1258
1259/*
1260 * Convert a chain of labels to a C string.
1261 *
1262 * I'd rather use a custom formatter for e.g. %R[label] , but it needs
1263 * two arguments and microsoft VC doesn't support compound literals.
1264 */
1265static void
1266strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off)
1267{
1268 size_t cb;
1269 size_t llen;
1270
1271 namebuf[0] = '\0';
1272 cb = 0;
1273
1274 llen = 0;
1275
1276 while (cb < nbuflen - 1) {
1277 llen = msg[off];
1278 if ((llen & DNS_LABEL_PTR) == DNS_LABEL_PTR)
1279 {
1280 off = ((llen & ~DNS_LABEL_PTR) << 8) | msg[off + 1];
1281 llen = msg[off];
1282 }
1283
1284 /* pointers to pointers should not happen */
1285 if ((llen & DNS_LABEL_PTR) != 0)
1286 {
1287 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, "[???]");
1288 return;
1289 }
1290
1291 if (llen == 0)
1292 {
1293 if (namebuf[0] == '\0')
1294 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1295 break;
1296 }
1297
1298 if (namebuf[0] != '\0')
1299 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1300
1301 cb += RTStrPrintf(namebuf + cb, nbuflen - cb,
1302 "%.*s", llen, (char *)&msg[off+1]);
1303 off = off + 1 + llen;
1304 }
1305}
1306
1307
1308#if 0 /* unused */
1309static void
1310LogLabelsTree(const char *before, struct label *l, const char *after)
1311{
1312 size_t llen;
1313
1314 if (before != NULL)
1315 LogDbg(("%s", before));
1316
1317 if (l == NULL)
1318 {
1319 LogDbg(("NULL%s", after ? after : ""));
1320 return;
1321 }
1322
1323 if (l->children)
1324 LogDbg(("("));
1325
1326 if (l->buf != NULL)
1327 {
1328 llen = l->buf[l->off];
1329 if ((llen & DNS_LABEL_PTR) == 0)
1330 {
1331 LogDbg(("\"%.*s\"@%zu", llen, &l->buf[l->off+1], l->off));
1332 }
1333 else
1334 {
1335 LogDbg(("<invalid byte 0t%zu/0x%zf at offset %zd>",
1336 llen, llen, l->off));
1337 }
1338 }
1339 else
1340 {
1341 LogDbg(("<*>"));
1342 }
1343
1344 if (l->children)
1345 LogLabelsTree(" ", l->children, ")");
1346
1347 if (l->sibling)
1348 LogLabelsTree(" ", l->sibling, NULL);
1349
1350 if (after != NULL)
1351 LogDbg(("%s", after));
1352}
1353#endif /* unused */
1354
1355
1356static void
1357free_labels(struct label *root)
1358{
1359 struct label TOP; /* traverse the tree with pointer reversal */
1360 struct label *b, *f;
1361
1362 if (root == NULL)
1363 return;
1364
1365 RT_ZERO(TOP);
1366
1367 b = &TOP;
1368 f = root;
1369
1370 while (f != &TOP) {
1371 if (f->children) { /* recurse left */
1372 struct label *oldf = f;
1373 struct label *newf = f->children;
1374 oldf->children = b; /* reverse the pointer */
1375 b = oldf;
1376 f = newf;
1377 }
1378 else if (f->sibling) { /* turn right */
1379 f->children = f->sibling;
1380 f->sibling = NULL;
1381 }
1382 else { /* backtrack */
1383 struct label *oldf = f; /* garbage */
1384 struct label *oldb = b;
1385 b = oldb->children;
1386 oldb->children = NULL; /* oldf, but we are g/c'ing it */
1387 f = oldb;
1388
1389 RTMemFree(oldf);
1390 }
1391 }
1392}
1393
1394#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1395void
1396slirp_add_host_resolver_mapping(PNATState pData,
1397 const char *pszHostName, bool fPattern,
1398 uint32_t u32HostIP)
1399{
1400 LogRel(("ENTER: pszHostName:%s%s, u32HostIP:%RTnaipv4\n",
1401 pszHostName ? pszHostName : "(null)",
1402 fPattern ? " (pattern)" : "",
1403 u32HostIP));
1404
1405 if ( pszHostName != NULL
1406 && u32HostIP != INADDR_ANY
1407 && u32HostIP != INADDR_BROADCAST)
1408 {
1409 PDNSMAPPINGENTRY pDnsMapping = RTMemAllocZ(sizeof(DNSMAPPINGENTRY));
1410 if (!pDnsMapping)
1411 {
1412 LogFunc(("Can't allocate DNSMAPPINGENTRY\n"));
1413 LogFlowFuncLeave();
1414 return;
1415 }
1416
1417 pDnsMapping->u32IpAddress = u32HostIP;
1418 pDnsMapping->fPattern = fPattern;
1419 pDnsMapping->pszName = RTStrDup(pszHostName);
1420
1421 if (pDnsMapping->pszName == NULL)
1422 {
1423 LogFunc(("Can't allocate enough room for host name\n"));
1424 RTMemFree(pDnsMapping);
1425 LogFlowFuncLeave();
1426 return;
1427 }
1428
1429 if (fPattern) /* there's no case-insensitive pattern-match function */
1430 RTStrToLower(pDnsMapping->pszName);
1431
1432 STAILQ_INSERT_TAIL(fPattern ? &pData->DNSMapPatterns : &pData->DNSMapNames,
1433 pDnsMapping, MapList);
1434
1435 LogRel(("NAT: User-defined mapping %s%s = %RTnaipv4 is registered\n",
1436 pDnsMapping->pszName,
1437 pDnsMapping->fPattern ? " (pattern)" : "",
1438 pDnsMapping->u32IpAddress));
1439 }
1440 LogFlowFuncLeave();
1441}
1442
1443
1444static PDNSMAPPINGENTRY
1445getDNSMapByName(PNATState pData, const char *pszName)
1446{
1447 PDNSMAPPINGENTRY pDNSMapingEntry;
1448 char *pszNameLower;
1449
1450 pszNameLower = RTStrDup(pszName);
1451 if (RT_UNLIKELY(pszNameLower == NULL))
1452 return NULL;
1453 RTStrToLower(pszNameLower);
1454
1455 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapNames, MapList)
1456 {
1457 if (RTStrICmp(pDNSMapingEntry->pszName, pszNameLower) == 0)
1458 goto done;
1459 }
1460
1461 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapPatterns, MapList)
1462 {
1463 if (RTStrSimplePatternMultiMatch(pDNSMapingEntry->pszName, RTSTR_MAX,
1464 pszNameLower, RTSTR_MAX, NULL))
1465 goto done;
1466 }
1467
1468 done:
1469 RTStrFree(pszNameLower);
1470 return pDNSMapingEntry;
1471}
1472
1473
1474static PDNSMAPPINGENTRY
1475getDNSMapByAddr(PNATState pData, const uint32_t *pu32IpAddress)
1476{
1477 PDNSMAPPINGENTRY pDNSMapingEntry;
1478
1479 if (pu32IpAddress == NULL)
1480 return NULL;
1481
1482 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapNames, MapList)
1483 {
1484 if (pDNSMapingEntry->u32IpAddress == *pu32IpAddress)
1485 return pDNSMapingEntry;
1486 }
1487
1488 return NULL;
1489}
1490
1491
1492static void
1493alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h)
1494{
1495 PDNSMAPPINGENTRY pDNSMapingEntry = NULL;
1496 char **ppszAlias;
1497
1498 if (h->h_name != NULL)
1499 {
1500 pDNSMapingEntry = getDNSMapByName(pData, h->h_name);
1501 if (pDNSMapingEntry != NULL)
1502 goto done;
1503 }
1504
1505 for (ppszAlias = h->h_aliases; *ppszAlias != NULL; ++ppszAlias)
1506 {
1507 pDNSMapingEntry = getDNSMapByName(pData, *ppszAlias);
1508 if (pDNSMapingEntry != NULL)
1509 goto done;
1510 }
1511
1512 done:
1513 if (pDNSMapingEntry != NULL)
1514 {
1515 *(uint32_t *)h->h_addr_list[0] = pDNSMapingEntry->u32IpAddress;
1516 h->h_addr_list[1] = NULL;
1517 }
1518}
1519#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