~ubuntu-branches/ubuntu/trusty/virtualbox-lts-xenial/trusty-updates

« back to all changes in this revision

Viewing changes to src/VBox/Devices/Network/slirp/resolv_conf_parser.c

  • Committer: Package Import Robot
  • Author(s): Gianfranco Costamagna
  • Date: 2016-02-23 14:28:26 UTC
  • Revision ID: package-import@ubuntu.com-20160223142826-bdu69el2z6wa2a44
Tags: upstream-4.3.36-dfsg
ImportĀ upstreamĀ versionĀ 4.3.36-dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: resolv_conf_parser.c $ */
 
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 */
 
33
enum 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
 
 
59
struct 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
 
 
81
static 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
 */
 
228
static 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
 */
 
331
static 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
 */
 
354
static 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
 */
 
389
static 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
 */
 
462
static 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
 
 
496
int 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
}