VirtualBox

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

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

NAT: If a query to the host resolver has anything except single
question, reply with RCODE NotImp, not FormErr.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use