1 /* $OpenBSD: small.c,v 1.12 2019/11/18 04:37:35 deraadt Exp $ */ 2 /* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */ 3 4 /* 5 * Copyright (C) 1997 and 1998 WIDE Project. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the project nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 1985, 1989, 1993, 1994 35 * The Regents of the University of California. All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 */ 61 62 /* 63 * FTP User Program -- Command Routines. 64 */ 65 #include <sys/types.h> 66 #include <sys/socket.h> 67 #include <sys/stat.h> 68 #include <sys/wait.h> 69 #include <arpa/ftp.h> 70 71 #include <ctype.h> 72 #include <err.h> 73 #include <fnmatch.h> 74 #include <glob.h> 75 #include <netdb.h> 76 #include <stdio.h> 77 #include <stdlib.h> 78 #include <string.h> 79 #include <unistd.h> 80 #include <errno.h> 81 82 #include "ftp_var.h" 83 #include "pathnames.h" 84 #include "small.h" 85 86 jmp_buf jabort; 87 char *mname; 88 char *home = "/"; 89 90 struct types { 91 char *t_name; 92 char *t_mode; 93 int t_type; 94 } types[] = { 95 { "ascii", "A", TYPE_A }, 96 { "binary", "I", TYPE_I }, 97 { "image", "I", TYPE_I }, 98 { NULL } 99 }; 100 101 /* 102 * Set transfer type. 103 */ 104 void 105 settype(int argc, char *argv[]) 106 { 107 struct types *p; 108 int comret; 109 110 if (argc > 2) { 111 char *sep; 112 113 fprintf(ttyout, "usage: %s [", argv[0]); 114 sep = ""; 115 for (p = types; p->t_name; p++) { 116 fprintf(ttyout, "%s%s", sep, p->t_name); 117 sep = " | "; 118 } 119 fputs("]\n", ttyout); 120 code = -1; 121 return; 122 } 123 if (argc < 2) { 124 fprintf(ttyout, "Using %s mode to transfer files.\n", typename); 125 code = 0; 126 return; 127 } 128 for (p = types; p->t_name; p++) 129 if (strcmp(argv[1], p->t_name) == 0) 130 break; 131 if (p->t_name == 0) { 132 fprintf(ttyout, "%s: unknown mode.\n", argv[1]); 133 code = -1; 134 return; 135 } 136 comret = command("TYPE %s", p->t_mode); 137 if (comret == COMPLETE) { 138 (void)strlcpy(typename, p->t_name, sizeof typename); 139 curtype = type = p->t_type; 140 } 141 } 142 143 /* 144 * Internal form of settype; changes current type in use with server 145 * without changing our notion of the type for data transfers. 146 * Used to change to and from ascii for listings. 147 */ 148 void 149 changetype(int newtype, int show) 150 { 151 struct types *p; 152 int comret, oldverbose = verbose; 153 154 if (newtype == 0) 155 newtype = TYPE_I; 156 if (newtype == curtype) 157 return; 158 if ( 159 #ifndef SMALL 160 !debug && 161 #endif /* !SMALL */ 162 show == 0) 163 verbose = 0; 164 for (p = types; p->t_name; p++) 165 if (newtype == p->t_type) 166 break; 167 if (p->t_name == 0) { 168 warnx("internal error: unknown type %d.", newtype); 169 return; 170 } 171 if (newtype == TYPE_L && bytename[0] != '\0') 172 comret = command("TYPE %s %s", p->t_mode, bytename); 173 else 174 comret = command("TYPE %s", p->t_mode); 175 if (comret == COMPLETE) 176 curtype = newtype; 177 verbose = oldverbose; 178 } 179 180 char *stype[] = { 181 "type", 182 "", 183 0 184 }; 185 186 /* 187 * Set binary transfer type. 188 */ 189 /*ARGSUSED*/ 190 void 191 setbinary(int argc, char *argv[]) 192 { 193 194 stype[1] = "binary"; 195 settype(2, stype); 196 } 197 198 void 199 get(int argc, char *argv[]) 200 { 201 202 (void)getit(argc, argv, 0, restart_point ? "a+w" : "w" ); 203 } 204 205 /* 206 * Receive one file. 207 */ 208 int 209 getit(int argc, char *argv[], int restartit, const char *mode) 210 { 211 int loc = 0; 212 int rval = 0; 213 char *oldargv1, *oldargv2, *globargv2; 214 215 if (argc == 2) { 216 argc++; 217 argv[2] = argv[1]; 218 loc++; 219 } 220 #ifndef SMALL 221 if (argc < 2 && !another(&argc, &argv, "remote-file")) 222 goto usage; 223 if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { 224 usage: 225 fprintf(ttyout, "usage: %s remote-file [local-file]\n", 226 argv[0]); 227 code = -1; 228 return (0); 229 } 230 #endif /* !SMALL */ 231 oldargv1 = argv[1]; 232 oldargv2 = argv[2]; 233 if (!globulize(&argv[2])) { 234 code = -1; 235 return (0); 236 } 237 globargv2 = argv[2]; 238 if (loc && mcase) { 239 char *tp = argv[1], *tp2, tmpbuf[PATH_MAX]; 240 241 while (*tp && !islower((unsigned char)*tp)) { 242 tp++; 243 } 244 if (!*tp) { 245 tp = argv[2]; 246 tp2 = tmpbuf; 247 while ((*tp2 = *tp) != '\0') { 248 if (isupper((unsigned char)*tp2)) { 249 *tp2 = tolower((unsigned char)*tp2); 250 } 251 tp++; 252 tp2++; 253 } 254 argv[2] = tmpbuf; 255 } 256 } 257 if (loc && ntflag) 258 argv[2] = dotrans(argv[2]); 259 if (loc && mapflag) 260 argv[2] = domap(argv[2]); 261 #ifndef SMALL 262 if (restartit) { 263 struct stat stbuf; 264 int ret; 265 266 ret = stat(argv[2], &stbuf); 267 if (restartit == 1) { 268 restart_point = (ret < 0) ? 0 : stbuf.st_size; 269 } else { 270 if (ret == 0) { 271 time_t mtime; 272 273 mtime = remotemodtime(argv[1], 0); 274 if (mtime == -1) 275 goto freegetit; 276 if (stbuf.st_mtime >= mtime) { 277 rval = 1; 278 fprintf(ttyout, 279 "Local file \"%s\" is newer "\ 280 "than remote file \"%s\".\n", 281 argv[2], argv[1]); 282 goto freegetit; 283 } 284 } 285 } 286 } 287 #endif /* !SMALL */ 288 289 recvrequest("RETR", argv[2], argv[1], mode, 290 argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc); 291 restart_point = 0; 292 #ifndef SMALL 293 freegetit: 294 #endif 295 if (oldargv2 != globargv2) /* free up after globulize() */ 296 free(globargv2); 297 return (rval); 298 } 299 300 /* XXX - Signal race. */ 301 /* ARGSUSED */ 302 void 303 mabort(int signo) 304 { 305 int save_errno = errno; 306 307 alarmtimer(0); 308 (void) write(fileno(ttyout), "\n\r", 2); 309 #ifndef SMALL 310 if (mflag && fromatty) { 311 /* XXX signal race, crazy unbelievable stdio misuse */ 312 if (confirm(mname, NULL)) { 313 errno = save_errno; 314 longjmp(jabort, 1); 315 } 316 } 317 #endif /* !SMALL */ 318 mflag = 0; 319 errno = save_errno; 320 longjmp(jabort, 1); 321 } 322 323 /* 324 * Get multiple files. 325 */ 326 void 327 mget(int argc, char *argv[]) 328 { 329 extern int optind, optreset; 330 sig_t oldintr; 331 int xargc = 2; 332 char *cp, localcwd[PATH_MAX], *xargv[] = { argv[0], NULL, NULL }; 333 static int restartit = 0; 334 #ifndef SMALL 335 extern char *optarg; 336 const char *errstr; 337 int ch, i = 1; 338 char type = 0, *dummyargv[] = { argv[0], ".", NULL }; 339 FILE *ftemp = NULL; 340 static int depth = 0, max_depth = 0; 341 342 optind = optreset = 1; 343 344 if (depth) 345 depth++; 346 347 while ((ch = getopt(argc, argv, "cd:nr")) != -1) { 348 switch(ch) { 349 case 'c': 350 restartit = 1; 351 break; 352 case 'd': 353 max_depth = strtonum(optarg, 0, INT_MAX, &errstr); 354 if (errstr != NULL) { 355 fprintf(ttyout, "bad depth value, %s: %s\n", 356 errstr, optarg); 357 code = -1; 358 return; 359 } 360 break; 361 case 'n': 362 restartit = -1; 363 break; 364 case 'r': 365 depth = 1; 366 break; 367 default: 368 goto usage; 369 } 370 } 371 372 if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) { 373 usage: 374 fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n", 375 argv[0]); 376 code = -1; 377 return; 378 } 379 380 argv[optind - 1] = argv[0]; 381 argc -= optind - 1; 382 argv += optind - 1; 383 #endif /* !SMALL */ 384 385 mname = argv[0]; 386 mflag = 1; 387 if (getcwd(localcwd, sizeof(localcwd)) == NULL) 388 err(1, "can't get cwd"); 389 390 oldintr = signal(SIGINT, mabort); 391 (void)setjmp(jabort); 392 while ((cp = 393 #ifdef SMALL 394 remglob(argv, proxy, NULL)) != NULL 395 ) { 396 #else /* SMALL */ 397 depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) : 398 remglob(argv, proxy, NULL)) != NULL 399 || (mflag && depth && ++i < argc) 400 ) { 401 if (cp == NULL) 402 continue; 403 #endif /* SMALL */ 404 if (*cp == '\0') { 405 mflag = 0; 406 continue; 407 } 408 if (!mflag) 409 continue; 410 #ifndef SMALL 411 if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0) 412 continue; 413 #endif /* !SMALL */ 414 if (!fileindir(cp, localcwd)) { 415 fprintf(ttyout, "Skipping non-relative filename `%s'\n", 416 cp); 417 continue; 418 } 419 #ifndef SMALL 420 if (type == 'd' && depth == max_depth) 421 continue; 422 if (!confirm(argv[0], cp)) 423 continue; 424 if (type == 'd') { 425 mkdir(cp, 0755); 426 if (chdir(cp) != 0) { 427 warn("local: %s", cp); 428 continue; 429 } 430 431 xargv[1] = cp; 432 cd(xargc, xargv); 433 if (dirchange != 1) 434 goto out; 435 436 xargv[1] = "*"; 437 mget(xargc, xargv); 438 439 xargv[1] = ".."; 440 cd(xargc, xargv); 441 if (dirchange != 1) { 442 mflag = 0; 443 goto out; 444 } 445 446 out: 447 if (chdir("..") != 0) { 448 warn("local: %s", cp); 449 mflag = 0; 450 } 451 continue; 452 } 453 if (type == 's') 454 /* Currently ignored. */ 455 continue; 456 #endif /* !SMALL */ 457 xargv[1] = cp; 458 (void)getit(xargc, xargv, restartit, 459 (restartit == 1 || restart_point) ? "a+w" : "w"); 460 #ifndef SMALL 461 if (!mflag && fromatty) { 462 if (confirm(argv[0], NULL)) 463 mflag = 1; 464 } 465 #endif /* !SMALL */ 466 } 467 (void)signal(SIGINT, oldintr); 468 #ifndef SMALL 469 if (depth) 470 depth--; 471 if (depth == 0 || mflag == 0) 472 depth = max_depth = mflag = restartit = 0; 473 #else /* !SMALL */ 474 mflag = 0; 475 #endif /* !SMALL */ 476 } 477 478 /* 479 * Set current working directory on remote machine. 480 */ 481 void 482 cd(int argc, char *argv[]) 483 { 484 int r; 485 486 #ifndef SMALL 487 if ((argc < 2 && !another(&argc, &argv, "remote-directory")) || 488 argc > 2) { 489 fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); 490 code = -1; 491 return; 492 } 493 #endif /* !SMALL */ 494 r = command("CWD %s", argv[1]); 495 if (r == ERROR && code == 500) { 496 if (verbose) 497 fputs("CWD command not recognized, trying XCWD.\n", ttyout); 498 r = command("XCWD %s", argv[1]); 499 } 500 if (r == ERROR && code == 550) { 501 dirchange = 0; 502 return; 503 } 504 if (r == COMPLETE) 505 dirchange = 1; 506 } 507 508 /* 509 * Terminate session, but don't exit. 510 */ 511 /* ARGSUSED */ 512 void 513 disconnect(int argc, char *argv[]) 514 { 515 516 if (!connected) 517 return; 518 (void)command("QUIT"); 519 if (cout) { 520 (void)fclose(cout); 521 } 522 cout = NULL; 523 connected = 0; 524 data = -1; 525 #ifndef SMALL 526 if (!proxy) { 527 macnum = 0; 528 } 529 #endif /* !SMALL */ 530 } 531 532 char * 533 dotrans(char *name) 534 { 535 static char new[PATH_MAX]; 536 char *cp1, *cp2 = new; 537 int i, ostop, found; 538 539 for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) 540 continue; 541 for (cp1 = name; *cp1; cp1++) { 542 found = 0; 543 for (i = 0; *(ntin + i) && i < 16; i++) { 544 if (*cp1 == *(ntin + i)) { 545 found++; 546 if (i < ostop) { 547 *cp2++ = *(ntout + i); 548 } 549 break; 550 } 551 } 552 if (!found) { 553 *cp2++ = *cp1; 554 } 555 } 556 *cp2 = '\0'; 557 return (new); 558 } 559 560 char * 561 domap(char *name) 562 { 563 static char new[PATH_MAX]; 564 char *cp1 = name, *cp2 = mapin; 565 char *tp[9], *te[9]; 566 int i, toks[9], toknum = 0, match = 1; 567 568 for (i=0; i < 9; ++i) { 569 toks[i] = 0; 570 } 571 while (match && *cp1 && *cp2) { 572 switch (*cp2) { 573 case '\\': 574 if (*++cp2 != *cp1) { 575 match = 0; 576 } 577 break; 578 case '$': 579 if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { 580 if (*cp1 != *(++cp2+1)) { 581 toks[toknum = *cp2 - '1']++; 582 tp[toknum] = cp1; 583 while (*++cp1 && *(cp2+1) 584 != *cp1); 585 te[toknum] = cp1; 586 } 587 cp2++; 588 break; 589 } 590 /* FALLTHROUGH */ 591 default: 592 if (*cp2 != *cp1) { 593 match = 0; 594 } 595 break; 596 } 597 if (match && *cp1) { 598 cp1++; 599 } 600 if (match && *cp2) { 601 cp2++; 602 } 603 } 604 if (!match && *cp1) /* last token mismatch */ 605 { 606 toks[toknum] = 0; 607 } 608 cp1 = new; 609 *cp1 = '\0'; 610 cp2 = mapout; 611 while (*cp2) { 612 match = 0; 613 switch (*cp2) { 614 case '\\': 615 if (*(cp2 + 1)) { 616 *cp1++ = *++cp2; 617 } 618 break; 619 case '[': 620 LOOP: 621 if (*++cp2 == '$' && isdigit((unsigned char)*(cp2 + 1))) { 622 if (*++cp2 == '0') { 623 char *cp3 = name; 624 625 while (*cp3) { 626 *cp1++ = *cp3++; 627 } 628 match = 1; 629 } else if (toks[toknum = *cp2 - '1']) { 630 char *cp3 = tp[toknum]; 631 632 while (cp3 != te[toknum]) { 633 *cp1++ = *cp3++; 634 } 635 match = 1; 636 } 637 } else { 638 while (*cp2 && *cp2 != ',' && 639 *cp2 != ']') { 640 if (*cp2 == '\\') { 641 cp2++; 642 } else if (*cp2 == '$' && 643 isdigit((unsigned char)*(cp2 + 1))) { 644 if (*++cp2 == '0') { 645 char *cp3 = name; 646 647 while (*cp3) { 648 *cp1++ = *cp3++; 649 } 650 } else if (toks[toknum = 651 *cp2 - '1']) { 652 char *cp3=tp[toknum]; 653 654 while (cp3 != 655 te[toknum]) { 656 *cp1++ = *cp3++; 657 } 658 } 659 } else if (*cp2) { 660 *cp1++ = *cp2++; 661 } 662 } 663 if (!*cp2) { 664 fputs( 665 "nmap: unbalanced brackets.\n", ttyout); 666 return (name); 667 } 668 match = 1; 669 cp2--; 670 } 671 if (match) { 672 while (*++cp2 && *cp2 != ']') { 673 if (*cp2 == '\\' && *(cp2 + 1)) { 674 cp2++; 675 } 676 } 677 if (!*cp2) { 678 fputs( 679 "nmap: unbalanced brackets.\n", ttyout); 680 return (name); 681 } 682 break; 683 } 684 switch (*++cp2) { 685 case ',': 686 goto LOOP; 687 case ']': 688 break; 689 default: 690 cp2--; 691 goto LOOP; 692 } 693 break; 694 case '$': 695 if (isdigit((unsigned char)*(cp2 + 1))) { 696 if (*++cp2 == '0') { 697 char *cp3 = name; 698 699 while (*cp3) { 700 *cp1++ = *cp3++; 701 } 702 } else if (toks[toknum = *cp2 - '1']) { 703 char *cp3 = tp[toknum]; 704 705 while (cp3 != te[toknum]) { 706 *cp1++ = *cp3++; 707 } 708 } 709 break; 710 } 711 /* FALLTHROUGH */ 712 default: 713 *cp1++ = *cp2; 714 break; 715 } 716 cp2++; 717 } 718 *cp1 = '\0'; 719 if (!*new) { 720 return (name); 721 } 722 return (new); 723 } 724 725