1 /* 2 * util.c -- set of various support routines. 3 * 4 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 * 8 */ 9 10 #include "config.h" 11 12 #include <assert.h> 13 #include <ctype.h> 14 #include <errno.h> 15 #ifdef HAVE_OPENSSL_RAND_H 16 #include <openssl/rand.h> 17 #endif 18 #include <stdarg.h> 19 #include <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #ifdef HAVE_SCHED_H 23 #include <sched.h> 24 #endif /* HAVE_SCHED_H */ 25 #ifdef HAVE_SYS_CPUSET_H 26 #include <sys/cpuset.h> 27 #endif /* HAVE_SYS_CPUSET_H */ 28 #ifdef HAVE_SYSLOG_H 29 #include <syslog.h> 30 #endif /* HAVE_SYSLOG_H */ 31 #include <unistd.h> 32 #ifdef HAVE_SYS_RANDOM_H 33 #include <sys/random.h> 34 #endif 35 36 #include "util.h" 37 #include "region-allocator.h" 38 #include "dname.h" 39 #include "namedb.h" 40 #include "rdata.h" 41 #include "zonec.h" 42 #include "nsd.h" 43 #include "options.h" 44 45 #ifdef USE_MMAP_ALLOC 46 #include <sys/mman.h> 47 48 #if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) 49 #define MAP_ANONYMOUS MAP_ANON 50 #elif defined(MAP_ANONYMOUS) && !defined(MAP_ANON) 51 #define MAP_ANON MAP_ANONYMOUS 52 #endif 53 54 #endif /* USE_MMAP_ALLOC */ 55 56 #ifndef NDEBUG 57 unsigned nsd_debug_facilities = 0xffff; 58 int nsd_debug_level = 0; 59 #endif 60 61 #define MSB_32 0x80000000 62 63 int verbosity = 0; 64 65 static const char *global_ident = NULL; 66 static log_function_type *current_log_function = log_file; 67 static FILE *current_log_file = NULL; 68 int log_time_asc = 1; 69 int log_time_iso = 0; 70 71 #ifdef USE_LOG_PROCESS_ROLE 72 void 73 log_set_process_role(const char *process_role) 74 { 75 global_ident = process_role; 76 } 77 #endif 78 79 void 80 log_init(const char *ident) 81 { 82 global_ident = ident; 83 current_log_file = stderr; 84 } 85 86 void 87 log_open(int option, int facility, const char *filename) 88 { 89 #ifdef HAVE_SYSLOG_H 90 openlog(global_ident, option, facility); 91 #endif /* HAVE_SYSLOG_H */ 92 if (filename) { 93 FILE *file = fopen(filename, "a"); 94 if (!file) { 95 log_msg(LOG_ERR, "Cannot open %s for appending (%s), " 96 "logging to stderr", 97 filename, strerror(errno)); 98 } else { 99 current_log_file = file; 100 } 101 } 102 } 103 104 void 105 log_reopen(const char *filename, uint8_t verbose) 106 { 107 if (filename) { 108 FILE *file; 109 if(strcmp(filename, "/dev/stdout")==0 || strcmp(filename, "/dev/stderr")==0) 110 return; 111 file = fopen(filename, "a"); 112 if (!file) { 113 if (verbose) 114 VERBOSITY(2, (LOG_WARNING, 115 "Cannot reopen %s for appending (%s), " 116 "keeping old logfile", 117 filename, strerror(errno))); 118 } else { 119 if (current_log_file && current_log_file != stderr) 120 fclose(current_log_file); 121 current_log_file = file; 122 } 123 } 124 } 125 126 void 127 log_finalize(void) 128 { 129 #ifdef HAVE_SYSLOG_H 130 closelog(); 131 #endif /* HAVE_SYSLOG_H */ 132 if (current_log_file && current_log_file != stderr) { 133 fclose(current_log_file); 134 } 135 current_log_file = NULL; 136 } 137 138 static lookup_table_type log_priority_table[] = { 139 { LOG_ERR, "error" }, 140 { LOG_WARNING, "warning" }, 141 { LOG_NOTICE, "notice" }, 142 { LOG_INFO, "info" }, 143 { 0, NULL } 144 }; 145 146 void 147 log_file(int priority, const char *message) 148 { 149 size_t length; 150 lookup_table_type *priority_info; 151 const char *priority_text = "unknown"; 152 153 assert(global_ident); 154 assert(current_log_file); 155 156 priority_info = lookup_by_id(log_priority_table, priority); 157 if (priority_info) { 158 priority_text = priority_info->name; 159 } 160 161 /* Bug #104, add time_t timestamp */ 162 #if defined(HAVE_STRFTIME) && defined(HAVE_LOCALTIME_R) 163 if(log_time_asc) { 164 struct timeval tv; 165 char tmbuf[32]; 166 tmbuf[0]=0; 167 tv.tv_usec = 0; 168 if(log_time_iso) { 169 char tzbuf[16]; 170 tzbuf[0]=0; 171 /* log time in iso format */ 172 if(gettimeofday(&tv, NULL) == 0) { 173 struct tm tm, *tm_p; 174 time_t now = (time_t)tv.tv_sec; 175 tm_p = localtime_r(&now, &tm); 176 strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%dT%H:%M:%S", 177 tm_p); 178 if(strftime(tzbuf, sizeof(tzbuf), "%z", tm_p) == 5) { 179 /* put ':' in "+hh:mm" */ 180 tzbuf[5] = tzbuf[4]; 181 tzbuf[4] = tzbuf[3]; 182 tzbuf[3] = ':'; 183 tzbuf[6] = 0; 184 } 185 } 186 fprintf(current_log_file, "%s.%3.3d%s %s[%d]: %s: %s", 187 tmbuf, (int)tv.tv_usec/1000, tzbuf, 188 global_ident, (int) getpid(), priority_text, message); 189 } else { 190 /* log time in ascii format */ 191 if(gettimeofday(&tv, NULL) == 0) { 192 struct tm tm; 193 time_t now = (time_t)tv.tv_sec; 194 strftime(tmbuf, sizeof(tmbuf), "%Y-%m-%d %H:%M:%S", 195 localtime_r(&now, &tm)); 196 } 197 fprintf(current_log_file, "[%s.%3.3d] %s[%d]: %s: %s", 198 tmbuf, (int)tv.tv_usec/1000, 199 global_ident, (int) getpid(), priority_text, message); 200 } 201 } else 202 #endif /* have time functions */ 203 fprintf(current_log_file, "[%d] %s[%d]: %s: %s", 204 (int)time(NULL), global_ident, (int) getpid(), priority_text, message); 205 length = strlen(message); 206 if (length == 0 || message[length - 1] != '\n') { 207 fprintf(current_log_file, "\n"); 208 } 209 fflush(current_log_file); 210 } 211 212 void 213 log_syslog(int priority, const char *message) 214 { 215 #ifdef HAVE_SYSLOG_H 216 syslog(priority, "%s", message); 217 #endif /* !HAVE_SYSLOG_H */ 218 log_file(priority, message); 219 } 220 221 void 222 log_only_syslog(int priority, const char *message) 223 { 224 #ifdef HAVE_SYSLOG_H 225 syslog(priority, "%s", message); 226 #else /* !HAVE_SYSLOG_H */ 227 /* no syslog, use stderr */ 228 log_file(priority, message); 229 #endif 230 } 231 232 void 233 log_set_log_function(log_function_type *log_function) 234 { 235 current_log_function = log_function; 236 } 237 238 void 239 log_msg(int priority, const char *format, ...) 240 { 241 va_list args; 242 va_start(args, format); 243 log_vmsg(priority, format, args); 244 va_end(args); 245 } 246 247 void 248 log_vmsg(int priority, const char *format, va_list args) 249 { 250 char message[MAXSYSLOGMSGLEN]; 251 vsnprintf(message, sizeof(message), format, args); 252 current_log_function(priority, message); 253 } 254 255 void 256 set_bit(uint8_t bits[], size_t index) 257 { 258 /* 259 * The bits are counted from left to right, so bit #0 is the 260 * left most bit. 261 */ 262 bits[index / 8] |= (1 << (7 - index % 8)); 263 } 264 265 void 266 clear_bit(uint8_t bits[], size_t index) 267 { 268 /* 269 * The bits are counted from left to right, so bit #0 is the 270 * left most bit. 271 */ 272 bits[index / 8] &= ~(1 << (7 - index % 8)); 273 } 274 275 int 276 get_bit(const uint8_t bits[], size_t index) 277 { 278 /* 279 * The bits are counted from left to right, so bit #0 is the 280 * left most bit. 281 */ 282 return bits[index / 8] & (1 << (7 - index % 8)); 283 } 284 285 lookup_table_type * 286 lookup_by_name(lookup_table_type *table, const char *name) 287 { 288 while (table->name != NULL) { 289 if (strcasecmp(name, table->name) == 0) 290 return table; 291 table++; 292 } 293 return NULL; 294 } 295 296 lookup_table_type * 297 lookup_by_id(lookup_table_type *table, int id) 298 { 299 while (table->name != NULL) { 300 if (table->id == id) 301 return table; 302 table++; 303 } 304 return NULL; 305 } 306 307 char * 308 xstrdup(const char *src) 309 { 310 char *result = strdup(src); 311 312 if(!result) { 313 log_msg(LOG_ERR, "strdup failed: %s", strerror(errno)); 314 exit(1); 315 } 316 317 return result; 318 } 319 320 void * 321 xalloc(size_t size) 322 { 323 void *result = malloc(size); 324 325 if (!result) { 326 log_msg(LOG_ERR, "malloc failed: %s", strerror(errno)); 327 exit(1); 328 } 329 return result; 330 } 331 332 void * 333 xmallocarray(size_t num, size_t size) 334 { 335 void *result = reallocarray(NULL, num, size); 336 337 if (!result) { 338 log_msg(LOG_ERR, "reallocarray failed: %s", strerror(errno)); 339 exit(1); 340 } 341 return result; 342 } 343 344 void * 345 xalloc_zero(size_t size) 346 { 347 void *result = calloc(1, size); 348 if (!result) { 349 log_msg(LOG_ERR, "calloc failed: %s", strerror(errno)); 350 exit(1); 351 } 352 return result; 353 } 354 355 void * 356 xalloc_array_zero(size_t num, size_t size) 357 { 358 void *result = calloc(num, size); 359 if (!result) { 360 log_msg(LOG_ERR, "calloc failed: %s", strerror(errno)); 361 exit(1); 362 } 363 return result; 364 } 365 366 void * 367 xrealloc(void *ptr, size_t size) 368 { 369 ptr = realloc(ptr, size); 370 if (!ptr) { 371 log_msg(LOG_ERR, "realloc failed: %s", strerror(errno)); 372 exit(1); 373 } 374 return ptr; 375 } 376 377 #ifdef USE_MMAP_ALLOC 378 379 void * 380 mmap_alloc(size_t size) 381 { 382 void *base; 383 384 size += MMAP_ALLOC_HEADER_SIZE; 385 #ifdef HAVE_MMAP 386 base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 387 if (base == MAP_FAILED) { 388 log_msg(LOG_ERR, "mmap failed: %s", strerror(errno)); 389 exit(1); 390 } 391 #else /* !HAVE_MMAP */ 392 log_msg(LOG_ERR, "mmap failed: don't have mmap"); 393 exit(1); 394 #endif /* HAVE_MMAP */ 395 396 *((size_t*) base) = size; 397 return (void*)((uintptr_t)base + MMAP_ALLOC_HEADER_SIZE); 398 } 399 400 401 void 402 mmap_free(void *ptr) 403 { 404 void *base; 405 size_t size; 406 407 if (!ptr) return; 408 409 base = (void*)((uintptr_t)ptr - MMAP_ALLOC_HEADER_SIZE); 410 size = *((size_t*) base); 411 412 #ifdef HAVE_MUNMAP 413 if (munmap(base, size) == -1) { 414 log_msg(LOG_ERR, "munmap failed: %s", strerror(errno)); 415 exit(1); 416 } 417 #else /* !HAVE_MUNMAP */ 418 log_msg(LOG_ERR, "munmap failed: don't have munmap"); 419 exit(1); 420 #endif /* HAVE_MUNMAP */ 421 } 422 423 #endif /* USE_MMAP_ALLOC */ 424 425 int 426 write_data(FILE *file, const void *data, size_t size) 427 { 428 size_t result; 429 430 if (size == 0) 431 return 1; 432 433 result = fwrite(data, 1, size, file); 434 435 if (result == 0) { 436 log_msg(LOG_ERR, "write failed: %s", strerror(errno)); 437 return 0; 438 } else if (result < size) { 439 log_msg(LOG_ERR, "short write (disk full?)"); 440 return 0; 441 } else { 442 return 1; 443 } 444 } 445 446 int 447 write_socket(int s, const void *buf, size_t size) 448 { 449 const char* data = (const char*)buf; 450 size_t total_count = 0; 451 452 while (total_count < size) { 453 ssize_t count 454 = write(s, data + total_count, size - total_count); 455 if (count == -1) { 456 if (errno != EAGAIN && errno != EINTR) { 457 return 0; 458 } else { 459 continue; 460 } 461 } 462 total_count += count; 463 } 464 return 1; 465 } 466 467 void get_time(struct timespec* t) 468 { 469 struct timeval tv; 470 #ifdef HAVE_CLOCK_GETTIME 471 /* first try nanosecond precision */ 472 if(clock_gettime(CLOCK_REALTIME, t)>=0) { 473 return; /* success */ 474 } 475 log_msg(LOG_ERR, "clock_gettime: %s", strerror(errno)); 476 #endif 477 /* try millisecond precision */ 478 if(gettimeofday(&tv, NULL)>=0) { 479 t->tv_sec = tv.tv_sec; 480 t->tv_nsec = tv.tv_usec*1000; 481 return; /* success */ 482 } 483 log_msg(LOG_ERR, "gettimeofday: %s", strerror(errno)); 484 /* whole seconds precision */ 485 t->tv_sec = time(0); 486 t->tv_nsec = 0; 487 } 488 489 int 490 timespec_compare(const struct timespec *left, 491 const struct timespec *right) 492 { 493 /* Compare seconds. */ 494 if (left->tv_sec < right->tv_sec) { 495 return -1; 496 } else if (left->tv_sec > right->tv_sec) { 497 return 1; 498 } else { 499 /* Seconds are equal, compare nanoseconds. */ 500 if (left->tv_nsec < right->tv_nsec) { 501 return -1; 502 } else if (left->tv_nsec > right->tv_nsec) { 503 return 1; 504 } else { 505 return 0; 506 } 507 } 508 } 509 510 511 /* One second is 1e9 nanoseconds. */ 512 #define NANOSECONDS_PER_SECOND 1000000000L 513 514 void 515 timespec_add(struct timespec *left, 516 const struct timespec *right) 517 { 518 left->tv_sec += right->tv_sec; 519 left->tv_nsec += right->tv_nsec; 520 if (left->tv_nsec >= NANOSECONDS_PER_SECOND) { 521 /* Carry. */ 522 ++left->tv_sec; 523 left->tv_nsec -= NANOSECONDS_PER_SECOND; 524 } 525 } 526 527 void 528 timespec_subtract(struct timespec *left, 529 const struct timespec *right) 530 { 531 left->tv_sec -= right->tv_sec; 532 left->tv_nsec -= right->tv_nsec; 533 if (left->tv_nsec < 0L) { 534 /* Borrow. */ 535 --left->tv_sec; 536 left->tv_nsec += NANOSECONDS_PER_SECOND; 537 } 538 } 539 540 ssize_t 541 hex_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) 542 { 543 static char hexdigits[] = { 544 '0', '1', '2', '3', '4', '5', '6', '7', 545 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 546 }; 547 size_t i; 548 549 if (targsize < srclength * 2 + 1) { 550 return -1; 551 } 552 553 for (i = 0; i < srclength; ++i) { 554 *target++ = hexdigits[src[i] >> 4U]; 555 *target++ = hexdigits[src[i] & 0xfU]; 556 } 557 *target = '\0'; 558 return 2 * srclength; 559 } 560 561 ssize_t 562 hex_pton(const char* src, uint8_t* target, size_t targsize) 563 { 564 uint8_t *t = target; 565 if(strlen(src) % 2 != 0 || strlen(src)/2 > targsize) { 566 return -1; 567 } 568 while(*src) { 569 if(!isxdigit((unsigned char)src[0]) || 570 !isxdigit((unsigned char)src[1])) 571 return -1; 572 *t++ = hexdigit_to_int(src[0]) * 16 + 573 hexdigit_to_int(src[1]) ; 574 src += 2; 575 } 576 return t-target; 577 } 578 579 int 580 b32_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) 581 { 582 static char b32[]="0123456789abcdefghijklmnopqrstuv"; 583 char buf[9]; 584 ssize_t len=0; 585 586 while(srclength > 0) 587 { 588 int t; 589 memset(buf,'\0',sizeof buf); 590 591 /* xxxxx000 00000000 00000000 00000000 00000000 */ 592 buf[0]=b32[src[0] >> 3]; 593 594 /* 00000xxx xx000000 00000000 00000000 00000000 */ 595 t=(src[0]&7) << 2; 596 if(srclength > 1) 597 t+=src[1] >> 6; 598 buf[1]=b32[t]; 599 if(srclength == 1) 600 break; 601 602 /* 00000000 00xxxxx0 00000000 00000000 00000000 */ 603 buf[2]=b32[(src[1] >> 1)&0x1f]; 604 605 /* 00000000 0000000x xxxx0000 00000000 00000000 */ 606 t=(src[1]&1) << 4; 607 if(srclength > 2) 608 t+=src[2] >> 4; 609 buf[3]=b32[t]; 610 if(srclength == 2) 611 break; 612 613 /* 00000000 00000000 0000xxxx x0000000 00000000 */ 614 t=(src[2]&0xf) << 1; 615 if(srclength > 3) 616 t+=src[3] >> 7; 617 buf[4]=b32[t]; 618 if(srclength == 3) 619 break; 620 621 /* 00000000 00000000 00000000 0xxxxx00 00000000 */ 622 buf[5]=b32[(src[3] >> 2)&0x1f]; 623 624 /* 00000000 00000000 00000000 000000xx xxx00000 */ 625 t=(src[3]&3) << 3; 626 if(srclength > 4) 627 t+=src[4] >> 5; 628 buf[6]=b32[t]; 629 if(srclength == 4) 630 break; 631 632 /* 00000000 00000000 00000000 00000000 000xxxxx */ 633 buf[7]=b32[src[4]&0x1f]; 634 635 if(targsize < 8) 636 return -1; 637 638 src += 5; 639 srclength -= 5; 640 641 memcpy(target,buf,8); 642 target += 8; 643 targsize -= 8; 644 len += 8; 645 } 646 if(srclength) 647 { 648 size_t tlen = strlcpy(target, buf, targsize); 649 if (tlen >= targsize) 650 return -1; 651 len += tlen; 652 } 653 else if(targsize < 1) 654 return -1; 655 else 656 *target='\0'; 657 return len; 658 } 659 660 int 661 b32_pton(const char *src, uint8_t *target, size_t tsize) 662 { 663 char ch; 664 size_t p=0; 665 666 memset(target,'\0',tsize); 667 while((ch = *src++)) { 668 uint8_t d; 669 size_t b; 670 size_t n; 671 672 if(p+5 >= tsize*8) 673 return -1; 674 675 if(isspace((unsigned char)ch)) 676 continue; 677 678 if(ch >= '0' && ch <= '9') 679 d=ch-'0'; 680 else if(ch >= 'A' && ch <= 'V') 681 d=ch-'A'+10; 682 else if(ch >= 'a' && ch <= 'v') 683 d=ch-'a'+10; 684 else 685 return -1; 686 687 b=7-p%8; 688 n=p/8; 689 690 if(b >= 4) 691 target[n]|=d << (b-4); 692 else { 693 target[n]|=d >> (4-b); 694 target[n+1]|=d << (b+4); 695 } 696 p+=5; 697 } 698 return (p+7)/8; 699 } 700 701 void 702 strip_string(char *str) 703 { 704 char *start = str; 705 char *end = str + strlen(str) - 1; 706 707 while (isspace((unsigned char)*start)) 708 ++start; 709 if (start > end) { 710 /* Completely blank. */ 711 str[0] = '\0'; 712 } else { 713 while (isspace((unsigned char)*end)) 714 --end; 715 *++end = '\0'; 716 717 if (str != start) 718 memmove(str, start, end - start + 1); 719 } 720 } 721 722 int 723 hexdigit_to_int(char ch) 724 { 725 switch (ch) { 726 case '0': return 0; 727 case '1': return 1; 728 case '2': return 2; 729 case '3': return 3; 730 case '4': return 4; 731 case '5': return 5; 732 case '6': return 6; 733 case '7': return 7; 734 case '8': return 8; 735 case '9': return 9; 736 case 'a': case 'A': return 10; 737 case 'b': case 'B': return 11; 738 case 'c': case 'C': return 12; 739 case 'd': case 'D': return 13; 740 case 'e': case 'E': return 14; 741 case 'f': case 'F': return 15; 742 default: 743 abort(); 744 } 745 } 746 747 /* code to calculate CRC. Lifted from BSD 4.4 crc.c in cksum(1). BSD license. 748 http://www.tsfr.org/~orc/Code/bsd/bsd-current/cksum/crc.c. 749 or http://gobsd.com/code/freebsd/usr.bin/cksum/crc.c 750 The polynomial is 0x04c11db7L. */ 751 static uint32_t crctab[] = { 752 0x0, 753 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 754 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 755 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 756 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 757 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, 758 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 759 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 760 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 761 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 762 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, 763 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, 764 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 765 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, 766 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 767 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 768 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 769 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 770 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 771 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, 772 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 773 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 774 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 775 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 776 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, 777 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 778 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 779 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 780 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 781 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, 782 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 783 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 784 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 785 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 786 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 787 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, 788 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 789 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, 790 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 791 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 792 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 793 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 794 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, 795 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, 796 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 797 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 798 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 799 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 800 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, 801 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 802 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 803 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 804 }; 805 806 #define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] 807 808 uint32_t 809 compute_crc(uint32_t crc, uint8_t* data, size_t len) 810 { 811 size_t i; 812 for(i=0; i<len; ++i) 813 COMPUTE(crc, data[i]); 814 return crc; 815 } 816 817 int 818 write_data_crc(FILE *file, const void *data, size_t size, uint32_t* crc) 819 { 820 int ret = write_data(file, data, size); 821 *crc = compute_crc(*crc, (uint8_t*)data, size); 822 return ret; 823 } 824 825 #define SERIAL_BITS 32 826 int 827 compare_serial(uint32_t a, uint32_t b) 828 { 829 const uint32_t cutoff = ((uint32_t) 1 << (SERIAL_BITS - 1)); 830 831 if (a == b) { 832 return 0; 833 } else if ((a < b && b - a < cutoff) || (a > b && a - b > cutoff)) { 834 return -1; 835 } else { 836 return 1; 837 } 838 } 839 840 uint16_t 841 qid_generate(void) 842 { 843 #ifdef HAVE_GETRANDOM 844 uint16_t r; 845 if(getrandom(&r, sizeof(r), 0) == -1) { 846 log_msg(LOG_ERR, "getrandom failed: %s", strerror(errno)); 847 exit(1); 848 } 849 return r; 850 #elif defined(HAVE_ARC4RANDOM) 851 /* arc4random_uniform not needed because range is a power of 2 */ 852 return (uint16_t) arc4random(); 853 #else 854 return (uint16_t) random(); 855 #endif 856 } 857 858 int 859 random_generate(int max) 860 { 861 #ifdef HAVE_GETRANDOM 862 int r; 863 if(getrandom(&r, sizeof(r), 0) == -1) { 864 log_msg(LOG_ERR, "getrandom failed: %s", strerror(errno)); 865 exit(1); 866 } 867 return (int)(((unsigned)r)%max); 868 #elif defined(HAVE_ARC4RANDOM_UNIFORM) 869 return (int) arc4random_uniform(max); 870 #elif defined(HAVE_ARC4RANDOM) 871 return (int) (arc4random() % max); 872 #else 873 return (int) ((unsigned)random() % max); 874 #endif 875 } 876 877 void 878 cleanup_region(void *data) 879 { 880 region_type *region = (region_type *) data; 881 region_destroy(region); 882 } 883 884 struct state_pretty_rr* 885 create_pretty_rr(struct region* region) 886 { 887 struct state_pretty_rr* state = (struct state_pretty_rr*) 888 region_alloc(region, sizeof(struct state_pretty_rr)); 889 state->previous_owner_region = region_create(xalloc, free); 890 state->previous_owner = NULL; 891 state->previous_owner_origin = NULL; 892 region_add_cleanup(region, cleanup_region, 893 state->previous_owner_region); 894 return state; 895 } 896 897 static void 898 set_previous_owner(struct state_pretty_rr *state, const dname_type *dname) 899 { 900 region_free_all(state->previous_owner_region); 901 state->previous_owner = dname_copy(state->previous_owner_region, dname); 902 state->previous_owner_origin = dname_origin( 903 state->previous_owner_region, state->previous_owner); 904 } 905 906 int 907 print_rr(FILE *out, 908 struct state_pretty_rr *state, 909 rr_type *record, 910 region_type* rr_region, 911 buffer_type* output) 912 { 913 const nsd_type_descriptor_type *descriptor = 914 nsd_type_descriptor(record->type); 915 int result; 916 const dname_type *owner = domain_dname(record->owner); 917 buffer_clear(output); 918 if (state) { 919 if (!state->previous_owner 920 || dname_compare(state->previous_owner, owner) != 0) { 921 const dname_type *owner_origin 922 = dname_origin(rr_region, owner); 923 int origin_changed = (!state->previous_owner_origin 924 || dname_compare(state->previous_owner_origin, 925 owner_origin) != 0); 926 if (origin_changed) { 927 buffer_printf(output, "$ORIGIN %s\n", 928 dname_to_string(owner_origin, NULL)); 929 } 930 931 set_previous_owner(state, owner); 932 buffer_printf(output, "%s", 933 dname_to_string(owner, 934 state->previous_owner_origin)); 935 region_free_all(rr_region); 936 } 937 } else { 938 buffer_printf(output, "%s", dname_to_string(owner, NULL)); 939 } 940 941 buffer_printf(output, "\t%lu\t%s\t%s", 942 (unsigned long) record->ttl, 943 rrclass_to_string(record->klass), 944 rrtype_to_string(record->type)); 945 946 if(record->type == TYPE_SOA) { 947 buffer_printf(output, "\t"); 948 result = print_soa_rdata_twoline(output, record); 949 } else { 950 result = print_rdata(output, descriptor, record); 951 } 952 if (!result) { 953 /* 954 * Some RDATA failed to print, so print the record's 955 * RDATA in unknown format. 956 */ 957 result = print_unknown_rdata(output, descriptor, record); 958 } 959 960 if (result) { 961 buffer_printf(output, "\n"); 962 buffer_flip(output); 963 result = write_data(out, buffer_current(output), 964 buffer_remaining(output)); 965 } 966 return result; 967 } 968 969 const char* 970 rcode2str(int rc) 971 { 972 switch(rc) { 973 case RCODE_OK: 974 return "NO ERROR"; 975 case RCODE_FORMAT: 976 return "FORMAT ERROR"; 977 case RCODE_SERVFAIL: 978 return "SERVFAIL"; 979 case RCODE_NXDOMAIN: 980 return "NAME ERROR"; 981 case RCODE_IMPL: 982 return "NOT IMPL"; 983 case RCODE_REFUSE: 984 return "REFUSED"; 985 case RCODE_YXDOMAIN: 986 return "YXDOMAIN"; 987 case RCODE_YXRRSET: 988 return "YXRRSET"; 989 case RCODE_NXRRSET: 990 return "NXRRSET"; 991 case RCODE_NOTAUTH: 992 return "SERVER NOT AUTHORITATIVE FOR ZONE"; 993 case RCODE_NOTZONE: 994 /* Name not contained in zone */ 995 return "NOTZONE"; 996 default: 997 return "UNKNOWN ERROR"; 998 } 999 return NULL; /* ENOREACH */ 1000 } 1001 1002 void 1003 addr2str( 1004 #ifdef INET6 1005 struct sockaddr_storage *addr 1006 #else 1007 struct sockaddr_in *addr 1008 #endif 1009 , char* str, size_t len) 1010 { 1011 #ifdef INET6 1012 if (addr->ss_family == AF_INET6) { 1013 if (!inet_ntop(AF_INET6, 1014 &((struct sockaddr_in6 *)addr)->sin6_addr, str, len)) 1015 strlcpy(str, "[unknown ip6, inet_ntop failed]", len); 1016 return; 1017 } 1018 #endif 1019 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, 1020 str, len)) 1021 strlcpy(str, "[unknown ip4, inet_ntop failed]", len); 1022 } 1023 1024 void 1025 addrport2str( 1026 #ifdef INET6 1027 struct sockaddr_storage *addr 1028 #else 1029 struct sockaddr_in *addr 1030 #endif 1031 , char* str, size_t len) 1032 { 1033 char ip[256]; 1034 #ifdef INET6 1035 if (addr->ss_family == AF_INET6) { 1036 if (!inet_ntop(AF_INET6, 1037 &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip))) 1038 strlcpy(ip, "[unknown ip6, inet_ntop failed]", sizeof(ip)); 1039 /* append port number */ 1040 snprintf(str, len, "%s@%u", ip, 1041 (unsigned)ntohs(((struct sockaddr_in6 *)addr)->sin6_port)); 1042 return; 1043 } else 1044 #endif 1045 if (!inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, 1046 ip, sizeof(ip))) 1047 strlcpy(ip, "[unknown ip4, inet_ntop failed]", sizeof(ip)); 1048 /* append port number */ 1049 snprintf(str, len, "%s@%u", ip, 1050 (unsigned)ntohs(((struct sockaddr_in *)addr)->sin_port)); 1051 } 1052 1053 void 1054 append_trailing_slash(const char** dirname, region_type* region) 1055 { 1056 int l = strlen(*dirname); 1057 if (l>0 && (*dirname)[l-1] != '/' && l < 0xffffff) { 1058 char *dirname_slash = region_alloc(region, l+2); 1059 memcpy(dirname_slash, *dirname, l+1); 1060 strlcat(dirname_slash, "/", l+2); 1061 /* old dirname is leaked, this is only used for chroot, once */ 1062 *dirname = dirname_slash; 1063 } 1064 } 1065 1066 int 1067 file_inside_chroot(const char* fname, const char* chr) 1068 { 1069 /* true if filename starts with chroot or is not absolute */ 1070 return ((fname && fname[0] && strncmp(fname, chr, strlen(chr)) == 0) || 1071 (fname && fname[0] != '/')); 1072 } 1073 1074 /* 1075 * Something went wrong, give error messages and exit. 1076 */ 1077 void 1078 error(const char *format, ...) 1079 { 1080 va_list args; 1081 va_start(args, format); 1082 log_vmsg(LOG_ERR, format, args); 1083 va_end(args); 1084 exit(1); 1085 } 1086 1087 #ifdef HAVE_CPUSET_T 1088 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) 1089 /* exists on Linux and FreeBSD */ 1090 int number_of_cpus(void) 1091 { 1092 return (int)sysconf(_SC_NPROCESSORS_CONF); 1093 } 1094 #else 1095 int number_of_cpus(void) 1096 { 1097 return -1; 1098 } 1099 #endif 1100 #ifdef __gnu_hurd__ 1101 /* HURD has no sched_setaffinity implementation, but links an always fail, 1102 * with a linker error, we print an error when it is used */ 1103 int set_cpu_affinity(cpuset_t *ATTR_UNUSED(set)) 1104 { 1105 log_msg(LOG_ERR, "sched_setaffinity: not available on this system"); 1106 return -1; 1107 } 1108 #elif defined(HAVE_SCHED_SETAFFINITY) 1109 /* Linux */ 1110 int set_cpu_affinity(cpuset_t *set) 1111 { 1112 assert(set != NULL); 1113 return sched_setaffinity(getpid(), sizeof(*set), set); 1114 } 1115 #else 1116 /* FreeBSD */ 1117 int set_cpu_affinity(cpuset_t *set) 1118 { 1119 assert(set != NULL); 1120 return cpuset_setaffinity( 1121 CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(*set), set); 1122 } 1123 #endif 1124 #endif /* HAVE_CPUSET_T */ 1125 1126 void add_cookie_secret(struct nsd* nsd, uint8_t* secret) 1127 { 1128 /* New cookie secret becomes the staging secret (position 1) 1129 * unless there is no active cookie yet, then it becomes the active 1130 * secret. If the NSD_COOKIE_HISTORY_SIZE > 2 then all staging cookies 1131 * are moved one position down. 1132 */ 1133 if(nsd->cookie_count == 0) { 1134 memcpy( nsd->cookie_secrets->cookie_secret 1135 , secret, NSD_COOKIE_SECRET_SIZE); 1136 nsd->cookie_count = 1; 1137 explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); 1138 return; 1139 } 1140 #if NSD_COOKIE_HISTORY_SIZE > 2 1141 memmove( &nsd->cookie_secrets[2], &nsd->cookie_secrets[1] 1142 , sizeof(struct cookie_secret) * (NSD_COOKIE_HISTORY_SIZE - 2)); 1143 #endif 1144 memcpy( nsd->cookie_secrets[1].cookie_secret 1145 , secret, NSD_COOKIE_SECRET_SIZE); 1146 nsd->cookie_count = nsd->cookie_count < NSD_COOKIE_HISTORY_SIZE 1147 ? nsd->cookie_count + 1 : NSD_COOKIE_HISTORY_SIZE; 1148 explicit_bzero(secret, NSD_COOKIE_SECRET_SIZE); 1149 } 1150 1151 void activate_cookie_secret(struct nsd* nsd) 1152 { 1153 uint8_t active_secret[NSD_COOKIE_SECRET_SIZE]; 1154 /* The staging secret becomes the active secret. 1155 * The active secret becomes a staging secret. 1156 * If the NSD_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved 1157 * one position up and the previously active secret becomes the last 1158 * staging secret. 1159 */ 1160 if(nsd->cookie_count < 2) 1161 return; 1162 memcpy( active_secret, nsd->cookie_secrets[0].cookie_secret 1163 , NSD_COOKIE_SECRET_SIZE); 1164 memmove( &nsd->cookie_secrets[0], &nsd->cookie_secrets[1] 1165 , sizeof(struct cookie_secret) * (NSD_COOKIE_HISTORY_SIZE - 1)); 1166 memcpy( nsd->cookie_secrets[nsd->cookie_count - 1].cookie_secret 1167 , active_secret, NSD_COOKIE_SECRET_SIZE); 1168 explicit_bzero(active_secret, NSD_COOKIE_SECRET_SIZE); 1169 } 1170 1171 void drop_cookie_secret(struct nsd* nsd) 1172 { 1173 /* Drops a staging cookie secret. If there are more than one, it will 1174 * drop the last staging secret. */ 1175 if(nsd->cookie_count < 2) 1176 return; 1177 explicit_bzero( nsd->cookie_secrets[nsd->cookie_count - 1].cookie_secret 1178 , NSD_COOKIE_SECRET_SIZE); 1179 nsd->cookie_count -= 1; 1180 } 1181 1182 void reconfig_cookies(struct nsd* nsd, struct nsd_options* options) 1183 { 1184 cookie_secret_type cookie_secrets[NSD_COOKIE_HISTORY_SIZE]; 1185 char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; 1186 FILE* f = NULL; 1187 size_t count = 0; 1188 const char* fn; 1189 size_t i, j; 1190 1191 nsd->do_answer_cookie = options->answer_cookie; 1192 1193 /* Cookie secrets in the configuration file take precedence */ 1194 if(options->cookie_secret) { 1195 #ifndef NDEBUG 1196 ssize_t len = 1197 #endif 1198 hex_pton(options->cookie_secret, 1199 nsd->cookie_secrets[0].cookie_secret, 1200 NSD_COOKIE_SECRET_SIZE); 1201 /* Cookie length guaranteed in configparser.y */ 1202 assert(len == NSD_COOKIE_SECRET_SIZE); 1203 nsd->cookie_count = 1; 1204 if(options->cookie_staging_secret) { 1205 #ifndef NDEBUG 1206 len = 1207 #endif 1208 hex_pton(options->cookie_staging_secret, 1209 nsd->cookie_secrets[1].cookie_secret, 1210 NSD_COOKIE_SECRET_SIZE); 1211 /* Cookie length guaranteed in configparser.y */ 1212 assert(len == NSD_COOKIE_SECRET_SIZE); 1213 nsd->cookie_count = 2; 1214 } 1215 /*************************************************************/ 1216 nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_CONFIG; 1217 return; 1218 /*************************************************************/ 1219 } 1220 /* Are cookies from file explicitly disabled? */ 1221 if(!(fn = nsd->options->cookie_secret_file)) 1222 goto generate_cookie_secrets; 1223 1224 else if((f = fopen(fn, "r")) != NULL) 1225 ; /* pass */ 1226 1227 /* a non-existing cookie file is not necessarily an error */ 1228 else if(errno != ENOENT) { 1229 log_msg( LOG_ERR 1230 , "error reading cookie secret file \"%s\": \"%s\"" 1231 , fn, strerror(errno)); 1232 goto generate_cookie_secrets; 1233 1234 /* Only at startup cookie_secrets_source == COOKIE_SECRETS_NONE. 1235 * Only then the previous default file location will be tried 1236 * when the current default file location didn't exist. 1237 */ 1238 } else if(nsd->cookie_secrets_source == COOKIE_SECRETS_NONE 1239 && nsd->options->cookie_secret_file_is_default 1240 && (f = fopen((fn = CONFIGDIR"/nsd_cookiesecrets.txt"),"r"))) 1241 ; /* pass */ 1242 1243 else if(errno != ENOENT) { 1244 log_msg( LOG_ERR 1245 , "error reading cookie secret file \"%s\": \"%s\"" 1246 , fn, strerror(errno)); 1247 goto generate_cookie_secrets; 1248 } else 1249 goto generate_cookie_secrets; 1250 1251 /* cookie secret file exists and is readable */ 1252 for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) { 1253 size_t secret_len = 0; 1254 ssize_t decoded_len = 0; 1255 if( fgets(secret, sizeof(secret), f) == NULL ) { break; } 1256 secret_len = strlen(secret); 1257 if( secret_len == 0 ) { break; } 1258 assert( secret_len <= sizeof(secret) ); 1259 secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len; 1260 if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) { 1261 fclose(f); 1262 log_msg( LOG_ERR 1263 , "error parsing cookie secret file \"%s\"" 1264 , fn); 1265 explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); 1266 explicit_bzero(secret, sizeof(secret)); 1267 goto generate_cookie_secrets; 1268 } 1269 /* needed for `hex_pton`; stripping potential `\n` */ 1270 secret[secret_len] = '\0'; 1271 decoded_len = hex_pton(secret, cookie_secrets[count].cookie_secret, 1272 NSD_COOKIE_SECRET_SIZE); 1273 if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { 1274 fclose(f); 1275 log_msg( LOG_ERR 1276 , "error parsing cookie secret file \"%s\"" 1277 , fn); 1278 explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); 1279 explicit_bzero(secret, sizeof(secret)); 1280 goto generate_cookie_secrets; 1281 } 1282 explicit_bzero(secret, sizeof(secret)); 1283 } 1284 fclose(f); 1285 if(count) { 1286 nsd->cookie_count = count; 1287 memcpy(nsd->cookie_secrets, cookie_secrets, sizeof(cookie_secrets)); 1288 region_str_replace( nsd->region 1289 , &nsd->cookie_secrets_filename, fn ); 1290 explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); 1291 /*************************************************************/ 1292 nsd->cookie_secrets_source = COOKIE_SECRETS_FROM_FILE; 1293 return; 1294 /*************************************************************/ 1295 } 1296 explicit_bzero(cookie_secrets, sizeof(cookie_secrets)); 1297 1298 generate_cookie_secrets: 1299 /* Calculate a new random secret */ 1300 srandom(getpid() ^ time(NULL)); 1301 1302 for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) { 1303 #if defined(HAVE_SSL) 1304 if (!RAND_status() 1305 || !RAND_bytes(nsd->cookie_secrets[j].cookie_secret, NSD_COOKIE_SECRET_SIZE)) 1306 #endif 1307 for (i = 0; i < NSD_COOKIE_SECRET_SIZE; i++) 1308 nsd->cookie_secrets[j].cookie_secret[i] = random_generate(256); 1309 } 1310 nsd->cookie_count = 1; 1311 /*********************************************************************/ 1312 nsd->cookie_secrets_source = COOKIE_SECRETS_GENERATED; 1313 /*********************************************************************/ 1314 } 1315 1316 ssize_t 1317 print_socket_servers(struct nsd_bitset *bitset, char *buf, size_t bufsz) 1318 { 1319 /* x and y are the start and end points of a range of set bits */ 1320 /* z is the last unset bit */ 1321 int i, x, y, z, n = (int)(bitset->size); 1322 char *sep = ""; 1323 size_t off, written_total; 1324 ssize_t written = 0; 1325 1326 assert(bufsz != 0); 1327 1328 off = written_total = 0; 1329 x = y = z = -1; 1330 for (i = 0; i <= n; i++) { 1331 if (i == n || !nsd_bitset_isset(bitset, i)) { 1332 written = 0; 1333 if (i == n && x == -1) { 1334 assert(y == -1); 1335 assert(z == (n - 1)); 1336 written = snprintf(buf, bufsz, "(none)"); 1337 } else if (y > z) { 1338 assert(x > z); 1339 if (x == 0 && y == (n - 1)) { 1340 assert(z == -1); 1341 written = snprintf(buf+off, bufsz-off, 1342 "(all)"); 1343 } else if (x == y) { 1344 written = snprintf(buf+off, bufsz-off, 1345 "%s%d", sep, x+1); 1346 } else if (x == (y - 1)) { 1347 written = snprintf(buf+off, bufsz-off, 1348 "%s%d %d", sep, x+1, y+1); 1349 } else { 1350 assert(y > (x + 1)); 1351 written = snprintf(buf+off, bufsz-off, 1352 "%s%d-%d", sep, x+1, y+1); 1353 } 1354 } 1355 z = i; 1356 if (written > 0) { 1357 written_total += (size_t)written; 1358 off = (written_total < bufsz) ? written_total : bufsz - 1; 1359 sep = " "; 1360 } else if (written < 0) { 1361 return -1; 1362 } 1363 } else if (x <= z) { 1364 x = y = i; 1365 } else { 1366 assert(x > z); 1367 y = i; 1368 } 1369 } 1370 return written_total; 1371 } 1372