]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - ServiceRegistration/config-parse.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / config-parse.c
diff --git a/ServiceRegistration/config-parse.c b/ServiceRegistration/config-parse.c
new file mode 100644 (file)
index 0000000..344def7
--- /dev/null
@@ -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 <stdio.h>          // For printf()
+#include <stdlib.h>         // For malloc()
+#include <string.h>         // For strrchr(), strcmp()
+#include <time.h>           // For "struct tm" etc.
+#include <signal.h>         // For SIGINT, SIGTERM
+#include <assert.h>
+#include <netdb.h>           // For gethostbyname()
+#include <sys/socket.h>      // For AF_INET, AF_INET6, etc.
+#include <net/if.h>          // For IF_NAMESIZE
+#include <netinet/in.h>      // For INADDR_NONE
+#include <netinet/tcp.h>     // For SOL_TCP, TCP_NOTSENT_LOWAT
+#include <arpa/inet.h>       // For inet_addr()
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+#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;
+}
+