VirtualBox

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

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

NAT/Slirp:resolv_conf_parser.c: drop nameservers which has order number more than 3.

from resolver(5)
nameserver ...

Up to MAXNS (currently 3) name servers may be listed, one per keyword.

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