summaryrefslogtreecommitdiffstats
path: root/src/util/known_tcp_ports.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/known_tcp_ports.c')
-rw-r--r--src/util/known_tcp_ports.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/src/util/known_tcp_ports.c b/src/util/known_tcp_ports.c
new file mode 100644
index 0000000..1d524d4
--- /dev/null
+++ b/src/util/known_tcp_ports.c
@@ -0,0 +1,253 @@
+/*++
+/* NAME
+/* known_tcp_ports 3
+/* SUMMARY
+/* reduce dependency on the services(5) database
+/* SYNOPSIS
+/* #include <known_tcp_ports.h>
+/*
+/* const char *add_known_tcp_port(
+/* const char *name)
+/* const char *port)
+/*
+/* const char *filter_known_tcp_port(
+/* const char *name_or_port)
+/*
+/* void clear_known_tcp_ports(void)
+/* AUXILIARY FUNCTIONS
+/* char *export_known_tcp_ports(
+/* VSTRING *result)
+/* DESCRIPTION
+/* This module reduces dependency on the services(5) database.
+/*
+/* add_known_tcp_port() associates a symbolic name with a numerical
+/* port. The function returns a pointer to error text if the
+/* arguments are malformed or if the symbolic name already has
+/* an association.
+/*
+/* filter_known_tcp_port() returns the argument if it does not
+/* specify a symbolic name, or if the argument specifies a symbolic
+/* name that is not associated with a numerical port. Otherwise,
+/* it returns the associated numerical port.
+/*
+/* clear_known_tcp_ports() destroys all name-number associations.
+/* string.
+/*
+/* export_known_tcp_ports() overwrites a VSTRING with all known
+/* name=port associations, sorted by service name, and separated
+/* by whitespace. The result is pointer to the VSTRING payload.
+/* 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>
+#include <stdlib.h>
+#include <string.h>
+
+ /*
+ * Utility library
+ */
+#include <htable.h>
+#include <mymalloc.h>
+#include <stringops.h>
+
+ /*
+ * Application-specific.
+ */
+#include <known_tcp_ports.h>
+
+#define STR(x) vstring_str(x)
+
+static HTABLE *known_tcp_ports;
+
+/* add_known_tcp_port - associate symbolic name with numerical port */
+
+const char *add_known_tcp_port(const char *name, const char *port)
+{
+ if (alldig(name))
+ return ("numerical service name");
+ if (!alldig(port))
+ return ("non-numerical service port");
+ if (known_tcp_ports == 0)
+ known_tcp_ports = htable_create(10);
+ if (htable_locate(known_tcp_ports, name) != 0)
+ return ("duplicate service name");
+ (void) htable_enter(known_tcp_ports, name, mystrdup(port));
+ return (0);
+}
+
+/* filter_known_tcp_port - replace argument if associated with known port */
+
+const char *filter_known_tcp_port(const char *name_or_port)
+{
+ HTABLE_INFO *ht;
+
+ if (name_or_port == 0 || known_tcp_ports == 0 || alldig(name_or_port)) {
+ return (name_or_port);
+ } else if ((ht = htable_locate(known_tcp_ports, name_or_port)) != 0) {
+ return (ht->value);
+ } else {
+ return (name_or_port);
+ }
+}
+
+/* clear_known_tcp_ports - destroy all name-port associations */
+
+void clear_known_tcp_ports(void)
+{
+ htable_free(known_tcp_ports, myfree);
+ known_tcp_ports = 0;
+}
+
+/* compare_ht_keys - compare table keys */
+
+static int compare_ht_keys(const void *a, const void *b)
+{
+ HTABLE_INFO **ap = (HTABLE_INFO **) a;
+ HTABLE_INFO **bp = (HTABLE_INFO **) b;
+
+ return (strcmp((const char *) ap[0]->key, (const char *) bp[0]->key));
+}
+
+/* export_known_tcp_ports - sorted dump */
+
+char *export_known_tcp_ports(VSTRING *out)
+{
+ HTABLE_INFO **list;
+ HTABLE_INFO **ht;
+
+ VSTRING_RESET(out);
+ if (known_tcp_ports) {
+ list = htable_list(known_tcp_ports);
+ qsort((void *) list, known_tcp_ports->used, sizeof(*list),
+ compare_ht_keys);
+ for (ht = list; *ht; ht++)
+ vstring_sprintf_append(out, "%s%s=%s", ht > list ? " " : "",
+ ht[0]->key, (const char *) ht[0]->value);
+ myfree((void *) list);
+ }
+ VSTRING_TERMINATE(out);
+ return (STR(out));
+}
+
+#ifdef TEST
+
+#include <msg.h>
+
+struct association {
+ const char *lhs; /* service name */
+ const char *rhs; /* service port */
+};
+
+struct probe {
+ const char *query; /* query */
+ const char *exp_reply; /* expected reply */
+};
+
+struct test_case {
+ const char *label; /* identifies test case */
+ struct association associations[10];
+ const char *exp_err; /* expected error */
+ const char *exp_export; /* expected export output */
+ struct probe probes[10];
+};
+
+struct test_case test_cases[] = {
+ {"good",
+ /* association */ {{"smtp", "25"}, {"lmtp", "24"}, 0},
+ /* error */ 0,
+ /* export */ "lmtp=24 smtp=25",
+ /* probe */ {{"smtp", "25"}, {"1", "1"}, {"x", "x"}, {"lmtp", "24"}, 0}
+ },
+ {"duplicate lhs",
+ /* association */ {{"smtp", "25"}, {"smtp", "100"}, 0},
+ /* error */ "duplicate service name"
+ },
+ {"numerical lhs",
+ /* association */ {{"100", "100"}, 0},
+ /* error */ "numerical service name"
+ },
+ {"symbolic rhs",
+ /* association */ {{"smtp", "lmtp"}, 0},
+ /* error */ "non-numerical service port"
+ },
+ {"uninitialized",
+ /* association */ {0},
+ /* error */ 0,
+ /* export */ "",
+ /* probe */ {{"smtp", "smtp"}, {"1", "1"}, {"x", "x"}, 0}
+ },
+ 0,
+};
+
+int main(int argc, char **argv)
+{
+ VSTRING *export_buf;
+ struct test_case *tp;
+ struct association *ap;
+ struct probe *pp;
+ int pass = 0;
+ int fail = 0;
+ const char *err;
+ int test_failed;
+ const char *reply;
+ const char *export;
+
+#define STRING_OR_NULL(s) ((s) ? (s) : "(null)")
+
+ export_buf = vstring_alloc(100);
+ for (tp = test_cases; tp->label != 0; tp++) {
+ test_failed = 0;
+ for (err = 0, ap = tp->associations; err == 0 && ap->lhs != 0; ap++)
+ err = add_known_tcp_port(ap->lhs, ap->rhs);
+ if (!err != !tp->exp_err) {
+ msg_warn("test case %s: got error: \"%s\", want: \"%s\"",
+ tp->label, STRING_OR_NULL(err), STRING_OR_NULL(tp->exp_err));
+ test_failed = 1;
+ } else if (err != 0) {
+ if (strcmp(err, tp->exp_err) != 0) {
+ msg_warn("test case %s: got err: \"%s\", want: \"%s\"",
+ tp->label, err, tp->exp_err);
+ 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;
+ }
+ for (pp = tp->probes; test_failed == 0 && pp->query != 0; pp++) {
+ reply = filter_known_tcp_port(pp->query);
+ if (strcmp(reply, pp->exp_reply) != 0) {
+ msg_warn("test case %s: got reply: \"%s\", want: \"%s\"",
+ tp->label, reply, pp->exp_reply);
+ test_failed = 1;
+ }
+ }
+ }
+ clear_known_tcp_ports();
+ 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(export_buf);
+ exit(fail != 0);
+}
+
+#endif