VirtualBox

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

Last change on this file since 55095 was 55095, checked in by vboxsync, 10 years ago

Assorted fixes for FreeBSD hosts, VBox compiles and runs again without further patches (tested on 10.1 amd64 )

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.6 KB
Line 
1/* $Id: resolv_conf_parser.c 55095 2015-04-02 16:52:46Z vboxsync $ */
2/** @file
3 * resolv_conf_parser.c - parser of resolv.conf resolver(5)
4 */
5
6/*
7 * Copyright (C) 2014 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#include <iprt/assert.h>
19#include <iprt/initterm.h>
20#include <iprt/net.h>
21#include <iprt/string.h>
22#include <iprt/stream.h>
23#include <iprt/thread.h>
24
25#ifdef RT_OS_FREEBSD
26# include <sys/socket.h>
27#endif
28
29#include <arpa/inet.h>
30
31#include <ctype.h>
32
33#include "resolv_conf_parser.h"
34
35/* XXX: it's required to add the aliases for keywords and
36 * types to handle conditions more clearly */
37enum RCP_TOKEN
38{
39 tok_eof = -1, /* EOF */
40 tok_string = -2, /* string */
41 tok_number = -3, /* number */
42 tok_ipv4 = -4, /* ipv4 */
43 tok_ipv4_port = -5, /* ipv4 port */
44 tok_ipv6 = -6, /* ipv6 */
45 tok_ipv6_port = -7, /* ipv6 port */
46 /* keywords */
47 tok_nameserver = -8, /* nameserver */
48 tok_port = -9, /* port, Mac OSX specific */
49 tok_domain = -10, /* domain */
50 tok_search = -11, /* search */
51 tok_search_order = -12, /* search order */
52 tok_sortlist = -13, /* sortlist */
53 tok_timeout = -14, /* timeout */
54 tok_options = -15, /* options */
55 tok_option = -16, /* option */
56 tok_comment = -17, /* comment */
57 tok_error = -20
58};
59
60#define RCP_BUFFER_SIZE 256
61
62
63struct rcp_parser
64{
65 enum RCP_TOKEN rcpp_token;
66 char rcpp_str_buffer[RCP_BUFFER_SIZE];
67 struct rcp_state *rcpp_state;
68 PRTSTREAM rcpp_stream;
69};
70
71
72#define GETCHAR(parser) (RTStrmGetCh((parser)->rcpp_stream))
73#define EOF (-1)
74
75
76#define PARSER_STOP(tok, parser, ptr) ( (tok) != EOF \
77 && (((ptr) - (parser)->rcpp_str_buffer) != (RCP_BUFFER_SIZE - 1)))
78#define PARSER_BUFFER_EXCEEDED(parser, ptr) \
79 do { \
80 if (((ptr) - (parser)->rcpp_str_buffer) == (RCP_BUFFER_SIZE - 1)) { \
81 return tok_error; \
82 } \
83 }while(0);
84
85static int rcp_get_token(struct rcp_parser *parser)
86{
87 char tok = ' ';
88 char *ptr;
89 size_t ptr_len;
90
91 while (isspace(tok))
92 tok = GETCHAR(parser);
93
94 ptr = parser->rcpp_str_buffer;
95
96 /* tok can't be ipv4 */
97 if (isalnum(tok)) {
98 int xdigit, digit, dot_number;
99 RT_ZERO(parser->rcpp_str_buffer);
100
101 dot_number = 0;
102 xdigit = 1;
103 digit = 1;
104 do {
105 *ptr++ = tok;
106 tok = GETCHAR(parser);
107
108 if (!isalnum(tok) && tok != ':' && tok != '.' && tok != '-' && tok != '_')
109 break;
110
111 /**
112 * if before ':' there were only [0-9][a-f][A-F],
113 * then it can't be option.
114 */
115 xdigit &= (isxdigit(tok) || (tok == ':'));
116 /**
117 * We want hint to differ ipv4 and network name.
118 */
119 digit &= (isdigit(tok) || (tok == '.'));
120
121 if (tok == ':')
122 {
123 if (xdigit == 1)
124 {
125 int port = 0;
126 do
127 {
128 *ptr++ = tok;
129 tok = GETCHAR(parser);
130
131 if (tok == '.')
132 port++;
133
134 } while(PARSER_STOP(tok, parser, ptr) && (tok == ':' || tok == '.' || isxdigit(tok)));
135
136 PARSER_BUFFER_EXCEEDED(parser, ptr);
137
138 if (port == 0)
139 return tok_ipv6;
140 else if (port == 1)
141 return tok_ipv6_port;
142 else
143 {
144 /* eats rest of the token */
145 do
146 {
147 *ptr++ = tok;
148 tok = GETCHAR(parser);
149 } while( PARSER_STOP(tok, parser, ptr)
150 && (isalnum(tok) || tok == '.' || tok == '_' || tok == '-'));
151
152 PARSER_BUFFER_EXCEEDED(parser, ptr);
153
154 return tok_string;
155 }
156 }
157 else {
158 /* XXX: need further experiments */
159 return tok_option; /* option with value */
160 }
161 }
162
163 if (tok == '.')
164 {
165 do {
166 if (tok == '.') dot_number++;
167
168 *ptr++ = tok;
169 digit &= (isdigit(tok) || (tok == '.'));
170 tok = GETCHAR(parser);
171 } while( PARSER_STOP(tok, parser, ptr)
172 && (isalnum(tok) || tok == '.' || tok == '_' || tok == '-'));
173
174 PARSER_BUFFER_EXCEEDED(parser, ptr);
175
176 if (dot_number == 3 && digit)
177 return tok_ipv4;
178 else if (dot_number == 4 && digit)
179 return tok_ipv4_port;
180 else
181 return tok_string;
182 }
183 } while( PARSER_STOP(tok, parser, ptr)
184 && (isalnum(tok) || tok == ':' || tok == '.' || tok == '-' || tok == '_'));
185
186 PARSER_BUFFER_EXCEEDED(parser, ptr);
187
188 if (digit || xdigit)
189 return tok_number;
190 if (RTStrCmp(parser->rcpp_str_buffer, "nameserver") == 0)
191 return tok_nameserver;
192 if (RTStrCmp(parser->rcpp_str_buffer, "port") == 0)
193 return tok_port;
194 if (RTStrCmp(parser->rcpp_str_buffer, "domain") == 0)
195 return tok_domain;
196 if (RTStrCmp(parser->rcpp_str_buffer, "search") == 0)
197 return tok_search;
198 if (RTStrCmp(parser->rcpp_str_buffer, "search_order") == 0)
199 return tok_search_order;
200 if (RTStrCmp(parser->rcpp_str_buffer, "sortlist") == 0)
201 return tok_sortlist;
202 if (RTStrCmp(parser->rcpp_str_buffer, "timeout") == 0)
203 return tok_timeout;
204 if (RTStrCmp(parser->rcpp_str_buffer, "options") == 0)
205 return tok_options;
206
207 return tok_string;
208 }
209
210 if (tok == EOF) return tok_eof;
211
212 if (tok == '#')
213 {
214 do{
215 tok = GETCHAR(parser);
216 } while (tok != EOF && tok != '\r' && tok != '\n');
217
218 if (tok == EOF) return tok_eof;
219
220 return tok_comment;
221 }
222 return tok;
223}
224
225#undef PARSER_STOP
226#undef PARSER_BUFFER_EXCEEDED
227
228/**
229 * nameserverexpr ::= 'nameserver' ip+
230 * @note: resolver(5) ip ::= (ipv4|ipv6)(.number)?
231 */
232static enum RCP_TOKEN rcp_parse_nameserver(struct rcp_parser *parser)
233{
234 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'nameserver' */
235
236 if ( ( tok != tok_ipv4
237 && tok != tok_ipv4_port
238 && tok != tok_ipv6
239 && tok != tok_ipv6_port)
240 || tok == EOF)
241 return tok_error;
242
243 while ( tok == tok_ipv4
244 || tok == tok_ipv4_port
245 || tok == tok_ipv6
246 || tok == tok_ipv6_port)
247 {
248 struct rcp_state *st;
249 RTNETADDR *address;
250 char *str_address;
251
252 Assert(parser->rcpp_state);
253
254 st = parser->rcpp_state;
255
256 /* It's still valid resolv.conf file, just rest of the nameservers should be ignored */
257 if (st->rcps_num_nameserver >= RCPS_MAX_NAMESERVERS)
258 return rcp_get_token(parser);
259
260 address = &st->rcps_nameserver[st->rcps_num_nameserver];
261 str_address = &st->rcps_nameserver_str_buffer[st->rcps_num_nameserver * RCPS_IPVX_SIZE];
262#ifdef RT_OS_DARWIN
263 if ( tok == tok_ipv4_port
264 || ( tok == tok_ipv6_port
265 && (st->rcps_flags & RCPSF_IGNORE_IPV6) == 0))
266 {
267 char *ptr = &parser->rcpp_str_buffer[strlen(parser->rcpp_str_buffer)];
268 while (*(--ptr) != '.');
269 *ptr = '\0';
270 address->uPort = RTStrToUInt16(ptr + 1);
271
272 if (address->uPort == 0) return tok_error;
273 }
274#endif
275 /**
276 * if we on Darwin upper code will cut off port if it's.
277 */
278 if ((st->rcps_flags & RCPSF_NO_STR2IPCONV) != 0)
279 {
280 if (strlen(parser->rcpp_str_buffer) > RCPS_IPVX_SIZE)
281 return tok_error;
282
283 strcpy(str_address, parser->rcpp_str_buffer);
284
285 st->rcps_str_nameserver[st->rcps_num_nameserver] = str_address;
286
287 goto loop_prolog;
288 }
289
290 switch (tok)
291 {
292 case tok_ipv4:
293 case tok_ipv4_port:
294 {
295 int rc = RTNetStrToIPv4Addr(parser->rcpp_str_buffer, &address->uAddr.IPv4);
296 if (RT_FAILURE(rc)) return tok_error;
297
298 address->enmType = RTNETADDRTYPE_IPV4;
299 }
300
301 break;
302 case tok_ipv6:
303 case tok_ipv6_port:
304 {
305 int rc;
306
307 if ((st->rcps_flags & RCPSF_IGNORE_IPV6) != 0)
308 return rcp_get_token(parser);
309
310 rc = inet_pton(AF_INET6, parser->rcpp_str_buffer,
311 &address->uAddr.IPv6);
312 if (rc == -1)
313 return tok_error;
314
315 address->enmType = RTNETADDRTYPE_IPV6;
316 }
317
318 break;
319 default: /* loop condition doesn't let enter enything */
320 AssertMsgFailed(("shouldn't ever happen tok:%d, %s", tok,
321 isprint(tok) ? parser->rcpp_str_buffer : "#"));
322 break;
323 }
324
325 loop_prolog:
326 st->rcps_num_nameserver++;
327 tok = rcp_get_token(parser);
328 }
329 return tok;
330}
331
332/**
333 * portexpr ::= 'port' [0-9]+
334 */
335static enum RCP_TOKEN rcp_parse_port(struct rcp_parser *parser)
336{
337 struct rcp_state *st;
338 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'port' */
339
340 Assert(parser->rcpp_state);
341 st = parser->rcpp_state;
342
343 if ( tok != tok_number
344 || tok == tok_eof)
345 return tok_error;
346
347 st->rcps_port = RTStrToUInt16(parser->rcpp_str_buffer);
348
349 if (st->rcps_port == 0)
350 return tok_error;
351
352 return rcp_get_token(parser);
353}
354
355/**
356 * domainexpr ::= 'domain' string
357 */
358static enum RCP_TOKEN rcp_parse_domain(struct rcp_parser *parser)
359{
360 struct rcp_state *st;
361 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'domain' */
362
363 Assert(parser->rcpp_state);
364 st = parser->rcpp_state;
365
366 /**
367 * It's nowhere specified how resolver should react on dublicats
368 * of 'domain' declarations, let's assume that resolv.conf is broken.
369 */
370 if ( tok == tok_eof
371 || tok == tok_error
372 || st->rcps_domain != NULL)
373 return tok_error;
374
375 strcpy(st->rcps_domain_buffer, parser->rcpp_str_buffer);
376 /**
377 * We initialize this pointer in place, just make single pointer check
378 * in 'domain'-less resolv.conf.
379 */
380 st->rcps_domain = st->rcps_domain_buffer;
381
382 return rcp_get_token(parser);
383}
384
385/**
386 * searchexpr ::= 'search' (string)+
387 * @note: resolver (5) Mac OSX:
388 * "The search list is currently limited to six domains with a total of 256 characters."
389 * @note: resolv.conf (5) Linux:
390 * "The search list is currently limited to six domains with a total of 256 characters."
391 * @note: 'search' parameter could contains numbers only hex or decimal, 1c1e or 111
392 */
393static enum RCP_TOKEN rcp_parse_search(struct rcp_parser *parser)
394{
395 unsigned i, len, trailing;
396 char *ptr;
397 struct rcp_state *st;
398 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'search' */
399
400 Assert(parser->rcpp_state);
401 st = parser->rcpp_state;
402
403 if ( tok == tok_eof
404 || tok == tok_error)
405 return tok_error;
406
407 /* just ignore "too many search list" */
408 if (st->rcps_num_searchlist >= RCPS_MAX_SEARCHLIST)
409 return rcp_get_token(parser);
410
411 /* we don't want accept keywords */
412 if (tok <= tok_nameserver)
413 return tok;
414
415 /* if there're several entries of "search" we compose them together */
416 i = st->rcps_num_searchlist;
417 if ( i == 0)
418 trailing = RCPS_BUFFER_SIZE;
419 else
420 {
421 ptr = st->rcps_searchlist[i - 1];
422 trailing = RCPS_BUFFER_SIZE - (ptr -
423 st->rcps_searchlist_buffer + strlen(ptr) + 1);
424 }
425
426 while (1)
427 {
428 len = strlen(parser->rcpp_str_buffer);
429
430 if (len + 1 > trailing)
431 break; /* not enough room for new entry */
432
433 if (i >= RCPS_MAX_SEARCHLIST)
434 break; /* not enought free entries for 'search' items */
435
436 ptr = st->rcps_searchlist_buffer + RCPS_BUFFER_SIZE - trailing;
437 strcpy(ptr, parser->rcpp_str_buffer);
438
439 trailing -= len + 1; /* 1 reserved for '\0' */
440
441 st->rcps_searchlist[i++] = ptr;
442 tok = rcp_get_token(parser);
443
444 /* token filter */
445 if ( tok == tok_eof
446 || tok == tok_error
447 || tok <= tok_nameserver)
448 break;
449 }
450
451 st->rcps_num_searchlist = i;
452
453 return tok;
454}
455
456/**
457 * expr ::= nameserverexpr | expr
458 * ::= portexpr | expr
459 * ::= domainexpr | expr
460 * ::= searchexpr | expr
461 * ::= searchlistexpr | expr
462 * ::= search_orderexpr | expr
463 * ::= timeoutexpr | expr
464 * ::= optionsexpr | expr
465 */
466static int rcp_parse_primary(struct rcp_parser *parser)
467{
468 enum RCP_TOKEN tok;
469 tok = rcp_get_token(parser);
470
471 while( tok != tok_eof
472 && tok != tok_error)
473 {
474 switch (tok)
475 {
476 case tok_nameserver:
477 tok = rcp_parse_nameserver(parser);
478 break;
479 case tok_port:
480 tok = rcp_parse_port(parser);
481 break;
482 case tok_domain:
483 tok = rcp_parse_domain(parser);
484 break;
485 case tok_search:
486 tok = rcp_parse_search(parser);
487 break;
488 default:
489 tok = rcp_get_token(parser);
490 }
491 }
492
493 if (tok == tok_error)
494 return -1;
495
496 return 0;
497}
498
499
500int rcp_parse(struct rcp_state* state, const char *filename)
501{
502 unsigned i;
503 uint32_t flags;
504 int rc;
505 struct rcp_parser parser;
506 flags = state->rcps_flags;
507
508 RT_ZERO(parser);
509 RT_ZERO(*state);
510
511 state->rcps_flags = flags;
512
513 parser.rcpp_state = state;
514
515 /**
516 * for debugging need: with RCP_STANDALONE it's possible
517 * to run simplefied scenarious like
518 *
519 * # cat /etc/resolv.conf | rcp-test-0
520 * or in lldb
521 * # process launch -i /etc/resolv.conf
522 */
523#ifdef RCP_STANDALONE
524 if (filename == NULL)
525 parser.rcpp_stream = g_pStdIn;
526#else
527 if (filename == NULL)
528 return -1;
529#endif
530 else
531 {
532 rc = RTStrmOpen(filename, "r", &parser.rcpp_stream);
533 if (RT_FAILURE(rc)) return -1;
534 }
535
536 rc = rcp_parse_primary(&parser);
537
538 if (filename != NULL)
539 RTStrmClose(parser.rcpp_stream);
540
541 if (rc == -1)
542 return -1;
543
544#ifdef RT_OS_DARWIN
545 /**
546 * port recolv.conf's option and IP.port are Mac OSX extentions, there're no need to care on
547 * other hosts.
548 */
549 if (state->rcps_port == 0)
550 state->rcps_port = 53;
551
552 for(i = 0; (state->rcps_flags & RCPSF_NO_STR2IPCONV) == 0
553 && i != RCPS_MAX_NAMESERVERS; ++i)
554 {
555 RTNETADDR *addr = &state->rcps_nameserver[i];
556
557 if (addr->uPort == 0)
558 addr->uPort = state->rcps_port;
559 }
560#endif
561
562 if ( state->rcps_domain == NULL
563 && state->rcps_searchlist[0] != NULL)
564 state->rcps_domain = state->rcps_searchlist[0];
565
566 return 0;
567}
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