VirtualBox

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

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

NAT: Don't exceed RCPS_MAX_SEARCHLIST. Patch from ticketref:15948.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: resolv_conf_parser.c 63876 2016-09-19 10:41:38Z 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 if (cbSearchBuf == 0)
365 {
366 LogRel(("NAT: resolv.conf: no buffer space, ignoring search list %s\n", s));
367 break;
368 }
369
370 while ((tok = getToken(NULL, &s)) && !NO_VALUE(tok))
371 {
372 i = state->rcps_num_searchlist;
373 if (RT_UNLIKELY(i >= RCPS_MAX_SEARCHLIST))
374 {
375 LogRel(("NAT: resolv.conf: too many search domains, ignoring %s\n", tok));
376 continue;
377 }
378
379 Log2(("NAT: resolv.conf: saving search @%td,+%zu\n",
380 pszSearchBuf - state->rcps_searchlist_buffer, cbSearchBuf));
381 state->rcps_searchlist[i] = pszSearchBuf;
382 rc = RTStrCopyP(&pszSearchBuf, &cbSearchBuf, tok);
383 if (RT_SUCCESS(rc))
384 {
385 ++pszSearchBuf; /* skip '\0' */
386 if (cbSearchBuf > 0) /* on overflow we get 1 (for the '\0'), but be defensive */
387 --cbSearchBuf;
388 ++state->rcps_num_searchlist;
389 }
390 else
391 {
392 Log2(("NAT: resolv.conf: truncated: %s\n", tok));
393 pszSearchBuf = state->rcps_searchlist[i];
394 cbSearchBuf = sizeof(state->rcps_searchlist_buffer)
395 - (pszSearchBuf - state->rcps_searchlist_buffer);
396 Log2(("NAT: resolv.conf: backtracking to @%td,+%zu\n",
397 pszSearchBuf - state->rcps_searchlist_buffer, cbSearchBuf));
398 }
399 }
400
401 continue;
402 }
403
404
405 LogRel(("NAT: resolv.conf: ignoring \"%s %s\"\n", tok, s));
406 }
407
408 if (filename != NULL)
409 RTStrmClose(stream);
410
411 if (RT_FAILURE(rc))
412 return rc;
413
414
415 /* XXX: I don't like that OS X would return a different result here */
416#ifdef RCP_ACCEPT_PORT /* OS X extention */
417 if (default_port == RTNETADDR_PORT_NA)
418 default_port = 53;
419
420 for (i = 0; i < state->rcps_num_nameserver; ++i)
421 {
422 RTNETADDR *addr = &state->rcps_nameserver[i];
423 if (addr->uPort == RTNETADDR_PORT_NA || addr->uPort == 0)
424 addr->uPort = (uint16_t)default_port;
425 }
426#endif
427
428 if ( state->rcps_domain == NULL
429 && state->rcps_num_searchlist > 0)
430 {
431 state->rcps_domain = state->rcps_searchlist[0];
432 }
433
434 return VINF_SUCCESS;
435}
436
437
438static int
439rcp_address_trailer(char **ppszNext, PRTNETADDR pNetAddr, RTNETADDRTYPE enmType)
440{
441 char *pszNext = *ppszNext;
442 int rc = VINF_SUCCESS;
443
444 if (*pszNext == '\0')
445 {
446 pNetAddr->enmType = enmType;
447 rc = VINF_SUCCESS;
448 }
449#ifdef RCP_ACCEPT_PORT /* OS X extention */
450 else if (*pszNext == '.')
451 {
452 uint16_t port;
453
454 rc = RTStrToUInt16Ex(++pszNext, NULL, 10, &port);
455 if (RT_SUCCESS(rc))
456 {
457 pNetAddr->enmType = enmType;
458 pNetAddr->uPort = port;
459 }
460 }
461#endif
462 else
463 {
464 rc = VERR_TRAILING_CHARS;
465 }
466
467 return rc;
468}
469
470
471static char *getToken(char *psz, char **ppszSavePtr)
472{
473 char *pszToken;
474
475 AssertPtrReturn(ppszSavePtr, NULL);
476
477 if (psz == NULL)
478 {
479 psz = *ppszSavePtr;
480 if (psz == NULL)
481 return NULL;
482 }
483
484 while (*psz == ' ' || *psz == '\t')
485 ++psz;
486
487 if (*psz == '\0')
488 {
489 *ppszSavePtr = NULL;
490 return NULL;
491 }
492
493 pszToken = psz;
494 while (*psz && *psz != ' ' && *psz != '\t')
495 ++psz;
496
497 if (*psz == '\0')
498 psz = NULL;
499 else
500 *psz++ = '\0';
501
502 *ppszSavePtr = psz;
503 return pszToken;
504}
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