/*++ /* NAME /* known_tcp_ports 3 /* SUMMARY /* reduce dependency on the services(5) database /* SYNOPSIS /* #include /* /* 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 #include #include /* * Utility library */ #include #include #include /* * Application-specific. */ #include #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 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