ftpd.c revision 1.3
1/* $OpenBSD: ftpd.c,v 1.3 1996/07/27 07:26:39 joshd 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 if (ntohs(from.sin_port) < IPPORT_RESERVED) { 906 perror_reply(425, "Can't build data connection"); 907 (void) close(pdata); 908 (void) close(s); 909 pdata = -1; 910 return (NULL); 911 } 912 if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) { 913 perror_reply(435, "Can't build data connection"); 914 (void) close(pdata); 915 (void) close(s); 916 pdata = -1; 917 return (NULL); 918 } 919 (void) close(pdata); 920 pdata = s; 921#ifdef IP_TOS 922 tos = IPTOS_THROUGHPUT; 923 (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, 924 sizeof(int)); 925#endif 926 reply(150, "Opening %s mode data connection for '%s'%s.", 927 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 928 return (fdopen(pdata, mode)); 929 } 930 if (data >= 0) { 931 reply(125, "Using existing data connection for '%s'%s.", 932 name, sizebuf); 933 usedefault = 1; 934 return (fdopen(data, mode)); 935 } 936 if (usedefault) 937 data_dest = his_addr; 938 usedefault = 1; 939 file = getdatasock(mode); 940 if (file == NULL) { 941 reply(425, "Can't create data socket (%s,%d): %s.", 942 inet_ntoa(data_source.sin_addr), 943 ntohs(data_source.sin_port), strerror(errno)); 944 return (NULL); 945 } 946 data = fileno(file); 947 948 /* 949 * attempt to connect to reserved port on client machine; 950 * this looks like an attack 951 */ 952 if (ntohs(data_dest.sin_port) < IPPORT_RESERVED) { 953 perror_reply(425, "Can't build data connection"); 954 (void) fclose(file); 955 data = -1; 956 return NULL; 957 } 958 if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) { 959 perror_reply(435, "Can't build data connection"); 960 (void) fclose(file); 961 data = -1; 962 return NULL; 963 } 964 while (connect(data, (struct sockaddr *)&data_dest, 965 sizeof(data_dest)) < 0) { 966 if (errno == EADDRINUSE && retry < swaitmax) { 967 sleep((unsigned) swaitint); 968 retry += swaitint; 969 continue; 970 } 971 perror_reply(425, "Can't build data connection"); 972 (void) fclose(file); 973 data = -1; 974 return (NULL); 975 } 976 reply(150, "Opening %s mode data connection for '%s'%s.", 977 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); 978 return (file); 979} 980 981/* 982 * Tranfer the contents of "instr" to "outstr" peer using the appropriate 983 * encapsulation of the data subject * to Mode, Structure, and Type. 984 * 985 * NB: Form isn't handled. 986 */ 987static void 988send_data(instr, outstr, blksize) 989 FILE *instr, *outstr; 990 off_t blksize; 991{ 992 int c, cnt, filefd, netfd; 993 char *buf; 994 995 transflag++; 996 if (setjmp(urgcatch)) { 997 transflag = 0; 998 return; 999 } 1000 switch (type) { 1001 1002 case TYPE_A: 1003 while ((c = getc(instr)) != EOF) { 1004 byte_count++; 1005 if (c == '\n') { 1006 if (ferror(outstr)) 1007 goto data_err; 1008 (void) putc('\r', outstr); 1009 } 1010 (void) putc(c, outstr); 1011 } 1012 fflush(outstr); 1013 transflag = 0; 1014 if (ferror(instr)) 1015 goto file_err; 1016 if (ferror(outstr)) 1017 goto data_err; 1018 reply(226, "Transfer complete."); 1019 return; 1020 1021 case TYPE_I: 1022 case TYPE_L: 1023 if ((buf = malloc((u_int)blksize)) == NULL) { 1024 transflag = 0; 1025 perror_reply(451, "Local resource failure: malloc"); 1026 return; 1027 } 1028 netfd = fileno(outstr); 1029 filefd = fileno(instr); 1030 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && 1031 write(netfd, buf, cnt) == cnt) 1032 byte_count += cnt; 1033 transflag = 0; 1034 (void)free(buf); 1035 if (cnt != 0) { 1036 if (cnt < 0) 1037 goto file_err; 1038 goto data_err; 1039 } 1040 reply(226, "Transfer complete."); 1041 return; 1042 default: 1043 transflag = 0; 1044 reply(550, "Unimplemented TYPE %d in send_data", type); 1045 return; 1046 } 1047 1048data_err: 1049 transflag = 0; 1050 perror_reply(426, "Data connection"); 1051 return; 1052 1053file_err: 1054 transflag = 0; 1055 perror_reply(551, "Error on input file"); 1056} 1057 1058/* 1059 * Transfer data from peer to "outstr" using the appropriate encapulation of 1060 * the data subject to Mode, Structure, and Type. 1061 * 1062 * N.B.: Form isn't handled. 1063 */ 1064static int 1065receive_data(instr, outstr) 1066 FILE *instr, *outstr; 1067{ 1068 int c; 1069 int cnt, bare_lfs = 0; 1070 char buf[BUFSIZ]; 1071 1072 transflag++; 1073 if (setjmp(urgcatch)) { 1074 transflag = 0; 1075 return (-1); 1076 } 1077 switch (type) { 1078 1079 case TYPE_I: 1080 case TYPE_L: 1081 while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { 1082 if (write(fileno(outstr), buf, cnt) != cnt) 1083 goto file_err; 1084 byte_count += cnt; 1085 } 1086 if (cnt < 0) 1087 goto data_err; 1088 transflag = 0; 1089 return (0); 1090 1091 case TYPE_E: 1092 reply(553, "TYPE E not implemented."); 1093 transflag = 0; 1094 return (-1); 1095 1096 case TYPE_A: 1097 while ((c = getc(instr)) != EOF) { 1098 byte_count++; 1099 if (c == '\n') 1100 bare_lfs++; 1101 while (c == '\r') { 1102 if (ferror(outstr)) 1103 goto data_err; 1104 if ((c = getc(instr)) != '\n') { 1105 (void) putc ('\r', outstr); 1106 if (c == '\0' || c == EOF) 1107 goto contin2; 1108 } 1109 } 1110 (void) putc(c, outstr); 1111 contin2: ; 1112 } 1113 fflush(outstr); 1114 if (ferror(instr)) 1115 goto data_err; 1116 if (ferror(outstr)) 1117 goto file_err; 1118 transflag = 0; 1119 if (bare_lfs) { 1120 lreply(226, 1121 "WARNING! %d bare linefeeds received in ASCII mode", 1122 bare_lfs); 1123 (void)printf(" File may not have transferred correctly.\r\n"); 1124 } 1125 return (0); 1126 default: 1127 reply(550, "Unimplemented TYPE %d in receive_data", type); 1128 transflag = 0; 1129 return (-1); 1130 } 1131 1132data_err: 1133 transflag = 0; 1134 perror_reply(426, "Data Connection"); 1135 return (-1); 1136 1137file_err: 1138 transflag = 0; 1139 perror_reply(452, "Error writing file"); 1140 return (-1); 1141} 1142 1143void 1144statfilecmd(filename) 1145 char *filename; 1146{ 1147 FILE *fin; 1148 int c; 1149 char line[LINE_MAX]; 1150 1151 (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); 1152 fin = ftpd_popen(line, "r"); 1153 lreply(211, "status of %s:", filename); 1154 while ((c = getc(fin)) != EOF) { 1155 if (c == '\n') { 1156 if (ferror(stdout)){ 1157 perror_reply(421, "control connection"); 1158 (void) ftpd_pclose(fin); 1159 dologout(1); 1160 /* NOTREACHED */ 1161 } 1162 if (ferror(fin)) { 1163 perror_reply(551, filename); 1164 (void) ftpd_pclose(fin); 1165 return; 1166 } 1167 (void) putc('\r', stdout); 1168 } 1169 (void) putc(c, stdout); 1170 } 1171 (void) ftpd_pclose(fin); 1172 reply(211, "End of Status"); 1173} 1174 1175void 1176statcmd() 1177{ 1178 struct sockaddr_in *sin; 1179 u_char *a, *p; 1180 1181 lreply(211, "%s FTP server status:", hostname, version); 1182 printf(" %s\r\n", version); 1183 printf(" Connected to %s", remotehost); 1184 if (!isdigit(remotehost[0])) 1185 printf(" (%s)", inet_ntoa(his_addr.sin_addr)); 1186 printf("\r\n"); 1187 if (logged_in) { 1188 if (guest) 1189 printf(" Logged in anonymously\r\n"); 1190 else 1191 printf(" Logged in as %s\r\n", pw->pw_name); 1192 } else if (askpasswd) 1193 printf(" Waiting for password\r\n"); 1194 else 1195 printf(" Waiting for user name\r\n"); 1196 printf(" TYPE: %s", typenames[type]); 1197 if (type == TYPE_A || type == TYPE_E) 1198 printf(", FORM: %s", formnames[form]); 1199 if (type == TYPE_L) 1200#if NBBY == 8 1201 printf(" %d", NBBY); 1202#else 1203 printf(" %d", bytesize); /* need definition! */ 1204#endif 1205 printf("; STRUcture: %s; transfer MODE: %s\r\n", 1206 strunames[stru], modenames[mode]); 1207 if (data != -1) 1208 printf(" Data connection open\r\n"); 1209 else if (pdata != -1) { 1210 printf(" in Passive mode"); 1211 sin = &pasv_addr; 1212 goto printaddr; 1213 } else if (usedefault == 0) { 1214 printf(" PORT"); 1215 sin = &data_dest; 1216printaddr: 1217 a = (u_char *) &sin->sin_addr; 1218 p = (u_char *) &sin->sin_port; 1219#define UC(b) (((int) b) & 0xff) 1220 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), 1221 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1222#undef UC 1223 } else 1224 printf(" No data connection\r\n"); 1225 reply(211, "End of status"); 1226} 1227 1228void 1229fatal(s) 1230 char *s; 1231{ 1232 1233 reply(451, "Error in server: %s\n", s); 1234 reply(221, "Closing connection due to server error."); 1235 dologout(0); 1236 /* NOTREACHED */ 1237} 1238 1239void 1240#if __STDC__ 1241reply(int n, const char *fmt, ...) 1242#else 1243reply(n, fmt, va_alist) 1244 int n; 1245 char *fmt; 1246 va_dcl 1247#endif 1248{ 1249 va_list ap; 1250#if __STDC__ 1251 va_start(ap, fmt); 1252#else 1253 va_start(ap); 1254#endif 1255 (void)printf("%d ", n); 1256 (void)vprintf(fmt, ap); 1257 (void)printf("\r\n"); 1258 (void)fflush(stdout); 1259 if (debug) { 1260 syslog(LOG_DEBUG, "<--- %d ", n); 1261 vsyslog(LOG_DEBUG, fmt, ap); 1262 } 1263} 1264 1265void 1266#if __STDC__ 1267lreply(int n, const char *fmt, ...) 1268#else 1269lreply(n, fmt, va_alist) 1270 int n; 1271 char *fmt; 1272 va_dcl 1273#endif 1274{ 1275 va_list ap; 1276#if __STDC__ 1277 va_start(ap, fmt); 1278#else 1279 va_start(ap); 1280#endif 1281 (void)printf("%d- ", n); 1282 (void)vprintf(fmt, ap); 1283 (void)printf("\r\n"); 1284 (void)fflush(stdout); 1285 if (debug) { 1286 syslog(LOG_DEBUG, "<--- %d- ", n); 1287 vsyslog(LOG_DEBUG, fmt, ap); 1288 } 1289} 1290 1291static void 1292ack(s) 1293 char *s; 1294{ 1295 1296 reply(250, "%s command successful.", s); 1297} 1298 1299void 1300nack(s) 1301 char *s; 1302{ 1303 1304 reply(502, "%s command not implemented.", s); 1305} 1306 1307/* ARGSUSED */ 1308void 1309yyerror(s) 1310 char *s; 1311{ 1312 char *cp; 1313 1314 if (cp = strchr(cbuf,'\n')) 1315 *cp = '\0'; 1316 reply(500, "'%s': command not understood.", cbuf); 1317} 1318 1319void 1320delete(name) 1321 char *name; 1322{ 1323 struct stat st; 1324 1325 LOGCMD("delete", name); 1326 if (stat(name, &st) < 0) { 1327 perror_reply(550, name); 1328 return; 1329 } 1330 if ((st.st_mode&S_IFMT) == S_IFDIR) { 1331 if (rmdir(name) < 0) { 1332 perror_reply(550, name); 1333 return; 1334 } 1335 goto done; 1336 } 1337 if (unlink(name) < 0) { 1338 perror_reply(550, name); 1339 return; 1340 } 1341done: 1342 ack("DELE"); 1343} 1344 1345void 1346cwd(path) 1347 char *path; 1348{ 1349 1350 if (chdir(path) < 0) 1351 perror_reply(550, path); 1352 else 1353 ack("CWD"); 1354} 1355 1356void 1357makedir(name) 1358 char *name; 1359{ 1360 1361 LOGCMD("mkdir", name); 1362 if (mkdir(name, 0777) < 0) 1363 perror_reply(550, name); 1364 else 1365 reply(257, "MKD command successful."); 1366} 1367 1368void 1369removedir(name) 1370 char *name; 1371{ 1372 1373 LOGCMD("rmdir", name); 1374 if (rmdir(name) < 0) 1375 perror_reply(550, name); 1376 else 1377 ack("RMD"); 1378} 1379 1380void 1381pwd() 1382{ 1383 char path[MAXPATHLEN + 1]; 1384 1385 if (getwd(path) == (char *)NULL) 1386 reply(550, "%s.", path); 1387 else 1388 reply(257, "\"%s\" is current directory.", path); 1389} 1390 1391char * 1392renamefrom(name) 1393 char *name; 1394{ 1395 struct stat st; 1396 1397 if (stat(name, &st) < 0) { 1398 perror_reply(550, name); 1399 return ((char *)0); 1400 } 1401 reply(350, "File exists, ready for destination name"); 1402 return (name); 1403} 1404 1405void 1406renamecmd(from, to) 1407 char *from, *to; 1408{ 1409 1410 LOGCMD2("rename", from, to); 1411 if (rename(from, to) < 0) 1412 perror_reply(550, "rename"); 1413 else 1414 ack("RNTO"); 1415} 1416 1417static void 1418dolog(sin) 1419 struct sockaddr_in *sin; 1420{ 1421 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, 1422 sizeof(struct in_addr), AF_INET); 1423 1424 if (hp) 1425 (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); 1426 else 1427 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), 1428 sizeof(remotehost)); 1429#ifdef HASSETPROCTITLE 1430 snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); 1431 setproctitle(proctitle); 1432#endif /* HASSETPROCTITLE */ 1433 1434 if (logging) 1435 syslog(LOG_INFO, "connection from %s", remotehost); 1436} 1437 1438/* 1439 * Record logout in wtmp file 1440 * and exit with supplied status. 1441 */ 1442void 1443dologout(status) 1444 int status; 1445{ 1446 1447 if (logged_in) { 1448 (void) seteuid((uid_t)0); 1449 logwtmp(ttyline, "", ""); 1450 if (doutmp) 1451 logout(utmp.ut_line); 1452#if defined(KERBEROS) 1453 if (!notickets && krbtkfile_env) 1454 unlink(krbtkfile_env); 1455#endif 1456 } 1457 /* beware of flushing buffers after a SIGPIPE */ 1458 _exit(status); 1459} 1460 1461static void 1462myoob(signo) 1463 int signo; 1464{ 1465 char *cp; 1466 1467 /* only process if transfer occurring */ 1468 if (!transflag) 1469 return; 1470 cp = tmpline; 1471 if (getline(cp, 7, stdin) == NULL) { 1472 reply(221, "You could at least say goodbye."); 1473 dologout(0); 1474 } 1475 upper(cp); 1476 if (strcmp(cp, "ABOR\r\n") == 0) { 1477 tmpline[0] = '\0'; 1478 reply(426, "Transfer aborted. Data connection closed."); 1479 reply(226, "Abort successful"); 1480 longjmp(urgcatch, 1); 1481 } 1482 if (strcmp(cp, "STAT\r\n") == 0) { 1483 if (file_size != (off_t) -1) 1484 reply(213, "Status: %qd of %qd bytes transferred", 1485 byte_count, file_size); 1486 else 1487 reply(213, "Status: %qd bytes transferred", byte_count); 1488 } 1489} 1490 1491/* 1492 * Note: a response of 425 is not mentioned as a possible response to 1493 * the PASV command in RFC959. However, it has been blessed as 1494 * a legitimate response by Jon Postel in a telephone conversation 1495 * with Rick Adams on 25 Jan 89. 1496 */ 1497void 1498passive() 1499{ 1500 int len; 1501 char *p, *a; 1502 1503 pdata = socket(AF_INET, SOCK_STREAM, 0); 1504 if (pdata < 0) { 1505 perror_reply(425, "Can't open passive connection"); 1506 return; 1507 } 1508 pasv_addr = ctrl_addr; 1509 pasv_addr.sin_port = 0; 1510 (void) seteuid((uid_t)0); 1511 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 1512 (void) seteuid((uid_t)pw->pw_uid); 1513 goto pasv_error; 1514 } 1515 (void) seteuid((uid_t)pw->pw_uid); 1516 len = sizeof(pasv_addr); 1517 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) 1518 goto pasv_error; 1519 if (listen(pdata, 1) < 0) 1520 goto pasv_error; 1521 a = (char *) &pasv_addr.sin_addr; 1522 p = (char *) &pasv_addr.sin_port; 1523 1524#define UC(b) (((int) b) & 0xff) 1525 1526 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), 1527 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); 1528 return; 1529 1530pasv_error: 1531 (void) close(pdata); 1532 pdata = -1; 1533 perror_reply(425, "Can't open passive connection"); 1534 return; 1535} 1536 1537/* 1538 * Generate unique name for file with basename "local". 1539 * The file named "local" is already known to exist. 1540 * Generates failure reply on error. 1541 */ 1542static char * 1543gunique(local) 1544 char *local; 1545{ 1546 static char new[MAXPATHLEN]; 1547 struct stat st; 1548 int count; 1549 char *cp; 1550 1551 cp = strrchr(local, '/'); 1552 if (cp) 1553 *cp = '\0'; 1554 if (stat(cp ? local : ".", &st) < 0) { 1555 perror_reply(553, cp ? local : "."); 1556 return ((char *) 0); 1557 } 1558 if (cp) 1559 *cp = '/'; 1560 (void) strcpy(new, local); 1561 cp = new + strlen(new); 1562 *cp++ = '.'; 1563 for (count = 1; count < 100; count++) { 1564 (void)sprintf(cp, "%d", count); 1565 if (stat(new, &st) < 0) 1566 return (new); 1567 } 1568 reply(452, "Unique file name cannot be created."); 1569 return (NULL); 1570} 1571 1572/* 1573 * Format and send reply containing system error number. 1574 */ 1575void 1576perror_reply(code, string) 1577 int code; 1578 char *string; 1579{ 1580 1581 reply(code, "%s: %s.", string, strerror(errno)); 1582} 1583 1584static char *onefile[] = { 1585 "", 1586 0 1587}; 1588 1589void 1590send_file_list(whichf) 1591 char *whichf; 1592{ 1593 struct stat st; 1594 DIR *dirp = NULL; 1595 struct dirent *dir; 1596 FILE *dout = NULL; 1597 char **dirlist, *dirname; 1598 int simple = 0; 1599 int freeglob = 0; 1600 glob_t gl; 1601 1602 if (strpbrk(whichf, "~{[*?") != NULL) { 1603 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 1604 1605 memset(&gl, 0, sizeof(gl)); 1606 freeglob = 1; 1607 if (glob(whichf, flags, 0, &gl)) { 1608 reply(550, "not found"); 1609 goto out; 1610 } else if (gl.gl_pathc == 0) { 1611 errno = ENOENT; 1612 perror_reply(550, whichf); 1613 goto out; 1614 } 1615 dirlist = gl.gl_pathv; 1616 } else { 1617 onefile[0] = whichf; 1618 dirlist = onefile; 1619 simple = 1; 1620 } 1621 1622 if (setjmp(urgcatch)) { 1623 transflag = 0; 1624 goto out; 1625 } 1626 while (dirname = *dirlist++) { 1627 if (stat(dirname, &st) < 0) { 1628 /* 1629 * If user typed "ls -l", etc, and the client 1630 * used NLST, do what the user meant. 1631 */ 1632 if (dirname[0] == '-' && *dirlist == NULL && 1633 transflag == 0) { 1634 retrieve("/bin/ls %s", dirname); 1635 goto out; 1636 } 1637 perror_reply(550, whichf); 1638 if (dout != NULL) { 1639 (void) fclose(dout); 1640 transflag = 0; 1641 data = -1; 1642 pdata = -1; 1643 } 1644 goto out; 1645 } 1646 1647 if (S_ISREG(st.st_mode)) { 1648 if (dout == NULL) { 1649 dout = dataconn("file list", (off_t)-1, "w"); 1650 if (dout == NULL) 1651 goto out; 1652 transflag++; 1653 } 1654 fprintf(dout, "%s%s\n", dirname, 1655 type == TYPE_A ? "\r" : ""); 1656 byte_count += strlen(dirname) + 1; 1657 continue; 1658 } else if (!S_ISDIR(st.st_mode)) 1659 continue; 1660 1661 if ((dirp = opendir(dirname)) == NULL) 1662 continue; 1663 1664 while ((dir = readdir(dirp)) != NULL) { 1665 char nbuf[MAXPATHLEN]; 1666 1667 if (dir->d_name[0] == '.' && dir->d_namlen == 1) 1668 continue; 1669 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && 1670 dir->d_namlen == 2) 1671 continue; 1672 1673 sprintf(nbuf, "%s/%s", dirname, dir->d_name); 1674 1675 /* 1676 * We have to do a stat to insure it's 1677 * not a directory or special file. 1678 */ 1679 if (simple || (stat(nbuf, &st) == 0 && 1680 S_ISREG(st.st_mode))) { 1681 if (dout == NULL) { 1682 dout = dataconn("file list", (off_t)-1, 1683 "w"); 1684 if (dout == NULL) 1685 goto out; 1686 transflag++; 1687 } 1688 if (nbuf[0] == '.' && nbuf[1] == '/') 1689 fprintf(dout, "%s%s\n", &nbuf[2], 1690 type == TYPE_A ? "\r" : ""); 1691 else 1692 fprintf(dout, "%s%s\n", nbuf, 1693 type == TYPE_A ? "\r" : ""); 1694 byte_count += strlen(nbuf) + 1; 1695 } 1696 } 1697 (void) closedir(dirp); 1698 } 1699 1700 if (dout == NULL) 1701 reply(550, "No files found."); 1702 else if (ferror(dout) != 0) 1703 perror_reply(550, "Data connection"); 1704 else 1705 reply(226, "Transfer complete."); 1706 1707 transflag = 0; 1708 if (dout != NULL) 1709 (void) fclose(dout); 1710 data = -1; 1711 pdata = -1; 1712out: 1713 if (freeglob) { 1714 freeglob = 0; 1715 globfree(&gl); 1716 } 1717} 1718