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