X-Git-Url: https://git.saurik.com/apple/mdnsresponder.git/blobdiff_plain/c71e3068dca7ed8b0536b4ce3fe0552e9f39d389..19fa75a9b9d0f880da91504945516a17ce09a2fb:/ServiceRegistration/config-parse.c?ds=inline diff --git a/ServiceRegistration/config-parse.c b/ServiceRegistration/config-parse.c new file mode 100644 index 0000000..344def7 --- /dev/null +++ b/ServiceRegistration/config-parse.c @@ -0,0 +1,191 @@ +/* -*- Mode: C; tab-width: 4; c-file-style: "bsd"; c-basic-offset: 4; fill-column: 108 -*- + * + * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2018 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +//************************************************************************************************************* +// +// General purpose stupid little parser, currently used by dnssd-proxy for its configuration file +// +//************************************************************************************************************* +// Headers + +#include // For printf() +#include // For malloc() +#include // For strrchr(), strcmp() +#include // For "struct tm" etc. +#include // For SIGINT, SIGTERM +#include +#include // For gethostbyname() +#include // For AF_INET, AF_INET6, etc. +#include // For IF_NAMESIZE +#include // For INADDR_NONE +#include // For SOL_TCP, TCP_NOTSENT_LOWAT +#include // For inet_addr() +#include +#include +#include +#include + +#include "config-parse.h" + +#ifdef STANDALONE +#undef LogMsg +#define LogMsg(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) +#endif // STANDALONE + +// Parse one line of a config file. +// A line consists of a verb followed by one or more hunks of text. +// We parse the verb first, then that tells us how many hunks of text to expect. +// Each hunk is space-delineated; the last hunk can contain spaces. +static bool config_parse_line(void *context, const char *filename, char *line, int lineno, + config_file_verb_t *verbs, int num_verbs) +{ + char *sp; +#define MAXCFHUNKS 10 + char *hunks[MAXCFHUNKS]; + int num_hunks = 0; + config_file_verb_t *config_file_verb = NULL; + int i; + + sp = line; + do { + // Skip leading spaces. + while (*sp && (*sp == ' ' || *sp == '\t')) + sp++; + if (num_hunks == 0) { + // If this is a blank line with spaces on it or a comment line, we ignore it. + if (!*sp || *sp == '#') + return true; + } + hunks[num_hunks++] = sp; + // Find EOL or hunk + while (*sp && (*sp != ' ' && *sp != '\t')) { + sp++; + } + if (*sp) { + *sp++ = 0; + } + if (num_hunks == 1) { + for (i = 0; i < num_verbs; i++) { + // If the verb name matches, or the verb name is NULL (meaning whatever doesn't + // match a preceding verb), we've found our verb. + if (verbs[i].name == NULL || !strcmp(verbs[i].name, hunks[0])) { + config_file_verb = &verbs[i]; + break; + } + } + if (config_file_verb == NULL) { + LogMsg("cfParseLine: unknown verb %s at line %d", hunks[0], lineno); + return false; + } + } + } while (*sp && num_hunks < MAXCFHUNKS && config_file_verb->max_hunks > num_hunks); + + // If we didn't get the hunks we needed, bail. + if (config_file_verb->min_hunks > num_hunks) { + LogMsg("config_file_parse_line: error: verb %s requires between %d and %d modifiers; %d given at line %d", + hunks[0], config_file_verb->min_hunks, config_file_verb->max_hunks, num_hunks, lineno); + return false; + } + + return config_file_verb->handler(context, filename, hunks, num_hunks, lineno); +} + +// Parse a configuration file +bool config_parse(void *context, const char *filename, config_file_verb_t *verbs, int num_verbs) +{ + int file; + char *buf, *line, *eof, *eol, *nextCR, *nextNL; + off_t flen, have; + ssize_t len; + int lineno; + bool success = true; + + file = open(filename, O_RDONLY); + if (file < 0) { + LogMsg("cfParse: fatal: %s: %s", filename, strerror(errno)); + return false; + } + + // Get the length of the file. + flen = lseek(file, 0, SEEK_END); + lseek(file, 0, SEEK_SET); + buf = malloc(flen + 1); + if (buf == NULL) { + LogMsg("cfParse: fatal: not enough memory for %s", filename); + goto outclose; + } + + // Just in case we have a read() syscall that doesn't always read the whole file at once + have = 0; + while (have < flen) { + len = read(file, &buf[have], flen - have); + if (len < 0) { + LogMsg("cfParse: fatal: read of %s at %lld len %lld: %s", + filename, (long long)have, (long long)(flen - have), strerror(errno)); + goto outfree; + } + if (len == 0) { + LogMsg("cfParse: fatal: read of %s at %lld len %lld: zero bytes read", + filename, (long long)have, (long long)(flen - have)); + outfree: + free(buf); + outclose: + close(file); + return false; + } + have += len; + } + close(file); + buf[flen] = 0; // NUL terminate. + eof = buf + flen; + + // Parse through the file line by line. + line = buf; + lineno = 1; + while (line < eof) { // < because NUL at eof could be last eol. + nextCR = strchr(line, '\r'); + nextNL = strchr(line, '\n'); + + // Added complexity for CR/LF agnostic line endings. Necessary? + if (nextNL != NULL) { + if (nextCR != NULL && nextCR < nextNL) + eol = nextCR; + else + eol = nextNL; + } else { + if (nextCR != NULL) + eol = nextCR; + else + eol = buf + flen; + } + + // If this isn't a blank line or a comment line, parse it. + if (eol - line != 1 && line[0] != '#') { + *eol = 0; + // If we get a bad config line, we're going to return failure later, but continue parsing now. + if (!config_parse_line(context, filename, line, lineno, verbs, num_verbs)) + success = false; + } + line = eol + 1; + lineno++; + } + free(buf); + return success; +} +