VirtualBox

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

Last change on this file since 76031 was 76031, checked in by vboxsync, 6 years ago

NAT: Exegetical tradition of RFC1035 holds that "Not Implemented"
response code is strictly for unsupported OPCODEs. ticketref:18171

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