VirtualBox

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

Last change on this file since 60960 was 60960, checked in by vboxsync, 9 years ago

NAT: rewrite resolv.conf parser. The old code tried to be fancy, but
somehow manged to miss the fact that resolv.conf is line-oriented.

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