xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/known_tcp_ports.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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 
add_known_tcp_port(const char * name,const char * port)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 
filter_known_tcp_port(const char * name_or_port)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 
clear_known_tcp_ports(void)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 
compare_ht_keys(const void * a,const void * b)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 
export_known_tcp_ports(VSTRING * out)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 
main(int argc,char ** argv)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