]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/config-parse.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / config-parse.c
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 "config-parse.h"
45
46 #ifdef STANDALONE
47 #undef LogMsg
48 #define LogMsg(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
49 #endif // STANDALONE
50
51 // Parse one line of a config file.
52 // A line consists of a verb followed by one or more hunks of text.
53 // We parse the verb first, then that tells us how many hunks of text to expect.
54 // Each hunk is space-delineated; the last hunk can contain spaces.
55 static bool config_parse_line(void *context, const char *filename, char *line, int lineno,
56 config_file_verb_t *verbs, int num_verbs)
57 {
58 char *sp;
59 #define MAXCFHUNKS 10
60 char *hunks[MAXCFHUNKS];
61 int num_hunks = 0;
62 config_file_verb_t *config_file_verb = NULL;
63 int i;
64
65 sp = line;
66 do {
67 // Skip leading spaces.
68 while (*sp && (*sp == ' ' || *sp == '\t'))
69 sp++;
70 if (num_hunks == 0) {
71 // If this is a blank line with spaces on it or a comment line, we ignore it.
72 if (!*sp || *sp == '#')
73 return true;
74 }
75 hunks[num_hunks++] = sp;
76 // Find EOL or hunk
77 while (*sp && (*sp != ' ' && *sp != '\t')) {
78 sp++;
79 }
80 if (*sp) {
81 *sp++ = 0;
82 }
83 if (num_hunks == 1) {
84 for (i = 0; i < num_verbs; i++) {
85 // If the verb name matches, or the verb name is NULL (meaning whatever doesn't
86 // match a preceding verb), we've found our verb.
87 if (verbs[i].name == NULL || !strcmp(verbs[i].name, hunks[0])) {
88 config_file_verb = &verbs[i];
89 break;
90 }
91 }
92 if (config_file_verb == NULL) {
93 LogMsg("cfParseLine: unknown verb %s at line %d", hunks[0], lineno);
94 return false;
95 }
96 }
97 } while (*sp && num_hunks < MAXCFHUNKS && config_file_verb->max_hunks > num_hunks);
98
99 // If we didn't get the hunks we needed, bail.
100 if (config_file_verb->min_hunks > num_hunks) {
101 LogMsg("config_file_parse_line: error: verb %s requires between %d and %d modifiers; %d given at line %d",
102 hunks[0], config_file_verb->min_hunks, config_file_verb->max_hunks, num_hunks, lineno);
103 return false;
104 }
105
106 return config_file_verb->handler(context, filename, hunks, num_hunks, lineno);
107 }
108
109 // Parse a configuration file
110 bool config_parse(void *context, const char *filename, config_file_verb_t *verbs, int num_verbs)
111 {
112 int file;
113 char *buf, *line, *eof, *eol, *nextCR, *nextNL;
114 off_t flen, have;
115 ssize_t len;
116 int lineno;
117 bool success = true;
118
119 file = open(filename, O_RDONLY);
120 if (file < 0) {
121 LogMsg("cfParse: fatal: %s: %s", filename, strerror(errno));
122 return false;
123 }
124
125 // Get the length of the file.
126 flen = lseek(file, 0, SEEK_END);
127 lseek(file, 0, SEEK_SET);
128 buf = malloc(flen + 1);
129 if (buf == NULL) {
130 LogMsg("cfParse: fatal: not enough memory for %s", filename);
131 goto outclose;
132 }
133
134 // Just in case we have a read() syscall that doesn't always read the whole file at once
135 have = 0;
136 while (have < flen) {
137 len = read(file, &buf[have], flen - have);
138 if (len < 0) {
139 LogMsg("cfParse: fatal: read of %s at %lld len %lld: %s",
140 filename, (long long)have, (long long)(flen - have), strerror(errno));
141 goto outfree;
142 }
143 if (len == 0) {
144 LogMsg("cfParse: fatal: read of %s at %lld len %lld: zero bytes read",
145 filename, (long long)have, (long long)(flen - have));
146 outfree:
147 free(buf);
148 outclose:
149 close(file);
150 return false;
151 }
152 have += len;
153 }
154 close(file);
155 buf[flen] = 0; // NUL terminate.
156 eof = buf + flen;
157
158 // Parse through the file line by line.
159 line = buf;
160 lineno = 1;
161 while (line < eof) { // < because NUL at eof could be last eol.
162 nextCR = strchr(line, '\r');
163 nextNL = strchr(line, '\n');
164
165 // Added complexity for CR/LF agnostic line endings. Necessary?
166 if (nextNL != NULL) {
167 if (nextCR != NULL && nextCR < nextNL)
168 eol = nextCR;
169 else
170 eol = nextNL;
171 } else {
172 if (nextCR != NULL)
173 eol = nextCR;
174 else
175 eol = buf + flen;
176 }
177
178 // If this isn't a blank line or a comment line, parse it.
179 if (eol - line != 1 && line[0] != '#') {
180 *eol = 0;
181 // If we get a bad config line, we're going to return failure later, but continue parsing now.
182 if (!config_parse_line(context, filename, line, lineno, verbs, num_verbs))
183 success = false;
184 }
185 line = eol + 1;
186 lineno++;
187 }
188 free(buf);
189 return success;
190 }
191