VirtualBox

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

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

NAT: resolv_conf_parser.c:
1.search lists can be in numeric format, e.g.

search beadbabe 123cafe

are valid names

  1. droped limitation of single search per resolv.conf search 1cafe search 2cafe

is acceptable format.

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