VirtualBox

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

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

resolv_conf_parser.c: temporal build fix for hosts where isblank isn't defined.

  • 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 50130 2014-01-20 17:54:38Z 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/* If ctypes.h doesn't have definition for isblank(3) */
55#ifndef isblank
56# define isblank(c) ((c) == '\t' || (c) == '\ ')
57#endif
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 (isblank(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 Assert(parser->rcpp_state);
246
247 st = parser->rcpp_state;
248 RTNETADDR *address = &st->rcps_nameserver[st->rcps_num_nameserver];
249 char *str_address = &st->rcps_nameserver_str_buffer[st->rcps_num_nameserver * RCPS_IPVX_SIZE];
250#ifdef RT_OS_DARWIN
251 if ( tok == tok_ipv4_port
252 || ( tok == tok_ipv6_port
253 && (st->rcps_flags & RCPSF_IGNORE_IPV6) == 0))
254 {
255 char *ptr = &parser->rcpp_str_buffer[strlen(parser->rcpp_str_buffer)];
256 while (*(--ptr) != '.');
257 *ptr = '\0';
258 address->uPort = RTStrToUInt16(ptr + 1);
259
260 if (address->uPort == 0) return tok_error;
261 }
262#endif
263 /**
264 * if we on Darwin upper code will cut off port if it's.
265 */
266 if ((st->rcps_flags & RCPSF_NO_STR2IPCONV) != 0)
267 {
268 if (strlen(parser->rcpp_str_buffer) > RCPS_IPVX_SIZE)
269 return tok_error;
270
271 strcpy(str_address, parser->rcpp_str_buffer);
272
273 st->rcps_str_nameserver[st->rcps_num_nameserver] = str_address;
274
275 goto loop_prolog;
276 }
277
278 switch (tok)
279 {
280 case tok_ipv4:
281 case tok_ipv4_port:
282 {
283 int rc = RTNetStrToIPv4Addr(parser->rcpp_str_buffer, &address->uAddr.IPv4);
284 if (RT_FAILURE(rc)) return tok_error;
285
286 address->enmType = RTNETADDRTYPE_IPV4;
287 }
288
289 break;
290 case tok_ipv6:
291 case tok_ipv6_port:
292 {
293 int rc;
294
295 if ((st->rcps_flags & RCPSF_IGNORE_IPV6) != 0)
296 return rcp_get_token(parser);
297
298 rc = inet_pton(AF_INET6, parser->rcpp_str_buffer,
299 &address->uAddr.IPv6);
300 if (rc == -1)
301 return tok_error;
302
303 address->enmType = RTNETADDRTYPE_IPV6;
304 }
305
306 break;
307 default: /* loop condition doesn't let enter enything */
308 AssertMsgFailed(("shouldn't ever happen tok:%d, %s", tok,
309 isprint(tok) ? parser->rcpp_str_buffer : "#"));
310 break;
311 }
312
313 loop_prolog:
314 st->rcps_num_nameserver++;
315 tok = rcp_get_token(parser);
316 }
317 return tok;
318}
319
320/**
321 * portexpr ::= 'port' [0-9]+
322 */
323static enum RCP_TOKEN rcp_parse_port(struct rcp_parser *parser)
324{
325 struct rcp_state *st;
326 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'port' */
327
328 Assert(parser->rcpp_state);
329 st = parser->rcpp_state;
330
331 if ( tok != tok_number
332 || tok == tok_eof)
333 return tok_error;
334
335 st->rcps_port = RTStrToUInt16(parser->rcpp_str_buffer);
336
337 if (st->rcps_port == 0)
338 return tok_error;
339
340 return rcp_get_token(parser);
341}
342
343/**
344 * domainexpr ::= 'domain' string
345 */
346static enum RCP_TOKEN rcp_parse_domain(struct rcp_parser *parser)
347{
348 struct rcp_state *st;
349 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'domain' */
350
351 Assert(parser->rcpp_state);
352 st = parser->rcpp_state;
353
354 /**
355 * It's nowhere specified how resolver should react on dublicats
356 * of 'domain' declarations, let's assume that resolv.conf is broken.
357 */
358 if ( tok == tok_eof
359 || tok == tok_error
360 || st->rcps_domain != NULL)
361 return tok_error;
362
363 strcpy(st->rcps_domain_buffer, parser->rcpp_str_buffer);
364 /**
365 * We initialize this pointer in place, just make single pointer check
366 * in 'domain'-less resolv.conf.
367 */
368 st->rcps_domain = st->rcps_domain_buffer;
369
370 return rcp_get_token(parser);
371}
372
373/**
374 * searchexpr ::= 'search' (string)+
375 * @note: resolver (5) Mac OSX:
376 * "The search list is currently limited to six domains with a total of 256 characters."
377 * @note: resolv.conf (5) Linux:
378 * "The search list is currently limited to six domains with a total of 256 characters."
379 */
380static enum RCP_TOKEN rcp_parse_search(struct rcp_parser *parser)
381{
382 unsigned i, len, trailing;
383 char *ptr;
384 struct rcp_state *st;
385 enum RCP_TOKEN tok = rcp_get_token(parser); /* eats 'search' */
386
387 Assert(parser->rcpp_state);
388 st = parser->rcpp_state;
389
390 /**
391 * We asume that duplication of search list in resolv.conf isn't correct.
392 */
393 if ( tok == tok_eof
394 || tok == tok_error
395 || tok != tok_string
396 || st->rcps_searchlist[0] != NULL)
397 return tok_error;
398
399 i = 0;
400 trailing = RCPS_BUFFER_SIZE;
401 do {
402 len = strlen(parser->rcpp_str_buffer);
403
404 if (len + 1 > trailing)
405 break; /* not enough room for new entry */
406
407 ptr = st->rcps_searchlist_buffer + RCPS_BUFFER_SIZE - trailing;
408 strcpy(ptr, parser->rcpp_str_buffer);
409
410 trailing -= len + 1; /* 1 reserved for '\0' */
411
412 st->rcps_searchlist[i] = ptr;
413
414 } while( (tok = rcp_get_token(parser)) == tok_string
415 && ++i != RCPS_MAX_SEARCHLIST);
416
417 st->rcps_num_searchlist = i;
418
419 return tok;
420}
421
422/**
423 * expr ::= nameserverexpr | expr
424 * ::= portexpr | expr
425 * ::= domainexpr | expr
426 * ::= searchexpr | expr
427 * ::= searchlistexpr | expr
428 * ::= search_orderexpr | expr
429 * ::= timeoutexpr | expr
430 * ::= optionsexpr | expr
431 */
432static int rcp_parse_primary(struct rcp_parser *parser)
433{
434 enum RCP_TOKEN tok;
435 tok = rcp_get_token(parser);
436
437 while( tok != tok_eof
438 && tok != tok_error)
439 {
440 switch (tok)
441 {
442 case tok_nameserver:
443 tok = rcp_parse_nameserver(parser);
444 break;
445 case tok_port:
446 tok = rcp_parse_port(parser);
447 break;
448 case tok_domain:
449 tok = rcp_parse_domain(parser);
450 break;
451 case tok_search:
452 tok = rcp_parse_search(parser);
453 break;
454 default:
455 tok = rcp_get_token(parser);
456 }
457 }
458
459 if (tok == tok_error)
460 return -1;
461
462 return 0;
463}
464
465
466int rcp_parse(struct rcp_state* state, const char *filename)
467{
468 unsigned i;
469 uint32_t flags;
470 int rc;
471 struct rcp_parser parser;
472 flags = state->rcps_flags;
473
474 RT_ZERO(parser);
475 RT_ZERO(*state);
476
477 state->rcps_flags = flags;
478
479 parser.rcpp_state = state;
480
481 /**
482 * for debugging need: with RCP_STANDALONE it's possible
483 * to run simplefied scenarious like
484 *
485 * # cat /etc/resolv.conf | rcp-test-0
486 * or in lldb
487 * # process launch -i /etc/resolv.conf
488 */
489#ifdef RCP_STANDALONE
490 if (filename == NULL)
491 parser.rcpp_stream = g_pStdIn;
492#else
493 if (filename == NULL)
494 return -1;
495#endif
496 else
497 {
498 rc = RTStrmOpen(filename, "r", &parser.rcpp_stream);
499 if (RT_FAILURE(rc)) return -1;
500 }
501
502 rc = rcp_parse_primary(&parser);
503
504 if (filename != NULL)
505 RTStrmClose(parser.rcpp_stream);
506
507 if (rc == -1)
508 return -1;
509
510#ifdef RT_OS_DARWIN
511 /**
512 * port recolv.conf's option and IP.port are Mac OSX extentions, there're no need to care on
513 * other hosts.
514 */
515 if (state->rcps_port == 0)
516 state->rcps_port = 53;
517
518 for(i = 0; (state->rcps_flags & RCPSF_NO_STR2IPCONV) == 0
519 && i != RCPS_MAX_NAMESERVERS; ++i)
520 {
521 RTNETADDR *addr = &state->rcps_nameserver[i];
522
523 if (addr->uPort == 0)
524 addr->uPort = state->rcps_port;
525 }
526#endif
527
528 if ( state->rcps_domain == NULL
529 && state->rcps_searchlist[0] != NULL)
530 state->rcps_domain = state->rcps_searchlist[0];
531
532 return 0;
533}
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