ftpd.c revision 1.2
1/* $OpenBSD: ftpd.c,v 1.2 1996/06/18 10:09:20 downsj Exp $ */ 2/* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */ 3 4/* 5 * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 6 * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38static char copyright[] = 39"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44#if 0 45static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; 46#else 47static char rcsid[] = "$NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $"; 48#endif 49#endif /* not lint */ 50 51/* 52 * FTP server. 53 */ 54#include <sys/param.h> 55#include <sys/stat.h> 56#include <sys/ioctl.h> 57#include <sys/socket.h> 58#include <sys/wait.h> 59 60#include <netinet/in.h> 61#include <netinet/in_systm.h> 62#include <netinet/ip.h> 63 64#define FTP_NAMES 65#include <arpa/ftp.h> 66#include <arpa/inet.h> 67#include <arpa/telnet.h> 68 69#include <ctype.h> 70#include <dirent.h> 71#include <err.h> 72#include <errno.h> 73#include <fcntl.h> 74#include <glob.h> 75#include <limits.h> 76#include <netdb.h> 77#include <pwd.h> 78#include <setjmp.h> 79#include <signal.h> 80#include <stdio.h> 81#include <stdlib.h> 82#include <string.h> 83#include <syslog.h> 84#include <time.h> 85#include <unistd.h> 86#include <utmp.h> 87 88#include "pathnames.h" 89#include "extern.h" 90 91#if __STDC__ 92#include <stdarg.h> 93#else 94#include <varargs.h> 95#endif 96 97static char version[] = "Version 6.00"; 98 99extern off_t restart_point; 100extern char cbuf[]; 101 102struct sockaddr_in ctrl_addr; 103struct sockaddr_in data_source; 104struct sockaddr_in data_dest; 105struct sockaddr_in his_addr; 106struct sockaddr_in pasv_addr; 107 108int data; 109jmp_buf errcatch, urgcatch; 110int logged_in; 111struct passwd *pw; 112int debug; 113int timeout = 900; /* timeout after 15 minutes of inactivity */ 114int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ 115int logging; 116int guest; 117int dochroot; 118int type; 119int form; 120int stru; /* avoid C keyword */ 121int mode; 122int doutmp = 0; /* update utmp file */ 123int usedefault = 1; /* for data transfers */ 124int pdata = -1; /* for passive mode */ 125sig_atomic_t transflag; 126off_t file_size; 127off_t byte_count; 128#if !defined(CMASK) || CMASK == 0 129#undef CMASK 130#define CMASK 027 131#endif 132int defumask = CMASK; /* default umask value */ 133char tmpline[7]; 134char hostname[MAXHOSTNAMELEN]; 135char remotehost[MAXHOSTNAMELEN]; 136static char ttyline[20]; 137char *tty = ttyline; /* for klogin */ 138static struct utmp utmp; /* for utmp */ 139 140#if defined(KERBEROS) 141int notickets = 1; 142char *krbtkfile_env = NULL; 143#endif 144 145/* 146 * Timeout intervals for retrying connections 147 * to hosts that don't accept PORT cmds. This 148 * is a kludge, but given the problems with TCP... 149 */ 150#define SWAITMAX 90 /* wait at most 90 seconds */ 151#define SWAITINT 5 /* interval between retries */ 152 153int swaitmax = SWAITMAX; 154int swaitint = SWAITINT; 155 156#ifdef HASSETPROCTITLE 157char proctitle[BUFSIZ]; /* initial part of title */ 158#endif /* HASSETPROCTITLE */ 159 160#define LOGCMD(cmd, file) \ 161 if (logging > 1) \ 162 syslog(LOG_INFO,"%s %s%s", cmd, \ 163 *(file) == '/' ? "" : curdir(), file); 164#define LOGCMD2(cmd, file1, file2) \ 165 if (logging > 1) \ 166 syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ 167 *(file1) == '/' ? "" : curdir(), file1, \ 168 *(file2) == '/' ? "" : curdir(), file2); 169#define LOGBYTES(cmd, file, cnt) \ 170 if (logging > 1) { \ 171 if (cnt == (off_t)-1) \ 172 syslog(LOG_INFO,"%s %s%s", cmd, \ 173 *(file) == '/' ? "" : curdir(), file); \ 174 else \ 175 syslog(LOG_INFO, "%s %s%s = %qd bytes", \ 176 cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ 177 } 178 179static void ack __P((char *)); 180static void myoob __P((int)); 181static int checkuser __P((char *, char *)); 182static FILE *dataconn __P((char *, off_t, char *)); 183static void dolog __P((struct sockaddr_in *)); 184static char *curdir __P((void)); 185static void end_login __P((void)); 186static FILE *getdatasock __P((char *)); 187static char *gunique __P((char *)); 188static void lostconn __P((int)); 189static int receive_data __P((FILE *, FILE *)); 190static void send_data __P((FILE *, FILE *, off_t)); 191static struct passwd * 192 sgetpwnam __P((char *)); 193static char *sgetsave __P((char *)); 194 195static char * 196curdir() 197{ 198 static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ 199 200 if (getcwd(path, sizeof(path)-2) == NULL) 201 return (""); 202 if (path[1] != '\0') /* special case for root dir. */ 203 strcat(path, "/"); 204 /* For guest account, skip / since it's chrooted */ 205 return (guest ? path+1 : path); 206} 207 208int 209main(argc, argv, envp) 210 int argc; 211 char *argv[]; 212 char **envp; 213{ 214 int addrlen, ch, on = 1, tos; 215 char *cp, line[LINE_MAX]; 216 FILE *fd; 217 218 /* 219 * LOG_NDELAY sets up the logging connection immediately, 220 * necessary for anonymous ftp's that chroot and can't do it later. 221 */ 222 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); 223 addrlen = sizeof(his_addr); 224 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { 225 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); 226 exit(1); 227 } 228 addrlen = sizeof(ctrl_addr); 229 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { 230 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); 231 exit(1); 232 } 233#ifdef IP_TOS 234 tos = IPTOS_LOWDELAY; 235 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) 236 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 237#endif 238 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); 239 debug = 0; 240 241 /* set this here so klogin can use it... */ 242 (void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid()); 243 244 while ((ch = getopt(argc, argv, "dlt:T:u:Uv")) != EOF) { 245 switch (ch) { 246 case 'd': 247 debug = 1; 248 break; 249 250 case 'l': 251 logging++; /* > 1 == extra logging */ 252 break; 253 254 case 't': 255 timeout = atoi(optarg); 256 if (maxtimeout < timeout) 257 maxtimeout = timeout; 258 break; 259 260 case 'T': 261 maxtimeout = atoi(optarg); 262 if (timeout > maxtimeout) 263 timeout = maxtimeout; 264 break; 265 266 case 'u': 267 { 268 long val = 0; 269 270 val = strtol(optarg, &optarg, 8); 271 if (*optarg != '\0' || val < 0) 272 warnx("bad value for -u"); 273 else 274 defumask = val; 275 break; 276 } 277 278 case 'U': 279 doutmp = 1; 280 break; 281 282 case 'v': 283 debug = 1; 284 break; 285 286 default: 287 warnx("unknown flag -%c ignored", optopt); 288 break; 289 } 290 } 291 (void) freopen(_PATH_DEVNULL, "w", stderr); 292 (void) signal(SIGPIPE, lostconn); 293 (void) signal(SIGCHLD, SIG_IGN); 294 if ((long)signal(SIGURG, myoob) < 0) 295 syslog(LOG_ERR, "signal: %m"); 296 297 /* Try to handle urgent data inline */ 298#ifdef SO_OOBINLINE 299 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) 300 syslog(LOG_ERR, "setsockopt: %m"); 301#endif 302 303#ifdef F_SETOWN 304 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) 305 syslog(LOG_ERR, "fcntl F_SETOWN: %m"); 306#endif 307 dolog(&his_addr); 308 /* 309 * Set up default state 310 */ 311 data = -1; 312 type = TYPE_A; 313 form = FORM_N; 314 stru = STRU_F; 315 mode = MODE_S; 316 tmpline[0] = '\0'; 317 318 /* If logins are disabled, print out the message. */ 319 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { 320 while (fgets(line, sizeof(line), fd) != NULL) { 321 if ((cp = strchr(line, '\n')) != NULL) 322 *cp = '\0'; 323 lreply(530, "%s", line); 324 } 325 (void) fflush(stdout); 326 (void) fclose(fd); 327 reply(530, "System not available."); 328 exit(0); 329 } 330 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { 331 while (fgets(line, sizeof(line), fd) != NULL) { 332 if ((cp = strchr(line, '\n')) != NULL) 333 *cp = '\0'; 334 lreply(220, "%s", line); 335 } 336 (void) fflush(stdout); 337 (void) fclose(fd); 338 /* reply(220,) must follow */ 339 } 340 (void) gethostname(hostname, sizeof(hostname)); 341 reply(220, "%s FTP server (%s) ready.", hostname, version); 342 (void) setjmp(errcatch); 343 for (;;) 344 (void) yyparse(); 345 /* NOTREACHED */ 346} 347 348static void 349lostconn(signo) 350 int signo; 351{ 352 353 if (debug) 354 syslog(LOG_DEBUG, "lost connection"); 355 dologout(-1); 356} 357 358/* 359 * Helper function for sgetpwnam(). 360 */ 361static char * 362sgetsave(s) 363 char *s; 364{ 365 char *new = malloc((unsigned) strlen(s) + 1); 366 367 if (new == NULL) { 368 perror_reply(421, "Local resource failure: malloc"); 369 dologout(1); 370 /* NOTREACHED */ 371 } 372 (void) strcpy(new, s); 373 return (new); 374} 375 376/* 377 * Save the result of a getpwnam. Used for USER command, since 378 * the data returned must not be clobbered by any other command 379 * (e.g., globbing). 380 */ 381static struct passwd * 382sgetpwnam(name) 383 char *name; 384{ 385 static struct passwd save; 386 struct passwd *p; 387 388 if ((p = getpwnam(name)) == NULL) 389 return (p); 390 if (save.pw_name) { 391 free(save.pw_name); 392 free(save.pw_passwd); 393 free(save.pw_gecos); 394 free(save.pw_dir); 395 free(save.pw_shell); 396 } 397 save = *p; 398 save.pw_name = sgetsave(p->pw_name); 399 save.pw_passwd = sgetsave(p->pw_passwd); 400 save.pw_gecos = sgetsave(p->pw_gecos); 401 save.pw_dir = sgetsave(p->pw_dir); 402 save.pw_shell = sgetsave(p->pw_shell); 403 return (&save); 404} 405 406static int login_attempts; /* number of failed login attempts */ 407static int askpasswd; /* had user command, ask for passwd */ 408static char curname[10]; /* current USER name */ 409 410/* 411 * USER command. 412 * Sets global passwd pointer pw if named account exists and is acceptable; 413 * sets askpasswd if a PASS command is expected. If logged in previously, 414 * need to reset state. If name is "ftp" or "anonymous", the name is not in 415 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. 416 * If account doesn't exist, ask for passwd anyway. Otherwise, check user 417 * requesting login privileges. Disallow anyone who does not have a standard 418 * shell as returned by getusershell(). Disallow anyone mentioned in the file 419 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. 420 */ 421void 422user(name) 423 char *name; 424{ 425 char *cp, *shell; 426 427 if (logged_in) { 428 if (guest) { 429 reply(530, "Can't change user from guest login."); 430 return; 431 } else if (dochroot) { 432 reply(530, "Can't change user from chroot user."); 433 return; 434 } 435 end_login(); 436 } 437 438 guest = 0; 439 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { 440 if (checkuser(_PATH_FTPUSERS, "ftp") || 441 checkuser(_PATH_FTPUSERS, "anonymous")) 442 reply(530, "User %s access denied.", name); 443 else if ((pw = sgetpwnam("ftp")) != NULL) { 444 guest = 1; 445 askpasswd = 1; 446 reply(331, 447 "Guest login ok, type your name as password."); 448 } else 449 reply(530, "User %s unknown.", name); 450 if (!askpasswd && logging) 451 syslog(LOG_NOTICE, 452 "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); 453 return; 454 } 455 if (pw = sgetpwnam(name)) { 456 if ((shell = pw->pw_shell) == NULL || *shell == 0) 457 shell = _PATH_BSHELL; 458 while ((cp = getusershell()) != NULL) 459 if (strcmp(cp, shell) == 0) 460 break; 461 endusershell(); 462 463 if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) { 464 reply(530, "User %s access denied.", name); 465 if (logging) 466 syslog(LOG_NOTICE, 467 "FTP LOGIN REFUSED FROM %s, %s", 468 remotehost, name); 469 pw = (struct passwd *) NULL; 470 return; 471 } 472 } 473 if (logging) 474 strncpy(curname, name, sizeof(curname)-1); 475#ifdef SKEY 476 if (!skey_haskey(name)) { 477 char *myskey, *skey_keyinfo __P((char *name)); 478 479 myskey = skey_keyinfo(name); 480 reply(331, "Password [%s] for %s required.", 481 myskey ? myskey : "error getting challenge", name); 482 } else 483#endif 484 reply(331, "Password required for %s.", name); 485 486 askpasswd = 1; 487 /* 488 * Delay before reading passwd after first failed 489 * attempt to slow down passwd-guessing programs. 490 */ 491 if (login_attempts) 492 sleep((unsigned) login_attempts); 493} 494 495/* 496 * Check if a user is in the file "fname" 497 */ 498static int 499checkuser(fname, name) 500 char *fname; 501 char *name; 502{ 503 FILE *fd; 504 int found = 0; 505 char *p, line[BUFSIZ]; 506 507 if ((fd = fopen(fname, "r")) != NULL) { 508 while (fgets(line, sizeof(line), fd) != NULL) 509 if ((p = strchr(line, '\n')) != NULL) { 510 *p = '\0'; 511 if (line[0] == '#') 512 continue; 513 if (strcmp(line, name) == 0) { 514 found = 1; 515 break; 516 } 517 } 518 (void) fclose(fd); 519 } 520 return (found); 521} 522 523/* 524 * Terminate login as previous user, if any, resetting state; 525 * used when USER command is given or login fails. 526 */ 527static void 528end_login() 529{ 530 531 (void) seteuid((uid_t)0); 532 if (logged_in) { 533 logwtmp(ttyline, "", ""); 534 if (doutmp) 535 logout(utmp.ut_line); 536 } 537 pw = NULL; 538 logged_in = 0; 539 guest = 0; 540 dochroot = 0; 541} 542 543void 544pass(passwd) 545 char *passwd; 546{ 547 int rval; 548 FILE *fd; 549 550 if (logged_in || askpasswd == 0) { 551 reply(503, "Login with USER first."); 552 return; 553 } 554 askpasswd = 0; 555 if (!guest) { /* "ftp" is only account allowed no password */ 556 if (pw == NULL) { 557 rval = 1; /* failure below */ 558 goto skip; 559 } 560#if defined(KERBEROS) 561 rval = klogin(pw, "", hostname, passwd); 562 if (rval == 0) 563 goto skip; 564#endif 565#ifdef SKEY 566 if (skey_haskey(pw->pw_name) == 0 && 567 (skey_passcheck(pw->pw_name, passwd) != -1)) { 568 rval = 0; 569 goto skip; 570 } 571#endif 572 /* the strcmp does not catch null passwords! */ 573 if (pw == NULL || *pw->pw_passwd == '\0' || 574 strcmp(crypt(passwd, (pw ? pw->pw_passwd : "xx")), pw->pw_passwd)) { 575 rval = 1; /* failure */ 576 goto skip; 577 } 578 rval = 0; 579 580skip: 581 /* 582 * If rval == 1, the user failed the authentication check 583 * above. If rval == 0, either Kerberos or local authentication 584 * succeeded. 585 */ 586 if (rval) { 587 reply(530, "Login incorrect."); 588 if (logging) 589 syslog(LOG_NOTICE, 590 "FTP LOGIN FAILED FROM %s, %s", 591 remotehost, curname); 592 pw = NULL; 593 if (login_attempts++ >= 5) { 594 syslog(LOG_NOTICE, 595 "repeated login failures from %s", 596 remotehost); 597 exit(0); 598 } 599 return; 600 } 601 } 602 login_attempts = 0; /* this time successful */ 603 if (setegid((gid_t)pw->pw_gid) < 0) { 604 reply(550, "Can't set gid."); 605 return; 606 } 607 (void) initgroups(pw->pw_name, pw->pw_gid); 608 609 /* open wtmp before chroot */ 610 logwtmp(ttyline, pw->pw_name, remotehost); 611 612 /* open utmp before chroot */ 613 if (doutmp) { 614 memset((void *)&utmp, 0, sizeof(utmp)); 615 (void)time(&utmp.ut_time); 616 (void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name)); 617 (void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host)); 618 (void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line)); 619 login(&utmp); 620 } 621 622 logged_in = 1; 623 624 dochroot = checkuser(_PATH_FTPCHROOT, pw->pw_name); 625 if (guest) { 626 /* 627 * We MUST do a chdir() after the chroot. Otherwise 628 * the old current directory will be accessible as "." 629 * outside the new root! 630 */ 631 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 632 reply(550, "Can't set guest privileges."); 633 goto bad; 634 } 635 } else if (dochroot) { 636 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { 637 reply(550, "Can't change root."); 638 goto bad; 639 } 640 } else if (chdir(pw->pw_dir) < 0) { 641 if (chdir("/") < 0) { 642 reply(530, "User %s: can't change directory to %s.", 643 pw->pw_name, pw->pw_dir); 644 goto bad; 645 } else 646 lreply(230, "No directory! Logging in with home=/"); 647 } 648 if (seteuid((uid_t)pw->pw_uid) < 0) { 649 reply(550, "Can't set uid."); 650 goto bad; 651 } 652 /* 653 * Display a login message, if it exists. 654 * N.B. reply(230,) must follow the message. 655 */ 656 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { 657 char *cp, line[LINE_MAX]; 658 659 while (fgets(line, sizeof(line), fd) != NULL) { 660 if ((cp = strchr(line, '\n')) != NULL) 661 *cp = '\0'; 662 lreply(230, "%s", line); 663 } 664 (void) fflush(stdout); 665 (void) fclose(fd); 666 } 667 if (guest) { 668 reply(230, "Guest login ok, access restrictions apply."); 669#ifdef HASSETPROCTITLE 670 snprintf(proctitle, sizeof(proctitle), 671 "%s: anonymous/%.*s", remotehost, 672 sizeof(proctitle) - sizeof(remotehost) - 673 sizeof(": anonymous/"), passwd); 674 setproctitle(proctitle); 675#endif /* HASSETPROCTITLE */ 676 if (logging) 677 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", 678 remotehost, passwd); 679 } else { 680 reply(230, "User %s logged in.", pw->pw_name); 681#ifdef HASSETPROCTITLE 682 snprintf(proctitle, sizeof(proctitle), 683 "%s: %s", remotehost, pw->pw_name); 684 setproctitle(proctitle); 685#endif /* HASSETPROCTITLE */ 686 if (logging) 687 syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", 688 remotehost, pw->pw_name); 689 } 690 (void) umask(defumask); 691 return; 692bad: 693 /* Forget all about it... */ 694 end_login(); 695} 696 697void 698retrieve(cmd, name) 699 char *cmd, *name; 700{ 701 FILE *fin, *dout; 702 struct stat st; 703 int (*closefunc) __P((FILE *)); 704 705 if (cmd == 0) { 706 fin = fopen(name, "r"), closefunc = fclose; 707 st.st_size = 0; 708 } else { 709 char line[BUFSIZ]; 710 711 (void) sprintf(line, cmd, name), name = line; 712 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; 713 st.st_size = -1; 714 st.st_blksize = BUFSIZ; 715 } 716 if (fin == NULL) { 717 if (errno != 0) { 718 perror_reply(550, name); 719 if (cmd == 0) { 720 LOGCMD("get", name); 721 } 722 } 723 return; 724 } 725 byte_count = -1; 726 if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { 727 reply(550, "%s: not a plain file.", name); 728 goto done; 729 } 730 if (restart_point) { 731 if (type == TYPE_A) { 732 off_t i, n; 733 int c; 734 735 n = restart_point; 736 i = 0; 737 while (i++ < n) { 738 if ((c=getc(fin)) == EOF) { 739 perror_reply(550, name); 740 goto done; 741 } 742 if (c == '\n') 743 i++; 744 } 745 } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { 746 perror_reply(550, name); 747 goto done; 748 } 749 } 750 dout = dataconn(name, st.st_size, "w"); 751 if (dout == NULL) 752 goto done; 753 send_data(fin, dout, st.st_blksize); 754 (void) fclose(dout); 755 data = -1; 756 pdata = -1; 757done: 758 if (cmd == 0) 759 LOGBYTES("get", name, byte_count); 760 (*closefunc)(fin); 761} 762 763void 764store(name, mode, unique) 765 char *name, *mode; 766 int unique; 767{ 768 FILE *fout, *din; 769 struct stat st; 770 int (*closefunc) __P((FILE *)); 771 772 if (unique && stat(name, &st) == 0 && 773 (name = gunique(name)) == NULL) { 774 LOGCMD(*mode == 'w' ? "put" : "append", name); 775 return; 776 } 777 778 if (restart_point) 779 mode = "r+"; 780 fout = fopen(name, mode); 781 closefunc = fclose; 782 if (fout == NULL) { 783 perror_reply(553, name); 784 LOGCMD(*mode == 'w' ? "put" : "append", name); 785 return; 786 } 787 byte_count = -1; 788 if (restart_point) { 789 if (type == TYPE_A) { 790 off_t i, n; 791 int c; 792 793 n = restart_point; 794 i = 0; 795 while (i++ < n) { 796 if ((c=getc(fout)) == EOF) { 797 perror_reply(550, name); 798 goto done; 799 } 800 if (c == '\n') 801 i++; 802 } 803 /* 804 * We must do this seek to "current" position 805 * because we are changing from reading to 806 * writing. 807 */ 808 if (fseek(fout, 0L, L_INCR) < 0) { 809 perror_reply(550, name); 810 goto done; 811 } 812 } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { 813 perror_reply(550, name); 814 goto done; 815 } 816 } 817 din = dataconn(name, (off_t)-1, "r"); 818 if (din == NULL) 819 goto done; 820 if (receive_data(din, fout) == 0) { 821 if (unique) 822 reply(226, "Transfer complete (unique file name:%s).", 823 name); 824 else 825 reply(226, "Transfer complete."); 826 } 827 (void) fclose(din); 828 data = -1; 829 pdata = -1; 830done: 831 LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); 832 (*closefunc)(fout); 833} 834 835static FILE * 836getdatasock(mode) 837 char *mode; 838{ 839 int on = 1, s, t, tries; 840 841 if (data >= 0) 842 return (fdopen(data, mode)); 843 (void) seteuid((uid_t)0); 844 s = socket(AF_INET, SOCK_STREAM, 0); 845 if (s < 0) 846 goto bad; 847 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 848 (char *) &on, sizeof(on)) < 0) 849 goto bad; 850 /* anchor socket to avoid multi-homing problems */ 851 data_source.sin_len = sizeof(struct sockaddr_in); 852 data_source.sin_family = AF_INET; 853 data_source.sin_addr = ctrl_addr.sin_addr; 854 for (tries = 1; ; tries++) { 855 if (bind(s, (struct sockaddr *)&data_source, 856 sizeof(data_source)) >= 0) 857 break; 858 if (errno != EADDRINUSE || tries > 10) 859 goto bad; 860 sleep(tries); 861 } 862 (void) seteuid((uid_t)pw->pw_uid); 863#ifdef IP_TOS 864 on = IPTOS_THROUGHPUT; 865 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) 866 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 867#endif 868 return (fdopen(s, mode)); 869bad: 870 /* Return the real value of errno (close may change it) */ 871 t = errno; 872 (void) seteuid((uid_t)pw->pw_uid); 873 (void) close(s); 874 errno = t; 875 return (NULL); 876} 877 878static FILE * 879dataconn(name, size, mode) 880 char *name; 881 off_t size; 882 char *mode; 883{ 884 char sizebuf[32]; 885 FILE *file; 886 int retry = 0, tos; 887 888 file_size = size; 889 byte_count = 0; 890 if (size != (off_t) -1) 891 (void) sprintf(sizebuf, " (%qd bytes)", size); 892 else 893 (void) strcpy(sizebuf, ""); 894 if (pdata >= 0) { 895 struct sockaddr_in from; 896 int s, fromlen = sizeof(from); 897 898 s = accept(pdata, (struct sockaddr *)&from, &fromlen); 899 if (s < 0) { 900 reply(425, "Can't open data connection."); 901 (void) close(pdata); 902 pdata = -1; 903 return (NULL); 904 } 905 (void) close(pdata); 906 pdata = s; 907#ifdef IP_TOS 908 tos = IPTOS_THROUGHPUT; 909 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 910 sizeof(int)); 911#endif 912 reply(150, "Opening %s mode data connection for '%s'%s.", 913 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 914 return (fdopen(pdata, mode)); 915 } 916 if (data >= 0) { 917 reply(125, "Using existing data connection for '%s'%s.", 918 name, sizebuf); 919 usedefault = 1; 920 return (fdopen(data, mode)); 921 } 922 if (usedefault) 923 data_dest = his_addr; 924 usedefault = 1; 925 file = getdatasock(mode); 926 if (file == NULL) { 927 reply(425, "Can't create data socket (%s,%d): %s.", 928 inet_ntoa(data_source.sin_addr), 929 ntohs(data_source.sin_port), strerror(errno)); 930 return (NULL); 931 } 932 data = fileno(file); 933 while (connect(data, (struct sockaddr *)&data_dest, 934 sizeof(data_dest)) < 0) { 935 if (errno == EADDRINUSE && retry < swaitmax) { 936 sleep((unsigned) swaitint); 937 retry += swaitint; 938 continue; 939 } 940 perror_reply(425, "Can't build data connection"); 941 (void) fclose(file); 942 data = -1; 943 return (NULL); 944 } 945 reply(150, "Opening %s mode data connection for '%s'%s.", 946 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 947 return (file); 948} 949 950/* 951 * Tranfer the contents of "instr" to "outstr" peer using the appropriate 952 * encapsulation of the data subject * to Mode, Structure, and Type. 953 * 954 * NB: Form isn't handled. 955 */ 956static void 957send_data(instr, outstr, blksize) 958 FILE *instr, *outstr; 959 off_t blksize; 960{ 961 int c, cnt, filefd, netfd; 962 char *buf; 963 964 transflag++; 965 if (setjmp(urgcatch)) { 966 transflag = 0; 967 return; 968 } 969 switch (type) { 970 971 case TYPE_A: 972 while ((c = getc(instr)) != EOF) { 973 byte_count++; 974 if (c == '\n') { 975 if (ferror(outstr)) 976 goto data_err; 977 (void) putc('\r', outstr); 978 } 979 (void) putc(c, outstr); 980 } 981 fflush(outstr); 982 transflag = 0; 983 if (ferror(instr)) 984 goto file_err; 985 if (ferror(outstr)) 986 goto data_err; 987 reply(226, "Transfer complete."); 988 return; 989 990 case TYPE_I: 991 case TYPE_L: 992 if ((buf = malloc((u_int)blksize)) == NULL) { 993 transflag = 0; 994 perror_reply(451, "Local resource failure: malloc"); 995 return; 996 } 997 netfd = fileno(outstr); 998 filefd = fileno(instr); 999 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 1000 write(netfd, buf, cnt) == cnt) 1001 byte_count += cnt; 1002 transflag = 0; 1003 (void)free(buf); 1004 if (cnt != 0) { 1005 if (cnt < 0) 1006 goto file_err; 1007 goto data_err; 1008 } 1009 reply(226, "Transfer complete."); 1010 return; 1011 default: 1012 transflag = 0; 1013 reply(550, "Unimplemented TYPE %d in send_data", type); 1014 return; 1015 } 1016 1017data_err: 1018 transflag = 0; 1019 perror_reply(426, "Data connection"); 1020 return; 1021 1022file_err: 1023 transflag = 0; 1024 perror_reply(551, "Error on input file"); 1025} 1026 1027/* 1028 * Transfer data from peer to "outstr" using the appropriate encapulation of 1029 * the data subject to Mode, Structure, and Type. 1030 * 1031 * N.B.: Form isn't handled. 1032 */ 1033static int 1034receive_data(instr, outstr) 1035 FILE *instr, *outstr; 1036{ 1037 int c; 1038 int cnt, bare_lfs = 0; 1039 char buf[BUFSIZ]; 1040 1041 transflag++; 1042 if (setjmp(urgcatch)) { 1043 transflag = 0; 1044 return (-1); 1045 } 1046 switch (type) { 1047 1048 case TYPE_I: 1049 case TYPE_L: 1050 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 1051 if (write(fileno(outstr), buf, cnt) != cnt) 1052 goto file_err; 1053 byte_count += cnt; 1054 } 1055 if (cnt < 0) 1056 goto data_err; 1057 transflag = 0; 1058 return (0); 1059 1060 case TYPE_E: 1061 reply(553, "TYPE E not implemented."); 1062 transflag = 0; 1063 return (-1); 1064 1065 case TYPE_A: 1066 while ((c = getc(instr)) != EOF) { 1067 byte_count++; 1068 if (c == '\n') 1069 bare_lfs++; 1070 while (c == '\r') { 1071 if (ferror(outstr)) 1072 goto data_err; 1073 if ((c = getc(instr)) != '\n') { 1074 (void) putc ('\r', outstr); 1075 if (c == '\0' || c == EOF) 1076 goto contin2; 1077 } 1078 } 1079 (void) putc(c, outstr); 1080 contin2: ; 1081 } 1082 fflush(outstr); 1083 if (ferror(instr)) 1084 goto data_err; 1085 if (ferror(outstr)) 1086 goto file_err; 1087 transflag = 0; 1088 if (bare_lfs) { 1089 lreply(226, 1090 "WARNING! %d bare linefeeds received in ASCII mode", 1091 bare_lfs); 1092 (void)printf(" File may not have transferred correctly.\r\n"); 1093 } 1094 return (0); 1095 default: 1096 reply(550, "Unimplemented TYPE %d in receive_data", type); 1097 transflag = 0; 1098 return (-1); 1099 } 1100 1101data_err: 1102 transflag = 0; 1103 perror_reply(426, "Data Connection"); 1104 return (-1); 1105 1106file_err: 1107 transflag = 0; 1108 perror_reply(452, "Error writing file"); 1109 return (-1); 1110} 1111 1112void 1113statfilecmd(filename) 1114 char *filename; 1115{ 1116 FILE *fin; 1117 int c; 1118 char line[LINE_MAX]; 1119 1120 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); 1121 fin = ftpd_popen(line, "r"); 1122 lreply(211, "status of %s:", filename); 1123 while ((c = getc(fin)) != EOF) { 1124 if (c == '\n') { 1125 if (ferror(stdout)){ 1126 perror_reply(421, "control connection"); 1127 (void) ftpd_pclose(fin); 1128 dologout(1); 1129 /* NOTREACHED */ 1130 } 1131 if (ferror(fin)) { 1132 perror_reply(551, filename); 1133 (void) ftpd_pclose(fin); 1134 return; 1135 } 1136 (void) putc('\r', stdout); 1137 } 1138 (void) putc(c, stdout); 1139 } 1140 (void) ftpd_pclose(fin); 1141 reply(211, "End of Status"); 1142} 1143 1144void 1145statcmd() 1146{ 1147 struct sockaddr_in *sin; 1148 u_char *a, *p; 1149 1150 lreply(211, "%s FTP server status:", hostname, version); 1151 printf(" %s\r\n", version); 1152 printf(" Connected to %s", remotehost); 1153 if (!isdigit(remotehost[0])) 1154 printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 1155 printf("\r\n"); 1156 if (logged_in) { 1157 if (guest) 1158 printf(" Logged in anonymously\r\n"); 1159 else 1160 printf(" Logged in as %s\r\n", pw->pw_name); 1161 } else if (askpasswd) 1162 printf(" Waiting for password\r\n"); 1163 else 1164 printf(" Waiting for user name\r\n"); 1165 printf(" TYPE: %s", typenames[type]); 1166 if (type == TYPE_A || type == TYPE_E) 1167 printf(", FORM: %s", formnames[form]); 1168 if (type == TYPE_L) 1169#if NBBY == 8 1170 printf(" %d", NBBY); 1171#else 1172 printf(" %d", bytesize); /* need definition! */ 1173#endif 1174 printf("; STRUcture: %s; transfer MODE: %s\r\n", 1175 strunames[stru], modenames[mode]); 1176 if (data != -1) 1177 printf(" Data connection open\r\n"); 1178 else if (pdata != -1) { 1179 printf(" in Passive mode"); 1180 sin = &pasv_addr; 1181 goto printaddr; 1182 } else if (usedefault == 0) { 1183 printf(" PORT"); 1184 sin = &data_dest; 1185printaddr: 1186 a = (u_char *) &sin->sin_addr; 1187 p = (u_char *) &sin->sin_port; 1188#define UC(b) (((int) b) & 0xff) 1189 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 1190 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1191#undef UC 1192 } else 1193 printf(" No data connection\r\n"); 1194 reply(211, "End of status"); 1195} 1196 1197void 1198fatal(s) 1199 char *s; 1200{ 1201 1202 reply(451, "Error in server: %s\n", s); 1203 reply(221, "Closing connection due to server error."); 1204 dologout(0); 1205 /* NOTREACHED */ 1206} 1207 1208void 1209#if __STDC__ 1210reply(int n, const char *fmt, ...) 1211#else 1212reply(n, fmt, va_alist) 1213 int n; 1214 char *fmt; 1215 va_dcl 1216#endif 1217{ 1218 va_list ap; 1219#if __STDC__ 1220 va_start(ap, fmt); 1221#else 1222 va_start(ap); 1223#endif 1224 (void)printf("%d ", n); 1225 (void)vprintf(fmt, ap); 1226 (void)printf("\r\n"); 1227 (void)fflush(stdout); 1228 if (debug) { 1229 syslog(LOG_DEBUG, "<--- %d ", n); 1230 vsyslog(LOG_DEBUG, fmt, ap); 1231 } 1232} 1233 1234void 1235#if __STDC__ 1236lreply(int n, const char *fmt, ...) 1237#else 1238lreply(n, fmt, va_alist) 1239 int n; 1240 char *fmt; 1241 va_dcl 1242#endif 1243{ 1244 va_list ap; 1245#if __STDC__ 1246 va_start(ap, fmt); 1247#else 1248 va_start(ap); 1249#endif 1250 (void)printf("%d- ", n); 1251 (void)vprintf(fmt, ap); 1252 (void)printf("\r\n"); 1253 (void)fflush(stdout); 1254 if (debug) { 1255 syslog(LOG_DEBUG, "<--- %d- ", n); 1256 vsyslog(LOG_DEBUG, fmt, ap); 1257 } 1258} 1259 1260static void 1261ack(s) 1262 char *s; 1263{ 1264 1265 reply(250, "%s command successful.", s); 1266} 1267 1268void 1269nack(s) 1270 char *s; 1271{ 1272 1273 reply(502, "%s command not implemented.", s); 1274} 1275 1276/* ARGSUSED */ 1277void 1278yyerror(s) 1279 char *s; 1280{ 1281 char *cp; 1282 1283 if (cp = strchr(cbuf,'\n')) 1284 *cp = '\0'; 1285 reply(500, "'%s': command not understood.", cbuf); 1286} 1287 1288void 1289delete(name) 1290 char *name; 1291{ 1292 struct stat st; 1293 1294 LOGCMD("delete", name); 1295 if (stat(name, &st) < 0) { 1296 perror_reply(550, name); 1297 return; 1298 } 1299 if ((st.st_mode&S_IFMT) == S_IFDIR) { 1300 if (rmdir(name) < 0) { 1301 perror_reply(550, name); 1302 return; 1303 } 1304 goto done; 1305 } 1306 if (unlink(name) < 0) { 1307 perror_reply(550, name); 1308 return; 1309 } 1310done: 1311 ack("DELE"); 1312} 1313 1314void 1315cwd(path) 1316 char *path; 1317{ 1318 1319 if (chdir(path) < 0) 1320 perror_reply(550, path); 1321 else 1322 ack("CWD"); 1323} 1324 1325void 1326makedir(name) 1327 char *name; 1328{ 1329 1330 LOGCMD("mkdir", name); 1331 if (mkdir(name, 0777) < 0) 1332 perror_reply(550, name); 1333 else 1334 reply(257, "MKD command successful."); 1335} 1336 1337void 1338removedir(name) 1339 char *name; 1340{ 1341 1342 LOGCMD("rmdir", name); 1343 if (rmdir(name) < 0) 1344 perror_reply(550, name); 1345 else 1346 ack("RMD"); 1347} 1348 1349void 1350pwd() 1351{ 1352 char path[MAXPATHLEN + 1]; 1353 1354 if (getwd(path) == (char *)NULL) 1355 reply(550, "%s.", path); 1356 else 1357 reply(257, "\"%s\" is current directory.", path); 1358} 1359 1360char * 1361renamefrom(name) 1362 char *name; 1363{ 1364 struct stat st; 1365 1366 if (stat(name, &st) < 0) { 1367 perror_reply(550, name); 1368 return ((char *)0); 1369 } 1370 reply(350, "File exists, ready for destination name"); 1371 return (name); 1372} 1373 1374void 1375renamecmd(from, to) 1376 char *from, *to; 1377{ 1378 1379 LOGCMD2("rename", from, to); 1380 if (rename(from, to) < 0) 1381 perror_reply(550, "rename"); 1382 else 1383 ack("RNTO"); 1384} 1385 1386static void 1387dolog(sin) 1388 struct sockaddr_in *sin; 1389{ 1390 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 1391 sizeof(struct in_addr), AF_INET); 1392 1393 if (hp) 1394 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); 1395 else 1396 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 1397 sizeof(remotehost)); 1398#ifdef HASSETPROCTITLE 1399 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 1400 setproctitle(proctitle); 1401#endif /* HASSETPROCTITLE */ 1402 1403 if (logging) 1404 syslog(LOG_INFO, "connection from %s", remotehost); 1405} 1406 1407/* 1408 * Record logout in wtmp file 1409 * and exit with supplied status. 1410 */ 1411void 1412dologout(status) 1413 int status; 1414{ 1415 1416 if (logged_in) { 1417 (void) seteuid((uid_t)0); 1418 logwtmp(ttyline, "", ""); 1419 if (doutmp) 1420 logout(utmp.ut_line); 1421#if defined(KERBEROS) 1422 if (!notickets && krbtkfile_env) 1423 unlink(krbtkfile_env); 1424#endif 1425 } 1426 /* beware of flushing buffers after a SIGPIPE */ 1427 _exit(status); 1428} 1429 1430static void 1431myoob(signo) 1432 int signo; 1433{ 1434 char *cp; 1435 1436 /* only process if transfer occurring */ 1437 if (!transflag) 1438 return; 1439 cp = tmpline; 1440 if (getline(cp, 7, stdin) == NULL) { 1441 reply(221, "You could at least say goodbye."); 1442 dologout(0); 1443 } 1444 upper(cp); 1445 if (strcmp(cp, "ABOR\r\n") == 0) { 1446 tmpline[0] = '\0'; 1447 reply(426, "Transfer aborted. Data connection closed."); 1448 reply(226, "Abort successful"); 1449 longjmp(urgcatch, 1); 1450 } 1451 if (strcmp(cp, "STAT\r\n") == 0) { 1452 if (file_size != (off_t) -1) 1453 reply(213, "Status: %qd of %qd bytes transferred", 1454 byte_count, file_size); 1455 else 1456 reply(213, "Status: %qd bytes transferred", byte_count); 1457 } 1458} 1459 1460/* 1461 * Note: a response of 425 is not mentioned as a possible response to 1462 * the PASV command in RFC959. However, it has been blessed as 1463 * a legitimate response by Jon Postel in a telephone conversation 1464 * with Rick Adams on 25 Jan 89. 1465 */ 1466void 1467passive() 1468{ 1469 int len; 1470 char *p, *a; 1471 1472 pdata = socket(AF_INET, SOCK_STREAM, 0); 1473 if (pdata < 0) { 1474 perror_reply(425, "Can't open passive connection"); 1475 return; 1476 } 1477 pasv_addr = ctrl_addr; 1478 pasv_addr.sin_port = 0; 1479 (void) seteuid((uid_t)0); 1480 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 1481 (void) seteuid((uid_t)pw->pw_uid); 1482 goto pasv_error; 1483 } 1484 (void) seteuid((uid_t)pw->pw_uid); 1485 len = sizeof(pasv_addr); 1486 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 1487 goto pasv_error; 1488 if (listen(pdata, 1) < 0) 1489 goto pasv_error; 1490 a = (char *) &pasv_addr.sin_addr; 1491 p = (char *) &pasv_addr.sin_port; 1492 1493#define UC(b) (((int) b) & 0xff) 1494 1495 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 1496 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1497 return; 1498 1499pasv_error: 1500 (void) close(pdata); 1501 pdata = -1; 1502 perror_reply(425, "Can't open passive connection"); 1503 return; 1504} 1505 1506/* 1507 * Generate unique name for file with basename "local". 1508 * The file named "local" is already known to exist. 1509 * Generates failure reply on error. 1510 */ 1511static char * 1512gunique(local) 1513 char *local; 1514{ 1515 static char new[MAXPATHLEN]; 1516 struct stat st; 1517 int count; 1518 char *cp; 1519 1520 cp = strrchr(local, '/'); 1521 if (cp) 1522 *cp = '\0'; 1523 if (stat(cp ? local : ".", &st) < 0) { 1524 perror_reply(553, cp ? local : "."); 1525 return ((char *) 0); 1526 } 1527 if (cp) 1528 *cp = '/'; 1529 (void) strcpy(new, local); 1530 cp = new + strlen(new); 1531 *cp++ = '.'; 1532 for (count = 1; count < 100; count++) { 1533 (void)sprintf(cp, "%d", count); 1534 if (stat(new, &st) < 0) 1535 return (new); 1536 } 1537 reply(452, "Unique file name cannot be created."); 1538 return (NULL); 1539} 1540 1541/* 1542 * Format and send reply containing system error number. 1543 */ 1544void 1545perror_reply(code, string) 1546 int code; 1547 char *string; 1548{ 1549 1550 reply(code, "%s: %s.", string, strerror(errno)); 1551} 1552 1553static char *onefile[] = { 1554 "", 1555 0 1556}; 1557 1558void 1559send_file_list(whichf) 1560 char *whichf; 1561{ 1562 struct stat st; 1563 DIR *dirp = NULL; 1564 struct dirent *dir; 1565 FILE *dout = NULL; 1566 char **dirlist, *dirname; 1567 int simple = 0; 1568 int freeglob = 0; 1569 glob_t gl; 1570 1571 if (strpbrk(whichf, "~{[*?") != NULL) { 1572 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 1573 1574 memset(&gl, 0, sizeof(gl)); 1575 freeglob = 1; 1576 if (glob(whichf, flags, 0, &gl)) { 1577 reply(550, "not found"); 1578 goto out; 1579 } else if (gl.gl_pathc == 0) { 1580 errno = ENOENT; 1581 perror_reply(550, whichf); 1582 goto out; 1583 } 1584 dirlist = gl.gl_pathv; 1585 } else { 1586 onefile[0] = whichf; 1587 dirlist = onefile; 1588 simple = 1; 1589 } 1590 1591 if (setjmp(urgcatch)) { 1592 transflag = 0; 1593 goto out; 1594 } 1595 while (dirname = *dirlist++) { 1596 if (stat(dirname, &st) < 0) { 1597 /* 1598 * If user typed "ls -l", etc, and the client 1599 * used NLST, do what the user meant. 1600 */ 1601 if (dirname[0] == '-' && *dirlist == NULL && 1602 transflag == 0) { 1603 retrieve("/bin/ls %s", dirname); 1604 goto out; 1605 } 1606 perror_reply(550, whichf); 1607 if (dout != NULL) { 1608 (void) fclose(dout); 1609 transflag = 0; 1610 data = -1; 1611 pdata = -1; 1612 } 1613 goto out; 1614 } 1615 1616 if (S_ISREG(st.st_mode)) { 1617 if (dout == NULL) { 1618 dout = dataconn("file list", (off_t)-1, "w"); 1619 if (dout == NULL) 1620 goto out; 1621 transflag++; 1622 } 1623 fprintf(dout, "%s%s\n", dirname, 1624 type == TYPE_A ? "\r" : ""); 1625 byte_count += strlen(dirname) + 1; 1626 continue; 1627 } else if (!S_ISDIR(st.st_mode)) 1628 continue; 1629 1630 if ((dirp = opendir(dirname)) == NULL) 1631 continue; 1632 1633 while ((dir = readdir(dirp)) != NULL) { 1634 char nbuf[MAXPATHLEN]; 1635 1636 if (dir->d_name[0] == '.' && dir->d_namlen == 1) 1637 continue; 1638 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 1639 dir->d_namlen == 2) 1640 continue; 1641 1642 sprintf(nbuf, "%s/%s", dirname, dir->d_name); 1643 1644 /* 1645 * We have to do a stat to insure it's 1646 * not a directory or special file. 1647 */ 1648 if (simple || (stat(nbuf, &st) == 0 && 1649 S_ISREG(st.st_mode))) { 1650 if (dout == NULL) { 1651 dout = dataconn("file list", (off_t)-1, 1652 "w"); 1653 if (dout == NULL) 1654 goto out; 1655 transflag++; 1656 } 1657 if (nbuf[0] == '.' && nbuf[1] == '/') 1658 fprintf(dout, "%s%s\n", &nbuf[2], 1659 type == TYPE_A ? "\r" : ""); 1660 else 1661 fprintf(dout, "%s%s\n", nbuf, 1662 type == TYPE_A ? "\r" : ""); 1663 byte_count += strlen(nbuf) + 1; 1664 } 1665 } 1666 (void) closedir(dirp); 1667 } 1668 1669 if (dout == NULL) 1670 reply(550, "No files found."); 1671 else if (ferror(dout) != 0) 1672 perror_reply(550, "Data connection"); 1673 else 1674 reply(226, "Transfer complete."); 1675 1676 transflag = 0; 1677 if (dout != NULL) 1678 (void) fclose(dout); 1679 data = -1; 1680 pdata = -1; 1681out: 1682 if (freeglob) { 1683 freeglob = 0; 1684 globfree(&gl); 1685 } 1686} 1687