+/* -*-c-*-
+ *
+ * $Id: conf.c,v 1.1 1999/07/01 08:56:23 mdw Exp $
+ *
+ * Configuration parsing
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the `fw' port forwarder.
+ *
+ * `fw' is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * `fw' is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with `fw'; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: conf.c,v $
+ * Revision 1.1 1999/07/01 08:56:23 mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+
+#include "acl.h"
+#include "listener.h"
+#include "scan.h"
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define CTOK_EOF (-1)
+#define CTOK_WORD 256
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @token@ --- *
+ *
+ * Arguments: @scanner *sc@ = pointer to scanner definition
+ *
+ * Returns: Type of token scanned.
+ *
+ * Use: Reads the next token from the character scanner.
+ */
+
+static int token(scanner *sc)
+{
+#define SCAN(sc) (sc)->ops->scan((sc))
+#define UNSCAN(sc, x) (sc)->ops->unscan((x), (sc))
+
+ int ch;
+
+ for (;;) {
+ ch = SCAN(sc);
+ if (ch == '\n')
+ sc->line++;
+ else if (isspace((unsigned char)ch))
+ ;
+ else switch (ch) {
+ case EOF:
+ return (sc->t = CTOK_EOF);
+ case '#':
+ do ch = SCAN(sc); while (ch != EOF && ch != '\n');
+ if (ch == '\n')
+ sc->line++;
+ break;
+ case '{':
+ case '}':
+ case ':':
+ case '/':
+ case ';':
+ return (sc->t = ch);
+ default:
+ DRESET(&sc->d);
+ do {
+ DPUTC(&sc->d, ch);
+ ch = SCAN(sc);
+ } while (ch != EOF && ch != '{' && ch != ';' &&
+ ch != '}' && ch != ':' && ch != '/' &&
+ !isspace((unsigned char)(ch)));
+ UNSCAN(sc, ch);
+ DPUTZ(&sc->d);
+ return (sc->t = CTOK_WORD);
+ }
+ }
+
+#undef SCAN
+#undef UNSCAN
+}
+
+/* --- @error@ --- *
+ *
+ * Arguments: @scanner *sc@ = pointer to scanner definition
+ * @const char *msg@ = message skeleton string
+ * @...@ = extra arguments for the skeleton
+ *
+ * Returns: Doesn't
+ *
+ * Use: Reports an error at the current scanner location.
+ */
+
+static void error(scanner *sc, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ fprintf(stderr, "%s: %s:%i: ", QUIS, sc->src, sc->line);
+ vfprintf(stderr, msg, ap);
+ fputc('\n', stderr);
+ exit(1);
+}
+
+/* --- @portnum@ --- *
+ *
+ * Arguments: @scanner *sc@ = pointer to scanner (for error reporting)
+ * @const char *p@ = pointer to port name
+ *
+ * Returns: Port number (network byte order)
+ *
+ * Use: Converts a textual port name or number into a usable thing.
+ */
+
+static unsigned short portnum(scanner *sc, const char *p)
+{
+ struct servent *s;
+ if (isdigit((unsigned char)*p))
+ return (htons(atoi(p)));
+ if ((s = getservbyname(p, "tcp")) == 0)
+ error(sc, "unknown tcp service `%s'", p);
+ return (s->s_port);
+}
+
+/* --- @getconf@ --- *
+ *
+ * Arguments: @scanner *sc@ = pointer to scanner to read from
+ * @listener *l@ = listener to configure (or zero)
+ * @acl_entry ***a@ = pointer to tail of ACL (or zero)
+ *
+ * Returns: ---
+ *
+ * Use: Reads a local or global configuration statement.
+ */
+
+static void getconf(scanner *sc, listener *l, acl_entry ***a)
+{
+ unsigned act;
+
+ /* --- Access control limitations --- */
+
+ if ((strcmp(sc->d.buf, "allow") == 0 && (act = ACL_ALLOW, 1)) ||
+ (strcmp(sc->d.buf, "deny") == 0 && (act = ACL_DENY, 1))) {
+ struct hostent *h;
+ struct netent *n;
+ struct in_addr addr, mask;
+
+ /* --- Find the host or network address --- */
+
+ token(sc);
+ if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "from") == 0)
+ token(sc);
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, address expected");
+ if ((n = getnetbyname(sc->d.buf)) != 0)
+ addr.s_addr = htonl(n->n_net);
+ else if ((h = gethostbyname(sc->d.buf)) == 0)
+ error(sc, "couldn't resolve address `%s'", sc->d.buf);
+ else
+ memcpy(&addr, h->h_addr, sizeof(struct in_addr));
+ token(sc);
+
+ /* --- Find the netmask, if any --- */
+
+ if (sc->t != '/')
+ mask.s_addr = ~0ul;
+ else {
+ token(sc);
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, netmask expected");
+ if (strchr(sc->d.buf, '.') == 0)
+ mask.s_addr = htonl((~0ul << (32 - atoi(sc->d.buf))) & 0xffffffff);
+ else {
+#ifdef HAVE_INET_ATON
+ if (!inet_aton(sc->d.buf, &mask))
+ error(sc, "bad netmask `%s'", sc->d.buf);
+#else
+ mask.s_addr = inet_addr(sc->d.buf);
+#endif
+ }
+ token(sc);
+ }
+
+ /* --- Add the access control entry --- */
+
+ acl_add(a, act, addr, mask);
+ }
+
+ /* --- Anything unrecognized --- */
+
+ else
+ error(sc, "parse error, unknown configuration keyword `%s'", sc->d.buf);
+}
+
+/* --- @conf_parse@ --- *
+ *
+ * Arguments: @void *scp@ = pointer to scanner definition
+ *
+ * Returns: ---
+ *
+ * Use: Parses a configuration file from the scanner.
+ */
+
+void conf_parse(void *scp)
+{
+ scanner *sc = scp;
+
+ token(sc);
+
+ for (;;) {
+ if (sc->t == CTOK_EOF)
+ break;
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, keyword expected");
+
+ /* --- Handle a forwarding request --- */
+
+ if (strcmp(sc->d.buf, "forward") == 0 ||
+ strcmp(sc->d.buf, "fw") == 0) {
+ unsigned short sp, dp;
+ struct sockaddr_in sin;
+ struct hostent *h;
+ int fd;
+ listener *l;
+
+ /* --- Read the source port --- */
+
+ token(sc);
+ if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "port") == 0)
+ token(sc);
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, source port expected");
+ sp = portnum(sc, sc->d.buf);
+
+ /* --- Read the destination address --- */
+
+ token(sc);
+ if (sc->t == CTOK_WORD && strcmp(sc->d.buf, "to") == 0)
+ token(sc);
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, destination address expected");
+ if ((h = gethostbyname(sc->d.buf)) == 0)
+ error(sc, "couldn't resolve address `%s'", sc->d.buf);
+
+ token(sc);
+ if (sc->t == ':')
+ token(sc);
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, destination port expected");
+ dp = portnum(sc, sc->d.buf);
+
+ /* --- Make the socket --- */
+
+ if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
+ error(sc, "couldn't create socket: %s", strerror(errno));
+
+ /* --- Set it to allow address reuse --- */
+
+ {
+ int opt = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+ }
+
+ /* --- Bind it to the right port --- */
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = sp;
+ if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) {
+ error(sc, "couldn't bind to port %i: %s",
+ ntohs(sp), strerror(errno));
+ }
+
+ /* --- Set it to listen for connections --- */
+
+ if (listen(fd, 5))
+ error(sc, "couldn't listen on socket: %s", strerror(errno));
+
+ /* --- Fill in a new listener --- */
+
+ memcpy(&sin.sin_addr, h->h_addr, sizeof(struct in_addr));
+ sin.sin_port = dp;
+ l = listener_add(fd, sp, &sin);
+
+ /* --- Snarf access controls and other attributes --- */
+
+ token(sc);
+ if (sc->t == '{') {
+ acl_entry **a = &l->acl;
+ token(sc);
+ while (sc->t != '}') {
+ if (sc->t != CTOK_WORD)
+ error(sc, "parse error, keyword or `}' expected");
+ getconf(sc, l, &a);
+ if (sc->t == ';')
+ token(sc);
+ }
+ *a = 0;
+ token(sc);
+ }
+ }
+
+ /* --- Other configuration is handled elsewhere --- */
+
+ else
+ getconf(sc, 0, 0);
+
+ if (sc->t == ';')
+ token(sc);
+ }
+}
+
+/*----- That's all, folks -------------------------------------------------*/