Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108 -*-
      2  *
      3  * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
      4  * Copyright (c) 2018 Apple Computer, Inc. All rights reserved.
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  *
     18  */
     19 
     20 //*************************************************************************************************************
     21 //
     22 // General purpose stupid little parser, currently used by dnssd-proxy for its configuration file
     23 //
     24 //*************************************************************************************************************
     25 // Headers
     26 
     27 #include <stdio.h>          // For printf()
     28 #include <stdlib.h>         // For malloc()
     29 #include <string.h>         // For strrchr(), strcmp()
     30 #include <time.h>           // For "struct tm" etc.
     31 #include <signal.h>         // For SIGINT, SIGTERM
     32 #include <assert.h>
     33 #include <netdb.h>           // For gethostbyname()
     34 #include <sys/socket.h>      // For AF_INET, AF_INET6, etc.
     35 #include <net/if.h>          // For IF_NAMESIZE
     36 #include <netinet/in.h>      // For INADDR_NONE
     37 #include <netinet/tcp.h>     // For SOL_TCP, TCP_NOTSENT_LOWAT
     38 #include <arpa/inet.h>       // For inet_addr()
     39 #include <unistd.h>
     40 #include <errno.h>
     41 #include <fcntl.h>
     42 #include <stdbool.h>
     43 
     44 #include "srp.h"
     45 #include "config-parse.h"
     46 
     47 #ifdef STANDALONE
     48 #undef LogMsg
     49 #define LogMsg(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
     50 #endif // STANDALONE
     51 
     52 // Parse one line of a config file.
     53 // A line consists of a verb followed by one or more hunks of text.
     54 // We parse the verb first, then that tells us how many hunks of text to expect.
     55 // Each hunk is space-delineated; the last hunk can contain spaces.
     56 static bool config_parse_line(void *context, const char *filename, char *line, int lineno,
     57                               config_file_verb_t *verbs, int num_verbs)
     58 {
     59     char *sp;
     60 #define MAXCFHUNKS 10
     61     char *hunks[MAXCFHUNKS];
     62     int num_hunks = 0;
     63     config_file_verb_t *config_file_verb = NULL;
     64     int i;
     65 
     66     sp = line;
     67     do {
     68         // Skip leading spaces.
     69         while (*sp && (*sp == ' ' || *sp == '\t'))
     70             sp++;
     71         if (num_hunks == 0) {
     72             // If this is a blank line with spaces on it or a comment line, we ignore it.
     73             if (!*sp || *sp == '#')
     74                 return true;
     75         }
     76         hunks[num_hunks++] = sp;
     77         // Find EOL or hunk
     78         while (*sp && (*sp != ' ' && *sp != '\t')) {
     79             sp++;
     80         }
     81         if (*sp) {
     82             *sp++ = 0;
     83         }
     84         if (num_hunks == 1) {
     85             for (i = 0; i < num_verbs; i++) {
     86                 // If the verb name matches, or the verb name is NULL (meaning whatever doesn't
     87                 // match a preceding verb), we've found our verb.
     88                 if (verbs[i].name == NULL || !strcmp(verbs[i].name, hunks[0])) {
     89                     config_file_verb = &verbs[i];
     90                     break;
     91                 }
     92             }
     93             if (config_file_verb == NULL) {
     94                 INFO("unknown verb %s at line %d", hunks[0], lineno);
     95                 return false;
     96             }
     97         }
     98     } while (*sp && num_hunks < MAXCFHUNKS && config_file_verb->max_hunks > num_hunks);
     99 
    100     // If we didn't get the hunks we needed, bail.
    101     if (config_file_verb->min_hunks > num_hunks) {
    102         INFO("error: verb %s requires between %d and %d modifiers; %d given at line %d",
    103              hunks[0], config_file_verb->min_hunks, config_file_verb->max_hunks, num_hunks, lineno);
    104         return false;
    105     }
    106 
    107     return config_file_verb->handler(context, filename, hunks, num_hunks, lineno);
    108 }
    109 
    110 // Parse a configuration file
    111 bool config_parse(void *context, const char *filename, config_file_verb_t *verbs, int num_verbs)
    112 {
    113     int file;
    114     char *buf, *line, *eof, *eol, *nextCR, *nextNL;
    115     off_t flen;
    116     ssize_t len;
    117     size_t have;
    118     int lineno;
    119     bool success = true;
    120 
    121     file = open(filename, O_RDONLY);
    122     if (file < 0) {
    123         INFO("fatal: %s: %s", filename, strerror(errno));
    124         return false;
    125     }
    126 
    127     // Get the length of the file.
    128     flen = lseek(file, 0, SEEK_END);
    129     lseek(file, 0, SEEK_SET);
    130     if (flen > 500 * 1024 || (buf = malloc((size_t)flen + 1)) == NULL) {
    131         INFO("fatal: not enough memory for %s", filename);
    132         goto outclose;
    133     }
    134     size_t fsize = (size_t)flen;
    135 
    136     // Just in case we have a read() syscall that doesn't always read the whole file at once
    137     have = 0;
    138     while (have < fsize) {
    139         len = read(file, &buf[have], fsize - have);
    140         if (len < 0) {
    141             INFO("fatal: read of %s at %lld len %lld: %s",
    142                  filename, (long long)have, (long long)(fsize - have), strerror(errno));
    143             goto outfree;
    144         }
    145         if (len == 0) {
    146             INFO("fatal: read of %s at %lld len %lld: zero bytes read",
    147                  filename, (long long)have, (long long)(fsize - have));
    148         outfree:
    149             free(buf);
    150         outclose:
    151             close(file);
    152             return false;
    153         }
    154         have += len;
    155     }
    156     close(file);
    157     buf[flen] = 0; // NUL terminate.
    158     eof = buf + flen;
    159 
    160     // Parse through the file line by line.
    161     line = buf;
    162     lineno = 1;
    163     while (line < eof) { // < because NUL at eof could be last eol.
    164         nextCR = strchr(line, '\r');
    165         nextNL = strchr(line, '\n');
    166 
    167         // Added complexity for CR/LF agnostic line endings.   Necessary?
    168         if (nextNL != NULL) {
    169             if (nextCR != NULL && nextCR < nextNL)
    170                 eol = nextCR;
    171             else
    172                 eol = nextNL;
    173         } else {
    174             if (nextCR != NULL)
    175                 eol = nextCR;
    176             else
    177                 eol = buf + flen;
    178         }
    179 
    180         // If this isn't a blank line or a comment line, parse it.
    181         if (eol - line != 1 && line[0] != '#') {
    182             *eol = 0;
    183             // If we get a bad config line, we're going to return failure later, but continue parsing now.
    184             if (!config_parse_line(context, filename, line, lineno, verbs, num_verbs))
    185                 success = false;
    186         }
    187         line = eol + 1;
    188         lineno++;
    189     }
    190     free(buf);
    191     return success;
    192 }
    193