VirtualBox

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

Last change on this file since 62353 was 61801, checked in by vboxsync, 9 years ago

Summary: NAT: G/c unused variable.

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