VirtualBox

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

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

NAT: Improve dns mapping in host resolver - pattern match should
ignore case; make literal match take priority over pattern match if
both are applicable.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.2 KB
Line 
1/* $Id: hostres.c 59219 2015-12-24 22:45:49Z 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 *h);
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 if (h->h_length != sizeof(RTNETADDRIPV4))
480 {
481 /* Log: what kind of address did we get?! */
482 goto out;
483 }
484
485 if ( h->h_addr_list == NULL
486 || h->h_addr_list[0] == NULL)
487 {
488 /* Log: shouldn't happen */
489 goto out;
490 }
491
492#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
493 alterHostentWithDataFromDNSMap(pData, h);
494#endif
495
496 /*
497 * Emit CNAME record if canonical name differs from the qname.
498 */
499 if ( h->h_name != NULL
500 && RTStrICmp(h->h_name, name) != 0)
501 {
502 LogDbg(("NAT: hostres: %s CNAME %s\n", name, h->h_name));
503 nbytes = append_cname(res, name, h->h_name);
504 if (nbytes > 0)
505 {
506 ++nanswers;
507 }
508 else
509 {
510 LogErr(("NAT: hostres: failed to add %s CNAME %s\n",
511 name, h->h_name));
512 if (nbytes < 0)
513 return refuse(pData, m, RCode_ServFail);
514 else
515 {
516 pHdr->tc = 1;
517 goto out;
518 }
519 }
520
521 /*
522 * rfc1034#section-3.6.2 - ... a type CNAME or * query should
523 * return just the CNAME.
524 */
525 if (qtype == Type_CNAME || qtype == Type_ANY)
526 goto out;
527 }
528 else if (qtype == Type_CNAME)
529 {
530 LogErr(("NAT: hostres: %s is already canonical\n", name));
531 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
532 }
533
534 /*
535 * Emit A records.
536 */
537 for (i = 0; h->h_addr_list[i] != NULL; ++i)
538 {
539 const char *cname = h->h_name ? h->h_name : name;
540 struct in_addr addr;
541
542 addr.s_addr = *(uint32_t *)h->h_addr_list[i];
543 nbytes = append_a(res, cname, addr);
544
545 if (nbytes > 0)
546 {
547 ++nanswers;
548 }
549 else
550 {
551 LogErr(("NAT: hostres: failed to add %s A %RTnaipv4\n",
552 cname, addr.s_addr));
553 if (nbytes < 0)
554 return refuse(pData, m, RCode_ServFail);
555 else
556 {
557 pHdr->tc = 1;
558 goto out;
559 }
560 }
561 }
562
563#if 0
564 /*
565 * It's not clear what to do with h_aliases.
566 *
567 * For names from the DNS it seems to contain the chain of CNAMEs,
568 * starting with the original qname from the question. So for
569 * them we'd need to reply with a chain of:
570 *
571 * h_aliases[i] CNAME h_aliases[i+1]
572 *
573 * OTOH, for the names from the hosts file it seems to contain all
574 * the names except the first one (which is considered primary and
575 * is reported as h_name). In which case the reply should be:
576 *
577 * h_aliases[i] CNAME h_name
578 *
579 * Obviously, we have no idea how the name was resolved, so we
580 * generate at most one CNAME for h_host (if differs) and ignore
581 * aliases altogehter.
582 */
583 for (i = 0; h->h_aliases[i] != NULL; ++i)
584 {
585 LogDbg(("NAT: hostres: ... %s\n", h->h_aliases[i]));
586 }
587#endif
588
589 out:
590 if (nanswers > 0)
591 {
592 int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
593 if (!ok)
594 {
595 /* XXX: this may fail part way: restore old lenght, clear TC? */
596 return refuse(pData, m, RCode_ServFail);
597 }
598 pHdr->ancount = RT_H2N_U16(nanswers);
599 }
600 memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
601 return m;
602}
603
604
605struct mbuf *
606resolve_reverse(PNATState pData, struct mbuf *m, struct response *res,
607 uint16_t qtype, size_t qname, struct in_addr in_addr_arpa)
608{
609 struct dnsmsg_header *pHdr;
610 struct hostent *h;
611 size_t oend;
612 size_t nanswers;
613 ssize_t nbytes;
614 int i;
615
616 pHdr = (struct dnsmsg_header *)res->buf;
617 nanswers = 0;
618 oend = res->end;
619
620 LogDbg(("NAT: hostres: %RTnaipv4\n", in_addr_arpa.s_addr));
621
622 if (qtype != Type_PTR && qtype != Type_ANY)
623 {
624 /* can't answer CNAME to PTR queries using gethostby* */
625 goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
626 }
627
628 /* XXX: TODO: apply HostResolverMappings */
629 h = gethostbyaddr(&in_addr_arpa, sizeof(struct in_addr), AF_INET);
630 if (h == NULL)
631 {
632 /* LogErr: h_errno */
633 return refuse(pData, m, RCode_NXDomain);
634 }
635
636 if (h->h_name != NULL)
637 {
638 char name[DNS_MAX_NAME_LEN+1];
639 strnlabels(name, sizeof(name), res->buf, qname);
640
641 LogDbg(("NAT: hostres: %s PTR %s\n", name, h->h_name));
642 nbytes = append_ptr(res, name, h->h_name);
643 if (nbytes > 0)
644 {
645 ++nanswers;
646 }
647 else
648 {
649 LogErr(("NAT: hostres: failed to add %s PTR %s\n",
650 name, h->h_name));
651 if (nbytes < 0)
652 return refuse(pData, m, RCode_ServFail);
653 else
654 {
655 pHdr->tc = 1;
656 goto out;
657 }
658 }
659 }
660
661 out:
662 if (nanswers > 0)
663 {
664 int ok = m_append(pData, m, res->end - oend, (caddr_t)&res->buf[oend]);
665 if (!ok)
666 {
667 /* XXX: this may fail part way: restore old lenght, clear TC? */
668 return refuse(pData, m, RCode_ServFail);
669 }
670 pHdr->ancount = RT_H2N_U16(nanswers);
671 }
672 memcpy(mtod(m, char *), res->buf, sizeof(struct dnsmsg_header));
673 return m;
674}
675
676
677
678#define APPEND_PROLOGUE() \
679 ssize_t size = -1; \
680 size_t oend = res->end; \
681 ssize_t nbytes; \
682 do {} while (0)
683
684#define CHECKED(_append) \
685 do { \
686 nbytes = (_append); \
687 if (RT_UNLIKELY(nbytes <= 0)) \
688 { \
689 if (nbytes == 0) \
690 size = 0; \
691 goto out; \
692 } \
693 } while (0)
694
695#define APPEND_EPILOGUE() \
696 do { \
697 size = res->end - oend; \
698 out: \
699 if (RT_UNLIKELY(size <= 0)) \
700 res->end = oend; \
701 return size; \
702 } while (0)
703
704
705/*
706 * A RR - rfc1035#section-3.4.1
707 */
708static ssize_t
709append_a(struct response *res, const char *name, struct in_addr addr)
710{
711 APPEND_PROLOGUE();
712
713 CHECKED( append_rrhdr(res, name, Type_A, 3600) );
714 CHECKED( append_u16(res, RT_H2N_U16_C(sizeof(addr))) );
715 CHECKED( append_u32(res, addr.s_addr) );
716
717 APPEND_EPILOGUE();
718}
719
720
721/*
722 * CNAME RR - rfc1035#section-3.3.1
723 */
724static ssize_t
725append_cname(struct response *res, const char *name, const char *cname)
726{
727 return append_name_rr(res, name, Type_CNAME, cname);
728}
729
730
731/*
732 * PTR RR - rfc1035#section-3.3.12
733 */
734static ssize_t
735append_ptr(struct response *res, const char *inaddrname, const char *name)
736{
737 return append_name_rr(res, inaddrname, Type_PTR, name);
738}
739
740
741static ssize_t
742append_name_rr(struct response *res, const char *question,
743 int type, const char *answer)
744{
745 size_t rdlpos;
746 uint16_t rdlength;
747
748 APPEND_PROLOGUE();
749
750 CHECKED( append_rrhdr(res, question, type, 3600) );
751
752 rdlpos = res->end;
753 CHECKED( append_u16(res, 0) ); /* RDLENGTH placeholder */
754
755 CHECKED( append_name(res, answer) );
756
757 rdlength = RT_H2N_U16(nbytes);
758 memcpy(&res->buf[rdlpos], &rdlength, sizeof(rdlength));
759
760 APPEND_EPILOGUE();
761}
762
763
764/*
765 * Append common RR header, up to but not including RDLENGTH and RDATA
766 * proper (rfc1035#section-3.2.1).
767 */
768static ssize_t
769append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl)
770{
771 APPEND_PROLOGUE();
772
773 CHECKED( append_name(res, name) );
774 CHECKED( append_u16(res, RT_H2N_U16(type)) );
775 CHECKED( append_u16(res, RT_H2N_U16_C(Class_IN)) );
776 CHECKED( append_u32(res, RT_H2N_U32(ttl)) );
777
778 APPEND_EPILOGUE();
779}
780
781
782static ssize_t
783append_name(struct response *res, const char *name)
784{
785 ssize_t size, nbytes;
786 struct label *root;
787 struct label *haystack, *needle;
788 struct label *head, **neck;
789 struct label *tail, **graft;
790 uint8_t *buf;
791 size_t wr, oend;
792 const char *s;
793
794 size = -1;
795 oend = res->end;
796
797 /**
798 * Split new name into a list of labels encoding it into the
799 * temporary buffer.
800 */
801 root = NULL;
802
803 buf = RTMemAllocZ(strlen(name) + 1);
804 if (buf == NULL)
805 return -1;
806 wr = 0;
807
808 s = name;
809 while (*s != '\0') {
810 const char *part;
811 size_t poff, plen;
812 struct label *l;
813
814 part = s;
815 while (*s != '\0' && *s != '.')
816 ++s;
817
818 plen = s - part;
819
820 if (plen > DNS_MAX_LABEL_LEN)
821 {
822 LogErr(("NAT: hostres: name component too long\n"));
823 goto out;
824 }
825
826 if (*s == '.')
827 {
828 if (plen == 0)
829 {
830 LogErr(("NAT: hostres: empty name component\n"));
831 goto out;
832 }
833
834 ++s;
835 }
836
837 poff = wr;
838
839 buf[poff] = (uint8_t)plen; /* length byte */
840 ++wr;
841
842 memcpy(&buf[wr], part, plen); /* label text */
843 wr += plen;
844
845 l = RTMemAllocZ(sizeof(*l));
846 if (l == NULL)
847 goto out;
848
849 l->buf = buf;
850 l->off = poff;
851 l->children = root;
852 root = l;
853 }
854
855
856 /**
857 * Search for a tail that is already encoded in the message.
858 */
859 neck = &root; /* where needle head is connected */
860 needle = root;
861
862 tail = NULL; /* tail in the haystack */
863 graft = &res->labels;
864 haystack = res->labels;
865
866 while (needle != NULL && haystack != NULL)
867 {
868 size_t nlen, hlen;
869
870 nlen = needle->buf[needle->off];
871 Assert((nlen & DNS_LABEL_PTR) == 0);
872
873 hlen = haystack->buf[haystack->off];
874 Assert((hlen & DNS_LABEL_PTR) == 0);
875
876 if ( nlen == hlen
877 && RTStrNICmp((char *)&needle->buf[needle->off+1],
878 (char *)&haystack->buf[haystack->off+1],
879 nlen) == 0)
880 {
881 neck = &needle->children;
882 needle = needle->children;
883
884 tail = haystack;
885 graft = &haystack->children;
886 haystack = haystack->children;
887 }
888 else
889 {
890 haystack = haystack->sibling;
891 }
892 }
893
894
895 /**
896 * Head contains (in reverse) the prefix that needs to be encoded
897 * and added to the haystack. Tail points to existing suffix that
898 * can be compressed to a pointer into the haystack.
899 */
900 head = *neck;
901 if (head != NULL)
902 {
903 struct label *l;
904 size_t nlen, pfxlen, pfxdst;
905
906 nlen = needle->buf[head->off]; /* last component */
907 pfxlen = head->off + 1 + nlen; /* all prefix */
908 pfxdst = res->end; /* in response buffer */
909
910 /* copy new prefix into response buffer */
911 nbytes = append_bytes(res, buf, pfxlen);
912 if (nbytes <= 0)
913 {
914 if (nbytes == 0)
915 size = 0;
916 goto out;
917 }
918
919 /* adjust labels to point to the response */
920 for (l = head; l != NULL; l = l->children)
921 {
922 l->buf = res->buf;
923 l->off += pfxdst;
924 }
925
926 *neck = NULL; /* decapitate */
927
928 l = *graft; /* graft to the labels tree */
929 *graft = head;
930 head->sibling = l;
931 }
932
933 if (tail == NULL)
934 nbytes = append_u8(res, 0);
935 else
936 nbytes = append_u16(res, RT_H2N_U16((DNS_LABEL_PTR << 8) | tail->off));
937 if (nbytes <= 0)
938 {
939 if (nbytes == 0)
940 size = 0;
941 goto out;
942 }
943
944 size = res->end - oend;
945 out:
946 if (RT_UNLIKELY(size <= 0))
947 res->end = oend;
948 free_labels(root);
949 RTMemFree(buf);
950 return size;
951}
952
953
954static ssize_t
955append_u32(struct response *res, uint32_t value)
956{
957 return append_bytes(res, (uint8_t *)&value, sizeof(value));
958}
959
960
961static ssize_t
962append_u16(struct response *res, uint16_t value)
963{
964 return append_bytes(res, (uint8_t *)&value, sizeof(value));
965}
966
967
968static ssize_t
969append_u8(struct response *res, uint8_t value)
970{
971 return append_bytes(res, &value, sizeof(value));
972}
973
974
975static ssize_t
976append_bytes(struct response *res, uint8_t *p, size_t size)
977{
978 if (check_space(res, size) == 0)
979 return 0;
980
981 memcpy(&res->buf[res->end], p, size);
982 res->end += size;
983 return size;
984}
985
986
987static ssize_t
988check_space(struct response *res, size_t size)
989{
990 if ( size > sizeof(res->buf)
991 || res->end > sizeof(res->buf) - size)
992 return 0;
993
994 return size;
995}
996
997
998static int
999get_in_addr_arpa(struct in_addr *paddr, struct label *root)
1000{
1001 RTNETADDRIPV4 addr;
1002 struct label *l;
1003 int i;
1004
1005 l = root;
1006 if (l == NULL || labelstrcmp(l, "arpa") != 0)
1007 return 0;
1008
1009 l = l->children;
1010 if (l == NULL || labelstrcmp(l, "in-addr") != 0)
1011 return 0;
1012
1013 for (i = 0; i < 4; ++i)
1014 {
1015 char buf[4];
1016 size_t llen;
1017 int rc;
1018 uint8_t octet;
1019
1020 l = l->children;
1021 if (l == NULL)
1022 return 0;
1023
1024 llen = l->buf[l->off];
1025 Assert((llen & DNS_LABEL_PTR) == 0);
1026
1027 /* valid octet values are at most 3 digits */
1028 if (llen > 3)
1029 return 0;
1030
1031 /* copy to avoid dealing with trailing bytes */
1032 memcpy(buf, &l->buf[l->off + 1], llen);
1033 buf[llen] = '\0';
1034
1035 rc = RTStrToUInt8Full(buf, 10, &octet);
1036 if (rc != VINF_SUCCESS)
1037 return 0;
1038
1039 addr.au8[i] = octet;
1040 }
1041
1042 if (l->children != NULL)
1043 return 0; /* too many components */
1044
1045 if (paddr != NULL)
1046 paddr->s_addr = addr.u;
1047
1048 return 1;
1049}
1050
1051
1052/*
1053 * Compare label with string.
1054 */
1055static int
1056labelstrcmp(struct label *l, const char *s)
1057{
1058 size_t llen;
1059
1060 llen = l->buf[l->off];
1061 Assert((llen & DNS_LABEL_PTR) == 0);
1062
1063 return RTStrNICmp((char *)&l->buf[l->off + 1], s, llen);
1064}
1065
1066
1067/*
1068 * Convert a chain of labels to a C string.
1069 *
1070 * I'd rather use a custom formatter for e.g. %R[label] , but it needs
1071 * two arguments and microsoft VC doesn't support compound literals.
1072 */
1073static void
1074strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off)
1075{
1076 size_t cb;
1077 size_t llen;
1078
1079 namebuf[0] = '\0';
1080 cb = 0;
1081
1082 llen = 0;
1083
1084 while (cb < nbuflen - 1) {
1085 llen = msg[off];
1086 if ((llen & DNS_LABEL_PTR) == DNS_LABEL_PTR)
1087 {
1088 off = ((llen & ~DNS_LABEL_PTR) << 8) | msg[off + 1];
1089 llen = msg[off];
1090 }
1091
1092 /* pointers to pointers should not happen */
1093 if ((llen & DNS_LABEL_PTR) != 0)
1094 {
1095 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, "[???]");
1096 return;
1097 }
1098
1099 if (llen == 0)
1100 {
1101 if (namebuf[0] == '\0')
1102 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1103 break;
1104 }
1105
1106 if (namebuf[0] != '\0')
1107 cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
1108
1109 cb += RTStrPrintf(namebuf + cb, nbuflen - cb,
1110 "%.*s", llen, (char *)&msg[off+1]);
1111 off = off + 1 + llen;
1112 }
1113}
1114
1115
1116static void
1117LogLabelsTree(const char *before, struct label *l, const char *after)
1118{
1119 size_t llen;
1120
1121 if (before != NULL)
1122 LogDbg(("%s", before));
1123
1124 if (l == NULL)
1125 {
1126 LogDbg(("NULL%s", after ? after : ""));
1127 return;
1128 }
1129
1130 if (l->children)
1131 LogDbg(("("));
1132
1133 if (l->buf != NULL)
1134 {
1135 llen = l->buf[l->off];
1136 if ((llen & DNS_LABEL_PTR) == 0)
1137 {
1138 LogDbg(("\"%.*s\"@%zu", llen, &l->buf[l->off+1], l->off));
1139 }
1140 else
1141 {
1142 LogDbg(("<invalid byte 0t%zu/0x%zf at offset %zd>",
1143 llen, llen, l->off));
1144 }
1145 }
1146 else
1147 {
1148 LogDbg(("<*>"));
1149 }
1150
1151 if (l->children)
1152 LogLabelsTree(" ", l->children, ")");
1153
1154 if (l->sibling)
1155 LogLabelsTree(" ", l->sibling, NULL);
1156
1157 if (after != NULL)
1158 LogDbg(("%s", after));
1159}
1160
1161
1162static void
1163free_labels(struct label *root)
1164{
1165 struct label TOP; /* traverse the tree with pointer reversal */
1166 struct label *b, *f;
1167
1168 if (root == NULL)
1169 return;
1170
1171 RT_ZERO(TOP);
1172
1173 b = &TOP;
1174 f = root;
1175
1176 while (f != &TOP) {
1177 if (f->children) { /* recurse left */
1178 struct label *oldf = f;
1179 struct label *newf = f->children;
1180 oldf->children = b; /* reverse the pointer */
1181 b = oldf;
1182 f = newf;
1183 }
1184 else if (f->sibling) { /* turn right */
1185 f->children = f->sibling;
1186 f->sibling = NULL;
1187 }
1188 else { /* backtrack */
1189 struct label *oldf = f; /* garbage */
1190 struct label *oldb = b;
1191 b = oldb->children;
1192 oldb->children = NULL; /* oldf, but we are g/c'ing it */
1193 f = oldb;
1194
1195 RTMemFree(oldf);
1196 }
1197 }
1198}
1199
1200#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1201void
1202slirp_add_host_resolver_mapping(PNATState pData,
1203 const char *pszHostName, bool fPattern,
1204 uint32_t u32HostIP)
1205{
1206 LogRel(("ENTER: pszHostName:%s%s, u32HostIP:%RTnaipv4\n",
1207 pszHostName ? pszHostName : "(null)",
1208 fPattern ? " (pattern)" : "",
1209 u32HostIP));
1210
1211 if ( pszHostName != NULL
1212 && u32HostIP != INADDR_ANY
1213 && u32HostIP != INADDR_BROADCAST)
1214 {
1215 PDNSMAPPINGENTRY pDnsMapping = RTMemAllocZ(sizeof(DNSMAPPINGENTRY));
1216 if (!pDnsMapping)
1217 {
1218 LogFunc(("Can't allocate DNSMAPPINGENTRY\n"));
1219 LogFlowFuncLeave();
1220 return;
1221 }
1222
1223 pDnsMapping->u32IpAddress = u32HostIP;
1224 pDnsMapping->fPattern = fPattern;
1225 pDnsMapping->pszName = RTStrDup(pszHostName);
1226
1227 if (pDnsMapping->pszName == NULL)
1228 {
1229 LogFunc(("Can't allocate enough room for host name\n"));
1230 RTMemFree(pDnsMapping);
1231 LogFlowFuncLeave();
1232 return;
1233 }
1234
1235 if (fPattern) /* there's no case-insensitive pattern-match function */
1236 RTStrToLower(pDnsMapping->pszName);
1237
1238 STAILQ_INSERT_TAIL(fPattern ? &pData->DNSMapPatterns : &pData->DNSMapNames,
1239 pDnsMapping, MapList);
1240
1241 LogRel(("NAT: User-defined mapping %s%s = %RTnaipv4 is registered\n",
1242 pDnsMapping->pszName,
1243 pDnsMapping->fPattern ? " (pattern)" : "",
1244 pDnsMapping->u32IpAddress));
1245 }
1246 LogFlowFuncLeave();
1247}
1248
1249
1250static void
1251alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h)
1252{
1253 PDNSMAPPINGENTRY pDNSMapingEntry = NULL;
1254 char **pszAlias;
1255
1256 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapNames, MapList)
1257 {
1258 Assert(!pDNSMapingEntry->fPattern);
1259
1260 if (RTStrICmp(pDNSMapingEntry->pszName, h->h_name) == 0)
1261 goto done;
1262
1263 for (pszAlias = h->h_aliases; *pszAlias != NULL; ++pszAlias)
1264 {
1265 if (RTStrICmp(pDNSMapingEntry->pszName, *pszAlias) == 0)
1266 goto done;
1267 }
1268 }
1269
1270
1271# define MATCH(_pattern, _string) \
1272 (RTStrSimplePatternMultiMatch((_pattern), RTSTR_MAX, (_string), RTSTR_MAX, NULL))
1273
1274 STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapPatterns, MapList)
1275 {
1276 RTStrToLower(h->h_name);
1277 if (MATCH(pDNSMapingEntry->pszName, h->h_name))
1278 goto done;
1279
1280 for (pszAlias = h->h_aliases; *pszAlias != NULL; ++pszAlias)
1281 {
1282 RTStrToLower(*pszAlias);
1283 if (MATCH(pDNSMapingEntry->pszName, h->h_name))
1284 goto done;
1285 }
1286 }
1287
1288 done:
1289 if (pDNSMapingEntry != NULL)
1290 {
1291 *(uint32_t *)h->h_addr_list[0] = pDNSMapingEntry->u32IpAddress;
1292 h->h_addr_list[1] = NULL;
1293 }
1294}
1295#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use