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