1 /* $OpenBSD: misc.c,v 1.65 2006/11/23 01:35:11 ray Exp $ */ 2 /* 3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/ioctl.h> 29 #include <sys/socket.h> 30 #include <sys/param.h> 31 32 #include <net/if.h> 33 #include <netinet/in.h> 34 #include <netinet/tcp.h> 35 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <paths.h> 39 #include <pwd.h> 40 #include <stdarg.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 46 #include "xmalloc.h" 47 #include "misc.h" 48 #include "log.h" 49 #include "ssh.h" 50 51 /* remove newline at end of string */ 52 char * 53 chop(char *s) 54 { 55 char *t = s; 56 while (*t) { 57 if (*t == '\n' || *t == '\r') { 58 *t = '\0'; 59 return s; 60 } 61 t++; 62 } 63 return s; 64 65 } 66 67 /* set/unset filedescriptor to non-blocking */ 68 int 69 set_nonblock(int fd) 70 { 71 int val; 72 73 val = fcntl(fd, F_GETFL, 0); 74 if (val < 0) { 75 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 76 return (-1); 77 } 78 if (val & O_NONBLOCK) { 79 debug3("fd %d is O_NONBLOCK", fd); 80 return (0); 81 } 82 debug2("fd %d setting O_NONBLOCK", fd); 83 val |= O_NONBLOCK; 84 if (fcntl(fd, F_SETFL, val) == -1) { 85 debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 86 strerror(errno)); 87 return (-1); 88 } 89 return (0); 90 } 91 92 int 93 unset_nonblock(int fd) 94 { 95 int val; 96 97 val = fcntl(fd, F_GETFL, 0); 98 if (val < 0) { 99 error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 100 return (-1); 101 } 102 if (!(val & O_NONBLOCK)) { 103 debug3("fd %d is not O_NONBLOCK", fd); 104 return (0); 105 } 106 debug("fd %d clearing O_NONBLOCK", fd); 107 val &= ~O_NONBLOCK; 108 if (fcntl(fd, F_SETFL, val) == -1) { 109 debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 110 fd, strerror(errno)); 111 return (-1); 112 } 113 return (0); 114 } 115 116 /* disable nagle on socket */ 117 void 118 set_nodelay(int fd) 119 { 120 int opt; 121 socklen_t optlen; 122 123 optlen = sizeof opt; 124 if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 125 debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 126 return; 127 } 128 if (opt == 1) { 129 debug2("fd %d is TCP_NODELAY", fd); 130 return; 131 } 132 opt = 1; 133 debug2("fd %d setting TCP_NODELAY", fd); 134 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 135 error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 136 } 137 138 /* Characters considered whitespace in strsep calls. */ 139 #define WHITESPACE " \t\r\n" 140 #define QUOTE "\"" 141 142 /* return next token in configuration line */ 143 char * 144 strdelim(char **s) 145 { 146 char *old; 147 int wspace = 0; 148 149 if (*s == NULL) 150 return NULL; 151 152 old = *s; 153 154 *s = strpbrk(*s, WHITESPACE QUOTE "="); 155 if (*s == NULL) 156 return (old); 157 158 if (*s[0] == '\"') { 159 memmove(*s, *s + 1, strlen(*s)); /* move nul too */ 160 /* Find matching quote */ 161 if ((*s = strpbrk(*s, QUOTE)) == NULL) { 162 return (NULL); /* no matching quote */ 163 } else { 164 *s[0] = '\0'; 165 return (old); 166 } 167 } 168 169 /* Allow only one '=' to be skipped */ 170 if (*s[0] == '=') 171 wspace = 1; 172 *s[0] = '\0'; 173 174 /* Skip any extra whitespace after first token */ 175 *s += strspn(*s + 1, WHITESPACE) + 1; 176 if (*s[0] == '=' && !wspace) 177 *s += strspn(*s + 1, WHITESPACE) + 1; 178 179 return (old); 180 } 181 182 struct passwd * 183 pwcopy(struct passwd *pw) 184 { 185 struct passwd *copy = xcalloc(1, sizeof(*copy)); 186 187 copy->pw_name = xstrdup(pw->pw_name); 188 copy->pw_passwd = xstrdup(pw->pw_passwd); 189 copy->pw_gecos = xstrdup(pw->pw_gecos); 190 copy->pw_uid = pw->pw_uid; 191 copy->pw_gid = pw->pw_gid; 192 copy->pw_expire = pw->pw_expire; 193 copy->pw_change = pw->pw_change; 194 copy->pw_class = xstrdup(pw->pw_class); 195 copy->pw_dir = xstrdup(pw->pw_dir); 196 copy->pw_shell = xstrdup(pw->pw_shell); 197 return copy; 198 } 199 200 /* 201 * Convert ASCII string to TCP/IP port number. 202 * Port must be >0 and <=65535. 203 * Return 0 if invalid. 204 */ 205 int 206 a2port(const char *s) 207 { 208 long port; 209 char *endp; 210 211 errno = 0; 212 port = strtol(s, &endp, 0); 213 if (s == endp || *endp != '\0' || 214 (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || 215 port <= 0 || port > 65535) 216 return 0; 217 218 return port; 219 } 220 221 int 222 a2tun(const char *s, int *remote) 223 { 224 const char *errstr = NULL; 225 char *sp, *ep; 226 int tun; 227 228 if (remote != NULL) { 229 *remote = SSH_TUNID_ANY; 230 sp = xstrdup(s); 231 if ((ep = strchr(sp, ':')) == NULL) { 232 xfree(sp); 233 return (a2tun(s, NULL)); 234 } 235 ep[0] = '\0'; ep++; 236 *remote = a2tun(ep, NULL); 237 tun = a2tun(sp, NULL); 238 xfree(sp); 239 return (*remote == SSH_TUNID_ERR ? *remote : tun); 240 } 241 242 if (strcasecmp(s, "any") == 0) 243 return (SSH_TUNID_ANY); 244 245 tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 246 if (errstr != NULL) 247 return (SSH_TUNID_ERR); 248 249 return (tun); 250 } 251 252 #define SECONDS 1 253 #define MINUTES (SECONDS * 60) 254 #define HOURS (MINUTES * 60) 255 #define DAYS (HOURS * 24) 256 #define WEEKS (DAYS * 7) 257 258 /* 259 * Convert a time string into seconds; format is 260 * a sequence of: 261 * time[qualifier] 262 * 263 * Valid time qualifiers are: 264 * <none> seconds 265 * s|S seconds 266 * m|M minutes 267 * h|H hours 268 * d|D days 269 * w|W weeks 270 * 271 * Examples: 272 * 90m 90 minutes 273 * 1h30m 90 minutes 274 * 2d 2 days 275 * 1w 1 week 276 * 277 * Return -1 if time string is invalid. 278 */ 279 long 280 convtime(const char *s) 281 { 282 long total, secs; 283 const char *p; 284 char *endp; 285 286 errno = 0; 287 total = 0; 288 p = s; 289 290 if (p == NULL || *p == '\0') 291 return -1; 292 293 while (*p) { 294 secs = strtol(p, &endp, 10); 295 if (p == endp || 296 (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 297 secs < 0) 298 return -1; 299 300 switch (*endp++) { 301 case '\0': 302 endp--; 303 break; 304 case 's': 305 case 'S': 306 break; 307 case 'm': 308 case 'M': 309 secs *= MINUTES; 310 break; 311 case 'h': 312 case 'H': 313 secs *= HOURS; 314 break; 315 case 'd': 316 case 'D': 317 secs *= DAYS; 318 break; 319 case 'w': 320 case 'W': 321 secs *= WEEKS; 322 break; 323 default: 324 return -1; 325 } 326 total += secs; 327 if (total < 0) 328 return -1; 329 p = endp; 330 } 331 332 return total; 333 } 334 335 /* 336 * Returns a standardized host+port identifier string. 337 * Caller must free returned string. 338 */ 339 char * 340 put_host_port(const char *host, u_short port) 341 { 342 char *hoststr; 343 344 if (port == 0 || port == SSH_DEFAULT_PORT) 345 return(xstrdup(host)); 346 if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) 347 fatal("put_host_port: asprintf: %s", strerror(errno)); 348 debug3("put_host_port: %s", hoststr); 349 return hoststr; 350 } 351 352 /* 353 * Search for next delimiter between hostnames/addresses and ports. 354 * Argument may be modified (for termination). 355 * Returns *cp if parsing succeeds. 356 * *cp is set to the start of the next delimiter, if one was found. 357 * If this is the last field, *cp is set to NULL. 358 */ 359 char * 360 hpdelim(char **cp) 361 { 362 char *s, *old; 363 364 if (cp == NULL || *cp == NULL) 365 return NULL; 366 367 old = s = *cp; 368 if (*s == '[') { 369 if ((s = strchr(s, ']')) == NULL) 370 return NULL; 371 else 372 s++; 373 } else if ((s = strpbrk(s, ":/")) == NULL) 374 s = *cp + strlen(*cp); /* skip to end (see first case below) */ 375 376 switch (*s) { 377 case '\0': 378 *cp = NULL; /* no more fields*/ 379 break; 380 381 case ':': 382 case '/': 383 *s = '\0'; /* terminate */ 384 *cp = s + 1; 385 break; 386 387 default: 388 return NULL; 389 } 390 391 return old; 392 } 393 394 char * 395 cleanhostname(char *host) 396 { 397 if (*host == '[' && host[strlen(host) - 1] == ']') { 398 host[strlen(host) - 1] = '\0'; 399 return (host + 1); 400 } else 401 return host; 402 } 403 404 char * 405 colon(char *cp) 406 { 407 int flag = 0; 408 409 if (*cp == ':') /* Leading colon is part of file name. */ 410 return (0); 411 if (*cp == '[') 412 flag = 1; 413 414 for (; *cp; ++cp) { 415 if (*cp == '@' && *(cp+1) == '[') 416 flag = 1; 417 if (*cp == ']' && *(cp+1) == ':' && flag) 418 return (cp+1); 419 if (*cp == ':' && !flag) 420 return (cp); 421 if (*cp == '/') 422 return (0); 423 } 424 return (0); 425 } 426 427 /* function to assist building execv() arguments */ 428 void 429 addargs(arglist *args, char *fmt, ...) 430 { 431 va_list ap; 432 char *cp; 433 u_int nalloc; 434 int r; 435 436 va_start(ap, fmt); 437 r = vasprintf(&cp, fmt, ap); 438 va_end(ap); 439 if (r == -1) 440 fatal("addargs: argument too long"); 441 442 nalloc = args->nalloc; 443 if (args->list == NULL) { 444 nalloc = 32; 445 args->num = 0; 446 } else if (args->num+2 >= nalloc) 447 nalloc *= 2; 448 449 args->list = xrealloc(args->list, nalloc, sizeof(char *)); 450 args->nalloc = nalloc; 451 args->list[args->num++] = cp; 452 args->list[args->num] = NULL; 453 } 454 455 void 456 replacearg(arglist *args, u_int which, char *fmt, ...) 457 { 458 va_list ap; 459 char *cp; 460 int r; 461 462 va_start(ap, fmt); 463 r = vasprintf(&cp, fmt, ap); 464 va_end(ap); 465 if (r == -1) 466 fatal("replacearg: argument too long"); 467 468 if (which >= args->num) 469 fatal("replacearg: tried to replace invalid arg %d >= %d", 470 which, args->num); 471 xfree(args->list[which]); 472 args->list[which] = cp; 473 } 474 475 void 476 freeargs(arglist *args) 477 { 478 u_int i; 479 480 if (args->list != NULL) { 481 for (i = 0; i < args->num; i++) 482 xfree(args->list[i]); 483 xfree(args->list); 484 args->nalloc = args->num = 0; 485 args->list = NULL; 486 } 487 } 488 489 /* 490 * Expands tildes in the file name. Returns data allocated by xmalloc. 491 * Warning: this calls getpw*. 492 */ 493 char * 494 tilde_expand_filename(const char *filename, uid_t uid) 495 { 496 const char *path; 497 char user[128], ret[MAXPATHLEN]; 498 struct passwd *pw; 499 u_int len, slash; 500 501 if (*filename != '~') 502 return (xstrdup(filename)); 503 filename++; 504 505 path = strchr(filename, '/'); 506 if (path != NULL && path > filename) { /* ~user/path */ 507 slash = path - filename; 508 if (slash > sizeof(user) - 1) 509 fatal("tilde_expand_filename: ~username too long"); 510 memcpy(user, filename, slash); 511 user[slash] = '\0'; 512 if ((pw = getpwnam(user)) == NULL) 513 fatal("tilde_expand_filename: No such user %s", user); 514 } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 515 fatal("tilde_expand_filename: No such uid %d", uid); 516 517 if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret)) 518 fatal("tilde_expand_filename: Path too long"); 519 520 /* Make sure directory has a trailing '/' */ 521 len = strlen(pw->pw_dir); 522 if ((len == 0 || pw->pw_dir[len - 1] != '/') && 523 strlcat(ret, "/", sizeof(ret)) >= sizeof(ret)) 524 fatal("tilde_expand_filename: Path too long"); 525 526 /* Skip leading '/' from specified path */ 527 if (path != NULL) 528 filename = path + 1; 529 if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret)) 530 fatal("tilde_expand_filename: Path too long"); 531 532 return (xstrdup(ret)); 533 } 534 535 /* 536 * Expand a string with a set of %[char] escapes. A number of escapes may be 537 * specified as (char *escape_chars, char *replacement) pairs. The list must 538 * be terminated by a NULL escape_char. Returns replaced string in memory 539 * allocated by xmalloc. 540 */ 541 char * 542 percent_expand(const char *string, ...) 543 { 544 #define EXPAND_MAX_KEYS 16 545 struct { 546 const char *key; 547 const char *repl; 548 } keys[EXPAND_MAX_KEYS]; 549 u_int num_keys, i, j; 550 char buf[4096]; 551 va_list ap; 552 553 /* Gather keys */ 554 va_start(ap, string); 555 for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 556 keys[num_keys].key = va_arg(ap, char *); 557 if (keys[num_keys].key == NULL) 558 break; 559 keys[num_keys].repl = va_arg(ap, char *); 560 if (keys[num_keys].repl == NULL) 561 fatal("percent_expand: NULL replacement"); 562 } 563 va_end(ap); 564 565 if (num_keys >= EXPAND_MAX_KEYS) 566 fatal("percent_expand: too many keys"); 567 568 /* Expand string */ 569 *buf = '\0'; 570 for (i = 0; *string != '\0'; string++) { 571 if (*string != '%') { 572 append: 573 buf[i++] = *string; 574 if (i >= sizeof(buf)) 575 fatal("percent_expand: string too long"); 576 buf[i] = '\0'; 577 continue; 578 } 579 string++; 580 if (*string == '%') 581 goto append; 582 for (j = 0; j < num_keys; j++) { 583 if (strchr(keys[j].key, *string) != NULL) { 584 i = strlcat(buf, keys[j].repl, sizeof(buf)); 585 if (i >= sizeof(buf)) 586 fatal("percent_expand: string too long"); 587 break; 588 } 589 } 590 if (j >= num_keys) 591 fatal("percent_expand: unknown key %%%c", *string); 592 } 593 return (xstrdup(buf)); 594 #undef EXPAND_MAX_KEYS 595 } 596 597 /* 598 * Read an entire line from a public key file into a static buffer, discarding 599 * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 600 */ 601 int 602 read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 603 u_long *lineno) 604 { 605 while (fgets(buf, bufsz, f) != NULL) { 606 if (buf[0] == '\0') 607 continue; 608 (*lineno)++; 609 if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 610 return 0; 611 } else { 612 debug("%s: %s line %lu exceeds size limit", __func__, 613 filename, *lineno); 614 /* discard remainder of line */ 615 while (fgetc(f) != '\n' && !feof(f)) 616 ; /* nothing */ 617 } 618 } 619 return -1; 620 } 621 622 int 623 tun_open(int tun, int mode) 624 { 625 struct ifreq ifr; 626 char name[100]; 627 int fd = -1, sock; 628 629 /* Open the tunnel device */ 630 if (tun <= SSH_TUNID_MAX) { 631 snprintf(name, sizeof(name), "/dev/tun%d", tun); 632 fd = open(name, O_RDWR); 633 } else if (tun == SSH_TUNID_ANY) { 634 for (tun = 100; tun >= 0; tun--) { 635 snprintf(name, sizeof(name), "/dev/tun%d", tun); 636 if ((fd = open(name, O_RDWR)) >= 0) 637 break; 638 } 639 } else { 640 debug("%s: invalid tunnel %u", __func__, tun); 641 return (-1); 642 } 643 644 if (fd < 0) { 645 debug("%s: %s open failed: %s", __func__, name, strerror(errno)); 646 return (-1); 647 } 648 649 debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 650 651 /* Set the tunnel device operation mode */ 652 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun); 653 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 654 goto failed; 655 656 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) 657 goto failed; 658 659 /* Set interface mode */ 660 ifr.ifr_flags &= ~IFF_UP; 661 if (mode == SSH_TUNMODE_ETHERNET) 662 ifr.ifr_flags |= IFF_LINK0; 663 else 664 ifr.ifr_flags &= ~IFF_LINK0; 665 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 666 goto failed; 667 668 /* Bring interface up */ 669 ifr.ifr_flags |= IFF_UP; 670 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 671 goto failed; 672 673 close(sock); 674 return (fd); 675 676 failed: 677 if (fd >= 0) 678 close(fd); 679 if (sock >= 0) 680 close(sock); 681 debug("%s: failed to set %s mode %d: %s", __func__, name, 682 mode, strerror(errno)); 683 return (-1); 684 } 685 686 void 687 sanitise_stdfd(void) 688 { 689 int nullfd, dupfd; 690 691 if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 692 fprintf(stderr, "Couldn't open /dev/null: %s", strerror(errno)); 693 exit(1); 694 } 695 while (++dupfd <= 2) { 696 /* Only clobber closed fds */ 697 if (fcntl(dupfd, F_GETFL, 0) >= 0) 698 continue; 699 if (dup2(nullfd, dupfd) == -1) { 700 fprintf(stderr, "dup2: %s", strerror(errno)); 701 exit(1); 702 } 703 } 704 if (nullfd > 2) 705 close(nullfd); 706 } 707 708 char * 709 tohex(const void *vp, size_t l) 710 { 711 const u_char *p = (const u_char *)vp; 712 char b[3], *r; 713 size_t i, hl; 714 715 if (l > 65536) 716 return xstrdup("tohex: length > 65536"); 717 718 hl = l * 2 + 1; 719 r = xcalloc(1, hl); 720 for (i = 0; i < l; i++) { 721 snprintf(b, sizeof(b), "%02x", p[i]); 722 strlcat(r, b, hl); 723 } 724 return (r); 725 } 726 727 u_int64_t 728 get_u64(const void *vp) 729 { 730 const u_char *p = (const u_char *)vp; 731 u_int64_t v; 732 733 v = (u_int64_t)p[0] << 56; 734 v |= (u_int64_t)p[1] << 48; 735 v |= (u_int64_t)p[2] << 40; 736 v |= (u_int64_t)p[3] << 32; 737 v |= (u_int64_t)p[4] << 24; 738 v |= (u_int64_t)p[5] << 16; 739 v |= (u_int64_t)p[6] << 8; 740 v |= (u_int64_t)p[7]; 741 742 return (v); 743 } 744 745 u_int32_t 746 get_u32(const void *vp) 747 { 748 const u_char *p = (const u_char *)vp; 749 u_int32_t v; 750 751 v = (u_int32_t)p[0] << 24; 752 v |= (u_int32_t)p[1] << 16; 753 v |= (u_int32_t)p[2] << 8; 754 v |= (u_int32_t)p[3]; 755 756 return (v); 757 } 758 759 u_int16_t 760 get_u16(const void *vp) 761 { 762 const u_char *p = (const u_char *)vp; 763 u_int16_t v; 764 765 v = (u_int16_t)p[0] << 8; 766 v |= (u_int16_t)p[1]; 767 768 return (v); 769 } 770 771 void 772 put_u64(void *vp, u_int64_t v) 773 { 774 u_char *p = (u_char *)vp; 775 776 p[0] = (u_char)(v >> 56) & 0xff; 777 p[1] = (u_char)(v >> 48) & 0xff; 778 p[2] = (u_char)(v >> 40) & 0xff; 779 p[3] = (u_char)(v >> 32) & 0xff; 780 p[4] = (u_char)(v >> 24) & 0xff; 781 p[5] = (u_char)(v >> 16) & 0xff; 782 p[6] = (u_char)(v >> 8) & 0xff; 783 p[7] = (u_char)v & 0xff; 784 } 785 786 void 787 put_u32(void *vp, u_int32_t v) 788 { 789 u_char *p = (u_char *)vp; 790 791 p[0] = (u_char)(v >> 24) & 0xff; 792 p[1] = (u_char)(v >> 16) & 0xff; 793 p[2] = (u_char)(v >> 8) & 0xff; 794 p[3] = (u_char)v & 0xff; 795 } 796 797 798 void 799 put_u16(void *vp, u_int16_t v) 800 { 801 u_char *p = (u_char *)vp; 802 803 p[0] = (u_char)(v >> 8) & 0xff; 804 p[1] = (u_char)v & 0xff; 805 } 806