Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: known_tcp_ports.c,v 1.2 2022/10/08 16:12:50 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	known_tcp_ports 3
      6 /* SUMMARY
      7 /*	reduce dependency on the services(5) database
      8 /* SYNOPSIS
      9 /*	#include <known_tcp_ports.h>
     10 /*
     11 /*	const char *add_known_tcp_port(
     12 /*	const char *name)
     13 /*	const char *port)
     14 /*
     15 /*	const char *filter_known_tcp_port(
     16 /*	const char *name_or_port)
     17 /*
     18 /*	void    clear_known_tcp_ports(void)
     19 /* AUXILIARY FUNCTIONS
     20 /*	char	*export_known_tcp_ports(
     21 /*	VSTRING *result)
     22 /* DESCRIPTION
     23 /*	This module reduces dependency on the services(5) database.
     24 /*
     25 /*	add_known_tcp_port() associates a symbolic name with a numerical
     26 /*	port. The function returns a pointer to error text if the
     27 /*	arguments are malformed or if the symbolic name already has
     28 /*	an association.
     29 /*
     30 /*	filter_known_tcp_port() returns the argument if it does not
     31 /*	specify a symbolic name, or if the argument specifies a symbolic
     32 /*	name that is not associated with a numerical port.  Otherwise,
     33 /*	it returns the associated numerical port.
     34 /*
     35 /*	clear_known_tcp_ports() destroys all name-number associations.
     36 /*	string.
     37 /*
     38 /*	export_known_tcp_ports() overwrites a VSTRING with all known
     39 /*	name=port associations, sorted by service name, and separated
     40 /*	by whitespace. The result is pointer to the VSTRING payload.
     41 /* LICENSE
     42 /* .ad
     43 /* .fi
     44 /*	The Secure Mailer license must be distributed with this software.
     45 /* AUTHOR(S)
     46 /*	Wietse Venema
     47 /*	Google, Inc.
     48 /*	111 8th Avenue
     49 /*	New York, NY 10011, USA
     50 /*--*/
     51 
     52  /*
     53   * System library
     54   */
     55 #include <sys_defs.h>
     56 #include <stdlib.h>
     57 #include <string.h>
     58 
     59  /*
     60   * Utility library
     61   */
     62 #include <htable.h>
     63 #include <mymalloc.h>
     64 #include <stringops.h>
     65 
     66  /*
     67   * Application-specific.
     68   */
     69 #include <known_tcp_ports.h>
     70 
     71 #define STR(x)	vstring_str(x)
     72 
     73 static HTABLE *known_tcp_ports;
     74 
     75 /* add_known_tcp_port - associate symbolic name with numerical port */
     76 
     77 const char *add_known_tcp_port(const char *name, const char *port)
     78 {
     79     if (alldig(name))
     80 	return ("numerical service name");
     81     if (!alldig(port))
     82 	return ("non-numerical service port");
     83     if (known_tcp_ports == 0)
     84 	known_tcp_ports = htable_create(10);
     85     if (htable_locate(known_tcp_ports, name) != 0)
     86 	return ("duplicate service name");
     87     (void) htable_enter(known_tcp_ports, name, mystrdup(port));
     88     return (0);
     89 }
     90 
     91 /* filter_known_tcp_port - replace argument if associated with known port */
     92 
     93 const char *filter_known_tcp_port(const char *name_or_port)
     94 {
     95     HTABLE_INFO *ht;
     96 
     97     if (name_or_port == 0 || known_tcp_ports == 0 || alldig(name_or_port)) {
     98 	return (name_or_port);
     99     } else if ((ht = htable_locate(known_tcp_ports, name_or_port)) != 0) {
    100 	return (ht->value);
    101     } else {
    102 	return (name_or_port);
    103     }
    104 }
    105 
    106 /* clear_known_tcp_ports - destroy all name-port associations */
    107 
    108 void    clear_known_tcp_ports(void)
    109 {
    110     htable_free(known_tcp_ports, myfree);
    111     known_tcp_ports = 0;
    112 }
    113 
    114 /* compare_ht_keys - compare table keys */
    115 
    116 static int compare_ht_keys(const void *a, const void *b)
    117 {
    118     HTABLE_INFO **ap = (HTABLE_INFO **) a;
    119     HTABLE_INFO **bp = (HTABLE_INFO **) b;
    120 
    121     return (strcmp((const char *) ap[0]->key, (const char *) bp[0]->key));
    122 }
    123 
    124 /* export_known_tcp_ports - sorted dump */
    125 
    126 char   *export_known_tcp_ports(VSTRING *out)
    127 {
    128     HTABLE_INFO **list;
    129     HTABLE_INFO **ht;
    130 
    131     VSTRING_RESET(out);
    132     if (known_tcp_ports) {
    133 	list = htable_list(known_tcp_ports);
    134 	qsort((void *) list, known_tcp_ports->used, sizeof(*list),
    135 	      compare_ht_keys);
    136 	for (ht = list; *ht; ht++)
    137 	    vstring_sprintf_append(out, "%s%s=%s", ht > list ? " " : "",
    138 				   ht[0]->key, (const char *) ht[0]->value);
    139 	myfree((void *) list);
    140     }
    141     VSTRING_TERMINATE(out);
    142     return (STR(out));
    143 }
    144 
    145 #ifdef TEST
    146 
    147 #include <msg.h>
    148 
    149 struct association {
    150     const char *lhs;			/* service name */
    151     const char *rhs;			/* service port */
    152 };
    153 
    154 struct probe {
    155     const char *query;			/* query */
    156     const char *exp_reply;		/* expected reply */
    157 };
    158 
    159 struct test_case {
    160     const char *label;			/* identifies test case */
    161     struct association associations[10];
    162     const char *exp_err;		/* expected error */
    163     const char *exp_export;		/* expected export output */
    164     struct probe probes[10];
    165 };
    166 
    167 struct test_case test_cases[] = {
    168     {"good",
    169 	 /* association */ {{"smtp", "25"}, {"lmtp", "24"}, 0},
    170 	 /* error */ 0,
    171 	 /* export */ "lmtp=24 smtp=25",
    172 	 /* probe */ {{"smtp", "25"}, {"1", "1"}, {"x", "x"}, {"lmtp", "24"}, 0}
    173     },
    174     {"duplicate lhs",
    175 	 /* association */ {{"smtp", "25"}, {"smtp", "100"}, 0},
    176 	 /* error */ "duplicate service name"
    177     },
    178     {"numerical lhs",
    179 	 /* association */ {{"100", "100"}, 0},
    180 	 /* error */ "numerical service name"
    181     },
    182     {"symbolic rhs",
    183 	 /* association */ {{"smtp", "lmtp"}, 0},
    184 	 /* error */ "non-numerical service port"
    185     },
    186     {"uninitialized",
    187 	 /* association */ {0},
    188 	 /* error */ 0,
    189 	 /* export */ "",
    190 	 /* probe */ {{"smtp", "smtp"}, {"1", "1"}, {"x", "x"}, 0}
    191     },
    192     0,
    193 };
    194 
    195 int     main(int argc, char **argv)
    196 {
    197     VSTRING *export_buf;
    198     struct test_case *tp;
    199     struct association *ap;
    200     struct probe *pp;
    201     int     pass = 0;
    202     int     fail = 0;
    203     const char *err;
    204     int     test_failed;
    205     const char *reply;
    206     const char *export;
    207 
    208 #define STRING_OR_NULL(s) ((s) ? (s) : "(null)")
    209 
    210     export_buf = vstring_alloc(100);
    211     for (tp = test_cases; tp->label != 0; tp++) {
    212 	test_failed = 0;
    213 	for (err = 0, ap = tp->associations; err == 0 && ap->lhs != 0; ap++)
    214 	    err = add_known_tcp_port(ap->lhs, ap->rhs);
    215 	if (!err != !tp->exp_err) {
    216 	    msg_warn("test case %s: got error: \"%s\", want: \"%s\"",
    217 	       tp->label, STRING_OR_NULL(err), STRING_OR_NULL(tp->exp_err));
    218 	    test_failed = 1;
    219 	} else if (err != 0) {
    220 	    if (strcmp(err, tp->exp_err) != 0) {
    221 		msg_warn("test case %s: got err: \"%s\", want: \"%s\"",
    222 			 tp->label, err, tp->exp_err);
    223 		test_failed = 1;
    224 	    }
    225 	} else {
    226 	    export = export_known_tcp_ports(export_buf);
    227 	    if (strcmp(export, tp->exp_export) != 0) {
    228 		msg_warn("test case %s: got export: \"%s\", want: \"%s\"",
    229 			 tp->label, export, tp->exp_export);
    230 		test_failed = 1;
    231 	    }
    232 	    for (pp = tp->probes; test_failed == 0 && pp->query != 0; pp++) {
    233 		reply = filter_known_tcp_port(pp->query);
    234 		if (strcmp(reply, pp->exp_reply) != 0) {
    235 		    msg_warn("test case %s: got reply: \"%s\", want: \"%s\"",
    236 			     tp->label, reply, pp->exp_reply);
    237 		    test_failed = 1;
    238 		}
    239 	    }
    240 	}
    241 	clear_known_tcp_ports();
    242 	if (test_failed) {
    243 	    msg_info("%s: FAIL", tp->label);
    244 	    fail++;
    245 	} else {
    246 	    msg_info("%s: PASS", tp->label);
    247 	    pass++;
    248 	}
    249     }
    250     msg_info("PASS=%d FAIL=%d", pass, fail);
    251     vstring_free(export_buf);
    252     exit(fail != 0);
    253 }
    254 
    255 #endif
    256