1 /* $NetBSD: wks_11.c,v 1.11 2025/01/26 16:25:35 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #ifndef RDATA_IN_1_WKS_11_C 17 #define RDATA_IN_1_WKS_11_C 18 19 #include <limits.h> 20 #include <netdb.h> 21 22 #include <isc/ascii.h> 23 #include <isc/net.h> 24 #include <isc/once.h> 25 26 #define RRTYPE_WKS_ATTRIBUTES (0) 27 28 static isc_mutex_t wks_lock; 29 30 static void 31 init_lock(void) { 32 isc_mutex_init(&wks_lock); 33 } 34 35 static bool 36 mygetprotobyname(const char *name, long *proto) { 37 struct protoent *pe; 38 39 LOCK(&wks_lock); 40 pe = getprotobyname(name); 41 if (pe != NULL) { 42 *proto = pe->p_proto; 43 } 44 UNLOCK(&wks_lock); 45 return pe != NULL; 46 } 47 48 static bool 49 mygetservbyname(const char *name, const char *proto, long *port) { 50 struct servent *se; 51 52 LOCK(&wks_lock); 53 se = getservbyname(name, proto); 54 if (se != NULL) { 55 *port = ntohs(se->s_port); 56 } 57 UNLOCK(&wks_lock); 58 return se != NULL; 59 } 60 61 static isc_result_t 62 fromtext_in_wks(ARGS_FROMTEXT) { 63 static isc_once_t once = ISC_ONCE_INIT; 64 isc_token_t token; 65 isc_region_t region; 66 struct in_addr addr; 67 char *e = NULL; 68 long proto; 69 unsigned char bm[8 * 1024]; /* 64k bits */ 70 long port; 71 long maxport = -1; 72 const char *ps = NULL; 73 unsigned int n; 74 char service[32]; 75 isc_result_t result; 76 77 REQUIRE(type == dns_rdatatype_wks); 78 REQUIRE(rdclass == dns_rdataclass_in); 79 80 UNUSED(type); 81 UNUSED(origin); 82 UNUSED(options); 83 UNUSED(rdclass); 84 UNUSED(callbacks); 85 86 isc_once_do(&once, init_lock); 87 88 /* 89 * IPv4 dotted quad. 90 */ 91 CHECK(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 92 false)); 93 94 isc_buffer_availableregion(target, ®ion); 95 if (inet_pton(AF_INET, DNS_AS_STR(token), &addr) != 1) { 96 CHECKTOK(DNS_R_BADDOTTEDQUAD); 97 } 98 if (region.length < 4) { 99 return ISC_R_NOSPACE; 100 } 101 memmove(region.base, &addr, 4); 102 isc_buffer_add(target, 4); 103 104 /* 105 * Protocol. 106 */ 107 CHECK(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 108 false)); 109 110 proto = strtol(DNS_AS_STR(token), &e, 10); 111 if (*e != '\0' && !mygetprotobyname(DNS_AS_STR(token), &proto)) { 112 CHECKTOK(DNS_R_UNKNOWNPROTO); 113 } 114 115 if (proto < 0 || proto > 0xff) { 116 CHECKTOK(ISC_R_RANGE); 117 } 118 119 if (proto == IPPROTO_TCP) { 120 ps = "tcp"; 121 } else if (proto == IPPROTO_UDP) { 122 ps = "udp"; 123 } 124 125 CHECK(uint8_tobuffer(proto, target)); 126 127 memset(bm, 0, sizeof(bm)); 128 do { 129 CHECK(isc_lex_getmastertoken(lexer, &token, 130 isc_tokentype_string, true)); 131 if (token.type != isc_tokentype_string) { 132 break; 133 } 134 135 /* 136 * Lowercase the service string as some getservbyname() are 137 * case sensitive and the database is usually in lowercase. 138 */ 139 strlcpy(service, DNS_AS_STR(token), sizeof(service)); 140 isc_ascii_strtolower(service); 141 142 port = strtol(DNS_AS_STR(token), &e, 10); 143 if (*e != 0 && !mygetservbyname(service, ps, &port) && 144 !mygetservbyname(DNS_AS_STR(token), ps, &port)) 145 { 146 CHECKTOK(DNS_R_UNKNOWNSERVICE); 147 } 148 if (port < 0 || port > 0xffff) { 149 CHECKTOK(ISC_R_RANGE); 150 } 151 if (port > maxport) { 152 maxport = port; 153 } 154 bm[port / 8] |= (0x80 >> (port % 8)); 155 } while (1); 156 157 /* 158 * Let upper layer handle eol/eof. 159 */ 160 isc_lex_ungettoken(lexer, &token); 161 162 n = (maxport + 8) / 8; 163 result = mem_tobuffer(target, bm, n); 164 165 cleanup: 166 return result; 167 } 168 169 static isc_result_t 170 totext_in_wks(ARGS_TOTEXT) { 171 isc_region_t sr; 172 unsigned short proto; 173 char buf[sizeof("65535")]; 174 unsigned int i, j; 175 176 UNUSED(tctx); 177 178 REQUIRE(rdata->type == dns_rdatatype_wks); 179 REQUIRE(rdata->rdclass == dns_rdataclass_in); 180 REQUIRE(rdata->length >= 5); 181 182 dns_rdata_toregion(rdata, &sr); 183 RETERR(inet_totext(AF_INET, tctx->flags, &sr, target)); 184 isc_region_consume(&sr, 4); 185 186 proto = uint8_fromregion(&sr); 187 snprintf(buf, sizeof(buf), "%u", proto); 188 RETERR(str_totext(" ", target)); 189 RETERR(str_totext(buf, target)); 190 isc_region_consume(&sr, 1); 191 192 INSIST(sr.length <= 8 * 1024); 193 for (i = 0; i < sr.length; i++) { 194 if (sr.base[i] != 0) { 195 for (j = 0; j < 8; j++) { 196 if ((sr.base[i] & (0x80 >> j)) != 0) { 197 { 198 snprintf(buf, sizeof(buf), "%u", 199 i * 8 + j); 200 RETERR(str_totext(" ", target)); 201 RETERR(str_totext(buf, target)); 202 } 203 } 204 } 205 } 206 } 207 208 return ISC_R_SUCCESS; 209 } 210 211 static isc_result_t 212 fromwire_in_wks(ARGS_FROMWIRE) { 213 isc_region_t sr; 214 isc_region_t tr; 215 216 REQUIRE(type == dns_rdatatype_wks); 217 REQUIRE(rdclass == dns_rdataclass_in); 218 219 UNUSED(type); 220 UNUSED(dctx); 221 UNUSED(rdclass); 222 223 isc_buffer_activeregion(source, &sr); 224 isc_buffer_availableregion(target, &tr); 225 226 if (sr.length < 5) { 227 return ISC_R_UNEXPECTEDEND; 228 } 229 if (sr.length > 8 * 1024 + 5) { 230 return DNS_R_EXTRADATA; 231 } 232 if (sr.length > 5 && sr.base[sr.length - 1] == 0) { 233 return DNS_R_FORMERR; 234 } 235 if (tr.length < sr.length) { 236 return ISC_R_NOSPACE; 237 } 238 239 memmove(tr.base, sr.base, sr.length); 240 isc_buffer_add(target, sr.length); 241 isc_buffer_forward(source, sr.length); 242 243 return ISC_R_SUCCESS; 244 } 245 246 static isc_result_t 247 towire_in_wks(ARGS_TOWIRE) { 248 isc_region_t sr; 249 250 UNUSED(cctx); 251 252 REQUIRE(rdata->type == dns_rdatatype_wks); 253 REQUIRE(rdata->rdclass == dns_rdataclass_in); 254 REQUIRE(rdata->length != 0); 255 256 dns_rdata_toregion(rdata, &sr); 257 return mem_tobuffer(target, sr.base, sr.length); 258 } 259 260 static int 261 compare_in_wks(ARGS_COMPARE) { 262 isc_region_t r1; 263 isc_region_t r2; 264 265 REQUIRE(rdata1->type == rdata2->type); 266 REQUIRE(rdata1->rdclass == rdata2->rdclass); 267 REQUIRE(rdata1->type == dns_rdatatype_wks); 268 REQUIRE(rdata1->rdclass == dns_rdataclass_in); 269 REQUIRE(rdata1->length != 0); 270 REQUIRE(rdata2->length != 0); 271 272 dns_rdata_toregion(rdata1, &r1); 273 dns_rdata_toregion(rdata2, &r2); 274 return isc_region_compare(&r1, &r2); 275 } 276 277 static isc_result_t 278 fromstruct_in_wks(ARGS_FROMSTRUCT) { 279 dns_rdata_in_wks_t *wks = source; 280 uint32_t a; 281 282 REQUIRE(type == dns_rdatatype_wks); 283 REQUIRE(rdclass == dns_rdataclass_in); 284 REQUIRE(wks != NULL); 285 REQUIRE(wks->common.rdtype == type); 286 REQUIRE(wks->common.rdclass == rdclass); 287 REQUIRE((wks->map != NULL && wks->map_len <= 8 * 1024) || 288 wks->map_len == 0); 289 290 UNUSED(type); 291 UNUSED(rdclass); 292 293 a = ntohl(wks->in_addr.s_addr); 294 RETERR(uint32_tobuffer(a, target)); 295 RETERR(uint8_tobuffer(wks->protocol, target)); 296 return mem_tobuffer(target, wks->map, wks->map_len); 297 } 298 299 static isc_result_t 300 tostruct_in_wks(ARGS_TOSTRUCT) { 301 dns_rdata_in_wks_t *wks = target; 302 uint32_t n; 303 isc_region_t region; 304 305 REQUIRE(wks != NULL); 306 REQUIRE(rdata->type == dns_rdatatype_wks); 307 REQUIRE(rdata->rdclass == dns_rdataclass_in); 308 REQUIRE(rdata->length != 0); 309 310 wks->common.rdclass = rdata->rdclass; 311 wks->common.rdtype = rdata->type; 312 ISC_LINK_INIT(&wks->common, link); 313 314 dns_rdata_toregion(rdata, ®ion); 315 n = uint32_fromregion(®ion); 316 wks->in_addr.s_addr = htonl(n); 317 isc_region_consume(®ion, 4); 318 wks->protocol = uint8_fromregion(®ion); 319 isc_region_consume(®ion, 1); 320 wks->map_len = region.length; 321 wks->map = mem_maybedup(mctx, region.base, region.length); 322 wks->mctx = mctx; 323 return ISC_R_SUCCESS; 324 } 325 326 static void 327 freestruct_in_wks(ARGS_FREESTRUCT) { 328 dns_rdata_in_wks_t *wks = source; 329 330 REQUIRE(wks != NULL); 331 REQUIRE(wks->common.rdtype == dns_rdatatype_wks); 332 REQUIRE(wks->common.rdclass == dns_rdataclass_in); 333 334 if (wks->mctx == NULL) { 335 return; 336 } 337 338 if (wks->map != NULL) { 339 isc_mem_free(wks->mctx, wks->map); 340 } 341 wks->mctx = NULL; 342 } 343 344 static isc_result_t 345 additionaldata_in_wks(ARGS_ADDLDATA) { 346 REQUIRE(rdata->type == dns_rdatatype_wks); 347 REQUIRE(rdata->rdclass == dns_rdataclass_in); 348 349 UNUSED(rdata); 350 UNUSED(owner); 351 UNUSED(add); 352 UNUSED(arg); 353 354 return ISC_R_SUCCESS; 355 } 356 357 static isc_result_t 358 digest_in_wks(ARGS_DIGEST) { 359 isc_region_t r; 360 361 REQUIRE(rdata->type == dns_rdatatype_wks); 362 REQUIRE(rdata->rdclass == dns_rdataclass_in); 363 364 dns_rdata_toregion(rdata, &r); 365 366 return (digest)(arg, &r); 367 } 368 369 static bool 370 checkowner_in_wks(ARGS_CHECKOWNER) { 371 REQUIRE(type == dns_rdatatype_wks); 372 REQUIRE(rdclass == dns_rdataclass_in); 373 374 UNUSED(type); 375 UNUSED(rdclass); 376 377 return dns_name_ishostname(name, wildcard); 378 } 379 380 static bool 381 checknames_in_wks(ARGS_CHECKNAMES) { 382 REQUIRE(rdata->type == dns_rdatatype_wks); 383 REQUIRE(rdata->rdclass == dns_rdataclass_in); 384 385 UNUSED(rdata); 386 UNUSED(owner); 387 UNUSED(bad); 388 389 return true; 390 } 391 392 static int 393 casecompare_in_wks(ARGS_COMPARE) { 394 return compare_in_wks(rdata1, rdata2); 395 } 396 397 #endif /* RDATA_IN_1_WKS_11_C */ 398