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