VirtualBox

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

Last change on this file since 59203 was 59203, checked in by vboxsync, 8 years ago

NAT: Ignore/drop any authority or additional records in queries to the
host resolver, so that we can process queries with EDNS(0) RFC6891 OPT
RR in additional records section.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.6 KB
Line 
1/* $Id: hostres.c 59203 2015-12-21 17:21:56Z 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 /* XXX: endianness */
35 uint16_t rd:1;
36 uint16_t tc:1;
37 uint16_t aa:1;
38 uint16_t opcode:4;
39 uint16_t qr:1;
40 uint16_t rcode:4;
41 uint16_t Z:3;
42 uint16_t ra:1;
43
44 uint16_t qdcount;
45 uint16_t ancount;
46 uint16_t nscount;
47 uint16_t arcount;
48};
49AssertCompileSize(struct dnsmsg_header, 12);
50
51#define QR_Query 0
52#define QR_Response 1
53
54#define OpCode_Query 0
55
56#define RCode_NoError 0
57#define RCode_FormErr 1
58#define RCode_ServFail 2
59#define RCode_NXDomain 3
60#define RCode_NotImp 4
61#define RCode_Refused 5
62
63#define Type_A 1
64#define Type_CNAME 5
65#define Type_PTR 12
66#define Type_ANY 255
67
68#define Class_IN 1
69#define Class_ANY 255
70
71/* compressed label encoding */
72#define DNS_LABEL_PTR 0xc0
73
74#define DNS_MAX_UDP_LEN 512
75#define DNS_MAX_LABEL_LEN 63
76#define DNS_MAX_NAME_LEN 255
77
78
79/*
80 * A tree of labels.
81 *
82 * rfc1035#section-3.1
83 * rfc1035#section-4.1.4
84 */
85struct label
86{
87 const uint8_t *buf;
88 ssize_t off;
89 struct label *children;
90 struct label *sibling;
91};
92
93
94/*
95 * A structure to build DNS response.
96 */
97struct response
98{
99 struct label *labels; /* already encoded in buf */
100 size_t qlen; /* original question */
101 size_t end; /* of data in buf */
102
103 /* continuous buffer to build the response */
104 uint8_t buf[DNS_MAX_UDP_LEN];
105};
106
107
108static int verify_header(PNATState pData, struct mbuf **pMBuf);
109static struct mbuf *respond(PNATState pData, struct mbuf *m, struct response *res);
110struct mbuf *resolve(PNATState pData, struct mbuf *m, struct response *res,
111 uint16_t qtype, size_t qname);
112struct mbuf *resolve_reverse(PNATState pData, struct mbuf *m, struct response *res,
113 uint16_t qtype, size_t qname, struct in_addr addr);
114struct mbuf *refuse(PNATState pData, struct mbuf *m, unsigned int rcode);
115static ssize_t append_a(struct response *res, const char *name, struct in_addr addr);
116static ssize_t append_cname(struct response *res, const char *name, const char *cname);
117static ssize_t append_ptr(struct response *res, const char *inaddrname, const char *name);
118static ssize_t append_name_rr(struct response *res, const char *question, int type, const char *answer);
119static ssize_t append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl);
120static ssize_t append_name(struct response *res, const char *name);
121static ssize_t append_u32(struct response *res, uint32_t value);
122static ssize_t append_u16(struct response *res, uint16_t value);
123static ssize_t append_u8(struct response *res, uint8_t value);
124static ssize_t append_bytes(struct response *res, uint8_t *p, size_t size);
125static ssize_t check_space(struct response *res, size_t size);
126
127static int get_in_addr_arpa(struct in_addr *paddr, struct label *root);
128static int labelstrcmp(struct label *l, const char *s);
129static void strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off);
130
131static void LogLabelsTree(const char *before, struct label *l, const char *after);
132static void free_labels(struct label *root);
133
134#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
135static void alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *pHostent);
136#endif
137
138#if 1 /* XXX */
139# define LogErr(args) Log2(args)
140# define LogDbg(args) Log3(args)
141#else
142# define LogErr(args) LogRel(args)
143# define LogDbg(args) LogRel(args)
144#endif
145
146
147struct mbuf *
148hostresolver(PNATState pData, struct mbuf *m)
149{
150 int error;
151
152 struct response res;
153
154 error = verify_header(pData, &m);
155 if (error != 0)
156 return m;
157
158 RT_ZERO(res);
159
160 /*
161 * Do the real work
162 */
163 m = respond(pData, m, &res);
164
165 free_labels(res.labels);
166 return m;
167}
168
169
170struct mbuf *
171refuse(PNATState pData, struct mbuf *m, unsigned int rcode)
172{
173 struct dnsmsg_header *pHdr;
174
175 pHdr = mtod(m, struct dnsmsg_header *);
176 pHdr->qr = QR_Response;
177 pHdr->rcode = rcode;
178 pHdr->ra = 1;
179 pHdr->aa = 0;
180
181 return m;
182}
183
184
185static int
186verify_header(PNATState pData, struct mbuf **pMBuf)
187{
188 struct mbuf *m;
189 struct dnsmsg_header *pHdr;
190 size_t mlen;
191
192 m = *pMBuf;
193
194 /*
195 * In theory we should have called
196 *
197 * m = m_pullup(m, sizeof(struct dnsmsg_header));
198 *
199 * here first (which should have been a nop), but the way mbufs
200 * are used in NAT will always cause a copy that will have no
201 * leading space. We can use m_copyup() instead, but if we are
202 * peeking under the hood anyway, we might as well just rely on
203 * the fact that this header will be contiguous.
204 */
205 pHdr = mtod(m, struct dnsmsg_header *);
206
207 if (RT_UNLIKELY(pHdr->qr != QR_Query))
208 {
209 LogErr(("NAT: hostres: unexpected response\n"));
210 goto drop;
211 }
212
213 mlen = m_length(m, NULL);
214 if (RT_UNLIKELY(mlen > DNS_MAX_UDP_LEN))
215 {
216 LogErr(("NAT: hostres: packet too large\n"));
217 refuse(pData, m, RCode_FormErr); /* or drop? */
218 return 1;
219 }
220
221 if (RT_UNLIKELY(pHdr->opcode != OpCode_Query))
222 {
223 LogErr(("NAT: hostres: unsupported opcode\n"));
224 refuse(pData, m, RCode_NotImp);
225 return 1;
226 }
227
228 if (RT_UNLIKELY(pHdr->qdcount != RT_H2N_U16_C(1)))
229 {
230 LogErr(("NAT: hostres: multiple questions\n"));
231 refuse(pData, m, RCode_NotImp);
232 return 1;
233 }
234
235 if (RT_UNLIKELY(pHdr->ancount != 0))
236 {
237 LogErr(("NAT: hostres: answers in query\n"));
238 refuse(pData, m, RCode_NotImp);
239 return 1;
240 }
241
242 if (RT_UNLIKELY(mlen < sizeof(*pHdr)
243 + /* qname */ 1
244 + /* qtype */ 2
245 + /* qclass */ 2))
246 {
247 LogErr(("NAT: hostres: packet too small\n"));
248 refuse(pData, m, RCode_FormErr);
249 return 1;
250 }
251
252 return 0;
253
254 drop:
255 if (m != NULL)
256 m_freem(pData, m);
257 *pMBuf = NULL;
258 return 1;
259}
260
261
262static struct mbuf *
263respond(PNATState pData, struct mbuf *m, struct response *res)
264{
265 struct dnsmsg_header *pHdr;
266 size_t mlen;
267 size_t off;
268 size_t qname;
269 uint16_t qtype, qclass;
270 struct in_addr in_addr_arpa;
271 struct label *l;
272
273 /**
274 * Copy the request into the contiguous buffer for the response
275 * and parse the question.
276 */
277
278 mlen = m_length(m, NULL);
279 m_copydata(m, 0, mlen, (char *)res->buf);
280 res->end = res->qlen = mlen;
281
282 /* convert header to response */
283 pHdr = (struct dnsmsg_header *)res->buf;
284 pHdr->qr = QR_Response;
285 pHdr->rcode = RCode_NoError;
286 pHdr->ra = 1; /* the host provides recursion */
287 pHdr->aa = 0; /* we are not authoritative */
288 pHdr->Z = 0; /* clear rfc2535 dnssec bits */
289
290 off = sizeof(*pHdr);
291 qname = off;
292
293 /*
294 * Parse/verify QNAME and collect the suffixes to be used for
295 * compression in the answer.
296 */
297 while (off < mlen) {
298 size_t loff, llen;
299 uint8_t c;
300
301 c = res->buf[off];
302
303 /*
304 * There's just one question with just one name, so there are
305 * no other labels it can point to. Thus all well-formed
306 * names with a pointer can only be infinite loops.
307 */
308 if ((c & DNS_LABEL_PTR) == DNS_LABEL_PTR)
309 {
310 LogErr(("NAT: hostres: label pointer in the qname\n"));
311 return refuse(pData, m, RCode_FormErr);
312 }
313
314 if ((c & DNS_LABEL_PTR) != 0)
315 {
316 LogErr(("NAT: hostres: unexpected high bits\n"));
317 return refuse(pData, m, RCode_FormErr);
318 }
319
320 /*
321 * label of "llen" chars starts at offset "loff".
322 */
323 loff = off;
324 llen = c;
325 ++off;
326
327 if (loff + 1 + llen > mlen)
328 {
329 LogErr(("NAT: hostres: length byte points beyound packet boundary\n"));
330 return refuse(pData, m, RCode_FormErr);
331 }
332
333 if (llen == 0) /* end of the label list */
334 {
335 break;
336 }
337
338 /* do only minimal verification of the label */
339 while (off < loff + 1 + llen)
340 {
341 c = res->buf[off];
342 ++off;
343
344 if (c == '.')
345 {
346 LogErr(("NAT: hostres: dot inside label\n"));
347 return refuse(pData, m, RCode_FormErr);
348 }
349
350 if (c == '\0')
351 {
352 LogErr(("NAT: hostres: nul byte inside label\n"));
353 return refuse(pData, m, RCode_FormErr);
354 }
355 }
356
357 l = RTMemAllocZ(sizeof(*l));
358 l->buf = res->buf;
359 l->off = loff;
360 l->children = res->labels;
361 res->labels = l;
362 }
363
364 /*
365 * QTYPE and QCLASS
366 */
367 if (RT_UNLIKELY(off + 4 > mlen))
368 {
369 LogErr(("NAT: hostres: question too short\n"));
370 return refuse(pData, m, RCode_FormErr);
371 }
372
373 memcpy(&qtype, &res->buf[off], sizeof(qtype));
374 qtype = RT_N2H_U16(qtype);
375 off += sizeof(qtype);
376
377 memcpy(&qclass, &res->buf[off], sizeof(qclass));
378 qclass = RT_N2H_U16(qclass);
379 off += sizeof(qclass);
380
381 if ( qclass != Class_IN
382 && qclass != Class_ANY)
383 {
384 LogErr(("NAT: hostres: unsupported qclass %d\n", qclass));
385 return refuse(pData, m, RCode_NotImp);
386 }
387
388 if ( qtype != Type_A
389 && qtype != Type_CNAME
390 && qtype != Type_PTR
391 && qtype != Type_ANY)
392 {
393 LogErr(("NAT: hostres: unsupported qtype %d\n", qtype));
394 return refuse(pData, m, RCode_NotImp);
395 }
396
397
398 /**
399 * Check if there's anything after the question. If query says it
400 * has authority or additional records, ignore and drop them
401 * without parsing.
402 *
403 * We have already rejected queries with answer(s) before. We
404 * have ensured that qname in the question doesn't contain
405 * pointers, so truncating the buffer is safe.
406 */
407 if (off < mlen)
408 {
409 int trailer = mlen - off;
410
411 LogDbg(("NAT: hostres: question %zu < mlen %zu\n", off, mlen));
412
413 if (pHdr->nscount == 0 && pHdr->arcount == 0)
414 {
415 LogErr(("NAT: hostres: unexpected %d bytes after the question\n", trailer));
416 return refuse(pData, m, RCode_FormErr);
417 }
418
419 LogDbg(("NAT: hostres: ignoring %d bytes of %s%s%s records\n",
420 trailer,
421 pHdr->nscount != 0 ? "authority" : "",
422 pHdr->nscount != 0 && pHdr->arcount != 0 ? " and " : "",
423 pHdr->arcount != 0 ? "additional" : ""));
424
425 m_adj(m, -trailer);
426 mlen -= trailer;
427 res->end = res->qlen = mlen;
428
429 pHdr->nscount = 0;
430 pHdr->arcount = 0;
431 }
432
433
434 /*
435 * Check for IN-ADDR.ARPA. Use the fact that res->labels at this
436 * point contains only the qname, so we have easy top-down access
437 * to its components.
438 */
439 if (get_in_addr_arpa(&in_addr_arpa, res->labels))
440 return resolve_reverse(pData, m, res, qtype, qname, in_addr_arpa);
441 else
442 return resolve(pData, m, res, qtype, qname);
443}
444
445
446struct mbuf *
447resolve(PNATState pData, struct mbuf *m, struct response *res,
448 uint16_t qtype, size_t qname)
449{
450 struct dnsmsg_header *pHdr;
451 struct hostent *h;
452 size_t oend;
453 size_t nanswers;
454 ssize_t nbytes;
455 int i;
456
457 char name[DNS_MAX_NAME_LEN+1];
458
459 pHdr = (struct dnsmsg_header *)res->buf;
460 nanswers = 0;
461 oend = res->end;
462
463 strnlabels(name, sizeof(name), res->buf, qname);
464 LogDbg(("NAT: hostres: qname=\"%s\"\n", name));
465
466 if (qtype != Type_A && qtype != Type_CNAME && qtype != Type_ANY)
467 {
468 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
469 }
470
471
472 h = gethostbyname(name);
473 if (h == NULL)
474 {
475 /* LogErr: h_errno */
476 return refuse(pData, m, RCode_NXDomain);
477 }
478
479#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
480 if (!LIST_EMPTY(&pData->DNSMapHead))
481 alterHostentWithDataFromDNSMap(pData, h);
482#endif
483
484 /*
485 * Emit CNAME record if canonical name differs from the qname.
486 */
487 if ( h->h_name != NULL
488 && RTStrICmp(h->h_name, name) != 0)
489 {
490 LogDbg(("NAT: hostres: %s CNAME %s\n", name, h->h_name));
491 nbytes = append_cname(res, name, h->h_name);
492 if (nbytes > 0)
493 {
494 ++nanswers;
495 }
496 else
497 {
498 LogErr(("NAT: hostres: failed to add %s CNAME %s\n",
499 name, h->h_name));
500 if (nbytes < 0)
501 return refuse(pData, m, RCode_ServFail);
502 else
503 {
504 pHdr->tc = 1;
505 goto out;
506 }
507 }
508
509 /*
510 * rfc1034#section-3.6.2 - ... a type CNAME or * query should
511 * return just the CNAME.
512 */
513 if (qtype == Type_CNAME || qtype == Type_ANY)
514 goto out;
515 }
516 else if (qtype == Type_CNAME)
517 {
518 LogErr(("NAT: hostres: %s is already canonical\n", name));
519 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
520 }
521
522 /*
523 * Emit A records.
524 */
525 for (i = 0; h->h_addr_list[i] != NULL; ++i)
526 {
527 const char *cname = h->h_name ? h->h_name : name;
528 struct in_addr addr;
529
530 addr.s_addr = *(uint32_t *)h->h_addr_list[i];
531 nbytes = append_a(res, cname, addr);
532
533 if (nbytes > 0)
534 {
535 ++nanswers;
536 }
537 else
538 {
539 LogErr(("NAT: hostres: failed to add %s A %RTnaipv4\n",
540 cname, addr.s_addr));
541 if (nbytes < 0)
542 return refuse(pData, m, RCode_ServFail);
543 else
544 {
545 pHdr->tc = 1;
546 goto out;
547 }
548 }
549 }
550
551#if 0
552 /*
553 * It's not clear what to do with h_aliases.
554 *
555 * For names from the DNS it seems to contain the chain of CNAMEs,
556 * starting with the original qname from the question. So for
557 * them we'd need to reply with a chain of:
558 *
559 * h_aliases[i] CNAME h_aliases[i+1]
560 *
561 * OTOH, for the names from the hosts file it seems to contain all
562 * the names except the first one (which is considered primary and
563 * is reported as h_name). In which case the reply should be:
564 *
565 * h_aliases[i] CNAME h_name
566 *
567 * Obviously, we have no idea how the name was resolved, so we
568 * generate at most one CNAME for h_host (if differs) and ignore
569 * aliases altogehter.
570 */
571 for (i = 0; h->h_aliases[i] != NULL; ++i)
572 {
573 LogDbg(("NAT: hostres: ... %s\n", h->h_aliases[i]));
574 }
575#endif
576
577 out:
578 if (nanswers > 0)
579 {
580 int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
581 if (!ok)
582 {
583 /* XXX: this may fail part way: restore old lenght, clear TC? */
584 return refuse(pData, m, RCode_ServFail);
585 }
586 pHdr->ancount = RT_H2N_U16(nanswers);
587 }
588 memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
589 return m;
590}
591
592
593struct mbuf *
594resolve_reverse(PNATState pData, struct mbuf *m, struct response *res,
595 uint16_t qtype, size_t qname, struct in_addr in_addr_arpa)
596{
597 struct dnsmsg_header *pHdr;
598 struct hostent *h;
599 size_t oend;
600 size_t nanswers;
601 ssize_t nbytes;
602 int i;
603
604 pHdr = (struct dnsmsg_header *)res->buf;
605 nanswers = 0;
606 oend = res->end;
607
608 LogDbg(("NAT: hostres: %RTnaipv4\n", in_addr_arpa.s_addr));
609
610 if (qtype != Type_PTR && qtype != Type_ANY)
611 {
612 /* can't answer CNAME to PTR queries using gethostby* */
613 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
614 }
615
616 /* XXX: TODO: apply HostResolverMappings */
617 h = gethostbyaddr(&in_addr_arpa, sizeof(struct in_addr), AF_INET);
618 if (h == NULL)
619 {
620 /* LogErr: h_errno */
621 return refuse(pData, m, RCode_NXDomain);
622 }
623
624 if (h->h_name != NULL)
625 {
626 char name[DNS_MAX_NAME_LEN+1];
627 strnlabels(name, sizeof(name), res->buf, qname);
628
629 LogDbg(("NAT: hostres: %s PTR %s\n", name, h->h_name));
630 nbytes = append_ptr(res, name, h->h_name);
631 if (nbytes > 0)
632 {
633 ++nanswers;
634 }
635 else
636 {
637 LogErr(("NAT: hostres: failed to add %s PTR %s\n",
638 name, h->h_name));
639 if (nbytes < 0)
640 return refuse(pData, m, RCode_ServFail);
641 else
642 {
643 pHdr->tc = 1;
644 goto out;
645 }
646 }
647 }
648
649 out:
650 if (nanswers > 0)
651 {
652 int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
653 if (!ok)
654 {
655 /* XXX: this may fail part way: restore old lenght, clear TC? */
656 return refuse(pData, m, RCode_ServFail);
657 }
658 pHdr->ancount = RT_H2N_U16(nanswers);
659 }
660 memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
661 return m;
662}
663
664
665
666#define APPEND_PROLOGUE() \
667 ssize_t size = -1; \
668 size_t oend = res->end; \
669 ssize_t nbytes; \
670 do {} while (0)
671
672#define CHECKED(_append) \
673 do { \
674 nbytes = (_append); \
675 if (RT_UNLIKELY(nbytes <= 0)) \
676 { \
677 if (nbytes == 0) \
678 size = 0; \
679 goto out; \
680 } \
681 } while (0)
682
683#define APPEND_EPILOGUE() \
684 do { \
685 size = res->end - oend; \
686 out: \
687 if (RT_UNLIKELY(size <= 0)) \
688 res->end = oend; \
689 return size; \
690 } while (0)
691
692
693/*
694 * A RR - rfc1035#section-3.4.1
695 */
696static ssize_t
697append_a(struct response *res, const char *name, struct in_addr addr)
698{
699 APPEND_PROLOGUE();
700
701 CHECKED( append_rrhdr(res, name, Type_A, 3600) );
702 CHECKED( append_u16(res, RT_H2N_U16_C(sizeof(addr))) );
703 CHECKED( append_u32(res, addr.s_addr) );
704
705 APPEND_EPILOGUE();
706}
707
708
709/*
710 * CNAME RR - rfc1035#section-3.3.1
711 */
712static ssize_t
713append_cname(struct response *res, const char *name, const char *cname)
714{
715 return append_name_rr(res, name, Type_CNAME, cname);
716}
717
718
719/*
720 * PTR RR - rfc1035#section-3.3.12
721 */
722static ssize_t
723append_ptr(struct response *res, const char *inaddrname, const char *name)
724{
725 return append_name_rr(res, inaddrname, Type_PTR, name);
726}
727
728
729static ssize_t
730append_name_rr(struct response *res, const char *question,
731 int type, const char *answer)
732{
733 size_t rdlpos;
734 uint16_t rdlength;
735
736 APPEND_PROLOGUE();
737
738 CHECKED( append_rrhdr(res, question, type, 3600) );
739
740 rdlpos = res->end;
741 CHECKED( append_u16(res, 0) ); /* RDLENGTH placeholder */
742
743 CHECKED( append_name(res, answer) );
744
745 rdlength = RT_H2N_U16(nbytes);
746 memcpy(&res->buf[rdlpos], &rdlength, sizeof(rdlength));
747
748 APPEND_EPILOGUE();
749}
750
751
752/*
753 * Append common RR header, up to but not including RDLENGTH and RDATA
754 * proper (rfc1035#section-3.2.1).
755 */
756static ssize_t
757append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl)
758{
759 APPEND_PROLOGUE();
760
761 CHECKED( append_name(res, name) );
762 CHECKED( append_u16(res, RT_H2N_U16(type)) );
763 CHECKED( append_u16(res, RT_H2N_U16_C(Class_IN)) );
764 CHECKED( append_u32(res, RT_H2N_U32(ttl)) );
765
766 APPEND_EPILOGUE();
767}
768
769
770static ssize_t
771append_name(struct response *res, const char *name)
772{
773 ssize_t size, nbytes;
774 struct label *root;
775 struct label *haystack, *needle;
776 struct label *head, **neck;
777 struct label *tail, **graft;
778 uint8_t *buf;
779 size_t wr, oend;
780 const char *s;
781
782 size = -1;
783 oend = res->end;
784
785 /**
786 * Split new name into a list of labels encoding it into the
787 * temporary buffer.
788 */
789 root = NULL;
790
791 buf = RTMemAllocZ(strlen(name) + 1);
792 if (buf == NULL)
793 return -1;
794 wr = 0;
795
796 s = name;
797 while (*s != '\0') {
798 const char *part;
799 size_t poff, plen;
800 struct label *l;
801
802 part = s;
803 while (*s != '\0' && *s != '.')
804 ++s;
805
806 plen = s - part;
807
808 if (plen > DNS_MAX_LABEL_LEN)
809 {
810 LogErr(("NAT: hostres: name component too long\n"));
811 goto out;
812 }
813
814 if (*s == '.')
815 {
816 if (plen == 0)
817 {
818 LogErr(("NAT: hostres: empty name component\n"));
819 goto out;
820 }
821
822 ++s;
823 }
824
825 poff = wr;
826
827 buf[poff] = (uint8_t)plen; /* length byte */
828 ++wr;
829
830 memcpy(&buf[wr], part, plen); /* label text */
831 wr += plen;
832
833 l = RTMemAllocZ(sizeof(*l));
834 if (l == NULL)
835 goto out;
836
837 l->buf = buf;
838 l->off = poff;
839 l->children = root;
840 root = l;
841 }
842
843
844 /**
845 * Search for a tail that is already encoded in the message.
846 */
847 neck = &root; /* where needle head is connected */
848 needle = root;
849
850 tail = NULL; /* tail in the haystack */
851 graft = &res->labels;
852 haystack = res->labels;
853
854 while (needle != NULL && haystack != NULL)
855 {
856 size_t nlen, hlen;
857
858 nlen = needle->buf[needle->off];
859 Assert((nlen & DNS_LABEL_PTR) == 0);
860
861 hlen = haystack->buf[haystack->off];
862 Assert((hlen & DNS_LABEL_PTR) == 0);
863
864 if ( nlen == hlen
865 && RTStrNICmp((char *)&needle->buf[needle->off+1],
866 (char *)&haystack->buf[haystack->off+1],
867 nlen) == 0)
868 {
869 neck = &needle->children;
870 needle = needle->children;
871
872 tail = haystack;
873 graft = &haystack->children;
874 haystack = haystack->children;
875 }
876 else
877 {
878 haystack = haystack->sibling;
879 }
880 }
881
882
883 /**
884 * Head contains (in reverse) the prefix that needs to be encoded
885 * and added to the haystack. Tail points to existing suffix that
886 * can be compressed to a pointer into the haystack.
887 */
888 head = *neck;
889 if (head != NULL)
890 {
891 struct label *l;
892 size_t nlen, pfxlen, pfxdst;
893
894 nlen = needle->buf[head->off]; /* last component */
895 pfxlen = head->off + 1 + nlen; /* all prefix */
896 pfxdst = res->end; /* in response buffer */
897
898 /* copy new prefix into response buffer */
899 nbytes = append_bytes(res, buf, pfxlen);
900 if (nbytes <= 0)
901 {
902 if (nbytes == 0)
903 size = 0;
904 goto out;
905 }
906
907 /* adjust labels to point to the response */
908 for (l = head; l != NULL; l = l->children)
909 {
910 l->buf = res->buf;
911 l->off += pfxdst;
912 }
913
914 *neck = NULL; /* decapitate */
915
916 l = *graft; /* graft to the labels tree */
917 *graft = head;
918 head->sibling = l;
919 }
920
921 if (tail == NULL)
922 nbytes = append_u8(res, 0);
923 else
924 nbytes = append_u16(res, RT_H2N_U16((DNS_LABEL_PTR << 8) | tail->off));
925 if (nbytes <= 0)
926 {
927 if (nbytes == 0)
928 size = 0;
929 goto out;
930 }
931
932 size = res->end - oend;
933 out:
934 if (RT_UNLIKELY(size <= 0))
935 res->end = oend;
936 free_labels(root);
937 RTMemFree(buf);
938 return size;
939}
940
941
942static ssize_t
943append_u32(struct response *res, uint32_t value)
944{
945 return append_bytes(res, (uint8_t *)&value, sizeof(value));
946}
947
948
949static ssize_t
950append_u16(struct response *res, uint16_t value)
951{
952 return append_bytes(res, (uint8_t *)&value, sizeof(value));
953}
954
955
956static ssize_t
957append_u8(struct response *res, uint8_t value)
958{
959 return append_bytes(res, &value, sizeof(value));
960}
961
962
963static ssize_t
964append_bytes(struct response *res, uint8_t *p, size_t size)
965{
966 if (check_space(res, size) == 0)
967 return 0;
968
969 memcpy(&res->buf[res->end], p, size);
970 res->end += size;
971 return size;
972}
973
974
975static ssize_t
976check_space(struct response *res, size_t size)
977{
978 if ( size > sizeof(res->buf)
979 || res->end > sizeof(res->buf) - size)
980 return 0;
981
982 return size;
983}
984
985
986static int
987get_in_addr_arpa(struct in_addr *paddr, struct label *root)
988{
989 RTNETADDRIPV4 addr;
990 struct label *l;
991 int i;
992
993 l = root;
994 if (l == NULL || labelstrcmp(l, "arpa") != 0)
995 return 0;
996
997 l = l->children;
998 if (l == NULL || labelstrcmp(l, "in-addr") != 0)
999 return 0;
1000
1001 for (i = 0; i < 4; ++i)
1002 {
1003 char buf[4];
1004 size_t llen;
1005 int rc;
1006 uint8_t octet;
1007
1008 l = l->children;
1009 if (l == NULL)
1010 return 0;
1011
1012 llen = l->buf[l->off];
1013 Assert((llen & DNS_LABEL_PTR) == 0);
1014
1015 /* valid octet values are at most 3 digits */
1016 if (llen > 3)
1017 return 0;
1018
1019 /* copy to avoid dealing with trailing bytes */
1020 memcpy(buf, &l->buf[l->off + 1], llen);
1021 buf[llen] = '\0';
1022
1023 rc = RTStrToUInt8Full(buf, 10, &octet);
1024 if (rc != VINF_SUCCESS)
1025 return 0;
1026
1027 addr.au8[i] = octet;
1028 }
1029
1030 if (l->children != NULL)
1031 return 0; /* too many components */
1032
1033 if (paddr != NULL)
1034 paddr->s_addr = addr.u;
1035
1036 return 1;
1037}
1038
1039
1040/*
1041 * Compare label with string.
1042 */
1043static int
1044labelstrcmp(struct label *l, const char *s)
1045{
1046 size_t llen;
1047
1048 llen = l->buf[l->off];
1049 Assert((llen & DNS_LABEL_PTR) == 0);
1050
1051 return RTStrNICmp((char *)&l->buf[l->off + 1], s, llen);
1052}
1053
1054
1055/*
1056 * Convert a chain of labels to a C string.
1057 *
1058 * I'd rather use a custom formatter for e.g. %R[label] , but it needs
1059 * two arguments and microsoft VC doesn't support compound literals.
1060 */
1061static void
1062strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off)
1063{
1064 size_t cb;
1065 size_t llen;
1066
1067 namebuf[0] = '\0';
1068 cb = 0;
1069
1070 llen = 0;
1071
1072 while (cb < nbuflen - 1) {
1073 llen = msg[off];
1074 if ((llen & DNS_LABEL_PTR) == DNS_LABEL_PTR)
1075 {
1076 off = ((llen & ~DNS_LABEL_PTR) << 8) | msg[off + 1];
1077 llen = msg[off];
1078 }
1079
1080 /* pointers to pointers should not happen */
1081 if ((llen & DNS_LABEL_PTR) != 0)
1082 {
1083 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, "[???]");
1084 return;
1085 }
1086
1087 if (llen == 0)
1088 {
1089 if (namebuf[0] == '\0')
1090 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1091 break;
1092 }
1093
1094 if (namebuf[0] != '\0')
1095 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1096
1097 cb += RTStrPrintf(namebuf + cb, nbuflen - cb,
1098 "%.*s", llen, (char *)&msg[off+1]);
1099 off = off + 1 + llen;
1100 }
1101}
1102
1103
1104static void
1105LogLabelsTree(const char *before, struct label *l, const char *after)
1106{
1107 size_t llen;
1108
1109 if (before != NULL)
1110 LogDbg(("%s", before));
1111
1112 if (l == NULL)
1113 {
1114 LogDbg(("NULL%s", after ? after : ""));
1115 return;
1116 }
1117
1118 if (l->children)
1119 LogDbg(("("));
1120
1121 if (l->buf != NULL)
1122 {
1123 llen = l->buf[l->off];
1124 if ((llen & DNS_LABEL_PTR) == 0)
1125 {
1126 LogDbg(("\"%.*s\"@%zu", llen, &l->buf[l->off+1], l->off));
1127 }
1128 else
1129 {
1130 LogDbg(("<invalid byte 0t%zu/0x%zf at offset %zd>",
1131 llen, llen, l->off));
1132 }
1133 }
1134 else
1135 {
1136 LogDbg(("<*>"));
1137 }
1138
1139 if (l->children)
1140 LogLabelsTree(" ", l->children, ")");
1141
1142 if (l->sibling)
1143 LogLabelsTree(" ", l->sibling, NULL);
1144
1145 if (after != NULL)
1146 LogDbg(("%s", after));
1147}
1148
1149
1150static void
1151free_labels(struct label *root)
1152{
1153 struct label TOP; /* traverse the tree with pointer reversal */
1154 struct label *b, *f;
1155
1156 if (root == NULL)
1157 return;
1158
1159 RT_ZERO(TOP);
1160
1161 b = &TOP;
1162 f = root;
1163
1164 while (f != &TOP) {
1165 if (f->children) { /* recurse left */
1166 struct label *oldf = f;
1167 struct label *newf = f->children;
1168 oldf->children = b; /* reverse the pointer */
1169 b = oldf;
1170 f = newf;
1171 }
1172 else if (f->sibling) { /* turn right */
1173 f->children = f->sibling;
1174 f->sibling = NULL;
1175 }
1176 else { /* backtrack */
1177 struct label *oldf = f; /* garbage */
1178 struct label *oldb = b;
1179 b = oldb->children;
1180 oldb->children = NULL; /* oldf, but we are g/c'ing it */
1181 f = oldb;
1182
1183 RTMemFree(oldf);
1184 }
1185 }
1186}
1187
1188#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1189static bool isDnsMappingEntryMatchOrEqual2Str(const PDNSMAPPINGENTRY pDNSMapingEntry, const char *pcszString)
1190{
1191 return ( ( pDNSMapingEntry->pszCName
1192 && !strcmp(pDNSMapingEntry->pszCName, pcszString))
1193 || ( pDNSMapingEntry->pszPattern
1194 && RTStrSimplePatternMultiMatch(pDNSMapingEntry->pszPattern, RTSTR_MAX, pcszString, RTSTR_MAX, NULL)));
1195}
1196
1197static void alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *pHostent)
1198{
1199 PDNSMAPPINGENTRY pDNSMapingEntry = NULL;
1200 bool fMatch = false;
1201 LIST_FOREACH(pDNSMapingEntry, &pData->DNSMapHead, MapList)
1202 {
1203 char **pszAlias = NULL;
1204 if (isDnsMappingEntryMatchOrEqual2Str(pDNSMapingEntry, pHostent->h_name))
1205 {
1206 fMatch = true;
1207 break;
1208 }
1209
1210 for (pszAlias = pHostent->h_aliases; *pszAlias && !fMatch; pszAlias++)
1211 {
1212 if (isDnsMappingEntryMatchOrEqual2Str(pDNSMapingEntry, *pszAlias))
1213 {
1214
1215 PDNSMAPPINGENTRY pDnsMapping = RTMemAllocZ(sizeof(DNSMAPPINGENTRY));
1216 fMatch = true;
1217 if (!pDnsMapping)
1218 {
1219 LogFunc(("Can't allocate DNSMAPPINGENTRY\n"));
1220 LogFlowFuncLeave();
1221 return;
1222 }
1223 pDnsMapping->u32IpAddress = pDNSMapingEntry->u32IpAddress;
1224 pDnsMapping->pszCName = RTStrDup(pHostent->h_name);
1225 if (!pDnsMapping->pszCName)
1226 {
1227 LogFunc(("Can't allocate enough room for %s\n", pHostent->h_name));
1228 RTMemFree(pDnsMapping);
1229 LogFlowFuncLeave();
1230 return;
1231 }
1232 LIST_INSERT_HEAD(&pData->DNSMapHead, pDnsMapping, MapList);
1233 LogRel(("NAT: User-defined mapping %s: %RTnaipv4 is registered\n",
1234 pDnsMapping->pszCName ? pDnsMapping->pszCName : pDnsMapping->pszPattern,
1235 pDnsMapping->u32IpAddress));
1236 }
1237 }
1238 if (fMatch)
1239 break;
1240 }
1241
1242 /* h_lenght is lenght of h_addr_list in bytes, so we check that we have enough space for IPv4 address */
1243 if ( fMatch
1244 && pHostent->h_length >= sizeof(uint32_t)
1245 && pDNSMapingEntry)
1246 {
1247 pHostent->h_length = 1;
1248 *(uint32_t *)pHostent->h_addr_list[0] = pDNSMapingEntry->u32IpAddress;
1249 }
1250
1251}
1252#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use