VirtualBox

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use