diff options
Diffstat (limited to 'src/global/config_known_tcp_ports.c')
-rw-r--r-- | src/global/config_known_tcp_ports.c | 257 |
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 |