fetch.9: Minor fixes.
[dragonfly.git] / contrib / opie / opieftpd.c
blob143bf8365c1215e2fdf56f8b82778d879b4862d9
1 /* opieftpd.c: Main program for an FTP daemon.
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
15 History:
17 Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use
18 opiestrncpy(). Fix incorrect use of setproctitle().
19 Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
20 done already (and conditionally) in opie_cfg.h.
21 Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
22 Merged in a security fix to BSD-derived ftpds.
23 Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
24 Moved LS_COMMAND here.
25 Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
26 Removed useless strings (I don't think that removing the
27 ucb copyright one is a problem -- please let me know if
28 I'm wrong). Changed default CMASK to 077. Removed random
29 comments. Use ANSI stdargs for reply/lreply if we can,
30 added stdargs version of reply/lreply. Don't declare the
31 tos variable unless IP_TOS defined. Include stdargs headers
32 early. More headers ifdefed. Made everything static.
33 Got rid of gethostname() call and use of hostname. Pared
34 down status response for places where header files frequently
35 cause trouble. Made logging of user logins (ala -l)
36 non-optional. Moved reply()/lrepy(). Fixed some prototypes.
37 Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
38 result of opiechallenge (allows access control to work).
39 Added patches for AIX. Symbol changes for autoconf.
40 Modified at NRL for OPIE 2.01. Changed password lookup handling
41 to avoid problems with drain-bamaged shadow password packages.
42 Properly handle internal state for anonymous FTP. Unlock
43 user accounts properly if login fails because of /etc/shells.
44 Make sure to close syslog by function to avoid problems with
45 drain bamaged syslog implementations.
46 Modified at NRL for OPIE 2.0.
47 Originally from BSD Net/2.
49 There is some really, really ugly code in here.
51 $FreeBSD: src/contrib/opie/opieftpd.c,v 1.2.6.5 2002/07/15 14:48:43 des Exp $
52 $DragonFly: src/contrib/opie/opieftpd.c,v 1.2 2003/06/17 04:24:05 dillon Exp $
55 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
56 * All rights reserved.
58 * Redistribution and use in source and binary forms, with or without
59 * modification, are permitted provided that the following conditions
60 * are met:
61 * 1. Redistributions of source code must retain the above copyright
62 * notice, this list of conditions and the following disclaimer.
63 * 2. Redistributions in binary form must reproduce the above copyright
64 * notice, this list of conditions and the following disclaimer in the
65 * documentation and/or other materials provided with the distribution.
66 * 3. All advertising materials mentioning features or use of this software
67 * must display the following acknowledgement:
68 * This product includes software developed by the University of
69 * California, Berkeley and its contributors.
70 * 4. Neither the name of the University nor the names of its contributors
71 * may be used to endorse or promote products derived from this software
72 * without specific prior written permission.
74 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
75 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
76 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
77 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
78 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
79 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
80 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
81 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
82 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
83 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
84 * SUCH DAMAGE.
87 #include "opie_cfg.h"
89 #if HAVE_ANSISTDARG
90 #include <stdarg.h>
91 #endif /* HAVE_ANSISTDARG */
94 * FTP server.
97 #if HAVE_SYS_PARAM_H
98 #include <sys/param.h>
99 #endif /* HAVE_SYS_PARAM_H */
100 #include <sys/stat.h>
101 /* #include <sys/ioctl.h> */
102 #include <sys/socket.h>
103 #include <sys/wait.h>
104 #ifdef SYS_FCNTL_H
105 #include <sys/fcntl.h>
106 #else
107 #include <fcntl.h>
108 #endif /* SYS_FCNTL_H */
109 #include <sys/types.h>
111 #include <netinet/in.h>
112 #include <netinet/in_systm.h>
113 #include <netinet/ip.h>
115 #define FTP_NAMES
116 #include <arpa/ftp.h>
117 #include <arpa/inet.h>
118 #include <arpa/telnet.h>
120 #include <signal.h>
121 #include <fcntl.h>
122 #if HAVE_TIME_H
123 #include <time.h>
124 #endif /* HAVE_TIME_H */
125 #if HAVE_PWD_H
126 #include <pwd.h>
127 #endif /* HAVE_PWD_H */
128 #include <setjmp.h>
129 #include <netdb.h>
130 #include <errno.h>
131 #include <syslog.h>
132 #if HAVE_UNISTD_H
133 #include <unistd.h>
134 #endif /* HAVE_UNISTD_H */
135 #include <stdio.h>
136 #include <ctype.h>
137 #include <stdlib.h>
138 #include <string.h>
139 #include <grp.h>
141 #include "opie.h"
143 #if HAVE_SHADOW_H
144 #include <shadow.h>
145 #endif /* HAVE_SHADOW_H */
147 #if HAVE_CRYPT_H
148 #include <crypt.h>
149 #endif /* HAVE_CRYPT_H */
151 #if HAVE_SYS_UTSNAME_H
152 #include <sys/utsname.h>
153 #endif /* HAVE_SYS_UTSNAME_H */
155 #ifdef _AIX
156 #include <sys/id.h>
157 #include <sys/priv.h>
158 #endif /* _AIX */
160 #ifdef IP_TOS
161 #ifndef IPTOS_THROUGHPUT
162 #undef IP_TOS
163 #endif /* !IPTOS_THROUGHPUT */
164 #ifndef IPTOS_LOWDELAY
165 #undef IP_TOS
166 #endif /* !IPTOS_LOWDELAY */
167 #endif /* IP_TOS */
169 extern int errno;
170 extern char *home; /* pointer to home directory for glob */
171 extern FILE *ftpd_popen __P((char *, char *));
172 extern int ftpd_pclose __P((FILE *));
173 extern char cbuf[];
174 extern off_t restart_point;
176 static struct sockaddr_in ctrl_addr;
177 static struct sockaddr_in data_source;
178 struct sockaddr_in data_dest;
179 struct sockaddr_in his_addr;
180 static struct sockaddr_in pasv_addr;
182 static int data;
183 jmp_buf errcatch;
184 static jmp_buf urgcatch;
185 int logged_in;
186 struct passwd *pw;
187 int debug;
188 int timeout = 900; /* timeout after 15 minutes of inactivity */
189 int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */
191 #if DOANONYMOUS
192 static int guest;
193 #endif /* DOANONYMOUS */
194 int type;
195 int form;
196 static int stru; /* avoid C keyword */
197 static int mode;
198 int usedefault = 1; /* for data transfers */
199 int pdata = -1; /* for passive mode */
200 static int transflag;
201 static off_t file_size;
202 static off_t byte_count;
204 #if (!defined(CMASK) || CMASK == 0)
205 #undef CMASK
206 #define CMASK 077
207 #endif
209 static int defumask = CMASK; /* default umask value */
210 char tmpline[7];
211 char remotehost[MAXHOSTNAMELEN];
214 * Timeout intervals for retrying connections
215 * to hosts that don't accept PORT cmds. This
216 * is a kludge, but given the problems with TCP...
218 #define SWAITMAX 90 /* wait at most 90 seconds */
219 #define SWAITINT 5 /* interval between retries */
221 static int swaitmax = SWAITMAX;
222 static int swaitint = SWAITINT;
224 #if DOTITLE
225 static char **Argv = NULL; /* pointer to argument vector */
226 static char *LastArgv = NULL; /* end of argv */
227 static char proctitle[BUFSIZ]; /* initial part of title */
228 #endif /* DOTITLE */
230 static int af_pwok = 0, pwok = 0;
231 static struct opie opiestate;
233 VOIDRET perror_reply __P((int, char *));
234 VOIDRET dologout __P((int));
235 char *getline __P((char *, int, FILE *));
236 VOIDRET upper __P((char *));
238 static VOIDRET lostconn __P((int));
239 static VOIDRET myoob __P((int));
240 static FILE *getdatasock __P((char *));
241 static FILE *dataconn __P((char *, off_t, char *));
242 static int checkuser __P((char *));
243 static VOIDRET end_login __P((void));
244 static VOIDRET send_data __P((FILE *, FILE *, off_t));
245 static int receive_data __P((FILE *, FILE *));
246 static char *gunique __P((char *));
247 static char *sgetsave __P((char *));
249 int opielogwtmp __P((char *, char *, char *, char *));
251 int fclose __P((FILE *));
253 #ifdef HAVE_ANSISTDARG
254 VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
256 va_list ap;
257 char buffer[1024];
259 va_start(ap, fmt);
260 vsprintf(buffer, fmt, ap);
261 va_end(ap);
263 printf("%d %s\r\n", n, buffer);
264 fflush(stdout);
265 if (debug)
266 syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
268 #else /* HAVE_ANSISTDARG */
269 VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
271 printf("%d ", n);
272 printf(fmt, p0, p1, p2, p3, p4, p5);
273 printf("\r\n");
274 fflush(stdout);
275 if (debug) {
276 syslog(LOG_DEBUG, "<--- %d ", n);
277 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
280 #endif /* HAVE_ANSISTDARG */
282 #ifdef HAVE_ANSISTDARG
283 VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
285 va_list ap;
286 char buffer[1024];
288 va_start(ap, fmt);
289 vsprintf(buffer, fmt, ap);
290 va_end(ap);
292 printf("%d- %s\r\n", n, buffer);
293 fflush(stdout);
294 if (debug)
295 syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
297 #else /* HAVE_ANSISTDARG */
298 VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
300 printf("%d- ", n);
301 printf(fmt, p0, p1, p2, p3, p4, p5);
302 printf("\r\n");
303 fflush(stdout);
304 if (debug) {
305 syslog(LOG_DEBUG, "<--- %d- ", n);
306 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
309 #endif /* HAVE_ANSISTDARG */
311 VOIDRET enable_signalling FUNCTION_NOARGS
313 signal(SIGPIPE, lostconn);
314 if ((int)signal(SIGURG, myoob) < 0)
315 syslog(LOG_ERR, "signal: %m");
318 VOIDRET disable_signalling FUNCTION_NOARGS
320 signal(SIGPIPE, SIG_IGN);
321 if ((int)signal(SIGURG, SIG_IGN) < 0)
322 syslog(LOG_ERR, "signal: %m");
325 static VOIDRET lostconn FUNCTION((input), int input)
327 if (debug)
328 syslog(LOG_DEBUG, "lost connection");
329 dologout(-1);
332 static char ttyline[20];
335 * Helper function for sgetpwnam().
337 static char *sgetsave FUNCTION((s), char *s)
339 char *new = malloc((unsigned) strlen(s) + 1);
341 if (new == NULL) {
342 perror_reply(421, "Local resource failure: malloc");
343 dologout(1);
344 /* NOTREACHED */
346 strcpy(new, s);
347 return (new);
351 * Save the result of a getpwnam. Used for USER command, since
352 * the data returned must not be clobbered by any other command
353 * (e.g., globbing).
355 static struct passwd *sgetpwnam FUNCTION((name), char *name)
357 static struct passwd save;
358 register struct passwd *p;
360 #if HAVE_SHADOW
361 struct spwd *spwd;
362 #endif /* HAVE_SHADOW */
364 if ((p = getpwnam(name)) == NULL)
365 return (p);
367 #if HAVE_SHADOW
368 if ((spwd = getspnam(name)) == NULL)
369 return NULL;
371 endspent();
373 p->pw_passwd = spwd->sp_pwdp;
374 #endif /* HAVE_SHADOW */
376 endpwent();
378 if (save.pw_name) {
379 free(save.pw_name);
380 free(save.pw_passwd);
381 free(save.pw_gecos);
382 free(save.pw_dir);
383 free(save.pw_shell);
385 save = *p;
386 save.pw_name = sgetsave(p->pw_name);
387 save.pw_passwd = sgetsave(p->pw_passwd);
388 save.pw_gecos = sgetsave(p->pw_gecos);
389 save.pw_dir = sgetsave(p->pw_dir);
390 save.pw_shell = sgetsave(p->pw_shell);
391 return (&save);
394 int login_attempts; /* number of failed login attempts */
395 int askpasswd; /* had user command, ask for passwd */
398 * USER command.
399 * Sets global passwd pointer pw if named account exists and is acceptable;
400 * sets askpasswd if a PASS command is expected. If logged in previously,
401 * need to reset state. If name is "ftp" or "anonymous", the name is not in
402 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
403 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
404 * requesting login privileges. Disallow anyone who does not have a standard
405 * shell as returned by getusershell(). Disallow anyone mentioned in the file
406 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
408 int user FUNCTION((name), char *name)
410 register char *cp;
411 char *shell;
413 if (logged_in) {
414 #if DOANONYMOUS
415 if (guest) {
416 reply(530, "Can't change user from guest login.");
417 return -1;
419 #endif /* DOANONMOUS */
420 end_login();
422 askpasswd = 1;
423 #if DOANONYMOUS
424 guest = 0;
425 if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
426 if (!checkuser("ftp") && !checkuser("anonymous"))
427 if ((pw = sgetpwnam("ftp")) != NULL) {
428 guest = 1;
429 askpasswd = 1;
430 reply(331, "Guest login ok, send your e-mail address as your password.");
431 syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
432 return 0;
434 #endif /* DOANONYMOUS */
435 if (pw = sgetpwnam(name)) {
436 if ((shell = pw->pw_shell) == NULL || *shell == 0)
437 shell = _PATH_BSHELL;
438 while ((cp = getusershell()) != NULL)
439 if (!strcmp(cp, shell))
440 break;
441 endusershell();
442 if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
443 #if DEBUG
444 if (!cp)
445 syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
446 if (checkuser(name))
447 syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
448 if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
449 syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
450 #endif /* DEBUG */
451 pw = (struct passwd *) NULL;
452 askpasswd = -1;
456 char prompt[OPIE_CHALLENGE_MAX + 1];
458 opiechallenge(&opiestate, name, prompt);
460 if (askpasswd == -1) {
461 syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
462 pwok = 0;
463 } else
464 pwok = af_pwok && opiealways(pw->pw_dir);
466 #if NEW_PROMPTS
467 reply(331, "Response to %s %s for %s.", prompt,
468 #else /* NEW_PROMPTS */
469 reply(331, "OTP response %s %s for %s.", prompt,
470 #endif /* NEW_PROMPTS */
471 pwok ? "requested" : "required", name);
473 /* Delay before reading passwd after first failed attempt to slow down
474 passwd-guessing programs. */
475 if (login_attempts)
476 sleep((unsigned) login_attempts);
478 return 0;
482 * Check if a user is in the file _PATH_FTPUSERS
484 static int checkuser FUNCTION((name), char *name)
486 register FILE *fd;
487 register char *p;
488 char line[BUFSIZ];
490 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
491 while (fgets(line, sizeof(line), fd) != NULL)
492 if ((p = strchr(line, '\n')) != NULL) {
493 *p = '\0';
494 if (line[0] == '#')
495 continue;
496 if (!strcmp(line, name)) {
497 fclose(fd);
498 return (1);
501 fclose(fd);
503 return (0);
507 * Terminate login as previous user, if any, resetting state;
508 * used when USER command is given or login fails.
510 static VOIDRET end_login FUNCTION_NOARGS
512 disable_signalling();
513 if (seteuid((uid_t) 0))
514 syslog(LOG_ERR, "Can't set euid");
515 if (logged_in)
516 opielogwtmp(ttyline, "", "", "ftp");
517 pw = NULL;
518 logged_in = 0;
519 #if DOANONYMOUS
520 guest = 0;
521 #endif /* DOANONYMOUS */
522 enable_signalling();
525 VOIDRET pass FUNCTION((passwd), char *passwd)
527 int legit = askpasswd + 1, i;
529 if (logged_in || askpasswd == 0) {
530 reply(503, "Login with USER first.");
531 return;
533 askpasswd = 0;
535 #if DOANONYMOUS
536 if (!guest) { /* "ftp" is only account allowed no password */
537 #endif /* DOANONYMOUS */
538 i = opieverify(&opiestate, passwd);
539 if (legit && i && pwok)
540 i = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd);
541 if (!legit || i) {
542 reply(530, "Login incorrect.");
543 pw = NULL;
544 if (login_attempts++ >= 5) {
545 syslog(LOG_WARNING,
546 "Repeated login failures for user %s from %s",
547 pw->pw_name, remotehost);
548 exit(0);
550 return;
552 #if DOANONYMOUS
553 } else
554 if ((passwd[0] <= ' ') || checkuser(passwd)) {
555 reply(530, "No identity, no service.");
556 syslog(LOG_DEBUG, "Bogus address: %s", passwd);
557 exit(0);
559 #endif /* DOANONYMOUS */
560 login_attempts = 0; /* this time successful */
561 if (setegid((gid_t) pw->pw_gid) < 0) {
562 reply(550, "Can't set gid.");
563 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
564 return;
566 initgroups(pw->pw_name, pw->pw_gid);
568 /* open wtmp before chroot */
569 sprintf(ttyline, "ftp%d", getpid());
570 opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp");
571 logged_in = 1;
573 #if DOANONYMOUS
574 if (guest) {
575 /* We MUST do a chdir() after the chroot. Otherwise the old current
576 directory will be accessible as "." outside the new root! */
577 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
578 reply(550, "Can't set guest privileges.");
579 goto bad;
581 } else
582 #endif /* DOANONYMOUS */
583 if (chdir(pw->pw_dir) < 0) {
584 if (chdir("/") < 0) {
585 reply(530, "User %s: can't change directory to %s.",
586 pw->pw_name, pw->pw_dir);
587 goto bad;
588 } else
589 lreply(230, "No directory! Logging in with home=/");
591 /* This patch was contributed by an OPIE user. We don't know what it
592 does, exactly. It may or may not work. */
593 #ifdef _AIX
595 priv_t priv;
596 priv.pv_priv[0] = 0;
597 priv.pv_priv[1] = 0;
598 setgroups(NULL, NULL);
599 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
600 &priv, sizeof(priv_t)) < 0 ||
601 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
602 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
603 seteuid((uid_t)pw->pw_uid) < 0) {
604 reply(550, "Can't set uid (_AIX3).");
605 goto bad;
608 #else /* _AIX */
609 if (seteuid((uid_t) pw->pw_uid) < 0) {
610 reply(550, "Can't set uid.");
611 goto bad;
613 #endif /* _AIX */
615 * Display a login message, if it exists.
616 * N.B. reply(230,) must follow the message.
619 FILE *fd;
621 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
622 char *cp, line[128];
624 while (fgets(line, sizeof(line), fd) != NULL) {
625 if ((cp = strchr(line, '\n')) != NULL)
626 *cp = '\0';
627 lreply(230, "%s", line);
629 (void) fflush(stdout);
630 (void) fclose(fd);
633 #if DOANONYMOUS
634 if (guest) {
635 reply(230, "Guest login ok, access restrictions apply.");
636 #if DOTITLE
637 setproctitle("%s: anonymous/%.*s", remotehost,
638 sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"),
639 passwd);
640 #endif /* DOTITLE */
641 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
642 remotehost, passwd);
643 } else
644 #endif /* DOANONYMOUS */
646 reply(230, "User %s logged in.", pw->pw_name);
648 #if DOTITLE
649 setproctitle("%s: %s", remotehost, pw->pw_name);
650 #endif /* DOTITLE */
651 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
653 home = pw->pw_dir; /* home dir for globbing */
654 umask(defumask);
655 return;
657 bad:
658 /* Forget all about it... */
659 end_login();
662 VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
664 FILE *fin, *dout;
665 struct stat st;
666 int (*closefunc) ();
668 if (cmd == 0) {
669 fin = fopen(name, "r"), closefunc = fclose;
670 st.st_size = 0;
671 } else {
672 char line[BUFSIZ];
674 snprintf(line, sizeof(line), cmd, name);
675 name = line;
676 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
677 st.st_size = -1;
678 #if HAVE_ST_BLKSIZE
679 st.st_blksize = BUFSIZ;
680 #endif /* HAVE_ST_BLKSIZE */
682 if (fin == NULL) {
683 if (errno != 0)
684 perror_reply(550, name);
685 return;
687 if (cmd == 0 &&
688 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
689 reply(550, "%s: not a plain file.", name);
690 goto done;
692 if (restart_point) {
693 if (type == TYPE_A) {
694 register int i, n, c;
696 n = restart_point;
697 i = 0;
698 while (i++ < n) {
699 if ((c = getc(fin)) == EOF) {
700 perror_reply(550, name);
701 goto done;
703 if (c == '\n')
704 i++;
706 } else
707 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
708 perror_reply(550, name);
709 goto done;
712 dout = dataconn(name, st.st_size, "w");
713 if (dout == NULL)
714 goto done;
715 #if HAVE_ST_BLKSIZE
716 send_data(fin, dout, st.st_blksize);
717 #else /* HAVE_ST_BLKSIZE */
718 send_data(fin, dout, BUFSIZ);
719 #endif /* HAVE_ST_BLKSIZE */
720 fclose(dout);
721 data = -1;
722 pdata = -1;
723 done:
724 (*closefunc) (fin);
727 VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
729 FILE *fout, *din;
730 struct stat st;
731 int (*closefunc) ();
733 if (unique && stat(name, &st) == 0 &&
734 (name = gunique(name)) == NULL)
735 return;
737 if (restart_point)
738 mode = "r+w";
739 fout = fopen(name, mode);
740 closefunc = fclose;
741 if (fout == NULL) {
742 perror_reply(553, name);
743 return;
745 if (restart_point) {
746 if (type == TYPE_A) {
747 register int i, n, c;
749 n = restart_point;
750 i = 0;
751 while (i++ < n) {
752 if ((c = getc(fout)) == EOF) {
753 perror_reply(550, name);
754 goto done;
756 if (c == '\n')
757 i++;
759 /* We must do this seek to "current" position because we are changing
760 from reading to writing. */
761 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
762 perror_reply(550, name);
763 goto done;
765 } else
766 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
767 perror_reply(550, name);
768 goto done;
771 din = dataconn(name, (off_t) - 1, "r");
772 if (din == NULL)
773 goto done;
774 if (receive_data(din, fout) == 0) {
775 if (unique)
776 reply(226, "Transfer complete (unique file name:%s).",
777 name);
778 else
779 reply(226, "Transfer complete.");
781 fclose(din);
782 data = -1;
783 pdata = -1;
784 done:
785 (*closefunc) (fout);
788 static FILE *getdatasock FUNCTION((mode), char *mode)
790 int s, on = 1, tries;
792 if (data >= 0)
793 return (fdopen(data, mode));
794 disable_signalling();
795 if (seteuid((uid_t) 0))
796 syslog(LOG_ERR, "Can't set euid");
797 s = socket(AF_INET, SOCK_STREAM, 0);
798 if (s < 0)
799 goto bad;
800 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
801 (char *) &on, sizeof(on)) < 0)
802 goto bad;
803 /* anchor socket to avoid multi-homing problems */
804 data_source.sin_family = AF_INET;
805 data_source.sin_addr = ctrl_addr.sin_addr;
806 for (tries = 1;; tries++) {
807 if (bind(s, (struct sockaddr *) & data_source,
808 sizeof(data_source)) >= 0)
809 break;
810 if (errno != EADDRINUSE || tries > 10)
811 goto bad;
812 sleep(tries);
814 if (seteuid((uid_t) pw->pw_uid))
815 syslog(LOG_ERR, "Can't set euid");
816 enable_signalling();
817 #ifdef IP_TOS
818 on = IPTOS_THROUGHPUT;
819 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
820 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
821 #endif
822 return (fdopen(s, mode));
823 bad:
825 int t = errno;
827 if (seteuid((uid_t) pw->pw_uid))
828 syslog(LOG_ERR, "Can't set euid");
829 enable_signalling();
830 close(s);
832 errno = t;
834 return (NULL);
837 static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
839 char sizebuf[32];
840 FILE *file;
841 int retry = 0;
842 #ifdef IP_TOS
843 int tos;
844 #endif /* IP_TOS */
846 file_size = size;
847 byte_count = 0;
848 if (size != (off_t) - 1)
849 snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
850 else
851 strcpy(sizebuf, "");
852 if (pdata >= 0) {
853 struct sockaddr_in from;
854 int s, fromlen = sizeof(from);
856 s = accept(pdata, (struct sockaddr *) & from, &fromlen);
857 if (s < 0) {
858 reply(425, "Can't open data connection.");
859 close(pdata);
860 pdata = -1;
861 return (NULL);
863 close(pdata);
864 pdata = s;
865 #ifdef IP_TOS
866 tos = IPTOS_LOWDELAY;
867 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
868 sizeof(int));
870 #endif
871 reply(150, "Opening %s mode data connection for %s%s.",
872 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
873 return (fdopen(pdata, mode));
875 if (data >= 0) {
876 reply(125, "Using existing data connection for %s%s.",
877 name, sizebuf);
878 usedefault = 1;
879 return (fdopen(data, mode));
881 if (usedefault)
882 data_dest = his_addr;
883 usedefault = 1;
884 file = getdatasock(mode);
885 if (file == NULL) {
886 reply(425, "Can't create data socket (%s,%d): %s.",
887 inet_ntoa(data_source.sin_addr),
888 ntohs(data_source.sin_port), strerror(errno));
889 return (NULL);
891 data = fileno(file);
892 while (connect(data, (struct sockaddr *) & data_dest,
893 sizeof(data_dest)) < 0) {
894 if (errno == EADDRINUSE && retry < swaitmax) {
895 sleep((unsigned) swaitint);
896 retry += swaitint;
897 continue;
899 perror_reply(425, "Can't build data connection");
900 fclose(file);
901 data = -1;
902 return (NULL);
904 reply(150, "Opening %s mode data connection for %s%s.",
905 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
906 return (file);
910 * Tranfer the contents of "instr" to
911 * "outstr" peer using the appropriate
912 * encapsulation of the data subject
913 * to Mode, Structure, and Type.
915 * NB: Form isn't handled.
917 static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
919 register int c, cnt;
920 register char *buf;
921 int netfd, filefd;
923 transflag++;
924 if (setjmp(urgcatch)) {
925 transflag = 0;
926 return;
928 switch (type) {
930 case TYPE_A:
931 while ((c = getc(instr)) != EOF) {
932 byte_count++;
933 if (c == '\n') {
934 if (ferror(outstr))
935 goto data_err;
936 putc('\r', outstr);
938 putc(c, outstr);
940 fflush(outstr);
941 transflag = 0;
942 if (ferror(instr))
943 goto file_err;
944 if (ferror(outstr))
945 goto data_err;
946 reply(226, "Transfer complete.");
947 return;
949 case TYPE_I:
950 case TYPE_L:
951 if ((buf = malloc((u_int) blksize)) == NULL) {
952 transflag = 0;
953 perror_reply(451, "Local resource failure: malloc");
954 return;
956 netfd = fileno(outstr);
957 filefd = fileno(instr);
958 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
959 write(netfd, buf, cnt) == cnt)
960 byte_count += cnt;
961 transflag = 0;
962 free(buf);
963 if (cnt != 0) {
964 if (cnt < 0)
965 goto file_err;
966 goto data_err;
968 reply(226, "Transfer complete.");
969 return;
970 default:
971 transflag = 0;
972 reply(550, "Unimplemented TYPE %d in send_data", type);
973 return;
976 data_err:
977 transflag = 0;
978 perror_reply(426, "Data connection");
979 return;
981 file_err:
982 transflag = 0;
983 perror_reply(551, "Error on input file");
987 * Transfer data from peer to
988 * "outstr" using the appropriate
989 * encapulation of the data subject
990 * to Mode, Structure, and Type.
992 * N.B.: Form isn't handled.
994 static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
996 register int c;
997 int cnt, bare_lfs = 0;
998 char buf[BUFSIZ];
1000 transflag++;
1001 if (setjmp(urgcatch)) {
1002 transflag = 0;
1003 return (-1);
1005 switch (type) {
1007 case TYPE_I:
1008 case TYPE_L:
1009 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
1010 if (write(fileno(outstr), buf, cnt) != cnt)
1011 goto file_err;
1012 byte_count += cnt;
1014 if (cnt < 0)
1015 goto data_err;
1016 transflag = 0;
1017 return (0);
1019 case TYPE_E:
1020 reply(553, "TYPE E not implemented.");
1021 transflag = 0;
1022 return (-1);
1024 case TYPE_A:
1025 while ((c = getc(instr)) != EOF) {
1026 byte_count++;
1027 if (c == '\n')
1028 bare_lfs++;
1029 while (c == '\r') {
1030 if (ferror(outstr))
1031 goto data_err;
1032 if ((c = getc(instr)) != '\n') {
1033 putc('\r', outstr);
1034 if (c == '\0' || c == EOF)
1035 goto contin2;
1038 putc(c, outstr);
1039 contin2:;
1041 fflush(outstr);
1042 if (ferror(instr))
1043 goto data_err;
1044 if (ferror(outstr))
1045 goto file_err;
1046 transflag = 0;
1047 if (bare_lfs) {
1048 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1049 printf(" File may not have transferred correctly.\r\n");
1051 return (0);
1052 default:
1053 reply(550, "Unimplemented TYPE %d in receive_data", type);
1054 transflag = 0;
1055 return (-1);
1058 data_err:
1059 transflag = 0;
1060 perror_reply(426, "Data Connection");
1061 return (-1);
1063 file_err:
1064 transflag = 0;
1065 perror_reply(452, "Error writing file");
1066 return (-1);
1069 VOIDRET statfilecmd FUNCTION((filename), char *filename)
1071 char line[BUFSIZ];
1072 FILE *fin;
1073 int c;
1075 #if HAVE_LS_G_FLAG
1076 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1077 #else /* HAVE_LS_G_FLAG */
1078 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
1079 #endif /* HAVE_LS_G_FLAG */
1080 fin = ftpd_popen(line, "r");
1081 lreply(211, "status of %s:", filename);
1082 while ((c = getc(fin)) != EOF) {
1083 if (c == '\n') {
1084 if (ferror(stdout)) {
1085 perror_reply(421, "control connection");
1086 ftpd_pclose(fin);
1087 dologout(1);
1088 /* NOTREACHED */
1090 if (ferror(fin)) {
1091 perror_reply(551, filename);
1092 ftpd_pclose(fin);
1093 return;
1095 putc('\r', stdout);
1097 putc(c, stdout);
1099 ftpd_pclose(fin);
1100 reply(211, "End of Status");
1103 VOIDRET statcmd FUNCTION_NOARGS
1105 /* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1106 struct sockaddr_in *sin;
1107 u_char *a, *p;
1109 lreply(211, "FTP server status:");
1110 printf(" \r\n");
1111 printf(" Connected to %s", remotehost);
1112 if (!isdigit(remotehost[0]))
1113 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1114 printf("\r\n");
1115 if (logged_in) {
1116 #if DOANONYMOUS
1117 if (guest)
1118 printf(" Logged in anonymously\r\n");
1119 else
1120 #endif /* DOANONYMOUS */
1121 printf(" Logged in as %s\r\n", pw->pw_name);
1122 } else
1123 if (askpasswd)
1124 printf(" Waiting for password\r\n");
1125 else
1126 printf(" Waiting for user name\r\n");
1127 if (data != -1)
1128 printf(" Data connection open\r\n");
1129 else
1130 if (pdata != -1) {
1131 printf(" in Passive mode");
1132 sin = &pasv_addr;
1133 goto printaddr;
1134 } else
1135 if (usedefault == 0) {
1136 printf(" PORT");
1137 sin = &data_dest;
1138 printaddr:
1139 a = (u_char *) & sin->sin_addr;
1140 p = (u_char *) & sin->sin_port;
1141 #define UC(b) (((int) b) & 0xff)
1142 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1143 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1144 #undef UC
1145 } else
1146 printf(" No data connection\r\n");
1147 reply(211, "End of status");
1150 VOIDRET opiefatal FUNCTION((s), char *s)
1152 reply(451, "Error in server: %s\n", s);
1153 reply(221, "Closing connection due to server error.");
1154 dologout(0);
1155 /* NOTREACHED */
1158 static VOIDRET ack FUNCTION((s), char *s)
1160 reply(250, "%s command successful.", s);
1163 VOIDRET nack FUNCTION((s), char *s)
1165 reply(502, "%s command not implemented.", s);
1168 VOIDRET yyerror FUNCTION((s), char *s)
1170 char *cp;
1172 if (cp = strchr(cbuf, '\n'))
1173 *cp = '\0';
1174 reply(500, "'%s': command not understood.", cbuf);
1177 VOIDRET delete FUNCTION((name), char *name)
1179 struct stat st;
1181 if (stat(name, &st) < 0) {
1182 perror_reply(550, name);
1183 return;
1185 if ((st.st_mode & S_IFMT) == S_IFDIR) {
1186 if (rmdir(name) < 0) {
1187 perror_reply(550, name);
1188 return;
1190 goto done;
1192 if (unlink(name) < 0) {
1193 perror_reply(550, name);
1194 return;
1196 done:
1197 ack("DELE");
1200 VOIDRET cwd FUNCTION((path), char *path)
1202 if (chdir(path) < 0)
1203 perror_reply(550, path);
1204 else
1205 ack("CWD");
1208 VOIDRET makedir FUNCTION((name), char *name)
1210 if (mkdir(name, 0777) < 0)
1211 perror_reply(550, name);
1212 else
1213 reply(257, "MKD command successful.");
1216 VOIDRET removedir FUNCTION((name), char *name)
1218 if (rmdir(name) < 0)
1219 perror_reply(550, name);
1220 else
1221 ack("RMD");
1224 VOIDRET pwd FUNCTION_NOARGS
1226 char path[MAXPATHLEN + 1];
1228 if (getcwd(path, MAXPATHLEN) == (char *) NULL)
1229 reply(550, "%s.", path);
1230 else
1231 reply(257, "\"%s\" is current directory.", path);
1234 char *renamefrom FUNCTION((name), char *name)
1236 struct stat st;
1238 if (stat(name, &st) < 0) {
1239 perror_reply(550, name);
1240 return ((char *) 0);
1242 reply(350, "File exists, ready for destination name");
1243 return (name);
1246 VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
1248 if (rename(from, to) < 0)
1249 perror_reply(550, "rename");
1250 else
1251 ack("RNTO");
1254 static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
1256 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
1257 sizeof(struct in_addr), AF_INET);
1258 time_t t, time();
1260 if (hp)
1261 opiestrncpy(remotehost, hp->h_name, sizeof(remotehost));
1262 else
1263 opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1264 #if DOTITLE
1265 setproctitle("%s: connected", remotehost);
1266 #endif /* DOTITLE */
1268 t = time((time_t *) 0);
1269 syslog(LOG_INFO, "connection from %s at %s",
1270 remotehost, ctime(&t));
1274 * Record logout in wtmp file
1275 * and exit with supplied status.
1277 VOIDRET dologout FUNCTION((status), int status)
1279 disable_signalling();
1280 if (logged_in) {
1281 if (seteuid((uid_t) 0))
1282 syslog(LOG_ERR, "Can't set euid");
1283 opielogwtmp(ttyline, "", "", "ftp");
1285 /* beware of flushing buffers after a SIGPIPE */
1286 _exit(status);
1289 static VOIDRET myoob FUNCTION((input), int input)
1291 char *cp;
1293 /* only process if transfer occurring */
1294 if (!transflag)
1295 return;
1296 cp = tmpline;
1297 if (getline(cp, 7, stdin) == NULL) {
1298 reply(221, "You could at least say goodbye.");
1299 dologout(0);
1301 upper(cp);
1302 if (strcmp(cp, "ABOR\r\n") == 0) {
1303 tmpline[0] = '\0';
1304 reply(426, "Transfer aborted. Data connection closed.");
1305 reply(226, "Abort successful");
1306 longjmp(urgcatch, 1);
1308 if (strcmp(cp, "STAT\r\n") == 0) {
1309 if (file_size != (off_t) - 1)
1310 reply(213, "Status: %lu of %lu bytes transferred",
1311 byte_count, file_size);
1312 else
1313 reply(213, "Status: %lu bytes transferred", byte_count);
1318 * Note: a response of 425 is not mentioned as a possible response to
1319 * the PASV command in RFC959. However, it has been blessed as
1320 * a legitimate response by Jon Postel in a telephone conversation
1321 * with Rick Adams on 25 Jan 89.
1323 VOIDRET passive FUNCTION_NOARGS
1325 int len;
1326 register char *p, *a;
1328 pdata = socket(AF_INET, SOCK_STREAM, 0);
1329 if (pdata < 0) {
1330 perror_reply(425, "Can't open passive connection");
1331 return;
1333 pasv_addr = ctrl_addr;
1334 pasv_addr.sin_port = 0;
1335 if (seteuid((uid_t) 0))
1336 syslog(LOG_ERR, "Can't set euid");
1337 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1338 seteuid((uid_t) pw->pw_uid);
1339 goto pasv_error;
1341 if (seteuid((uid_t) pw->pw_uid))
1342 syslog(LOG_ERR, "Can't set euid");
1343 len = sizeof(pasv_addr);
1344 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1345 goto pasv_error;
1346 if (listen(pdata, 1) < 0)
1347 goto pasv_error;
1348 a = (char *) &pasv_addr.sin_addr;
1349 p = (char *) &pasv_addr.sin_port;
1351 #define UC(b) (((int) b) & 0xff)
1353 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1354 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1355 return;
1357 pasv_error:
1358 close(pdata);
1359 pdata = -1;
1360 perror_reply(425, "Can't open passive connection");
1361 return;
1365 * Generate unique name for file with basename "local".
1366 * The file named "local" is already known to exist.
1367 * Generates failure reply on error.
1369 static char *gunique FUNCTION((local), char *local)
1371 static char new[MAXPATHLEN+1];
1372 struct stat st;
1373 char *cp = strrchr(local, '/');
1374 int count = 0;
1376 if (cp)
1377 *cp = '\0';
1378 if (stat(cp ? local : ".", &st) < 0) {
1379 perror_reply(553, cp ? local : ".");
1380 return ((char *) 0);
1382 if (cp)
1383 *cp = '/';
1384 strcpy(new, local);
1385 cp = new + strlen(new);
1386 *cp++ = '.';
1387 for (count = 1; count < 100; count++) {
1388 snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1389 if (stat(new, &st) < 0)
1390 return (new);
1392 reply(452, "Unique file name cannot be created.");
1393 return ((char *) 0);
1397 * Format and send reply containing system error number.
1399 VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1401 reply(code, "%s: %s.", string, strerror(errno));
1404 static char *onefile[] =
1410 VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1412 struct stat st;
1413 DIR *dirp = NULL;
1414 struct dirent *dir;
1415 FILE *dout = NULL;
1416 register char **dirlist, *dirname;
1417 int simple = 0;
1419 if (strpbrk(whichfiles, "~{[*?") != NULL) {
1420 extern char **ftpglob(), *globerr;
1422 globerr = NULL;
1423 dirlist = ftpglob(whichfiles);
1424 if (globerr != NULL) {
1425 reply(550, globerr);
1426 return;
1427 } else
1428 if (dirlist == NULL) {
1429 errno = ENOENT;
1430 perror_reply(550, whichfiles);
1431 return;
1433 } else {
1434 onefile[0] = whichfiles;
1435 dirlist = onefile;
1436 simple = 1;
1439 if (setjmp(urgcatch)) {
1440 transflag = 0;
1441 return;
1443 while (dirname = *dirlist++) {
1444 if (stat(dirname, &st) < 0) {
1445 /* If user typed "ls -l", etc, and the client used NLST, do what the
1446 user meant. */
1447 if (dirname[0] == '-' && *dirlist == NULL &&
1448 transflag == 0) {
1449 retrieve("/bin/ls %s", dirname);
1450 return;
1452 perror_reply(550, whichfiles);
1453 if (dout != NULL) {
1454 fclose(dout);
1455 transflag = 0;
1456 data = -1;
1457 pdata = -1;
1459 return;
1461 if ((st.st_mode & S_IFMT) == S_IFREG) {
1462 if (dout == NULL) {
1463 dout = dataconn("file list", (off_t) - 1, "w");
1464 if (dout == NULL)
1465 return;
1466 transflag++;
1468 fprintf(dout, "%s%s\n", dirname,
1469 type == TYPE_A ? "\r" : "");
1470 byte_count += strlen(dirname) + 1;
1471 continue;
1472 } else
1473 if ((st.st_mode & S_IFMT) != S_IFDIR)
1474 continue;
1476 if ((dirp = opendir(dirname)) == NULL)
1477 continue;
1479 while ((dir = readdir(dirp)) != NULL) {
1480 char nbuf[MAXPATHLEN+1];
1482 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1483 continue;
1484 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1485 (strlen(dir->d_name) == 2))
1486 continue;
1488 snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1490 /* We have to do a stat to insure it's not a directory or special file. */
1491 if (simple || (stat(nbuf, &st) == 0 &&
1492 (st.st_mode & S_IFMT) == S_IFREG)) {
1493 if (dout == NULL) {
1494 dout = dataconn("file list", (off_t) - 1, "w");
1495 if (dout == NULL)
1496 return;
1497 transflag++;
1499 if (nbuf[0] == '.' && nbuf[1] == '/')
1500 fprintf(dout, "%s%s\n", &nbuf[2],
1501 type == TYPE_A ? "\r" : "");
1502 else
1503 fprintf(dout, "%s%s\n", nbuf,
1504 type == TYPE_A ? "\r" : "");
1505 byte_count += strlen(nbuf) + 1;
1508 closedir(dirp);
1511 if (dout == NULL)
1512 reply(550, "No files found.");
1513 else
1514 if (ferror(dout) != 0)
1515 perror_reply(550, "Data connection");
1516 else
1517 reply(226, "Transfer complete.");
1519 transflag = 0;
1520 if (dout != NULL)
1521 fclose(dout);
1522 data = -1;
1523 pdata = -1;
1526 #if DOTITLE
1528 * clobber argv so ps will show what we're doing.
1529 * (stolen from sendmail)
1530 * warning, since this is usually started from inetd.conf, it
1531 * often doesn't have much of an environment or arglist to overwrite.
1533 VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1535 register char *p, *bp, ch;
1536 register int i;
1537 char buf[BUFSIZ];
1539 snprintf(buf, sizeof(buf), fmt, a, b, c);
1541 /* make ps print our process name */
1542 p = Argv[0];
1543 *p++ = '-';
1545 i = strlen(buf);
1546 if (i > LastArgv - p - 2) {
1547 i = LastArgv - p - 2;
1548 buf[i] = '\0';
1550 bp = buf;
1551 while (ch = *bp++)
1552 if (ch != '\n' && ch != '\r')
1553 *p++ = ch;
1554 while (p < LastArgv)
1555 *p++ = ' ';
1557 #endif /* DOTITLE */
1559 VOIDRET catchexit FUNCTION_NOARGS
1561 closelog();
1564 int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1566 int addrlen, on = 1;
1567 char *cp;
1568 #ifdef IP_TOS
1569 int tos;
1570 #endif /* IP_TOS */
1573 int i;
1575 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1576 close(i);
1579 /* LOG_NDELAY sets up the logging connection immediately, necessary for
1580 anonymous ftp's that chroot and can't do it later. */
1581 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1582 atexit(catchexit);
1583 addrlen = sizeof(his_addr);
1584 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1585 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1586 exit(1);
1588 addrlen = sizeof(ctrl_addr);
1589 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1590 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1591 exit(1);
1593 #ifdef IP_TOS
1594 tos = IPTOS_LOWDELAY;
1595 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1596 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1597 #endif
1598 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1599 debug = 0;
1600 #if DOTITLE
1601 /* Save start and extent of argv for setproctitle. */
1602 Argv = argv;
1603 while (*envp)
1604 envp++;
1605 LastArgv = envp[-1] + strlen(envp[-1]);
1606 #endif /* DOTITLE */
1608 argc--, argv++;
1609 while (argc > 0 && *argv[0] == '-') {
1610 for (cp = &argv[0][1]; *cp; cp++)
1611 switch (*cp) {
1613 case 'v':
1614 debug = 1;
1615 break;
1617 case 'd':
1618 debug = 1;
1619 break;
1621 case 'l':
1622 break;
1624 case 't':
1625 timeout = atoi(++cp);
1626 if (maxtimeout < timeout)
1627 maxtimeout = timeout;
1628 goto nextopt;
1630 case 'T':
1631 maxtimeout = atoi(++cp);
1632 if (timeout > maxtimeout)
1633 timeout = maxtimeout;
1634 goto nextopt;
1636 case 'u':
1638 int val = 0;
1640 while (*++cp && *cp >= '0' && *cp <= '9')
1641 val = val * 8 + *cp - '0';
1642 if (*cp)
1643 fprintf(stderr, "ftpd: Bad value for -u\n");
1644 else
1645 defumask = val;
1646 goto nextopt;
1649 default:
1650 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1651 *cp);
1652 break;
1654 nextopt:
1655 argc--, argv++;
1657 freopen(_PATH_DEVNULL, "w", stderr);
1658 signal(SIGCHLD, SIG_IGN);
1659 enable_signalling();
1661 /* Try to handle urgent data inline */
1662 #ifdef SO_OOBINLINE
1663 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1664 syslog(LOG_ERR, "setsockopt: %m");
1665 #endif
1667 #ifdef F_SETOWN
1668 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1669 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1670 #endif
1671 dolog(&his_addr);
1672 /* Set up default state */
1673 data = -1;
1674 type = TYPE_A;
1675 form = FORM_N;
1676 stru = STRU_F;
1677 mode = MODE_S;
1678 tmpline[0] = '\0';
1679 af_pwok = opieaccessfile(remotehost);
1682 FILE *fd;
1683 char line[128];
1685 /* If logins are disabled, print out the message. */
1686 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1687 while (fgets(line, sizeof(line), fd) != NULL) {
1688 if ((cp = strchr(line, '\n')) != NULL)
1689 *cp = '\0';
1690 lreply(530, "%s", line);
1692 (void) fflush(stdout);
1693 (void) fclose(fd);
1694 reply(530, "System not available.");
1695 exit(0);
1697 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1698 while (fgets(line, sizeof(line), fd) != NULL) {
1699 if ((cp = strchr(line, '\n')) != NULL)
1700 *cp = '\0';
1701 lreply(220, "%s", line);
1703 (void) fflush(stdout);
1704 (void) fclose(fd);
1705 /* reply(220,) must follow */
1709 reply(220, "FTP server ready.");
1711 setjmp(errcatch);
1712 for (;;)
1713 yyparse();
1714 /* NOTREACHED */
1715 return 0;