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