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