1 /* $NetBSD: dlz_wildcard_dynamic.c,v 1.3 2014/12/10 04:37:55 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. 5 * Copyright (C) 2012 Vadim Goncharov, Russia, vadim_nuclight@mail.ru. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the 9 * above copyright notice and this permission notice appear in all 10 * copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET 13 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 15 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 16 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 17 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 18 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 19 * USE OR PERFORMANCE OF THIS SOFTWARE. 20 * 21 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was 22 * conceived and contributed by Rob Butler. 23 * 24 * Permission to use, copy, modify, and distribute this software for any 25 * purpose with or without fee is hereby granted, provided that the 26 * above copyright notice and this permission notice appear in all 27 * copies. 28 * 29 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER 30 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 32 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 33 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 34 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 35 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 36 * USE OR PERFORMANCE OF THIS SOFTWARE. 37 */ 38 39 /* 40 * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") 41 * Copyright (C) 1999-2001 Internet Software Consortium. 42 * 43 * Permission to use, copy, modify, and/or distribute this software for any 44 * purpose with or without fee is hereby granted, provided that the above 45 * copyright notice and this permission notice appear in all copies. 46 * 47 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 48 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 49 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 50 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 51 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 52 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 53 * PERFORMANCE OF THIS SOFTWARE. 54 */ 55 56 /* 57 * This provides the externally loadable wildcard DLZ module. 58 */ 59 60 #include <stdio.h> 61 #include <string.h> 62 #include <stdarg.h> 63 #include <stdlib.h> 64 65 #include <dlz_minimal.h> 66 #include <dlz_list.h> 67 #include <dlz_dbi.h> 68 69 #include <ctype.h> 70 71 #define DE_CONST(konst, var) \ 72 do { \ 73 union { const void *k; void *v; } _u; \ 74 _u.k = konst; \ 75 var = _u.v; \ 76 } while (/*CONSTCOND*/0) 77 78 /* fnmatch() return values. */ 79 #define FNM_NOMATCH 1 /* Match failed. */ 80 81 /* fnmatch() flags. */ 82 #define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ 83 #define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ 84 #define FNM_PERIOD 0x04 /* Period must be matched by period. */ 85 #define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */ 86 #define FNM_CASEFOLD 0x10 /* Case insensitive search. */ 87 #define FNM_IGNORECASE FNM_CASEFOLD 88 #define FNM_FILE_NAME FNM_PATHNAME 89 90 /* 91 * Our data structures. 92 */ 93 94 typedef struct named_rr nrr_t; 95 typedef DLZ_LIST(nrr_t) rr_list_t; 96 97 typedef struct config_data { 98 char *zone_pattern; 99 char *axfr_pattern; 100 rr_list_t rrs_list; 101 char *zone; 102 char *record; 103 char *client; 104 105 /* Helper functions from the dlz_dlopen driver */ 106 log_t *log; 107 dns_sdlz_putrr_t *putrr; 108 dns_sdlz_putnamedrr_t *putnamedrr; 109 dns_dlz_writeablezone_t *writeable_zone; 110 } config_data_t; 111 112 struct named_rr { 113 char *name; 114 char *type; 115 int ttl; 116 query_list_t *data; 117 DLZ_LINK(nrr_t) link; 118 }; 119 120 /* 121 * Forward references 122 */ 123 static int 124 rangematch(const char *, char, int, char **); 125 126 static int 127 fnmatch(const char *pattern, const char *string, int flags); 128 129 static void 130 b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr); 131 132 static const char * 133 shortest_match(const char *pattern, const char *string); 134 135 isc_result_t 136 dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) { 137 config_data_t *cd = (config_data_t *) dbdata; 138 isc_result_t result; 139 char *querystring = NULL; 140 nrr_t *nrec; 141 int i = 0; 142 143 DE_CONST(zone, cd->zone); 144 145 /* Write info message to log */ 146 cd->log(ISC_LOG_DEBUG(1), 147 "dlz_wildcard allnodes called for zone '%s'", zone); 148 149 result = ISC_R_FAILURE; 150 151 nrec = DLZ_LIST_HEAD(cd->rrs_list); 152 while (nrec != NULL) { 153 cd->record = nrec->name; 154 155 querystring = build_querystring(nrec->data); 156 157 if (querystring == NULL) { 158 result = ISC_R_NOMEMORY; 159 goto done; 160 } 161 162 cd->log(ISC_LOG_DEBUG(2), 163 "dlz_wildcard allnodes entry num %d: calling " 164 "putnamedrr(name=%s type=%s ttl=%d qs=%s)", 165 i++, nrec->name, nrec->type, nrec->ttl, querystring); 166 167 result = cd->putnamedrr(allnodes, nrec->name, nrec->type, 168 nrec->ttl, querystring); 169 if (result != ISC_R_SUCCESS) 170 goto done; 171 172 nrec = DLZ_LIST_NEXT(nrec, link); 173 } 174 175 done: 176 cd->zone = NULL; 177 178 if (querystring != NULL) 179 free(querystring); 180 181 return (result); 182 } 183 184 isc_result_t 185 dlz_allowzonexfr(void *dbdata, const char *name, const char *client) { 186 config_data_t *cd = (config_data_t *) dbdata; 187 188 UNUSED(name); 189 190 /* Write info message to log */ 191 cd->log(ISC_LOG_DEBUG(1), 192 "dlz_wildcard allowzonexfr called for client '%s'", client); 193 194 if (fnmatch(cd->axfr_pattern, client, FNM_CASEFOLD) == 0) 195 return (ISC_R_SUCCESS); 196 else 197 return (ISC_R_NOTFOUND); 198 } 199 200 #if DLZ_DLOPEN_VERSION < 3 201 isc_result_t 202 dlz_findzonedb(void *dbdata, const char *name) 203 #else 204 isc_result_t 205 dlz_findzonedb(void *dbdata, const char *name, 206 dns_clientinfomethods_t *methods, 207 dns_clientinfo_t *clientinfo) 208 #endif 209 { 210 config_data_t *cd = (config_data_t *) dbdata; 211 const char *p; 212 213 #if DLZ_DLOPEN_VERSION >= 3 214 UNUSED(methods); 215 UNUSED(clientinfo); 216 #endif 217 218 p = shortest_match(cd->zone_pattern, name); 219 if (p == NULL) 220 return (ISC_R_NOTFOUND); 221 222 /* Write info message to log */ 223 cd->log(ISC_LOG_DEBUG(1), 224 "dlz_wildcard findzonedb matched '%s'", p); 225 226 return (ISC_R_SUCCESS); 227 } 228 229 #if DLZ_DLOPEN_VERSION == 1 230 isc_result_t 231 dlz_lookup(const char *zone, const char *name, 232 void *dbdata, dns_sdlzlookup_t *lookup) 233 #else 234 isc_result_t 235 dlz_lookup(const char *zone, const char *name, 236 void *dbdata, dns_sdlzlookup_t *lookup, 237 dns_clientinfomethods_t *methods, 238 dns_clientinfo_t *clientinfo) 239 #endif 240 { 241 isc_result_t result; 242 config_data_t *cd = (config_data_t *) dbdata; 243 char *querystring = NULL; 244 const char *p; 245 char *namebuf; 246 nrr_t *nrec; 247 isc_boolean_t origin = ISC_TRUE; 248 249 #if DLZ_DLOPEN_VERSION >= 2 250 UNUSED(methods); 251 UNUSED(clientinfo); 252 #endif 253 254 p = shortest_match(cd->zone_pattern, zone); 255 if (p == NULL) 256 return (ISC_R_NOTFOUND); 257 258 DE_CONST(name, cd->record); 259 DE_CONST(p, cd->zone); 260 261 if ((p != zone) && (strcmp(name, "@") == 0 || strcmp(name, zone) == 0)) 262 { 263 size_t len = p - zone; 264 namebuf = malloc(len); 265 strncpy(namebuf, zone, len - 1); 266 namebuf[len - 1] = '\0'; 267 cd->record = namebuf; 268 origin = ISC_FALSE; 269 } else if (p == zone) 270 cd->record = "@"; 271 272 /* Write info message to log */ 273 cd->log(ISC_LOG_DEBUG(1), 274 "dlz_wildcard_dynamic: lookup for '%s' in '%s': " 275 "trying '%s' in '%s'", 276 name, zone, cd->record, cd->zone); 277 278 result = ISC_R_NOTFOUND; 279 nrec = DLZ_LIST_HEAD(cd->rrs_list); 280 while (nrec != NULL) { 281 nrr_t *next = DLZ_LIST_NEXT(nrec, link); 282 if (strcmp(cd->record, nrec->name) == 0) { 283 /* We handle authority data in dlz_authority() */ 284 if (strcmp(nrec->type, "SOA") == 0 || 285 strcmp(nrec->type, "NS") == 0) 286 { 287 nrec = next; 288 continue; 289 } 290 291 querystring = build_querystring(nrec->data); 292 if (querystring == NULL) { 293 result = ISC_R_NOMEMORY; 294 goto done; 295 } 296 297 result = cd->putrr(lookup, nrec->type, 298 nrec->ttl, querystring); 299 if (result != ISC_R_SUCCESS) 300 goto done; 301 302 result = ISC_R_SUCCESS; 303 304 free(querystring); 305 querystring = NULL; 306 } 307 nrec = next; 308 } 309 310 done: 311 cd->zone = NULL; 312 cd->record = NULL; 313 314 if (querystring != NULL) 315 free(querystring); 316 317 return (result); 318 } 319 320 isc_result_t 321 dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) { 322 isc_result_t result; 323 config_data_t *cd = (config_data_t *) dbdata; 324 char *querystring = NULL; 325 nrr_t *nrec; 326 const char *p, *name = "@"; 327 328 p = shortest_match(cd->zone_pattern, zone); 329 if (p == NULL) 330 return (ISC_R_NOTFOUND); 331 332 DE_CONST(p, cd->zone); 333 334 /* Write info message to log */ 335 cd->log(ISC_LOG_DEBUG(1), 336 "dlz_wildcard_dynamic: authority for '%s'", zone); 337 338 result = ISC_R_NOTFOUND; 339 nrec = DLZ_LIST_HEAD(cd->rrs_list); 340 while (nrec != NULL) { 341 isc_boolean_t origin; 342 if (strcmp("@", nrec->name) == 0) { 343 isc_result_t presult; 344 345 querystring = build_querystring(nrec->data); 346 if (querystring == NULL) { 347 result = ISC_R_NOMEMORY; 348 goto done; 349 } 350 351 presult = cd->putrr(lookup, nrec->type, 352 nrec->ttl, querystring); 353 if (presult != ISC_R_SUCCESS) { 354 result = presult; 355 goto done; 356 } 357 358 result = ISC_R_SUCCESS; 359 360 free(querystring); 361 querystring = NULL; 362 } 363 nrec = DLZ_LIST_NEXT(nrec, link); 364 } 365 366 done: 367 cd->zone = NULL; 368 369 if (querystring != NULL) 370 free(querystring); 371 372 return (result); 373 } 374 375 static void 376 destroy_rrlist(config_data_t *cd) { 377 nrr_t *trec, *nrec; 378 379 nrec = DLZ_LIST_HEAD(cd->rrs_list); 380 381 while (nrec != NULL) { 382 trec = nrec; 383 384 destroy_querylist(&trec->data); 385 386 if (trec->name != NULL) 387 free(trec->name); 388 if (trec->type != NULL) 389 free(trec->type); 390 trec->name = trec->type = NULL; 391 392 /* Get the next record, before we destroy this one. */ 393 nrec = DLZ_LIST_NEXT(nrec, link); 394 395 free(trec); 396 } 397 } 398 399 isc_result_t 400 dlz_create(const char *dlzname, unsigned int argc, char *argv[], 401 void **dbdata, ...) 402 { 403 config_data_t *cd; 404 char *endp; 405 int i, def_ttl; 406 nrr_t *trec = NULL; 407 isc_result_t result; 408 const char *helper_name; 409 va_list ap; 410 411 if (argc < 8 || argc % 4 != 0) 412 return (ISC_R_FAILURE); 413 414 cd = calloc(1, sizeof(config_data_t)); 415 if (cd == NULL) 416 return (ISC_R_NOMEMORY); 417 memset(cd, 0, sizeof(config_data_t)); 418 419 /* Fill in the helper functions */ 420 va_start(ap, dbdata); 421 while ((helper_name = va_arg(ap, const char*)) != NULL) 422 b9_add_helper(cd, helper_name, va_arg(ap, void*)); 423 va_end(ap); 424 425 /* 426 * Write info message to log 427 */ 428 cd->log(ISC_LOG_INFO, 429 "Loading '%s' using DLZ_wildcard driver. " 430 "Zone: %s, AXFR allowed for: %s, $TTL: %s", 431 dlzname, argv[1], argv[2], argv[3]); 432 433 /* initialize the records list here to simplify cleanup */ 434 DLZ_LIST_INIT(cd->rrs_list); 435 436 cd->zone_pattern = strdup(argv[1]); 437 if (cd->zone_pattern == NULL) 438 goto cleanup; 439 440 cd->axfr_pattern = strdup(argv[2]); 441 if (cd->axfr_pattern == NULL) 442 goto cleanup; 443 444 def_ttl = strtol(argv[3], &endp, 10); 445 if (*endp != '\0' || def_ttl < 0) { 446 def_ttl = 3600; 447 cd->log(ISC_LOG_ERROR, "default TTL invalid, using 3600"); 448 } 449 450 for (i = 4; i < argc; i += 4) { 451 result = ISC_R_NOMEMORY; 452 453 trec = malloc(sizeof(nrr_t)); 454 if (trec == NULL) 455 goto full_cleanup; 456 457 memset(trec, 0, sizeof(nrr_t)); 458 459 /* Initialize the record link */ 460 DLZ_LINK_INIT(trec, link); 461 /* Append the record to the list */ 462 DLZ_LIST_APPEND(cd->rrs_list, trec, link); 463 464 trec->name = strdup(argv[i]); 465 if (trec->name == NULL) 466 goto full_cleanup; 467 468 trec->type = strdup(argv[i + 2]); 469 if (trec->type == NULL) 470 goto full_cleanup; 471 472 trec->ttl = strtol(argv[i + 1], &endp, 10); 473 if (argv[i + 1][0] == '\0' || *endp != '\0' || trec->ttl < 0) 474 trec->ttl = def_ttl; 475 476 result = build_querylist(argv[i + 3], &cd->zone, 477 &cd->record, &cd->client, 478 &trec->data, 0, cd->log); 479 /* If unsuccessful, log err msg and cleanup */ 480 if (result != ISC_R_SUCCESS) { 481 cd->log(ISC_LOG_ERROR, 482 "Could not build RR data list at argv[%d]", 483 i + 3); 484 goto full_cleanup; 485 } 486 } 487 488 *dbdata = cd; 489 490 return (ISC_R_SUCCESS); 491 492 full_cleanup: 493 destroy_rrlist(cd); 494 495 cleanup: 496 if (cd->zone_pattern != NULL) 497 free(cd->zone_pattern); 498 if (cd->axfr_pattern != NULL) 499 free(cd->axfr_pattern); 500 free(cd); 501 502 return (result); 503 } 504 505 void 506 dlz_destroy(void *dbdata) { 507 config_data_t *cd = (config_data_t *) dbdata; 508 509 /* 510 * Write debugging message to log 511 */ 512 cd->log(ISC_LOG_DEBUG(2), "Unloading DLZ_wildcard driver."); 513 514 destroy_rrlist(cd); 515 516 free(cd->zone_pattern); 517 free(cd->axfr_pattern); 518 free(cd); 519 } 520 521 522 /* 523 * Return the version of the API 524 */ 525 int 526 dlz_version(unsigned int *flags) { 527 UNUSED(flags); 528 /* XXX: ok to set DNS_SDLZFLAG_THREADSAFE here? */ 529 return (DLZ_DLOPEN_VERSION); 530 } 531 532 /* 533 * Register a helper function from the bind9 dlz_dlopen driver 534 */ 535 static void 536 b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr) { 537 if (strcmp(helper_name, "log") == 0) 538 cd->log = (log_t *)ptr; 539 if (strcmp(helper_name, "putrr") == 0) 540 cd->putrr = (dns_sdlz_putrr_t *)ptr; 541 if (strcmp(helper_name, "putnamedrr") == 0) 542 cd->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr; 543 if (strcmp(helper_name, "writeable_zone") == 0) 544 cd->writeable_zone = (dns_dlz_writeablezone_t *)ptr; 545 } 546 547 static const char * 548 shortest_match(const char *pattern, const char *string) { 549 const char *p = string; 550 if (pattern == NULL || p == NULL || *p == '\0') 551 return (NULL); 552 553 p += strlen(p); 554 while (p-- > string) { 555 if (*p == '.') { 556 if (fnmatch(pattern, p + 1, FNM_CASEFOLD) == 0) 557 return (p + 1); 558 } 559 } 560 if (fnmatch(pattern, string, FNM_CASEFOLD) == 0) 561 return (string); 562 563 return (NULL); 564 } 565 566 /* 567 * The helper functions stolen from the FreeBSD kernel (sys/libkern/fnmatch.c). 568 * 569 * Why don't we use fnmatch(3) from libc? Because it is not thread-safe, and 570 * it is not thread-safe because it supports multibyte characters. But here, 571 * in BIND, we want to be thread-safe and don't need multibyte - DNS names are 572 * always ASCII. 573 */ 574 #define EOS '\0' 575 576 #define RANGE_MATCH 1 577 #define RANGE_NOMATCH 0 578 #define RANGE_ERROR (-1) 579 580 static int 581 fnmatch(const char *pattern, const char *string, int flags) { 582 const char *stringstart; 583 char *newp; 584 char c, test; 585 586 for (stringstart = string;;) 587 switch (c = *pattern++) { 588 case EOS: 589 if ((flags & FNM_LEADING_DIR) && *string == '/') 590 return (0); 591 return (*string == EOS ? 0 : FNM_NOMATCH); 592 case '?': 593 if (*string == EOS) 594 return (FNM_NOMATCH); 595 if (*string == '/' && (flags & FNM_PATHNAME)) 596 return (FNM_NOMATCH); 597 if (*string == '.' && (flags & FNM_PERIOD) && 598 (string == stringstart || 599 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 600 return (FNM_NOMATCH); 601 ++string; 602 break; 603 case '*': 604 c = *pattern; 605 /* Collapse multiple stars. */ 606 while (c == '*') 607 c = *++pattern; 608 609 if (*string == '.' && (flags & FNM_PERIOD) && 610 (string == stringstart || 611 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 612 return (FNM_NOMATCH); 613 614 /* Optimize for pattern with * at end or before /. */ 615 if (c == EOS) 616 if (flags & FNM_PATHNAME) 617 return ((flags & FNM_LEADING_DIR) || 618 index(string, '/') == NULL ? 619 0 : FNM_NOMATCH); 620 else 621 return (0); 622 else if (c == '/' && flags & FNM_PATHNAME) { 623 if ((string = index(string, '/')) == NULL) 624 return (FNM_NOMATCH); 625 break; 626 } 627 628 /* General case, use recursion. */ 629 while ((test = *string) != EOS) { 630 if (!fnmatch(pattern, string, 631 flags & ~FNM_PERIOD)) 632 return (0); 633 if (test == '/' && flags & FNM_PATHNAME) 634 break; 635 ++string; 636 } 637 return (FNM_NOMATCH); 638 case '[': 639 if (*string == EOS) 640 return (FNM_NOMATCH); 641 if (*string == '/' && (flags & FNM_PATHNAME)) 642 return (FNM_NOMATCH); 643 if (*string == '.' && (flags & FNM_PERIOD) && 644 (string == stringstart || 645 ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) 646 return (FNM_NOMATCH); 647 648 switch (rangematch(pattern, *string, flags, &newp)) { 649 case RANGE_ERROR: 650 goto norm; 651 case RANGE_MATCH: 652 pattern = newp; 653 break; 654 case RANGE_NOMATCH: 655 return (FNM_NOMATCH); 656 } 657 ++string; 658 break; 659 case '\\': 660 if (!(flags & FNM_NOESCAPE)) { 661 if ((c = *pattern++) == EOS) { 662 c = '\\'; 663 --pattern; 664 } 665 } 666 /* FALLTHROUGH */ 667 default: 668 norm: 669 if (c == *string) 670 ; 671 else if ((flags & FNM_CASEFOLD) && 672 (tolower((unsigned char)c) == 673 tolower((unsigned char)*string))) 674 ; 675 else 676 return (FNM_NOMATCH); 677 string++; 678 break; 679 } 680 /* NOTREACHED */ 681 } 682 683 static int 684 rangematch(const char *pattern, char test, int flags, char **newp) { 685 int negate, ok; 686 char c, c2; 687 688 /* 689 * A bracket expression starting with an unquoted circumflex 690 * character produces unspecified results (IEEE 1003.2-1992, 691 * 3.13.2). This implementation treats it like '!', for 692 * consistency with the regular expression syntax. 693 * J.T. Conklin (conklin@ngai.kaleida.com) 694 */ 695 if ( (negate = (*pattern == '!' || *pattern == '^')) ) 696 ++pattern; 697 698 if (flags & FNM_CASEFOLD) 699 test = tolower((unsigned char)test); 700 701 /* 702 * A right bracket shall lose its special meaning and represent 703 * itself in a bracket expression if it occurs first in the list. 704 * -- POSIX.2 2.8.3.2 705 */ 706 ok = 0; 707 c = *pattern++; 708 do { 709 if (c == '\\' && !(flags & FNM_NOESCAPE)) 710 c = *pattern++; 711 if (c == EOS) 712 return (RANGE_ERROR); 713 714 if (c == '/' && (flags & FNM_PATHNAME)) 715 return (RANGE_NOMATCH); 716 717 if (flags & FNM_CASEFOLD) 718 c = tolower((unsigned char)c); 719 720 if (*pattern == '-' 721 && (c2 = *(pattern+1)) != EOS && c2 != ']') { 722 pattern += 2; 723 if (c2 == '\\' && !(flags & FNM_NOESCAPE)) 724 c2 = *pattern++; 725 if (c2 == EOS) 726 return (RANGE_ERROR); 727 728 if (flags & FNM_CASEFOLD) 729 c2 = tolower((unsigned char)c2); 730 731 if (c <= test && test <= c2) 732 ok = 1; 733 } else if (c == test) 734 ok = 1; 735 } while ((c = *pattern++) != ']'); 736 737 *newp = (char *)(uintptr_t)pattern; 738 return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); 739 } 740