1 /* $OpenBSD: scp.c,v 1.203 2019/01/27 07:14:11 jmc Exp $ */ 2 /* 3 * scp - secure remote copy. This is basically patched BSD rcp which 4 * uses ssh to do the data transfer (instead of using rcmd). 5 * 6 * NOTE: This version should NOT be suid root. (This uses ssh to 7 * do the transfer and ssh has the necessary privileges.) 8 * 9 * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> 10 * 11 * As far as I am concerned, the code I have written for this software 12 * can be used freely for any purpose. Any derived versions of this 13 * software must be clearly marked as such, and if the derived work is 14 * incompatible with the protocol description in the RFC file, it must be 15 * called by a name other than "ssh" or "Secure Shell". 16 */ 17 /* 18 * Copyright (c) 1999 Theo de Raadt. All rights reserved. 19 * Copyright (c) 1999 Aaron Campbell. All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions 23 * are met: 24 * 1. Redistributions of source code must retain the above copyright 25 * notice, this list of conditions and the following disclaimer. 26 * 2. Redistributions in binary form must reproduce the above copyright 27 * notice, this list of conditions and the following disclaimer in the 28 * documentation and/or other materials provided with the distribution. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 33 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 35 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 39 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 */ 41 42 /* 43 * Parts from: 44 * 45 * Copyright (c) 1983, 1990, 1992, 1993, 1995 46 * The Regents of the University of California. All rights reserved. 47 * 48 * Redistribution and use in source and binary forms, with or without 49 * modification, are permitted provided that the following conditions 50 * are met: 51 * 1. Redistributions of source code must retain the above copyright 52 * notice, this list of conditions and the following disclaimer. 53 * 2. Redistributions in binary form must reproduce the above copyright 54 * notice, this list of conditions and the following disclaimer in the 55 * documentation and/or other materials provided with the distribution. 56 * 3. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 * 72 */ 73 74 #include "includes.h" 75 76 #include <sys/types.h> 77 #ifdef HAVE_SYS_STAT_H 78 # include <sys/stat.h> 79 #endif 80 #ifdef HAVE_POLL_H 81 #include <poll.h> 82 #else 83 # ifdef HAVE_SYS_POLL_H 84 # include <sys/poll.h> 85 # endif 86 #endif 87 #ifdef HAVE_SYS_TIME_H 88 # include <sys/time.h> 89 #endif 90 #include <sys/wait.h> 91 #include <sys/uio.h> 92 93 #include <ctype.h> 94 #include <dirent.h> 95 #include <errno.h> 96 #include <fcntl.h> 97 #include <fnmatch.h> 98 #include <limits.h> 99 #include <locale.h> 100 #include <pwd.h> 101 #include <signal.h> 102 #include <stdarg.h> 103 #ifdef HAVE_STDINT_H 104 #include <stdint.h> 105 #endif 106 #include <stdio.h> 107 #include <stdlib.h> 108 #include <string.h> 109 #include <time.h> 110 #include <unistd.h> 111 #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) 112 #include <vis.h> 113 #endif 114 115 #include "xmalloc.h" 116 #include "ssh.h" 117 #include "atomicio.h" 118 #include "pathnames.h" 119 #include "log.h" 120 #include "misc.h" 121 #include "progressmeter.h" 122 #include "utf8.h" 123 124 extern char *__progname; 125 126 #define COPY_BUFLEN 16384 127 128 int do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout); 129 int do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout); 130 131 /* Struct for addargs */ 132 arglist args; 133 arglist remote_remote_args; 134 135 /* Bandwidth limit */ 136 long long limit_kbps = 0; 137 struct bwlimit bwlimit; 138 139 /* Name of current file being transferred. */ 140 char *curfile; 141 142 /* This is set to non-zero to enable verbose mode. */ 143 int verbose_mode = 0; 144 145 /* This is set to zero if the progressmeter is not desired. */ 146 int showprogress = 1; 147 148 /* 149 * This is set to non-zero if remote-remote copy should be piped 150 * through this process. 151 */ 152 int throughlocal = 0; 153 154 /* Non-standard port to use for the ssh connection or -1. */ 155 int sshport = -1; 156 157 /* This is the program to execute for the secured connection. ("ssh" or -S) */ 158 char *ssh_program = _PATH_SSH_PROGRAM; 159 160 /* This is used to store the pid of ssh_program */ 161 pid_t do_cmd_pid = -1; 162 163 static void 164 killchild(int signo) 165 { 166 if (do_cmd_pid > 1) { 167 kill(do_cmd_pid, signo ? signo : SIGTERM); 168 waitpid(do_cmd_pid, NULL, 0); 169 } 170 171 if (signo) 172 _exit(1); 173 exit(1); 174 } 175 176 static void 177 suspchild(int signo) 178 { 179 int status; 180 181 if (do_cmd_pid > 1) { 182 kill(do_cmd_pid, signo); 183 while (waitpid(do_cmd_pid, &status, WUNTRACED) == -1 && 184 errno == EINTR) 185 ; 186 kill(getpid(), SIGSTOP); 187 } 188 } 189 190 static int 191 do_local_cmd(arglist *a) 192 { 193 u_int i; 194 int status; 195 pid_t pid; 196 197 if (a->num == 0) 198 fatal("do_local_cmd: no arguments"); 199 200 if (verbose_mode) { 201 fprintf(stderr, "Executing:"); 202 for (i = 0; i < a->num; i++) 203 fmprintf(stderr, " %s", a->list[i]); 204 fprintf(stderr, "\n"); 205 } 206 if ((pid = fork()) == -1) 207 fatal("do_local_cmd: fork: %s", strerror(errno)); 208 209 if (pid == 0) { 210 execvp(a->list[0], a->list); 211 perror(a->list[0]); 212 exit(1); 213 } 214 215 do_cmd_pid = pid; 216 signal(SIGTERM, killchild); 217 signal(SIGINT, killchild); 218 signal(SIGHUP, killchild); 219 220 while (waitpid(pid, &status, 0) == -1) 221 if (errno != EINTR) 222 fatal("do_local_cmd: waitpid: %s", strerror(errno)); 223 224 do_cmd_pid = -1; 225 226 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 227 return (-1); 228 229 return (0); 230 } 231 232 /* 233 * This function executes the given command as the specified user on the 234 * given host. This returns < 0 if execution fails, and >= 0 otherwise. This 235 * assigns the input and output file descriptors on success. 236 */ 237 238 int 239 do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) 240 { 241 int pin[2], pout[2], reserved[2]; 242 243 if (verbose_mode) 244 fmprintf(stderr, 245 "Executing: program %s host %s, user %s, command %s\n", 246 ssh_program, host, 247 remuser ? remuser : "(unspecified)", cmd); 248 249 if (port == -1) 250 port = sshport; 251 252 /* 253 * Reserve two descriptors so that the real pipes won't get 254 * descriptors 0 and 1 because that will screw up dup2 below. 255 */ 256 if (pipe(reserved) < 0) 257 fatal("pipe: %s", strerror(errno)); 258 259 /* Create a socket pair for communicating with ssh. */ 260 if (pipe(pin) < 0) 261 fatal("pipe: %s", strerror(errno)); 262 if (pipe(pout) < 0) 263 fatal("pipe: %s", strerror(errno)); 264 265 /* Free the reserved descriptors. */ 266 close(reserved[0]); 267 close(reserved[1]); 268 269 signal(SIGTSTP, suspchild); 270 signal(SIGTTIN, suspchild); 271 signal(SIGTTOU, suspchild); 272 273 /* Fork a child to execute the command on the remote host using ssh. */ 274 do_cmd_pid = fork(); 275 if (do_cmd_pid == 0) { 276 /* Child. */ 277 close(pin[1]); 278 close(pout[0]); 279 dup2(pin[0], 0); 280 dup2(pout[1], 1); 281 close(pin[0]); 282 close(pout[1]); 283 284 replacearg(&args, 0, "%s", ssh_program); 285 if (port != -1) { 286 addargs(&args, "-p"); 287 addargs(&args, "%d", port); 288 } 289 if (remuser != NULL) { 290 addargs(&args, "-l"); 291 addargs(&args, "%s", remuser); 292 } 293 addargs(&args, "--"); 294 addargs(&args, "%s", host); 295 addargs(&args, "%s", cmd); 296 297 execvp(ssh_program, args.list); 298 perror(ssh_program); 299 exit(1); 300 } else if (do_cmd_pid == -1) { 301 fatal("fork: %s", strerror(errno)); 302 } 303 /* Parent. Close the other side, and return the local side. */ 304 close(pin[0]); 305 *fdout = pin[1]; 306 close(pout[1]); 307 *fdin = pout[0]; 308 signal(SIGTERM, killchild); 309 signal(SIGINT, killchild); 310 signal(SIGHUP, killchild); 311 return 0; 312 } 313 314 /* 315 * This function executes a command similar to do_cmd(), but expects the 316 * input and output descriptors to be setup by a previous call to do_cmd(). 317 * This way the input and output of two commands can be connected. 318 */ 319 int 320 do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) 321 { 322 pid_t pid; 323 int status; 324 325 if (verbose_mode) 326 fmprintf(stderr, 327 "Executing: 2nd program %s host %s, user %s, command %s\n", 328 ssh_program, host, 329 remuser ? remuser : "(unspecified)", cmd); 330 331 if (port == -1) 332 port = sshport; 333 334 /* Fork a child to execute the command on the remote host using ssh. */ 335 pid = fork(); 336 if (pid == 0) { 337 dup2(fdin, 0); 338 dup2(fdout, 1); 339 340 replacearg(&args, 0, "%s", ssh_program); 341 if (port != -1) { 342 addargs(&args, "-p"); 343 addargs(&args, "%d", port); 344 } 345 if (remuser != NULL) { 346 addargs(&args, "-l"); 347 addargs(&args, "%s", remuser); 348 } 349 addargs(&args, "--"); 350 addargs(&args, "%s", host); 351 addargs(&args, "%s", cmd); 352 353 execvp(ssh_program, args.list); 354 perror(ssh_program); 355 exit(1); 356 } else if (pid == -1) { 357 fatal("fork: %s", strerror(errno)); 358 } 359 while (waitpid(pid, &status, 0) == -1) 360 if (errno != EINTR) 361 fatal("do_cmd2: waitpid: %s", strerror(errno)); 362 return 0; 363 } 364 365 typedef struct { 366 size_t cnt; 367 char *buf; 368 } BUF; 369 370 BUF *allocbuf(BUF *, int, int); 371 void lostconn(int); 372 int okname(char *); 373 void run_err(const char *,...); 374 void verifydir(char *); 375 376 struct passwd *pwd; 377 uid_t userid; 378 int errs, remin, remout; 379 int Tflag, pflag, iamremote, iamrecursive, targetshouldbedirectory; 380 381 #define CMDNEEDS 64 382 char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ 383 384 int response(void); 385 void rsource(char *, struct stat *); 386 void sink(int, char *[], const char *); 387 void source(int, char *[]); 388 void tolocal(int, char *[]); 389 void toremote(int, char *[]); 390 void usage(void); 391 392 int 393 main(int argc, char **argv) 394 { 395 int ch, fflag, tflag, status, n; 396 char **newargv; 397 const char *errstr; 398 extern char *optarg; 399 extern int optind; 400 401 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 402 sanitise_stdfd(); 403 404 msetlocale(); 405 406 /* Copy argv, because we modify it */ 407 newargv = xcalloc(MAXIMUM(argc + 1, 1), sizeof(*newargv)); 408 for (n = 0; n < argc; n++) 409 newargv[n] = xstrdup(argv[n]); 410 argv = newargv; 411 412 __progname = ssh_get_progname(argv[0]); 413 414 memset(&args, '\0', sizeof(args)); 415 memset(&remote_remote_args, '\0', sizeof(remote_remote_args)); 416 args.list = remote_remote_args.list = NULL; 417 addargs(&args, "%s", ssh_program); 418 addargs(&args, "-x"); 419 addargs(&args, "-oForwardAgent=no"); 420 addargs(&args, "-oPermitLocalCommand=no"); 421 addargs(&args, "-oClearAllForwardings=yes"); 422 addargs(&args, "-oRemoteCommand=none"); 423 addargs(&args, "-oRequestTTY=no"); 424 425 fflag = Tflag = tflag = 0; 426 while ((ch = getopt(argc, argv, 427 "dfl:prtTvBCc:i:P:q12346S:o:F:")) != -1) { 428 switch (ch) { 429 /* User-visible flags. */ 430 case '1': 431 fatal("SSH protocol v.1 is no longer supported"); 432 break; 433 case '2': 434 /* Ignored */ 435 break; 436 case '4': 437 case '6': 438 case 'C': 439 addargs(&args, "-%c", ch); 440 addargs(&remote_remote_args, "-%c", ch); 441 break; 442 case '3': 443 throughlocal = 1; 444 break; 445 case 'o': 446 case 'c': 447 case 'i': 448 case 'F': 449 addargs(&remote_remote_args, "-%c", ch); 450 addargs(&remote_remote_args, "%s", optarg); 451 addargs(&args, "-%c", ch); 452 addargs(&args, "%s", optarg); 453 break; 454 case 'P': 455 sshport = a2port(optarg); 456 if (sshport <= 0) 457 fatal("bad port \"%s\"\n", optarg); 458 break; 459 case 'B': 460 addargs(&remote_remote_args, "-oBatchmode=yes"); 461 addargs(&args, "-oBatchmode=yes"); 462 break; 463 case 'l': 464 limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, 465 &errstr); 466 if (errstr != NULL) 467 usage(); 468 limit_kbps *= 1024; /* kbps */ 469 bandwidth_limit_init(&bwlimit, limit_kbps, COPY_BUFLEN); 470 break; 471 case 'p': 472 pflag = 1; 473 break; 474 case 'r': 475 iamrecursive = 1; 476 break; 477 case 'S': 478 ssh_program = xstrdup(optarg); 479 break; 480 case 'v': 481 addargs(&args, "-v"); 482 addargs(&remote_remote_args, "-v"); 483 verbose_mode = 1; 484 break; 485 case 'q': 486 addargs(&args, "-q"); 487 addargs(&remote_remote_args, "-q"); 488 showprogress = 0; 489 break; 490 491 /* Server options. */ 492 case 'd': 493 targetshouldbedirectory = 1; 494 break; 495 case 'f': /* "from" */ 496 iamremote = 1; 497 fflag = 1; 498 break; 499 case 't': /* "to" */ 500 iamremote = 1; 501 tflag = 1; 502 #ifdef HAVE_CYGWIN 503 setmode(0, O_BINARY); 504 #endif 505 break; 506 case 'T': 507 Tflag = 1; 508 break; 509 default: 510 usage(); 511 } 512 } 513 argc -= optind; 514 argv += optind; 515 516 if ((pwd = getpwuid(userid = getuid())) == NULL) 517 fatal("unknown user %u", (u_int) userid); 518 519 if (!isatty(STDOUT_FILENO)) 520 showprogress = 0; 521 522 if (pflag) { 523 /* Cannot pledge: -p allows setuid/setgid files... */ 524 } else { 525 if (pledge("stdio rpath wpath cpath fattr tty proc exec", 526 NULL) == -1) { 527 perror("pledge"); 528 exit(1); 529 } 530 } 531 532 remin = STDIN_FILENO; 533 remout = STDOUT_FILENO; 534 535 if (fflag) { 536 /* Follow "protocol", send data. */ 537 (void) response(); 538 source(argc, argv); 539 exit(errs != 0); 540 } 541 if (tflag) { 542 /* Receive data. */ 543 sink(argc, argv, NULL); 544 exit(errs != 0); 545 } 546 if (argc < 2) 547 usage(); 548 if (argc > 2) 549 targetshouldbedirectory = 1; 550 551 remin = remout = -1; 552 do_cmd_pid = -1; 553 /* Command to be executed on remote system using "ssh". */ 554 (void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", 555 verbose_mode ? " -v" : "", 556 iamrecursive ? " -r" : "", pflag ? " -p" : "", 557 targetshouldbedirectory ? " -d" : ""); 558 559 (void) signal(SIGPIPE, lostconn); 560 561 if (colon(argv[argc - 1])) /* Dest is remote host. */ 562 toremote(argc, argv); 563 else { 564 if (targetshouldbedirectory) 565 verifydir(argv[argc - 1]); 566 tolocal(argc, argv); /* Dest is local host. */ 567 } 568 /* 569 * Finally check the exit status of the ssh process, if one was forked 570 * and no error has occurred yet 571 */ 572 if (do_cmd_pid != -1 && errs == 0) { 573 if (remin != -1) 574 (void) close(remin); 575 if (remout != -1) 576 (void) close(remout); 577 if (waitpid(do_cmd_pid, &status, 0) == -1) 578 errs = 1; 579 else { 580 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 581 errs = 1; 582 } 583 } 584 exit(errs != 0); 585 } 586 587 /* Callback from atomicio6 to update progress meter and limit bandwidth */ 588 static int 589 scpio(void *_cnt, size_t s) 590 { 591 off_t *cnt = (off_t *)_cnt; 592 593 *cnt += s; 594 if (limit_kbps > 0) 595 bandwidth_limit(&bwlimit, s); 596 return 0; 597 } 598 599 static int 600 do_times(int fd, int verb, const struct stat *sb) 601 { 602 /* strlen(2^64) == 20; strlen(10^6) == 7 */ 603 char buf[(20 + 7 + 2) * 2 + 2]; 604 605 (void)snprintf(buf, sizeof(buf), "T%llu 0 %llu 0\n", 606 (unsigned long long) (sb->st_mtime < 0 ? 0 : sb->st_mtime), 607 (unsigned long long) (sb->st_atime < 0 ? 0 : sb->st_atime)); 608 if (verb) { 609 fprintf(stderr, "File mtime %lld atime %lld\n", 610 (long long)sb->st_mtime, (long long)sb->st_atime); 611 fprintf(stderr, "Sending file timestamps: %s", buf); 612 } 613 (void) atomicio(vwrite, fd, buf, strlen(buf)); 614 return (response()); 615 } 616 617 static int 618 parse_scp_uri(const char *uri, char **userp, char **hostp, int *portp, 619 char **pathp) 620 { 621 int r; 622 623 r = parse_uri("scp", uri, userp, hostp, portp, pathp); 624 if (r == 0 && *pathp == NULL) 625 *pathp = xstrdup("."); 626 return r; 627 } 628 629 void 630 toremote(int argc, char **argv) 631 { 632 char *suser = NULL, *host = NULL, *src = NULL; 633 char *bp, *tuser, *thost, *targ; 634 int sport = -1, tport = -1; 635 arglist alist; 636 int i, r; 637 u_int j; 638 639 memset(&alist, '\0', sizeof(alist)); 640 alist.list = NULL; 641 642 /* Parse target */ 643 r = parse_scp_uri(argv[argc - 1], &tuser, &thost, &tport, &targ); 644 if (r == -1) { 645 fmprintf(stderr, "%s: invalid uri\n", argv[argc - 1]); 646 ++errs; 647 goto out; 648 } 649 if (r != 0) { 650 if (parse_user_host_path(argv[argc - 1], &tuser, &thost, 651 &targ) == -1) { 652 fmprintf(stderr, "%s: invalid target\n", argv[argc - 1]); 653 ++errs; 654 goto out; 655 } 656 } 657 if (tuser != NULL && !okname(tuser)) { 658 ++errs; 659 goto out; 660 } 661 662 /* Parse source files */ 663 for (i = 0; i < argc - 1; i++) { 664 free(suser); 665 free(host); 666 free(src); 667 r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); 668 if (r == -1) { 669 fmprintf(stderr, "%s: invalid uri\n", argv[i]); 670 ++errs; 671 continue; 672 } 673 if (r != 0) { 674 parse_user_host_path(argv[i], &suser, &host, &src); 675 } 676 if (suser != NULL && !okname(suser)) { 677 ++errs; 678 continue; 679 } 680 if (host && throughlocal) { /* extended remote to remote */ 681 xasprintf(&bp, "%s -f %s%s", cmd, 682 *src == '-' ? "-- " : "", src); 683 if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) 684 exit(1); 685 free(bp); 686 xasprintf(&bp, "%s -t %s%s", cmd, 687 *targ == '-' ? "-- " : "", targ); 688 if (do_cmd2(thost, tuser, tport, bp, remin, remout) < 0) 689 exit(1); 690 free(bp); 691 (void) close(remin); 692 (void) close(remout); 693 remin = remout = -1; 694 } else if (host) { /* standard remote to remote */ 695 if (tport != -1 && tport != SSH_DEFAULT_PORT) { 696 /* This would require the remote support URIs */ 697 fatal("target port not supported with two " 698 "remote hosts without the -3 option"); 699 } 700 701 freeargs(&alist); 702 addargs(&alist, "%s", ssh_program); 703 addargs(&alist, "-x"); 704 addargs(&alist, "-oClearAllForwardings=yes"); 705 addargs(&alist, "-n"); 706 for (j = 0; j < remote_remote_args.num; j++) { 707 addargs(&alist, "%s", 708 remote_remote_args.list[j]); 709 } 710 711 if (sport != -1) { 712 addargs(&alist, "-p"); 713 addargs(&alist, "%d", sport); 714 } 715 if (suser) { 716 addargs(&alist, "-l"); 717 addargs(&alist, "%s", suser); 718 } 719 addargs(&alist, "--"); 720 addargs(&alist, "%s", host); 721 addargs(&alist, "%s", cmd); 722 addargs(&alist, "%s", src); 723 addargs(&alist, "%s%s%s:%s", 724 tuser ? tuser : "", tuser ? "@" : "", 725 thost, targ); 726 if (do_local_cmd(&alist) != 0) 727 errs = 1; 728 } else { /* local to remote */ 729 if (remin == -1) { 730 xasprintf(&bp, "%s -t %s%s", cmd, 731 *targ == '-' ? "-- " : "", targ); 732 if (do_cmd(thost, tuser, tport, bp, &remin, 733 &remout) < 0) 734 exit(1); 735 if (response() < 0) 736 exit(1); 737 free(bp); 738 } 739 source(1, argv + i); 740 } 741 } 742 out: 743 free(tuser); 744 free(thost); 745 free(targ); 746 free(suser); 747 free(host); 748 free(src); 749 } 750 751 void 752 tolocal(int argc, char **argv) 753 { 754 char *bp, *host = NULL, *src = NULL, *suser = NULL; 755 arglist alist; 756 int i, r, sport = -1; 757 758 memset(&alist, '\0', sizeof(alist)); 759 alist.list = NULL; 760 761 for (i = 0; i < argc - 1; i++) { 762 free(suser); 763 free(host); 764 free(src); 765 r = parse_scp_uri(argv[i], &suser, &host, &sport, &src); 766 if (r == -1) { 767 fmprintf(stderr, "%s: invalid uri\n", argv[i]); 768 ++errs; 769 continue; 770 } 771 if (r != 0) 772 parse_user_host_path(argv[i], &suser, &host, &src); 773 if (suser != NULL && !okname(suser)) { 774 ++errs; 775 continue; 776 } 777 if (!host) { /* Local to local. */ 778 freeargs(&alist); 779 addargs(&alist, "%s", _PATH_CP); 780 if (iamrecursive) 781 addargs(&alist, "-r"); 782 if (pflag) 783 addargs(&alist, "-p"); 784 addargs(&alist, "--"); 785 addargs(&alist, "%s", argv[i]); 786 addargs(&alist, "%s", argv[argc-1]); 787 if (do_local_cmd(&alist)) 788 ++errs; 789 continue; 790 } 791 /* Remote to local. */ 792 xasprintf(&bp, "%s -f %s%s", 793 cmd, *src == '-' ? "-- " : "", src); 794 if (do_cmd(host, suser, sport, bp, &remin, &remout) < 0) { 795 free(bp); 796 ++errs; 797 continue; 798 } 799 free(bp); 800 sink(1, argv + argc - 1, src); 801 (void) close(remin); 802 remin = remout = -1; 803 } 804 free(suser); 805 free(host); 806 free(src); 807 } 808 809 void 810 source(int argc, char **argv) 811 { 812 struct stat stb; 813 static BUF buffer; 814 BUF *bp; 815 off_t i, statbytes; 816 size_t amt, nr; 817 int fd = -1, haderr, indx; 818 char *last, *name, buf[2048], encname[PATH_MAX]; 819 int len; 820 821 for (indx = 0; indx < argc; ++indx) { 822 name = argv[indx]; 823 statbytes = 0; 824 len = strlen(name); 825 while (len > 1 && name[len-1] == '/') 826 name[--len] = '\0'; 827 if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) < 0) 828 goto syserr; 829 if (strchr(name, '\n') != NULL) { 830 strnvis(encname, name, sizeof(encname), VIS_NL); 831 name = encname; 832 } 833 if (fstat(fd, &stb) < 0) { 834 syserr: run_err("%s: %s", name, strerror(errno)); 835 goto next; 836 } 837 if (stb.st_size < 0) { 838 run_err("%s: %s", name, "Negative file size"); 839 goto next; 840 } 841 unset_nonblock(fd); 842 switch (stb.st_mode & S_IFMT) { 843 case S_IFREG: 844 break; 845 case S_IFDIR: 846 if (iamrecursive) { 847 rsource(name, &stb); 848 goto next; 849 } 850 /* FALLTHROUGH */ 851 default: 852 run_err("%s: not a regular file", name); 853 goto next; 854 } 855 if ((last = strrchr(name, '/')) == NULL) 856 last = name; 857 else 858 ++last; 859 curfile = last; 860 if (pflag) { 861 if (do_times(remout, verbose_mode, &stb) < 0) 862 goto next; 863 } 864 #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) 865 snprintf(buf, sizeof buf, "C%04o %lld %s\n", 866 (u_int) (stb.st_mode & FILEMODEMASK), 867 (long long)stb.st_size, last); 868 if (verbose_mode) 869 fmprintf(stderr, "Sending file modes: %s", buf); 870 (void) atomicio(vwrite, remout, buf, strlen(buf)); 871 if (response() < 0) 872 goto next; 873 if ((bp = allocbuf(&buffer, fd, COPY_BUFLEN)) == NULL) { 874 next: if (fd != -1) { 875 (void) close(fd); 876 fd = -1; 877 } 878 continue; 879 } 880 if (showprogress) 881 start_progress_meter(curfile, stb.st_size, &statbytes); 882 set_nonblock(remout); 883 for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { 884 amt = bp->cnt; 885 if (i + (off_t)amt > stb.st_size) 886 amt = stb.st_size - i; 887 if (!haderr) { 888 if ((nr = atomicio(read, fd, 889 bp->buf, amt)) != amt) { 890 haderr = errno; 891 memset(bp->buf + nr, 0, amt - nr); 892 } 893 } 894 /* Keep writing after error to retain sync */ 895 if (haderr) { 896 (void)atomicio(vwrite, remout, bp->buf, amt); 897 memset(bp->buf, 0, amt); 898 continue; 899 } 900 if (atomicio6(vwrite, remout, bp->buf, amt, scpio, 901 &statbytes) != amt) 902 haderr = errno; 903 } 904 unset_nonblock(remout); 905 906 if (fd != -1) { 907 if (close(fd) < 0 && !haderr) 908 haderr = errno; 909 fd = -1; 910 } 911 if (!haderr) 912 (void) atomicio(vwrite, remout, "", 1); 913 else 914 run_err("%s: %s", name, strerror(haderr)); 915 (void) response(); 916 if (showprogress) 917 stop_progress_meter(); 918 } 919 } 920 921 void 922 rsource(char *name, struct stat *statp) 923 { 924 DIR *dirp; 925 struct dirent *dp; 926 char *last, *vect[1], path[PATH_MAX]; 927 928 if (!(dirp = opendir(name))) { 929 run_err("%s: %s", name, strerror(errno)); 930 return; 931 } 932 last = strrchr(name, '/'); 933 if (last == NULL) 934 last = name; 935 else 936 last++; 937 if (pflag) { 938 if (do_times(remout, verbose_mode, statp) < 0) { 939 closedir(dirp); 940 return; 941 } 942 } 943 (void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", 944 (u_int) (statp->st_mode & FILEMODEMASK), 0, last); 945 if (verbose_mode) 946 fmprintf(stderr, "Entering directory: %s", path); 947 (void) atomicio(vwrite, remout, path, strlen(path)); 948 if (response() < 0) { 949 closedir(dirp); 950 return; 951 } 952 while ((dp = readdir(dirp)) != NULL) { 953 if (dp->d_ino == 0) 954 continue; 955 if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 956 continue; 957 if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) { 958 run_err("%s/%s: name too long", name, dp->d_name); 959 continue; 960 } 961 (void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); 962 vect[0] = path; 963 source(1, vect); 964 } 965 (void) closedir(dirp); 966 (void) atomicio(vwrite, remout, "E\n", 2); 967 (void) response(); 968 } 969 970 #define TYPE_OVERFLOW(type, val) \ 971 ((sizeof(type) == 4 && (val) > INT32_MAX) || \ 972 (sizeof(type) == 8 && (val) > INT64_MAX) || \ 973 (sizeof(type) != 4 && sizeof(type) != 8)) 974 975 void 976 sink(int argc, char **argv, const char *src) 977 { 978 static BUF buffer; 979 struct stat stb; 980 enum { 981 YES, NO, DISPLAYED 982 } wrerr; 983 BUF *bp; 984 off_t i; 985 size_t j, count; 986 int amt, exists, first, ofd; 987 mode_t mode, omode, mask; 988 off_t size, statbytes; 989 unsigned long long ull; 990 int setimes, targisdir, wrerrno = 0; 991 char ch, *cp, *np, *targ, *why, *vect[1], buf[2048], visbuf[2048]; 992 char *src_copy = NULL, *restrict_pattern = NULL; 993 struct timeval tv[2]; 994 995 #define atime tv[0] 996 #define mtime tv[1] 997 #define SCREWUP(str) { why = str; goto screwup; } 998 999 if (TYPE_OVERFLOW(time_t, 0) || TYPE_OVERFLOW(off_t, 0)) 1000 SCREWUP("Unexpected off_t/time_t size"); 1001 1002 setimes = targisdir = 0; 1003 mask = umask(0); 1004 if (!pflag) 1005 (void) umask(mask); 1006 if (argc != 1) { 1007 run_err("ambiguous target"); 1008 exit(1); 1009 } 1010 targ = *argv; 1011 if (targetshouldbedirectory) 1012 verifydir(targ); 1013 1014 (void) atomicio(vwrite, remout, "", 1); 1015 if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) 1016 targisdir = 1; 1017 if (src != NULL && !iamrecursive && !Tflag) { 1018 /* 1019 * Prepare to try to restrict incoming filenames to match 1020 * the requested destination file glob. 1021 */ 1022 if ((src_copy = strdup(src)) == NULL) 1023 fatal("strdup failed"); 1024 if ((restrict_pattern = strrchr(src_copy, '/')) != NULL) { 1025 *restrict_pattern++ = '\0'; 1026 } 1027 } 1028 for (first = 1;; first = 0) { 1029 cp = buf; 1030 if (atomicio(read, remin, cp, 1) != 1) 1031 return; 1032 if (*cp++ == '\n') 1033 SCREWUP("unexpected <newline>"); 1034 do { 1035 if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 1036 SCREWUP("lost connection"); 1037 *cp++ = ch; 1038 } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); 1039 *cp = 0; 1040 if (verbose_mode) 1041 fmprintf(stderr, "Sink: %s", buf); 1042 1043 if (buf[0] == '\01' || buf[0] == '\02') { 1044 if (iamremote == 0) { 1045 (void) snmprintf(visbuf, sizeof(visbuf), 1046 NULL, "%s", buf + 1); 1047 (void) atomicio(vwrite, STDERR_FILENO, 1048 visbuf, strlen(visbuf)); 1049 } 1050 if (buf[0] == '\02') 1051 exit(1); 1052 ++errs; 1053 continue; 1054 } 1055 if (buf[0] == 'E') { 1056 (void) atomicio(vwrite, remout, "", 1); 1057 return; 1058 } 1059 if (ch == '\n') 1060 *--cp = 0; 1061 1062 cp = buf; 1063 if (*cp == 'T') { 1064 setimes++; 1065 cp++; 1066 if (!isdigit((unsigned char)*cp)) 1067 SCREWUP("mtime.sec not present"); 1068 ull = strtoull(cp, &cp, 10); 1069 if (!cp || *cp++ != ' ') 1070 SCREWUP("mtime.sec not delimited"); 1071 if (TYPE_OVERFLOW(time_t, ull)) 1072 setimes = 0; /* out of range */ 1073 mtime.tv_sec = ull; 1074 mtime.tv_usec = strtol(cp, &cp, 10); 1075 if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || 1076 mtime.tv_usec > 999999) 1077 SCREWUP("mtime.usec not delimited"); 1078 if (!isdigit((unsigned char)*cp)) 1079 SCREWUP("atime.sec not present"); 1080 ull = strtoull(cp, &cp, 10); 1081 if (!cp || *cp++ != ' ') 1082 SCREWUP("atime.sec not delimited"); 1083 if (TYPE_OVERFLOW(time_t, ull)) 1084 setimes = 0; /* out of range */ 1085 atime.tv_sec = ull; 1086 atime.tv_usec = strtol(cp, &cp, 10); 1087 if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || 1088 atime.tv_usec > 999999) 1089 SCREWUP("atime.usec not delimited"); 1090 (void) atomicio(vwrite, remout, "", 1); 1091 continue; 1092 } 1093 if (*cp != 'C' && *cp != 'D') { 1094 /* 1095 * Check for the case "rcp remote:foo\* local:bar". 1096 * In this case, the line "No match." can be returned 1097 * by the shell before the rcp command on the remote is 1098 * executed so the ^Aerror_message convention isn't 1099 * followed. 1100 */ 1101 if (first) { 1102 run_err("%s", cp); 1103 exit(1); 1104 } 1105 SCREWUP("expected control record"); 1106 } 1107 mode = 0; 1108 for (++cp; cp < buf + 5; cp++) { 1109 if (*cp < '0' || *cp > '7') 1110 SCREWUP("bad mode"); 1111 mode = (mode << 3) | (*cp - '0'); 1112 } 1113 if (!pflag) 1114 mode &= ~mask; 1115 if (*cp++ != ' ') 1116 SCREWUP("mode not delimited"); 1117 1118 if (!isdigit((unsigned char)*cp)) 1119 SCREWUP("size not present"); 1120 ull = strtoull(cp, &cp, 10); 1121 if (!cp || *cp++ != ' ') 1122 SCREWUP("size not delimited"); 1123 if (TYPE_OVERFLOW(off_t, ull)) 1124 SCREWUP("size out of range"); 1125 size = (off_t)ull; 1126 1127 if (*cp == '\0' || strchr(cp, '/') != NULL || 1128 strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { 1129 run_err("error: unexpected filename: %s", cp); 1130 exit(1); 1131 } 1132 if (restrict_pattern != NULL && 1133 fnmatch(restrict_pattern, cp, 0) != 0) 1134 SCREWUP("filename does not match request"); 1135 if (targisdir) { 1136 static char *namebuf; 1137 static size_t cursize; 1138 size_t need; 1139 1140 need = strlen(targ) + strlen(cp) + 250; 1141 if (need > cursize) { 1142 free(namebuf); 1143 namebuf = xmalloc(need); 1144 cursize = need; 1145 } 1146 (void) snprintf(namebuf, need, "%s%s%s", targ, 1147 strcmp(targ, "/") ? "/" : "", cp); 1148 np = namebuf; 1149 } else 1150 np = targ; 1151 curfile = cp; 1152 exists = stat(np, &stb) == 0; 1153 if (buf[0] == 'D') { 1154 int mod_flag = pflag; 1155 if (!iamrecursive) 1156 SCREWUP("received directory without -r"); 1157 if (exists) { 1158 if (!S_ISDIR(stb.st_mode)) { 1159 errno = ENOTDIR; 1160 goto bad; 1161 } 1162 if (pflag) 1163 (void) chmod(np, mode); 1164 } else { 1165 /* Handle copying from a read-only 1166 directory */ 1167 mod_flag = 1; 1168 if (mkdir(np, mode | S_IRWXU) < 0) 1169 goto bad; 1170 } 1171 vect[0] = xstrdup(np); 1172 sink(1, vect, src); 1173 if (setimes) { 1174 setimes = 0; 1175 if (utimes(vect[0], tv) < 0) 1176 run_err("%s: set times: %s", 1177 vect[0], strerror(errno)); 1178 } 1179 if (mod_flag) 1180 (void) chmod(vect[0], mode); 1181 free(vect[0]); 1182 continue; 1183 } 1184 omode = mode; 1185 mode |= S_IWUSR; 1186 if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { 1187 bad: run_err("%s: %s", np, strerror(errno)); 1188 continue; 1189 } 1190 (void) atomicio(vwrite, remout, "", 1); 1191 if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { 1192 (void) close(ofd); 1193 continue; 1194 } 1195 cp = bp->buf; 1196 wrerr = NO; 1197 1198 statbytes = 0; 1199 if (showprogress) 1200 start_progress_meter(curfile, size, &statbytes); 1201 set_nonblock(remin); 1202 for (count = i = 0; i < size; i += bp->cnt) { 1203 amt = bp->cnt; 1204 if (i + amt > size) 1205 amt = size - i; 1206 count += amt; 1207 do { 1208 j = atomicio6(read, remin, cp, amt, 1209 scpio, &statbytes); 1210 if (j == 0) { 1211 run_err("%s", j != EPIPE ? 1212 strerror(errno) : 1213 "dropped connection"); 1214 exit(1); 1215 } 1216 amt -= j; 1217 cp += j; 1218 } while (amt > 0); 1219 1220 if (count == bp->cnt) { 1221 /* Keep reading so we stay sync'd up. */ 1222 if (wrerr == NO) { 1223 if (atomicio(vwrite, ofd, bp->buf, 1224 count) != count) { 1225 wrerr = YES; 1226 wrerrno = errno; 1227 } 1228 } 1229 count = 0; 1230 cp = bp->buf; 1231 } 1232 } 1233 unset_nonblock(remin); 1234 if (count != 0 && wrerr == NO && 1235 atomicio(vwrite, ofd, bp->buf, count) != count) { 1236 wrerr = YES; 1237 wrerrno = errno; 1238 } 1239 if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && 1240 ftruncate(ofd, size) != 0) { 1241 run_err("%s: truncate: %s", np, strerror(errno)); 1242 wrerr = DISPLAYED; 1243 } 1244 if (pflag) { 1245 if (exists || omode != mode) 1246 #ifdef HAVE_FCHMOD 1247 if (fchmod(ofd, omode)) { 1248 #else /* HAVE_FCHMOD */ 1249 if (chmod(np, omode)) { 1250 #endif /* HAVE_FCHMOD */ 1251 run_err("%s: set mode: %s", 1252 np, strerror(errno)); 1253 wrerr = DISPLAYED; 1254 } 1255 } else { 1256 if (!exists && omode != mode) 1257 #ifdef HAVE_FCHMOD 1258 if (fchmod(ofd, omode & ~mask)) { 1259 #else /* HAVE_FCHMOD */ 1260 if (chmod(np, omode & ~mask)) { 1261 #endif /* HAVE_FCHMOD */ 1262 run_err("%s: set mode: %s", 1263 np, strerror(errno)); 1264 wrerr = DISPLAYED; 1265 } 1266 } 1267 if (close(ofd) == -1) { 1268 wrerr = YES; 1269 wrerrno = errno; 1270 } 1271 (void) response(); 1272 if (showprogress) 1273 stop_progress_meter(); 1274 if (setimes && wrerr == NO) { 1275 setimes = 0; 1276 if (utimes(np, tv) < 0) { 1277 run_err("%s: set times: %s", 1278 np, strerror(errno)); 1279 wrerr = DISPLAYED; 1280 } 1281 } 1282 switch (wrerr) { 1283 case YES: 1284 run_err("%s: %s", np, strerror(wrerrno)); 1285 break; 1286 case NO: 1287 (void) atomicio(vwrite, remout, "", 1); 1288 break; 1289 case DISPLAYED: 1290 break; 1291 } 1292 } 1293 screwup: 1294 run_err("protocol error: %s", why); 1295 exit(1); 1296 } 1297 1298 int 1299 response(void) 1300 { 1301 char ch, *cp, resp, rbuf[2048], visbuf[2048]; 1302 1303 if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) 1304 lostconn(0); 1305 1306 cp = rbuf; 1307 switch (resp) { 1308 case 0: /* ok */ 1309 return (0); 1310 default: 1311 *cp++ = resp; 1312 /* FALLTHROUGH */ 1313 case 1: /* error, followed by error msg */ 1314 case 2: /* fatal error, "" */ 1315 do { 1316 if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) 1317 lostconn(0); 1318 *cp++ = ch; 1319 } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); 1320 1321 if (!iamremote) { 1322 cp[-1] = '\0'; 1323 (void) snmprintf(visbuf, sizeof(visbuf), 1324 NULL, "%s\n", rbuf); 1325 (void) atomicio(vwrite, STDERR_FILENO, 1326 visbuf, strlen(visbuf)); 1327 } 1328 ++errs; 1329 if (resp == 1) 1330 return (-1); 1331 exit(1); 1332 } 1333 /* NOTREACHED */ 1334 } 1335 1336 void 1337 usage(void) 1338 { 1339 (void) fprintf(stderr, 1340 "usage: scp [-346BCpqrTv] [-c cipher] [-F ssh_config] [-i identity_file]\n" 1341 " [-l limit] [-o ssh_option] [-P port] [-S program] source ... target\n"); 1342 exit(1); 1343 } 1344 1345 void 1346 run_err(const char *fmt,...) 1347 { 1348 static FILE *fp; 1349 va_list ap; 1350 1351 ++errs; 1352 if (fp != NULL || (remout != -1 && (fp = fdopen(remout, "w")))) { 1353 (void) fprintf(fp, "%c", 0x01); 1354 (void) fprintf(fp, "scp: "); 1355 va_start(ap, fmt); 1356 (void) vfprintf(fp, fmt, ap); 1357 va_end(ap); 1358 (void) fprintf(fp, "\n"); 1359 (void) fflush(fp); 1360 } 1361 1362 if (!iamremote) { 1363 va_start(ap, fmt); 1364 vfmprintf(stderr, fmt, ap); 1365 va_end(ap); 1366 fprintf(stderr, "\n"); 1367 } 1368 } 1369 1370 void 1371 verifydir(char *cp) 1372 { 1373 struct stat stb; 1374 1375 if (!stat(cp, &stb)) { 1376 if (S_ISDIR(stb.st_mode)) 1377 return; 1378 errno = ENOTDIR; 1379 } 1380 run_err("%s: %s", cp, strerror(errno)); 1381 killchild(0); 1382 } 1383 1384 int 1385 okname(char *cp0) 1386 { 1387 int c; 1388 char *cp; 1389 1390 cp = cp0; 1391 do { 1392 c = (int)*cp; 1393 if (c & 0200) 1394 goto bad; 1395 if (!isalpha(c) && !isdigit((unsigned char)c)) { 1396 switch (c) { 1397 case '\'': 1398 case '"': 1399 case '`': 1400 case ' ': 1401 case '#': 1402 goto bad; 1403 default: 1404 break; 1405 } 1406 } 1407 } while (*++cp); 1408 return (1); 1409 1410 bad: fmprintf(stderr, "%s: invalid user name\n", cp0); 1411 return (0); 1412 } 1413 1414 BUF * 1415 allocbuf(BUF *bp, int fd, int blksize) 1416 { 1417 size_t size; 1418 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE 1419 struct stat stb; 1420 1421 if (fstat(fd, &stb) < 0) { 1422 run_err("fstat: %s", strerror(errno)); 1423 return (0); 1424 } 1425 size = ROUNDUP(stb.st_blksize, blksize); 1426 if (size == 0) 1427 size = blksize; 1428 #else /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1429 size = blksize; 1430 #endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */ 1431 if (bp->cnt >= size) 1432 return (bp); 1433 bp->buf = xrecallocarray(bp->buf, bp->cnt, size, 1); 1434 bp->cnt = size; 1435 return (bp); 1436 } 1437 1438 void 1439 lostconn(int signo) 1440 { 1441 if (!iamremote) 1442 (void)write(STDERR_FILENO, "lost connection\n", 16); 1443 if (signo) 1444 _exit(1); 1445 else 1446 exit(1); 1447 } 1448