summaryrefslogtreecommitdiffstats
path: root/src/global/config_known_tcp_ports.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/config_known_tcp_ports.c')
-rw-r--r--src/global/config_known_tcp_ports.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/src/global/config_known_tcp_ports.c b/src/global/config_known_tcp_ports.c
new file mode 100644
index 0000000..563bbd3
--- /dev/null
+++ b/src/global/config_known_tcp_ports.c
@@ -0,0 +1,257 @@
+/*++
+/* NAME
+/* config_known_tcp_ports 3
+/* SUMMARY
+/* parse and store known TCP port configuration
+/* SYNOPSIS
+/* #include <config_known_tcp_ports.h>
+/*
+/* void config_known_tcp_ports(
+/* const char *source,
+/* const char *settings);
+/* DESCRIPTION
+/* config_known_tcp_ports() parses the known TCP port information
+/* in the settings argument, and reports any warnings to the standard
+/* error stream. The source argument is used to provide warning
+/* context. It typically is a configuration parameter name.
+/* .SH EXPECTED SYNTAX (ABNF)
+/* configuration = empty | name-to-port *("," name-to-port)
+/* name-to-port = 1*(name "=") port
+/* SH EXAMPLES
+/* In the example below, the whitespace is optional.
+/* smtp = 25, smtps = submissions = 465, submission = 587
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+ /*
+ * System library.
+ */
+#include <sys_defs.h>
+
+ /*
+ * Utility library.
+ */
+#include <argv.h>
+#include <known_tcp_ports.h>
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+ /*
+ * Application-specific.
+ */
+#include <config_known_tcp_ports.h>
+
+/* config_known_tcp_ports - parse configuration and store associations */
+
+void config_known_tcp_ports(const char *source, const char *settings)
+{
+ ARGV *associations;
+ ARGV *association;
+ char **cpp;
+
+ clear_known_tcp_ports();
+
+ /*
+ * The settings is in the form of associations separated by comma. Split
+ * it into separate associations.
+ */
+ associations = argv_split(settings, ",");
+ if (associations->argc == 0) {
+ argv_free(associations);
+ return;
+ }
+
+ /*
+ * Each association is in the form of "1*(name =) port". We use
+ * argv_split() to carve this up, then we use mystrtok() to validate the
+ * individual fragments. But first we prepend and append space so that we
+ * get sensible results when an association starts or ends in "=".
+ */
+ for (cpp = associations->argv; *cpp != 0; cpp++) {
+ char *temp = concatenate(" ", *cpp, " ", (char *) 0);
+
+ association = argv_split_at(temp, '=');
+ myfree(temp);
+
+ if (association->argc == 0) {
+ /* empty, ignore */ ;
+ } else if (association->argc == 1) {
+ msg_warn("%s: in \"%s\" is not in \"name = value\" form",
+ source, *cpp);
+ } else {
+ char *bp;
+ char *lhs;
+ char *rhs;
+ const char *err = 0;
+ int n;
+
+ bp = association->argv[association->argc - 1];
+ if ((rhs = mystrtok(&bp, CHARS_SPACE)) == 0) {
+ err = "missing port value after \"=\"";
+ } else if (mystrtok(&bp, CHARS_SPACE) != 0) {
+ err = "whitespace in port number";
+ } else {
+ for (n = 0; n < association->argc - 1; n++) {
+ const char *new_err;
+
+ bp = association->argv[n];
+ if ((lhs = mystrtok(&bp, CHARS_SPACE)) == 0) {
+ new_err = "missing service name before \"=\"";
+ } else if (mystrtok(&bp, CHARS_SPACE) != 0) {
+ new_err = "whitespace in service name";
+ } else {
+ new_err = add_known_tcp_port(lhs, rhs);
+ }
+ if (new_err != 0 && err == 0)
+ err = new_err;
+ }
+ }
+ if (err != 0) {
+ msg_warn("%s: in \"%s\": %s", source, *cpp, err);
+ }
+ }
+ argv_free(association);
+ }
+ argv_free(associations);
+}
+
+#ifdef TEST
+
+#include <stdlib.h>
+#include <string.h>
+#include <msg_vstream.h>
+
+#define STR(x) vstring_str(x)
+
+ /* TODO(wietse) make this a proper VSTREAM interface */
+
+/* vstream_swap - kludge to capture output for testing */
+
+static void vstream_swap(VSTREAM *one, VSTREAM *two)
+{
+ VSTREAM save;
+
+ save = *one;
+ *one = *two;
+ *two = save;
+}
+
+struct test_case {
+ const char *label; /* identifies test case */
+ const char *config; /* configuration under test */
+ const char *exp_warning; /* expected warning or null */
+ const char *exp_export; /* expected export or null */
+};
+
+static struct test_case test_cases[] = {
+ {"good",
+ /* config */ "smtp = 25, smtps = submissions = 465, lmtp = 24",
+ /* warning */ "",
+ /* export */ "lmtp=24 smtp=25 smtps=465 submissions=465"
+ },
+ {"equal-equal",
+ /* config */ "smtp = 25, smtps == submissions = 465, lmtp = 24",
+ /* warning */ "config_known_tcp_ports: warning: equal-equal: "
+ "in \" smtps == submissions = 465\": missing service name before "
+ "\"=\"\n",
+ /* export */ "lmtp=24 smtp=25 smtps=465 submissions=465"
+ },
+ {"port test 1",
+ /* config */ "smtps = submission =",
+ /* warning */ "config_known_tcp_ports: warning: port test 1: "
+ "in \"smtps = submission =\": missing port value after \"=\"\n",
+ /* export */ ""
+ },
+ {"port test 2",
+ /* config */ "smtps = submission = 4 65",
+ /* warning */ "config_known_tcp_ports: warning: port test 2: "
+ "in \"smtps = submission = 4 65\": whitespace in port number\n",
+ /* export */ ""
+ },
+ {"port test 3",
+ /* config */ "lmtp = 24, smtps = submission = foo",
+ /* warning */ "config_known_tcp_ports: warning: port test 3: "
+ "in \" smtps = submission = foo\": non-numerical service port\n",
+ /* export */ "lmtp=24"
+ },
+ {"service name test 1",
+ /* config */ "smtps = sub mission = 465",
+ /* warning */ "config_known_tcp_ports: warning: service name test 1: "
+ "in \"smtps = sub mission = 465\": whitespace in service name\n",
+ /* export */ "smtps=465"
+ },
+ {"service name test 2",
+ /* config */ "lmtp = 24, smtps = 1234 = submissions = 465",
+ /* warning */ "config_known_tcp_ports: warning: service name test 2: "
+ "in \" smtps = 1234 = submissions = 465\": numerical service name\n",
+ /* export */ "lmtp=24 smtps=465 submissions=465"
+ },
+ 0,
+};
+
+int main(int argc, char **argv)
+{
+ VSTRING *export_buf;
+ struct test_case *tp;
+ int pass = 0;
+ int fail = 0;
+ int test_failed;
+ const char *export;
+ VSTRING *msg_buf;
+ VSTREAM *memory_stream;
+
+#define STRING_OR_NULL(s) ((s) ? (s) : "(null)")
+
+ msg_vstream_init("config_known_tcp_ports", VSTREAM_ERR);
+
+ export_buf = vstring_alloc(100);
+ msg_buf = vstring_alloc(100);
+ for (tp = test_cases; tp->label != 0; tp++) {
+ test_failed = 0;
+ if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0)
+ msg_fatal("open memory stream: %m");
+ vstream_swap(VSTREAM_ERR, memory_stream);
+ config_known_tcp_ports(tp->label, tp->config);
+ vstream_swap(memory_stream, VSTREAM_ERR);
+ if (vstream_fclose(memory_stream))
+ msg_fatal("close memory stream: %m");
+ if (strcmp(STR(msg_buf), tp->exp_warning) != 0) {
+ msg_warn("test case %s: got error: \"%s\", want: \"%s\"",
+ tp->label, STR(msg_buf),
+ STRING_OR_NULL(tp->exp_warning));
+ test_failed = 1;
+ } else {
+ export = export_known_tcp_ports(export_buf);
+ if (strcmp(export, tp->exp_export) != 0) {
+ msg_warn("test case %s: got export: \"%s\", want: \"%s\"",
+ tp->label, export, tp->exp_export);
+ test_failed = 1;
+ }
+ clear_known_tcp_ports();
+ VSTRING_RESET(msg_buf);
+ VSTRING_TERMINATE(msg_buf);
+ }
+ if (test_failed) {
+ msg_info("%s: FAIL", tp->label);
+ fail++;
+ } else {
+ msg_info("%s: PASS", tp->label);
+ pass++;
+ }
+ }
+ msg_info("PASS=%d FAIL=%d", pass, fail);
+ vstring_free(msg_buf);
+ vstring_free(export_buf);
+ exit(fail != 0);
+}
+
+#endif