VirtualBox

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

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

NAT: don't stop parsing if we are out of buffer space for search
domains. LogRel search domains we've dropped b/c we didn't have
buffer space for them.

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