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