VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/resolv_conf_parser.c@ 91942

Last change on this file since 91942 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
Line 
1/* $Id: resolv_conf_parser.c 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * resolv_conf_parser.c - parser of resolv.conf resolver(5)
4 */
5
6/*
7 * Copyright (C) 2016-2020 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#ifdef RCP_STANDALONE
19#define IN_RING3
20#endif
21
22#ifndef LOG_GROUP
23# define LOG_GROUP LOG_GROUP_DRV_NAT
24#endif
25
26#include <iprt/assert.h>
27#include <iprt/err.h>
28#include <iprt/net.h>
29#include <iprt/string.h>
30#include <iprt/stream.h>
31#include <iprt/thread.h>
32
33#include <VBox/log.h>
34
35#ifdef RT_OS_FREEBSD
36# include <sys/socket.h>
37#endif
38
39#include <arpa/inet.h>
40
41#include "resolv_conf_parser.h"
42
43#if !defined(RCP_ACCEPT_PORT)
44# if defined(RT_OS_DARWIN)
45# define RCP_ACCEPT_PORT
46# endif
47#endif
48
49static int rcp_address_trailer(char **ppszNext, PRTNETADDR pNetAddr, RTNETADDRTYPE enmType);
50static char *getToken(char *psz, char **ppszSavePtr);
51
52#if 0
53#undef Log2
54#define Log2 LogRel
55#endif
56
57#ifdef RCP_STANDALONE
58#undef LogRel
59#define LogRel(a) RTPrintf a
60#endif
61
62
63#ifdef RCP_STANDALONE
64int main(int argc, char **argv)
65{
66 struct rcp_state state;
67 int i;
68 int rc;
69
70 rc = rcp_parse(&state, NULL);
71 if (RT_FAILURE(rc))
72 {
73 RTPrintf(">>> Failed: %Rrc\n", rc);
74 return 1;
75 }
76
77 RTPrintf(">>> Success:\n");
78
79 RTPrintf("rcps_num_nameserver = %u\n", state.rcps_num_nameserver);
80 for (i = 0; i < state.rcps_num_nameserver; ++i)
81 {
82 if (state.rcps_str_nameserver[i] == NULL)
83 LogRel((" nameserver %RTnaddr\n",
84 &state.rcps_nameserver[i]));
85 else
86 LogRel((" nameserver %RTnaddr (from \"%s\")\n",
87 &state.rcps_nameserver[i], state.rcps_str_nameserver[i]));
88 }
89
90 if (state.rcps_domain != NULL)
91 RTPrintf("domain %s\n", state.rcps_domain);
92
93 RTPrintf("rcps_num_searchlist = %u\n", state.rcps_num_searchlist);
94 for (i = 0; i < state.rcps_num_searchlist; ++i)
95 {
96 RTPrintf("... %s\n", state.rcps_searchlist[i] ? state.rcps_searchlist[i] : "(null)");
97 }
98
99 return 0;
100}
101#endif
102
103
104int rcp_parse(struct rcp_state *state, const char *filename)
105{
106 PRTSTREAM stream;
107# define RCP_BUFFER_SIZE 256
108 char buf[RCP_BUFFER_SIZE];
109 char *pszAddrBuf;
110 size_t cbAddrBuf;
111 char *pszSearchBuf;
112 size_t cbSearchBuf;
113 uint32_t flags;
114#ifdef RCP_ACCEPT_PORT /* OS X extention */
115 uint32_t default_port = RTNETADDR_PORT_NA;
116#endif
117 unsigned i;
118 int rc;
119
120 AssertPtrReturn(state, VERR_INVALID_PARAMETER);
121 flags = state->rcps_flags;
122
123 RT_ZERO(*state);
124 state->rcps_flags = flags;
125
126 if (RT_UNLIKELY(filename == NULL))
127 {
128#ifdef RCP_STANDALONE
129 stream = g_pStdIn; /* for testing/debugging */
130#else
131 return VERR_INVALID_PARAMETER;
132#endif
133 }
134 else
135 {
136 rc = RTStrmOpen(filename, "r", &stream);
137 if (RT_FAILURE(rc))
138 return rc;
139 }
140
141
142 pszAddrBuf = state->rcps_nameserver_str_buffer;
143 cbAddrBuf = sizeof(state->rcps_nameserver_str_buffer);
144
145 pszSearchBuf = state->rcps_searchlist_buffer;
146 cbSearchBuf = sizeof(state->rcps_searchlist_buffer);
147
148 for (;;)
149 {
150 char *s, *tok;
151
152 rc = RTStrmGetLine(stream, buf, sizeof(buf));
153 if (RT_FAILURE(rc))
154 {
155 if (rc == VERR_EOF)
156 rc = VINF_SUCCESS;
157 break;
158 }
159
160 /*
161 * Strip comment if present.
162 *
163 * This is not how ad-hoc parser in bind's res_init.c does it,
164 * btw, so this code will accept more input as valid compared
165 * to res_init. (e.g. "nameserver 1.1.1.1; comment" is
166 * misparsed by res_init).
167 */
168 for (s = buf; *s != '\0'; ++s)
169 {
170 if (*s == '#' || *s == ';')
171 {
172 *s = '\0';
173 break;
174 }
175 }
176
177 tok = getToken(buf, &s);
178 if (tok == NULL)
179 continue;
180
181
182 /*
183 * NAMESERVER
184 */
185 if (RTStrCmp(tok, "nameserver") == 0)
186 {
187 RTNETADDR NetAddr;
188 const char *pszAddr;
189 char *pszNext;
190
191 if (RT_UNLIKELY(state->rcps_num_nameserver >= RCPS_MAX_NAMESERVERS))
192 {
193 LogRel(("NAT: resolv.conf: too many nameserver lines, ignoring %s\n", s));
194 continue;
195 }
196
197 /* XXX: TODO: don't save strings unless asked to */
198 if (RT_UNLIKELY(cbAddrBuf == 0))
199 {
200 LogRel(("NAT: resolv.conf: no buffer space, ignoring %s\n", s));
201 continue;
202 }
203
204
205 /*
206 * parse next token as an IP address
207 */
208 tok = getToken(NULL, &s);
209 if (tok == NULL)
210 {
211 LogRel(("NAT: resolv.conf: nameserver line without value\n"));
212 continue;
213 }
214
215 pszAddr = tok;
216 RT_ZERO(NetAddr);
217 NetAddr.uPort = RTNETADDR_PORT_NA;
218
219 /* if (NetAddr.enmType == RTNETADDRTYPE_INVALID) */
220 {
221 rc = RTNetStrToIPv4AddrEx(tok, &NetAddr.uAddr.IPv4, &pszNext);
222 if (RT_SUCCESS(rc))
223 {
224 rc = rcp_address_trailer(&pszNext, &NetAddr, RTNETADDRTYPE_IPV4);
225 if (RT_FAILURE(rc))
226 {
227 LogRel(("NAT: resolv.conf: garbage at the end of IPv4 address %s\n", tok));
228 continue;
229 }
230
231 LogRel(("NAT: resolv.conf: nameserver %RTnaddr\n", &NetAddr));
232 }
233 } /* IPv4 */
234
235 if (NetAddr.enmType == RTNETADDRTYPE_INVALID)
236 {
237 rc = RTNetStrToIPv6AddrEx(tok, &NetAddr.uAddr.IPv6, &pszNext);
238 if (RT_SUCCESS(rc))
239 {
240 if (*pszNext == '%') /* XXX: TODO: IPv6 zones */
241 {
242 size_t zlen = RTStrOffCharOrTerm(pszNext, '.');
243 LogRel(("NAT: resolv.conf: FIXME: ignoring IPv6 zone %*.*s\n",
244 zlen, zlen, pszNext));
245 pszNext += zlen;
246 }
247
248 rc = rcp_address_trailer(&pszNext, &NetAddr, RTNETADDRTYPE_IPV6);
249 if (RT_FAILURE(rc))
250 {
251 LogRel(("NAT: resolv.conf: garbage at the end of IPv6 address %s\n", tok));
252 continue;
253 }
254
255 LogRel(("NAT: resolv.conf: nameserver %RTnaddr\n", &NetAddr));
256 }
257 } /* IPv6 */
258
259 if (NetAddr.enmType == RTNETADDRTYPE_INVALID)
260 {
261 LogRel(("NAT: resolv.conf: bad nameserver address %s\n", tok));
262 continue;
263 }
264
265
266 tok = getToken(NULL, &s);
267 if (tok != NULL)
268 LogRel(("NAT: resolv.conf: ignoring unexpected trailer on the nameserver line\n"));
269
270 if ((flags & RCPSF_IGNORE_IPV6) && NetAddr.enmType == RTNETADDRTYPE_IPV6)
271 {
272 Log2(("NAT: resolv.conf: IPv6 address ignored\n"));
273 continue;
274 }
275
276 /* seems ok, save it */
277 {
278 i = state->rcps_num_nameserver;
279
280 state->rcps_nameserver[i] = NetAddr;
281
282 /* XXX: TODO: don't save strings unless asked to */
283 Log2(("NAT: resolv.conf: saving address @%td,+%zu\n",
284 pszAddrBuf - state->rcps_nameserver_str_buffer, cbAddrBuf));
285 state->rcps_str_nameserver[i] = pszAddrBuf;
286 rc = RTStrCopyP(&pszAddrBuf, &cbAddrBuf, pszAddr);
287 if (RT_SUCCESS(rc))
288 {
289 ++pszAddrBuf; /* skip '\0' */
290 if (cbAddrBuf > 0) /* on overflow we get 1 (for the '\0'), but be defensive */
291 --cbAddrBuf;
292 ++state->rcps_num_nameserver;
293 }
294 else
295 {
296 Log2(("NAT: resolv.conf: ... truncated\n"));
297 }
298 }
299
300 continue;
301 }
302
303
304#ifdef RCP_ACCEPT_PORT /* OS X extention */
305 /*
306 * PORT
307 */
308 if (RTStrCmp(tok, "port") == 0)
309 {
310 uint16_t port;
311
312 if (default_port != RTNETADDR_PORT_NA)
313 {
314 LogRel(("NAT: resolv.conf: ignoring multiple port lines\n"));
315 continue;
316 }
317
318 tok = getToken(NULL, &s);
319 if (tok == NULL)
320 {
321 LogRel(("NAT: resolv.conf: port line without value\n"));
322 continue;
323 }
324
325 rc = RTStrToUInt16Full(tok, 10, &port);
326 if (RT_SUCCESS(rc))
327 {
328 if (port != 0)
329 default_port = port;
330 else
331 LogRel(("NAT: resolv.conf: port 0 is invalid\n"));
332 }
333
334 continue;
335 }
336#endif
337
338
339 /*
340 * DOMAIN
341 */
342 if (RTStrCmp(tok, "domain") == 0)
343 {
344 if (state->rcps_domain != NULL)
345 {
346 LogRel(("NAT: resolv.conf: ignoring multiple domain lines\n"));
347 continue;
348 }
349
350 tok = getToken(NULL, &s);
351 if (tok == NULL)
352 {
353 LogRel(("NAT: resolv.conf: domain line without value\n"));
354 continue;
355 }
356
357 rc = RTStrCopy(state->rcps_domain_buffer, sizeof(state->rcps_domain_buffer), tok);
358 if (RT_SUCCESS(rc))
359 {
360 state->rcps_domain = state->rcps_domain_buffer;
361 }
362 else
363 {
364 LogRel(("NAT: resolv.conf: domain name too long\n"));
365 RT_ZERO(state->rcps_domain_buffer);
366 }
367
368 continue;
369 }
370
371
372 /*
373 * SEARCH
374 */
375 if (RTStrCmp(tok, "search") == 0)
376 {
377 while ((tok = getToken(NULL, &s)) && tok != NULL)
378 {
379 i = state->rcps_num_searchlist;
380 if (RT_UNLIKELY(i >= RCPS_MAX_SEARCHLIST))
381 {
382 LogRel(("NAT: resolv.conf: too many search domains, ignoring %s\n", tok));
383 continue;
384 }
385
386 Log2(("NAT: resolv.conf: saving search %s @%td,+%zu\n",
387 tok, pszSearchBuf - state->rcps_searchlist_buffer, cbSearchBuf));
388 state->rcps_searchlist[i] = pszSearchBuf;
389 rc = RTStrCopyP(&pszSearchBuf, &cbSearchBuf, tok);
390 if (RT_SUCCESS(rc))
391 {
392 ++pszSearchBuf; /* skip '\0' */
393 if (cbSearchBuf > 0) /* on overflow we get 1 (for the '\0'), but be defensive */
394 --cbSearchBuf;
395 ++state->rcps_num_searchlist;
396 }
397 else
398 {
399 LogRel(("NAT: resolv.conf: no buffer space, ignoring search domain %s\n", tok));
400 pszSearchBuf = state->rcps_searchlist[i];
401 cbSearchBuf = sizeof(state->rcps_searchlist_buffer)
402 - (pszSearchBuf - state->rcps_searchlist_buffer);
403 Log2(("NAT: resolv.conf: backtracking to @%td,+%zu\n",
404 pszSearchBuf - state->rcps_searchlist_buffer, cbSearchBuf));
405 }
406 }
407
408 continue;
409 }
410
411
412 LogRel(("NAT: resolv.conf: ignoring \"%s %s\"\n", tok, s));
413 }
414
415 if (filename != NULL)
416 RTStrmClose(stream);
417
418 if (RT_FAILURE(rc))
419 return rc;
420
421
422 /* XXX: I don't like that OS X would return a different result here */
423#ifdef RCP_ACCEPT_PORT /* OS X extention */
424 if (default_port == RTNETADDR_PORT_NA)
425 default_port = 53;
426
427 for (i = 0; i < state->rcps_num_nameserver; ++i)
428 {
429 RTNETADDR *addr = &state->rcps_nameserver[i];
430 if (addr->uPort == RTNETADDR_PORT_NA || addr->uPort == 0)
431 addr->uPort = (uint16_t)default_port;
432 }
433#endif
434
435 if ( state->rcps_domain == NULL
436 && state->rcps_num_searchlist > 0)
437 {
438 state->rcps_domain = state->rcps_searchlist[0];
439 }
440
441 return VINF_SUCCESS;
442}
443
444
445static int
446rcp_address_trailer(char **ppszNext, PRTNETADDR pNetAddr, RTNETADDRTYPE enmType)
447{
448 char *pszNext = *ppszNext;
449 int rc = VINF_SUCCESS;
450
451 if (*pszNext == '\0')
452 {
453 pNetAddr->enmType = enmType;
454 rc = VINF_SUCCESS;
455 }
456#ifdef RCP_ACCEPT_PORT /* OS X extention */
457 else if (*pszNext == '.')
458 {
459 uint16_t port;
460
461 rc = RTStrToUInt16Ex(++pszNext, NULL, 10, &port);
462 if (RT_SUCCESS(rc))
463 {
464 pNetAddr->enmType = enmType;
465 pNetAddr->uPort = port;
466 }
467 }
468#endif
469 else
470 {
471 rc = VERR_TRAILING_CHARS;
472 }
473
474 return rc;
475}
476
477
478static char *getToken(char *psz, char **ppszSavePtr)
479{
480 char *pszToken;
481
482 AssertPtrReturn(ppszSavePtr, NULL);
483
484 if (psz == NULL)
485 {
486 psz = *ppszSavePtr;
487 if (psz == NULL)
488 return NULL;
489 }
490
491 while (*psz == ' ' || *psz == '\t')
492 ++psz;
493
494 if (*psz == '\0')
495 {
496 *ppszSavePtr = NULL;
497 return NULL;
498 }
499
500 pszToken = psz;
501 while (*psz && *psz != ' ' && *psz != '\t')
502 ++psz;
503
504 if (*psz == '\0')
505 psz = NULL;
506 else
507 *psz++ = '\0';
508
509 *ppszSavePtr = psz;
510 return pszToken;
511}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette