VirtualBox

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

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

Devices: Updated (C) year.

  • 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 56292 2015-06-09 14:20:46Z vboxsync $ */
2/** @file
3 * resolv_conf_parser.c - parser of resolv.conf resolver(5)
4 */
5
6/*
7 * Copyright (C) 2014-2015 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