VirtualBox

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

Last change on this file since 50088 was 50088, checked in by vboxsync, 11 years ago

Introduces resolv.conf parser replacement for Slirp and Main.

resolv_conf_parser.c - parser.
resolv_conf_parser.h - interface to parser.

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