examples/rconfig: Fix typos.
[dragonfly.git] / contrib / opie / opieftpd.c
blobe687a8d55d977206ade0a22768febf0949efbdf6
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: head/contrib/opie/opieftpd.c 92914 2002-03-21 23:42:52Z markm $
54 * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
55 * All rights reserved.
57 * Redistribution and use in source and binary forms, with or without
58 * modification, are permitted provided that the following conditions
59 * are met:
60 * 1. Redistributions of source code must retain the above copyright
61 * notice, this list of conditions and the following disclaimer.
62 * 2. Redistributions in binary form must reproduce the above copyright
63 * notice, this list of conditions and the following disclaimer in the
64 * documentation and/or other materials provided with the distribution.
65 * 3. All advertising materials mentioning features or use of this software
66 * must display the following acknowledgement:
67 * This product includes software developed by the University of
68 * California, Berkeley and its contributors.
69 * 4. Neither the name of the University nor the names of its contributors
70 * may be used to endorse or promote products derived from this software
71 * without specific prior written permission.
73 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83 * SUCH DAMAGE.
86 #include "opie_cfg.h"
88 #if HAVE_ANSISTDARG
89 #include <stdarg.h>
90 #endif /* HAVE_ANSISTDARG */
93 * FTP server.
96 #if HAVE_SYS_PARAM_H
97 #include <sys/param.h>
98 #endif /* HAVE_SYS_PARAM_H */
99 #include <sys/stat.h>
100 /* #include <sys/ioctl.h> */
101 #include <sys/socket.h>
102 #include <sys/wait.h>
103 #ifdef SYS_FCNTL_H
104 #include <sys/fcntl.h>
105 #else
106 #include <fcntl.h>
107 #endif /* SYS_FCNTL_H */
108 #include <sys/types.h>
110 #include <netinet/in.h>
111 #include <netinet/in_systm.h>
112 #include <netinet/ip.h>
114 #define FTP_NAMES
115 #include <arpa/ftp.h>
116 #include <arpa/inet.h>
117 #include <arpa/telnet.h>
119 #include <signal.h>
120 #include <fcntl.h>
121 #if HAVE_TIME_H
122 #include <time.h>
123 #endif /* HAVE_TIME_H */
124 #if HAVE_PWD_H
125 #include <pwd.h>
126 #endif /* HAVE_PWD_H */
127 #include <setjmp.h>
128 #include <netdb.h>
129 #include <errno.h>
130 #include <syslog.h>
131 #if HAVE_UNISTD_H
132 #include <unistd.h>
133 #endif /* HAVE_UNISTD_H */
134 #include <stdio.h>
135 #include <ctype.h>
136 #include <stdlib.h>
137 #include <string.h>
138 #include <grp.h>
140 #include "opie.h"
142 #if HAVE_SHADOW_H
143 #include <shadow.h>
144 #endif /* HAVE_SHADOW_H */
146 #if HAVE_CRYPT_H
147 #include <crypt.h>
148 #endif /* HAVE_CRYPT_H */
150 #if HAVE_SYS_UTSNAME_H
151 #include <sys/utsname.h>
152 #endif /* HAVE_SYS_UTSNAME_H */
154 #ifdef _AIX
155 #include <sys/id.h>
156 #include <sys/priv.h>
157 #endif /* _AIX */
159 #ifdef IP_TOS
160 #ifndef IPTOS_THROUGHPUT
161 #undef IP_TOS
162 #endif /* !IPTOS_THROUGHPUT */
163 #ifndef IPTOS_LOWDELAY
164 #undef IP_TOS
165 #endif /* !IPTOS_LOWDELAY */
166 #endif /* IP_TOS */
168 extern int errno;
169 extern char *home; /* pointer to home directory for glob */
170 extern FILE *ftpd_popen __P((char *, char *));
171 extern int ftpd_pclose __P((FILE *));
172 extern char cbuf[];
173 extern off_t restart_point;
175 static struct sockaddr_in ctrl_addr;
176 static struct sockaddr_in data_source;
177 struct sockaddr_in data_dest;
178 struct sockaddr_in his_addr;
179 static struct sockaddr_in pasv_addr;
181 static int data;
182 jmp_buf errcatch;
183 static jmp_buf urgcatch;
184 int logged_in;
185 struct passwd *pw;
186 int debug;
187 int timeout = 900; /* timeout after 15 minutes of inactivity */
188 int maxtimeout = 7200; /* don't allow idle time to be set beyond 2 hours */
190 #if DOANONYMOUS
191 static int guest;
192 #endif /* DOANONYMOUS */
193 int type;
194 int form;
195 static int stru; /* avoid C keyword */
196 static int mode;
197 int usedefault = 1; /* for data transfers */
198 int pdata = -1; /* for passive mode */
199 static int transflag;
200 static off_t file_size;
201 static off_t byte_count;
203 #if (!defined(CMASK) || CMASK == 0)
204 #undef CMASK
205 #define CMASK 077
206 #endif
208 static int defumask = CMASK; /* default umask value */
209 char tmpline[7];
210 char remotehost[MAXHOSTNAMELEN];
213 * Timeout intervals for retrying connections
214 * to hosts that don't accept PORT cmds. This
215 * is a kludge, but given the problems with TCP...
217 #define SWAITMAX 90 /* wait at most 90 seconds */
218 #define SWAITINT 5 /* interval between retries */
220 static int swaitmax = SWAITMAX;
221 static int swaitint = SWAITINT;
223 #if DOTITLE
224 static char **Argv = NULL; /* pointer to argument vector */
225 static char *LastArgv = NULL; /* end of argv */
226 static char proctitle[BUFSIZ]; /* initial part of title */
227 #endif /* DOTITLE */
229 static int af_pwok = 0, pwok = 0;
230 static struct opie opiestate;
232 VOIDRET perror_reply __P((int, char *));
233 VOIDRET dologout __P((int));
234 char *getline __P((char *, int, FILE *));
235 VOIDRET upper __P((char *));
237 static VOIDRET lostconn __P((int));
238 static VOIDRET myoob __P((int));
239 static FILE *getdatasock __P((char *));
240 static FILE *dataconn __P((char *, off_t, char *));
241 static int checkuser __P((char *));
242 static VOIDRET end_login __P((void));
243 static VOIDRET send_data __P((FILE *, FILE *, off_t));
244 static int receive_data __P((FILE *, FILE *));
245 static char *gunique __P((char *));
246 static char *sgetsave __P((char *));
248 int opielogwtmp __P((char *, char *, char *, char *));
250 int fclose __P((FILE *));
252 #ifdef HAVE_ANSISTDARG
253 VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
255 va_list ap;
256 char buffer[1024];
258 va_start(ap, fmt);
259 vsprintf(buffer, fmt, ap);
260 va_end(ap);
262 printf("%d %s\r\n", n, buffer);
263 fflush(stdout);
264 if (debug)
265 syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
267 #else /* HAVE_ANSISTDARG */
268 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)
270 printf("%d ", n);
271 printf(fmt, p0, p1, p2, p3, p4, p5);
272 printf("\r\n");
273 fflush(stdout);
274 if (debug) {
275 syslog(LOG_DEBUG, "<--- %d ", n);
276 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
279 #endif /* HAVE_ANSISTDARG */
281 #ifdef HAVE_ANSISTDARG
282 VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...)
284 va_list ap;
285 char buffer[1024];
287 va_start(ap, fmt);
288 vsprintf(buffer, fmt, ap);
289 va_end(ap);
291 printf("%d- %s\r\n", n, buffer);
292 fflush(stdout);
293 if (debug)
294 syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
296 #else /* HAVE_ANSISTDARG */
297 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)
299 printf("%d- ", n);
300 printf(fmt, p0, p1, p2, p3, p4, p5);
301 printf("\r\n");
302 fflush(stdout);
303 if (debug) {
304 syslog(LOG_DEBUG, "<--- %d- ", n);
305 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
308 #endif /* HAVE_ANSISTDARG */
310 VOIDRET enable_signalling FUNCTION_NOARGS
312 signal(SIGPIPE, lostconn);
313 if ((int)signal(SIGURG, myoob) < 0)
314 syslog(LOG_ERR, "signal: %m");
317 VOIDRET disable_signalling FUNCTION_NOARGS
319 signal(SIGPIPE, SIG_IGN);
320 if ((int)signal(SIGURG, SIG_IGN) < 0)
321 syslog(LOG_ERR, "signal: %m");
324 static VOIDRET lostconn FUNCTION((input), int input)
326 if (debug)
327 syslog(LOG_DEBUG, "lost connection");
328 dologout(-1);
331 static char ttyline[20];
334 * Helper function for sgetpwnam().
336 static char *sgetsave FUNCTION((s), char *s)
338 char *new = malloc((unsigned) strlen(s) + 1);
340 if (new == NULL) {
341 perror_reply(421, "Local resource failure: malloc");
342 dologout(1);
343 /* NOTREACHED */
345 strcpy(new, s);
346 return (new);
350 * Save the result of a getpwnam. Used for USER command, since
351 * the data returned must not be clobbered by any other command
352 * (e.g., globbing).
354 static struct passwd *sgetpwnam FUNCTION((name), char *name)
356 static struct passwd save;
357 register struct passwd *p;
359 #if HAVE_SHADOW
360 struct spwd *spwd;
361 #endif /* HAVE_SHADOW */
363 if ((p = getpwnam(name)) == NULL)
364 return (p);
366 #if HAVE_SHADOW
367 if ((spwd = getspnam(name)) == NULL)
368 return NULL;
370 endspent();
372 p->pw_passwd = spwd->sp_pwdp;
373 #endif /* HAVE_SHADOW */
375 endpwent();
377 if (save.pw_name) {
378 free(save.pw_name);
379 free(save.pw_passwd);
380 free(save.pw_gecos);
381 free(save.pw_dir);
382 free(save.pw_shell);
384 save = *p;
385 save.pw_name = sgetsave(p->pw_name);
386 save.pw_passwd = sgetsave(p->pw_passwd);
387 save.pw_gecos = sgetsave(p->pw_gecos);
388 save.pw_dir = sgetsave(p->pw_dir);
389 save.pw_shell = sgetsave(p->pw_shell);
390 return (&save);
393 int login_attempts; /* number of failed login attempts */
394 int askpasswd; /* had user command, ask for passwd */
397 * USER command.
398 * Sets global passwd pointer pw if named account exists and is acceptable;
399 * sets askpasswd if a PASS command is expected. If logged in previously,
400 * need to reset state. If name is "ftp" or "anonymous", the name is not in
401 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
403 * requesting login privileges. Disallow anyone who does not have a standard
404 * shell as returned by getusershell(). Disallow anyone mentioned in the file
405 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
407 int user FUNCTION((name), char *name)
409 register char *cp;
410 char *shell;
412 if (logged_in) {
413 #if DOANONYMOUS
414 if (guest) {
415 reply(530, "Can't change user from guest login.");
416 return -1;
418 #endif /* DOANONMOUS */
419 end_login();
421 askpasswd = 1;
422 #if DOANONYMOUS
423 guest = 0;
424 if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
425 if (!checkuser("ftp") && !checkuser("anonymous"))
426 if ((pw = sgetpwnam("ftp")) != NULL) {
427 guest = 1;
428 askpasswd = 1;
429 reply(331, "Guest login ok, send your e-mail address as your password.");
430 syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
431 return 0;
433 #endif /* DOANONYMOUS */
434 if (pw = sgetpwnam(name)) {
435 if ((shell = pw->pw_shell) == NULL || *shell == 0)
436 shell = _PATH_BSHELL;
437 while ((cp = getusershell()) != NULL)
438 if (!strcmp(cp, shell))
439 break;
440 endusershell();
441 if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
442 #if DEBUG
443 if (!cp)
444 syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
445 if (checkuser(name))
446 syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
447 if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
448 syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
449 #endif /* DEBUG */
450 pw = (struct passwd *) NULL;
451 askpasswd = -1;
455 char prompt[OPIE_CHALLENGE_MAX + 1];
457 opiechallenge(&opiestate, name, prompt);
459 if (askpasswd == -1) {
460 syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
461 pwok = 0;
462 } else
463 pwok = af_pwok && opiealways(pw->pw_dir);
465 #if NEW_PROMPTS
466 reply(331, "Response to %s %s for %s.", prompt,
467 #else /* NEW_PROMPTS */
468 reply(331, "OTP response %s %s for %s.", prompt,
469 #endif /* NEW_PROMPTS */
470 pwok ? "requested" : "required", name);
472 /* Delay before reading passwd after first failed attempt to slow down
473 passwd-guessing programs. */
474 if (login_attempts)
475 sleep((unsigned) login_attempts);
477 return 0;
481 * Check if a user is in the file _PATH_FTPUSERS
483 static int checkuser FUNCTION((name), char *name)
485 register FILE *fd;
486 register char *p;
487 char line[BUFSIZ];
489 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
490 while (fgets(line, sizeof(line), fd) != NULL)
491 if ((p = strchr(line, '\n')) != NULL) {
492 *p = '\0';
493 if (line[0] == '#')
494 continue;
495 if (!strcmp(line, name)) {
496 fclose(fd);
497 return (1);
500 fclose(fd);
502 return (0);
506 * Terminate login as previous user, if any, resetting state;
507 * used when USER command is given or login fails.
509 static VOIDRET end_login FUNCTION_NOARGS
511 disable_signalling();
512 if (seteuid((uid_t) 0))
513 syslog(LOG_ERR, "Can't set euid");
514 if (logged_in)
515 opielogwtmp(ttyline, "", "", "ftp");
516 pw = NULL;
517 logged_in = 0;
518 #if DOANONYMOUS
519 guest = 0;
520 #endif /* DOANONYMOUS */
521 enable_signalling();
524 VOIDRET pass FUNCTION((passwd), char *passwd)
526 int legit = askpasswd + 1, i;
527 char *cryptpw;
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 cryptpw = crypt(passwd, pw->pw_passwd);
541 i = (cryptpw == NULL || strcmp(cryptpw, pw->pw_passwd) != 0);
543 if (!legit || i) {
544 reply(530, "Login incorrect.");
545 pw = NULL;
546 if (login_attempts++ >= 5) {
547 syslog(LOG_WARNING,
548 "Repeated login failures for user %s from %s",
549 pw->pw_name, remotehost);
550 exit(0);
552 return;
554 #if DOANONYMOUS
555 } else
556 if ((passwd[0] <= ' ') || checkuser(passwd)) {
557 reply(530, "No identity, no service.");
558 syslog(LOG_DEBUG, "Bogus address: %s", passwd);
559 exit(0);
561 #endif /* DOANONYMOUS */
562 login_attempts = 0; /* this time successful */
563 if (setegid((gid_t) pw->pw_gid) < 0) {
564 reply(550, "Can't set gid.");
565 syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
566 return;
568 initgroups(pw->pw_name, pw->pw_gid);
570 /* open wtmp before chroot */
571 sprintf(ttyline, "ftp%d", getpid());
572 opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp");
573 logged_in = 1;
575 #if DOANONYMOUS
576 if (guest) {
577 /* We MUST do a chdir() after the chroot. Otherwise the old current
578 directory will be accessible as "." outside the new root! */
579 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
580 reply(550, "Can't set guest privileges.");
581 goto bad;
583 } else
584 #endif /* DOANONYMOUS */
585 if (chdir(pw->pw_dir) < 0) {
586 if (chdir("/") < 0) {
587 reply(530, "User %s: can't change directory to %s.",
588 pw->pw_name, pw->pw_dir);
589 goto bad;
590 } else
591 lreply(230, "No directory! Logging in with home=/");
593 /* This patch was contributed by an OPIE user. We don't know what it
594 does, exactly. It may or may not work. */
595 #ifdef _AIX
597 priv_t priv;
598 priv.pv_priv[0] = 0;
599 priv.pv_priv[1] = 0;
600 setgroups(NULL, NULL);
601 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
602 &priv, sizeof(priv_t)) < 0 ||
603 setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
604 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
605 seteuid((uid_t)pw->pw_uid) < 0) {
606 reply(550, "Can't set uid (_AIX3).");
607 goto bad;
610 #else /* _AIX */
611 if (seteuid((uid_t) pw->pw_uid) < 0) {
612 reply(550, "Can't set uid.");
613 goto bad;
615 #endif /* _AIX */
617 * Display a login message, if it exists.
618 * N.B. reply(230,) must follow the message.
621 FILE *fd;
623 if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
624 char *cp, line[128];
626 while (fgets(line, sizeof(line), fd) != NULL) {
627 if ((cp = strchr(line, '\n')) != NULL)
628 *cp = '\0';
629 lreply(230, "%s", line);
631 (void) fflush(stdout);
632 (void) fclose(fd);
635 #if DOANONYMOUS
636 if (guest) {
637 reply(230, "Guest login ok, access restrictions apply.");
638 #if DOTITLE
639 setproctitle("%s: anonymous/%.*s", remotehost,
640 sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"),
641 passwd);
642 #endif /* DOTITLE */
643 syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
644 remotehost, passwd);
645 } else
646 #endif /* DOANONYMOUS */
648 reply(230, "User %s logged in.", pw->pw_name);
650 #if DOTITLE
651 setproctitle("%s: %s", remotehost, pw->pw_name);
652 #endif /* DOTITLE */
653 syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
655 home = pw->pw_dir; /* home dir for globbing */
656 umask(defumask);
657 return;
659 bad:
660 /* Forget all about it... */
661 end_login();
664 VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
666 FILE *fin, *dout;
667 struct stat st;
668 int (*closefunc) ();
670 if (cmd == 0) {
671 fin = fopen(name, "r"), closefunc = fclose;
672 st.st_size = 0;
673 } else {
674 char line[BUFSIZ];
676 snprintf(line, sizeof(line), cmd, name);
677 name = line;
678 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
679 st.st_size = -1;
680 #if HAVE_ST_BLKSIZE
681 st.st_blksize = BUFSIZ;
682 #endif /* HAVE_ST_BLKSIZE */
684 if (fin == NULL) {
685 if (errno != 0)
686 perror_reply(550, name);
687 return;
689 if (cmd == 0 &&
690 (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
691 reply(550, "%s: not a plain file.", name);
692 goto done;
694 if (restart_point) {
695 if (type == TYPE_A) {
696 register int i, n, c;
698 n = restart_point;
699 i = 0;
700 while (i++ < n) {
701 if ((c = getc(fin)) == EOF) {
702 perror_reply(550, name);
703 goto done;
705 if (c == '\n')
706 i++;
708 } else
709 if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
710 perror_reply(550, name);
711 goto done;
714 dout = dataconn(name, st.st_size, "w");
715 if (dout == NULL)
716 goto done;
717 #if HAVE_ST_BLKSIZE
718 send_data(fin, dout, st.st_blksize);
719 #else /* HAVE_ST_BLKSIZE */
720 send_data(fin, dout, BUFSIZ);
721 #endif /* HAVE_ST_BLKSIZE */
722 fclose(dout);
723 data = -1;
724 pdata = -1;
725 done:
726 (*closefunc) (fin);
729 VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
731 FILE *fout, *din;
732 struct stat st;
733 int (*closefunc) ();
735 if (unique && stat(name, &st) == 0 &&
736 (name = gunique(name)) == NULL)
737 return;
739 if (restart_point)
740 mode = "r+w";
741 fout = fopen(name, mode);
742 closefunc = fclose;
743 if (fout == NULL) {
744 perror_reply(553, name);
745 return;
747 if (restart_point) {
748 if (type == TYPE_A) {
749 register int i, n, c;
751 n = restart_point;
752 i = 0;
753 while (i++ < n) {
754 if ((c = getc(fout)) == EOF) {
755 perror_reply(550, name);
756 goto done;
758 if (c == '\n')
759 i++;
761 /* We must do this seek to "current" position because we are changing
762 from reading to writing. */
763 if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
764 perror_reply(550, name);
765 goto done;
767 } else
768 if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
769 perror_reply(550, name);
770 goto done;
773 din = dataconn(name, (off_t) - 1, "r");
774 if (din == NULL)
775 goto done;
776 if (receive_data(din, fout) == 0) {
777 if (unique)
778 reply(226, "Transfer complete (unique file name:%s).",
779 name);
780 else
781 reply(226, "Transfer complete.");
783 fclose(din);
784 data = -1;
785 pdata = -1;
786 done:
787 (*closefunc) (fout);
790 static FILE *getdatasock FUNCTION((mode), char *mode)
792 int s, on = 1, tries;
794 if (data >= 0)
795 return (fdopen(data, mode));
796 disable_signalling();
797 if (seteuid((uid_t) 0))
798 syslog(LOG_ERR, "Can't set euid");
799 s = socket(AF_INET, SOCK_STREAM, 0);
800 if (s < 0)
801 goto bad;
802 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
803 (char *) &on, sizeof(on)) < 0)
804 goto bad;
805 /* anchor socket to avoid multi-homing problems */
806 data_source.sin_family = AF_INET;
807 data_source.sin_addr = ctrl_addr.sin_addr;
808 for (tries = 1;; tries++) {
809 if (bind(s, (struct sockaddr *) & data_source,
810 sizeof(data_source)) >= 0)
811 break;
812 if (errno != EADDRINUSE || tries > 10)
813 goto bad;
814 sleep(tries);
816 if (seteuid((uid_t) pw->pw_uid))
817 syslog(LOG_ERR, "Can't set euid");
818 enable_signalling();
819 #ifdef IP_TOS
820 on = IPTOS_THROUGHPUT;
821 if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
822 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
823 #endif
824 return (fdopen(s, mode));
825 bad:
827 int t = errno;
829 if (seteuid((uid_t) pw->pw_uid))
830 syslog(LOG_ERR, "Can't set euid");
831 enable_signalling();
832 close(s);
834 errno = t;
836 return (NULL);
839 static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
841 char sizebuf[32];
842 FILE *file;
843 int retry = 0;
844 #ifdef IP_TOS
845 int tos;
846 #endif /* IP_TOS */
848 file_size = size;
849 byte_count = 0;
850 if (size != (off_t) - 1)
851 snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
852 else
853 strcpy(sizebuf, "");
854 if (pdata >= 0) {
855 struct sockaddr_in from;
856 int s, fromlen = sizeof(from);
858 s = accept(pdata, (struct sockaddr *) & from, &fromlen);
859 if (s < 0) {
860 reply(425, "Can't open data connection.");
861 close(pdata);
862 pdata = -1;
863 return (NULL);
865 close(pdata);
866 pdata = s;
867 #ifdef IP_TOS
868 tos = IPTOS_LOWDELAY;
869 setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
870 sizeof(int));
872 #endif
873 reply(150, "Opening %s mode data connection for %s%s.",
874 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
875 return (fdopen(pdata, mode));
877 if (data >= 0) {
878 reply(125, "Using existing data connection for %s%s.",
879 name, sizebuf);
880 usedefault = 1;
881 return (fdopen(data, mode));
883 if (usedefault)
884 data_dest = his_addr;
885 usedefault = 1;
886 file = getdatasock(mode);
887 if (file == NULL) {
888 reply(425, "Can't create data socket (%s,%d): %s.",
889 inet_ntoa(data_source.sin_addr),
890 ntohs(data_source.sin_port), strerror(errno));
891 return (NULL);
893 data = fileno(file);
894 while (connect(data, (struct sockaddr *) & data_dest,
895 sizeof(data_dest)) < 0) {
896 if (errno == EADDRINUSE && retry < swaitmax) {
897 sleep((unsigned) swaitint);
898 retry += swaitint;
899 continue;
901 perror_reply(425, "Can't build data connection");
902 fclose(file);
903 data = -1;
904 return (NULL);
906 reply(150, "Opening %s mode data connection for %s%s.",
907 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
908 return (file);
912 * Tranfer the contents of "instr" to
913 * "outstr" peer using the appropriate
914 * encapsulation of the data subject
915 * to Mode, Structure, and Type.
917 * NB: Form isn't handled.
919 static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
921 register int c, cnt;
922 register char *buf;
923 int netfd, filefd;
925 transflag++;
926 if (setjmp(urgcatch)) {
927 transflag = 0;
928 return;
930 switch (type) {
932 case TYPE_A:
933 while ((c = getc(instr)) != EOF) {
934 byte_count++;
935 if (c == '\n') {
936 if (ferror(outstr))
937 goto data_err;
938 putc('\r', outstr);
940 putc(c, outstr);
942 fflush(outstr);
943 transflag = 0;
944 if (ferror(instr))
945 goto file_err;
946 if (ferror(outstr))
947 goto data_err;
948 reply(226, "Transfer complete.");
949 return;
951 case TYPE_I:
952 case TYPE_L:
953 if ((buf = malloc((u_int) blksize)) == NULL) {
954 transflag = 0;
955 perror_reply(451, "Local resource failure: malloc");
956 return;
958 netfd = fileno(outstr);
959 filefd = fileno(instr);
960 while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
961 write(netfd, buf, cnt) == cnt)
962 byte_count += cnt;
963 transflag = 0;
964 free(buf);
965 if (cnt != 0) {
966 if (cnt < 0)
967 goto file_err;
968 goto data_err;
970 reply(226, "Transfer complete.");
971 return;
972 default:
973 transflag = 0;
974 reply(550, "Unimplemented TYPE %d in send_data", type);
975 return;
978 data_err:
979 transflag = 0;
980 perror_reply(426, "Data connection");
981 return;
983 file_err:
984 transflag = 0;
985 perror_reply(551, "Error on input file");
989 * Transfer data from peer to
990 * "outstr" using the appropriate
991 * encapulation of the data subject
992 * to Mode, Structure, and Type.
994 * N.B.: Form isn't handled.
996 static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
998 register int c;
999 int cnt, bare_lfs = 0;
1000 char buf[BUFSIZ];
1002 transflag++;
1003 if (setjmp(urgcatch)) {
1004 transflag = 0;
1005 return (-1);
1007 switch (type) {
1009 case TYPE_I:
1010 case TYPE_L:
1011 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
1012 if (write(fileno(outstr), buf, cnt) != cnt)
1013 goto file_err;
1014 byte_count += cnt;
1016 if (cnt < 0)
1017 goto data_err;
1018 transflag = 0;
1019 return (0);
1021 case TYPE_E:
1022 reply(553, "TYPE E not implemented.");
1023 transflag = 0;
1024 return (-1);
1026 case TYPE_A:
1027 while ((c = getc(instr)) != EOF) {
1028 byte_count++;
1029 if (c == '\n')
1030 bare_lfs++;
1031 while (c == '\r') {
1032 if (ferror(outstr))
1033 goto data_err;
1034 if ((c = getc(instr)) != '\n') {
1035 putc('\r', outstr);
1036 if (c == '\0' || c == EOF)
1037 goto contin2;
1040 putc(c, outstr);
1041 contin2:;
1043 fflush(outstr);
1044 if (ferror(instr))
1045 goto data_err;
1046 if (ferror(outstr))
1047 goto file_err;
1048 transflag = 0;
1049 if (bare_lfs) {
1050 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1051 printf(" File may not have transferred correctly.\r\n");
1053 return (0);
1054 default:
1055 reply(550, "Unimplemented TYPE %d in receive_data", type);
1056 transflag = 0;
1057 return (-1);
1060 data_err:
1061 transflag = 0;
1062 perror_reply(426, "Data Connection");
1063 return (-1);
1065 file_err:
1066 transflag = 0;
1067 perror_reply(452, "Error writing file");
1068 return (-1);
1071 VOIDRET statfilecmd FUNCTION((filename), char *filename)
1073 char line[BUFSIZ];
1074 FILE *fin;
1075 int c;
1077 #if HAVE_LS_G_FLAG
1078 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1079 #else /* HAVE_LS_G_FLAG */
1080 snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
1081 #endif /* HAVE_LS_G_FLAG */
1082 fin = ftpd_popen(line, "r");
1083 lreply(211, "status of %s:", filename);
1084 while ((c = getc(fin)) != EOF) {
1085 if (c == '\n') {
1086 if (ferror(stdout)) {
1087 perror_reply(421, "control connection");
1088 ftpd_pclose(fin);
1089 dologout(1);
1090 /* NOTREACHED */
1092 if (ferror(fin)) {
1093 perror_reply(551, filename);
1094 ftpd_pclose(fin);
1095 return;
1097 putc('\r', stdout);
1099 putc(c, stdout);
1101 ftpd_pclose(fin);
1102 reply(211, "End of Status");
1105 VOIDRET statcmd FUNCTION_NOARGS
1107 /* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1108 struct sockaddr_in *sin;
1109 u_char *a, *p;
1111 lreply(211, "FTP server status:");
1112 printf(" \r\n");
1113 printf(" Connected to %s", remotehost);
1114 if (!isdigit(remotehost[0]))
1115 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1116 printf("\r\n");
1117 if (logged_in) {
1118 #if DOANONYMOUS
1119 if (guest)
1120 printf(" Logged in anonymously\r\n");
1121 else
1122 #endif /* DOANONYMOUS */
1123 printf(" Logged in as %s\r\n", pw->pw_name);
1124 } else
1125 if (askpasswd)
1126 printf(" Waiting for password\r\n");
1127 else
1128 printf(" Waiting for user name\r\n");
1129 if (data != -1)
1130 printf(" Data connection open\r\n");
1131 else
1132 if (pdata != -1) {
1133 printf(" in Passive mode");
1134 sin = &pasv_addr;
1135 goto printaddr;
1136 } else
1137 if (usedefault == 0) {
1138 printf(" PORT");
1139 sin = &data_dest;
1140 printaddr:
1141 a = (u_char *) & sin->sin_addr;
1142 p = (u_char *) & sin->sin_port;
1143 #define UC(b) (((int) b) & 0xff)
1144 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1145 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1146 #undef UC
1147 } else
1148 printf(" No data connection\r\n");
1149 reply(211, "End of status");
1152 VOIDRET opiefatal FUNCTION((s), char *s)
1154 reply(451, "Error in server: %s\n", s);
1155 reply(221, "Closing connection due to server error.");
1156 dologout(0);
1157 /* NOTREACHED */
1160 static VOIDRET ack FUNCTION((s), char *s)
1162 reply(250, "%s command successful.", s);
1165 VOIDRET nack FUNCTION((s), char *s)
1167 reply(502, "%s command not implemented.", s);
1170 VOIDRET yyerror FUNCTION((s), char *s)
1172 char *cp;
1174 if (cp = strchr(cbuf, '\n'))
1175 *cp = '\0';
1176 reply(500, "'%s': command not understood.", cbuf);
1179 VOIDRET delete FUNCTION((name), char *name)
1181 struct stat st;
1183 if (stat(name, &st) < 0) {
1184 perror_reply(550, name);
1185 return;
1187 if ((st.st_mode & S_IFMT) == S_IFDIR) {
1188 if (rmdir(name) < 0) {
1189 perror_reply(550, name);
1190 return;
1192 goto done;
1194 if (unlink(name) < 0) {
1195 perror_reply(550, name);
1196 return;
1198 done:
1199 ack("DELE");
1202 VOIDRET cwd FUNCTION((path), char *path)
1204 if (chdir(path) < 0)
1205 perror_reply(550, path);
1206 else
1207 ack("CWD");
1210 VOIDRET makedir FUNCTION((name), char *name)
1212 if (mkdir(name, 0777) < 0)
1213 perror_reply(550, name);
1214 else
1215 reply(257, "MKD command successful.");
1218 VOIDRET removedir FUNCTION((name), char *name)
1220 if (rmdir(name) < 0)
1221 perror_reply(550, name);
1222 else
1223 ack("RMD");
1226 VOIDRET pwd FUNCTION_NOARGS
1228 char path[MAXPATHLEN + 1];
1230 if (getcwd(path, MAXPATHLEN) == (char *) NULL)
1231 reply(550, "%s.", path);
1232 else
1233 reply(257, "\"%s\" is current directory.", path);
1236 char *renamefrom FUNCTION((name), char *name)
1238 struct stat st;
1240 if (stat(name, &st) < 0) {
1241 perror_reply(550, name);
1242 return ((char *) 0);
1244 reply(350, "File exists, ready for destination name");
1245 return (name);
1248 VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
1250 if (rename(from, to) < 0)
1251 perror_reply(550, "rename");
1252 else
1253 ack("RNTO");
1256 static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
1258 struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
1259 sizeof(struct in_addr), AF_INET);
1260 time_t t, time();
1262 if (hp)
1263 opiestrncpy(remotehost, hp->h_name, sizeof(remotehost));
1264 else
1265 opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1266 #if DOTITLE
1267 setproctitle("%s: connected", remotehost);
1268 #endif /* DOTITLE */
1270 t = time((time_t *) 0);
1271 syslog(LOG_INFO, "connection from %s at %s",
1272 remotehost, ctime(&t));
1276 * Record logout in wtmp file
1277 * and exit with supplied status.
1279 VOIDRET dologout FUNCTION((status), int status)
1281 disable_signalling();
1282 if (logged_in) {
1283 if (seteuid((uid_t) 0))
1284 syslog(LOG_ERR, "Can't set euid");
1285 opielogwtmp(ttyline, "", "", "ftp");
1287 /* beware of flushing buffers after a SIGPIPE */
1288 _exit(status);
1291 static VOIDRET myoob FUNCTION((input), int input)
1293 char *cp;
1295 /* only process if transfer occurring */
1296 if (!transflag)
1297 return;
1298 cp = tmpline;
1299 if (getline(cp, 7, stdin) == NULL) {
1300 reply(221, "You could at least say goodbye.");
1301 dologout(0);
1303 upper(cp);
1304 if (strcmp(cp, "ABOR\r\n") == 0) {
1305 tmpline[0] = '\0';
1306 reply(426, "Transfer aborted. Data connection closed.");
1307 reply(226, "Abort successful");
1308 longjmp(urgcatch, 1);
1310 if (strcmp(cp, "STAT\r\n") == 0) {
1311 if (file_size != (off_t) - 1)
1312 reply(213, "Status: %lu of %lu bytes transferred",
1313 byte_count, file_size);
1314 else
1315 reply(213, "Status: %lu bytes transferred", byte_count);
1320 * Note: a response of 425 is not mentioned as a possible response to
1321 * the PASV command in RFC959. However, it has been blessed as
1322 * a legitimate response by Jon Postel in a telephone conversation
1323 * with Rick Adams on 25 Jan 89.
1325 VOIDRET passive FUNCTION_NOARGS
1327 int len;
1328 register char *p, *a;
1330 pdata = socket(AF_INET, SOCK_STREAM, 0);
1331 if (pdata < 0) {
1332 perror_reply(425, "Can't open passive connection");
1333 return;
1335 pasv_addr = ctrl_addr;
1336 pasv_addr.sin_port = 0;
1337 if (seteuid((uid_t) 0))
1338 syslog(LOG_ERR, "Can't set euid");
1339 if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1340 seteuid((uid_t) pw->pw_uid);
1341 goto pasv_error;
1343 if (seteuid((uid_t) pw->pw_uid))
1344 syslog(LOG_ERR, "Can't set euid");
1345 len = sizeof(pasv_addr);
1346 if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1347 goto pasv_error;
1348 if (listen(pdata, 1) < 0)
1349 goto pasv_error;
1350 a = (char *) &pasv_addr.sin_addr;
1351 p = (char *) &pasv_addr.sin_port;
1353 #define UC(b) (((int) b) & 0xff)
1355 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1356 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1357 return;
1359 pasv_error:
1360 close(pdata);
1361 pdata = -1;
1362 perror_reply(425, "Can't open passive connection");
1363 return;
1367 * Generate unique name for file with basename "local".
1368 * The file named "local" is already known to exist.
1369 * Generates failure reply on error.
1371 static char *gunique FUNCTION((local), char *local)
1373 static char new[MAXPATHLEN+1];
1374 struct stat st;
1375 char *cp = strrchr(local, '/');
1376 int count = 0;
1378 if (cp)
1379 *cp = '\0';
1380 if (stat(cp ? local : ".", &st) < 0) {
1381 perror_reply(553, cp ? local : ".");
1382 return ((char *) 0);
1384 if (cp)
1385 *cp = '/';
1386 strcpy(new, local);
1387 cp = new + strlen(new);
1388 *cp++ = '.';
1389 for (count = 1; count < 100; count++) {
1390 snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1391 if (stat(new, &st) < 0)
1392 return (new);
1394 reply(452, "Unique file name cannot be created.");
1395 return ((char *) 0);
1399 * Format and send reply containing system error number.
1401 VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1403 reply(code, "%s: %s.", string, strerror(errno));
1406 static char *onefile[] =
1412 VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1414 struct stat st;
1415 DIR *dirp = NULL;
1416 struct dirent *dir;
1417 FILE *dout = NULL;
1418 register char **dirlist, *dirname;
1419 int simple = 0;
1421 if (strpbrk(whichfiles, "~{[*?") != NULL) {
1422 extern char **ftpglob(), *globerr;
1424 globerr = NULL;
1425 dirlist = ftpglob(whichfiles);
1426 if (globerr != NULL) {
1427 reply(550, globerr);
1428 return;
1429 } else
1430 if (dirlist == NULL) {
1431 errno = ENOENT;
1432 perror_reply(550, whichfiles);
1433 return;
1435 } else {
1436 onefile[0] = whichfiles;
1437 dirlist = onefile;
1438 simple = 1;
1441 if (setjmp(urgcatch)) {
1442 transflag = 0;
1443 return;
1445 while (dirname = *dirlist++) {
1446 if (stat(dirname, &st) < 0) {
1447 /* If user typed "ls -l", etc, and the client used NLST, do what the
1448 user meant. */
1449 if (dirname[0] == '-' && *dirlist == NULL &&
1450 transflag == 0) {
1451 retrieve("/bin/ls %s", dirname);
1452 return;
1454 perror_reply(550, whichfiles);
1455 if (dout != NULL) {
1456 fclose(dout);
1457 transflag = 0;
1458 data = -1;
1459 pdata = -1;
1461 return;
1463 if ((st.st_mode & S_IFMT) == S_IFREG) {
1464 if (dout == NULL) {
1465 dout = dataconn("file list", (off_t) - 1, "w");
1466 if (dout == NULL)
1467 return;
1468 transflag++;
1470 fprintf(dout, "%s%s\n", dirname,
1471 type == TYPE_A ? "\r" : "");
1472 byte_count += strlen(dirname) + 1;
1473 continue;
1474 } else
1475 if ((st.st_mode & S_IFMT) != S_IFDIR)
1476 continue;
1478 if ((dirp = opendir(dirname)) == NULL)
1479 continue;
1481 while ((dir = readdir(dirp)) != NULL) {
1482 char nbuf[MAXPATHLEN+1];
1484 if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1485 continue;
1486 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1487 (strlen(dir->d_name) == 2))
1488 continue;
1490 snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1492 /* We have to do a stat to insure it's not a directory or special file. */
1493 if (simple || (stat(nbuf, &st) == 0 &&
1494 (st.st_mode & S_IFMT) == S_IFREG)) {
1495 if (dout == NULL) {
1496 dout = dataconn("file list", (off_t) - 1, "w");
1497 if (dout == NULL)
1498 return;
1499 transflag++;
1501 if (nbuf[0] == '.' && nbuf[1] == '/')
1502 fprintf(dout, "%s%s\n", &nbuf[2],
1503 type == TYPE_A ? "\r" : "");
1504 else
1505 fprintf(dout, "%s%s\n", nbuf,
1506 type == TYPE_A ? "\r" : "");
1507 byte_count += strlen(nbuf) + 1;
1510 closedir(dirp);
1513 if (dout == NULL)
1514 reply(550, "No files found.");
1515 else
1516 if (ferror(dout) != 0)
1517 perror_reply(550, "Data connection");
1518 else
1519 reply(226, "Transfer complete.");
1521 transflag = 0;
1522 if (dout != NULL)
1523 fclose(dout);
1524 data = -1;
1525 pdata = -1;
1528 #if DOTITLE
1530 * clobber argv so ps will show what we're doing.
1531 * (stolen from sendmail)
1532 * warning, since this is usually started from inetd.conf, it
1533 * often doesn't have much of an environment or arglist to overwrite.
1535 VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1537 register char *p, *bp, ch;
1538 register int i;
1539 char buf[BUFSIZ];
1541 snprintf(buf, sizeof(buf), fmt, a, b, c);
1543 /* make ps print our process name */
1544 p = Argv[0];
1545 *p++ = '-';
1547 i = strlen(buf);
1548 if (i > LastArgv - p - 2) {
1549 i = LastArgv - p - 2;
1550 buf[i] = '\0';
1552 bp = buf;
1553 while (ch = *bp++)
1554 if (ch != '\n' && ch != '\r')
1555 *p++ = ch;
1556 while (p < LastArgv)
1557 *p++ = ' ';
1559 #endif /* DOTITLE */
1561 VOIDRET catchexit FUNCTION_NOARGS
1563 closelog();
1566 int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1568 int addrlen, on = 1;
1569 char *cp;
1570 #ifdef IP_TOS
1571 int tos;
1572 #endif /* IP_TOS */
1575 int i;
1577 for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1578 close(i);
1581 /* LOG_NDELAY sets up the logging connection immediately, necessary for
1582 anonymous ftp's that chroot and can't do it later. */
1583 openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1584 atexit(catchexit);
1585 addrlen = sizeof(his_addr);
1586 if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1587 syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1588 exit(1);
1590 addrlen = sizeof(ctrl_addr);
1591 if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1592 syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1593 exit(1);
1595 #ifdef IP_TOS
1596 tos = IPTOS_LOWDELAY;
1597 if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1598 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1599 #endif
1600 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1601 debug = 0;
1602 #if DOTITLE
1603 /* Save start and extent of argv for setproctitle. */
1604 Argv = argv;
1605 while (*envp)
1606 envp++;
1607 LastArgv = envp[-1] + strlen(envp[-1]);
1608 #endif /* DOTITLE */
1610 argc--, argv++;
1611 while (argc > 0 && *argv[0] == '-') {
1612 for (cp = &argv[0][1]; *cp; cp++)
1613 switch (*cp) {
1615 case 'v':
1616 debug = 1;
1617 break;
1619 case 'd':
1620 debug = 1;
1621 break;
1623 case 'l':
1624 break;
1626 case 't':
1627 timeout = atoi(++cp);
1628 if (maxtimeout < timeout)
1629 maxtimeout = timeout;
1630 goto nextopt;
1632 case 'T':
1633 maxtimeout = atoi(++cp);
1634 if (timeout > maxtimeout)
1635 timeout = maxtimeout;
1636 goto nextopt;
1638 case 'u':
1640 int val = 0;
1642 while (*++cp && *cp >= '0' && *cp <= '9')
1643 val = val * 8 + *cp - '0';
1644 if (*cp)
1645 fprintf(stderr, "ftpd: Bad value for -u\n");
1646 else
1647 defumask = val;
1648 goto nextopt;
1651 default:
1652 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1653 *cp);
1654 break;
1656 nextopt:
1657 argc--, argv++;
1659 freopen(_PATH_DEVNULL, "w", stderr);
1660 signal(SIGCHLD, SIG_IGN);
1661 enable_signalling();
1663 /* Try to handle urgent data inline */
1664 #ifdef SO_OOBINLINE
1665 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1666 syslog(LOG_ERR, "setsockopt: %m");
1667 #endif
1669 #ifdef F_SETOWN
1670 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1671 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1672 #endif
1673 dolog(&his_addr);
1674 /* Set up default state */
1675 data = -1;
1676 type = TYPE_A;
1677 form = FORM_N;
1678 stru = STRU_F;
1679 mode = MODE_S;
1680 tmpline[0] = '\0';
1681 af_pwok = opieaccessfile(remotehost);
1684 FILE *fd;
1685 char line[128];
1687 /* If logins are disabled, print out the message. */
1688 if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1689 while (fgets(line, sizeof(line), fd) != NULL) {
1690 if ((cp = strchr(line, '\n')) != NULL)
1691 *cp = '\0';
1692 lreply(530, "%s", line);
1694 (void) fflush(stdout);
1695 (void) fclose(fd);
1696 reply(530, "System not available.");
1697 exit(0);
1699 if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1700 while (fgets(line, sizeof(line), fd) != NULL) {
1701 if ((cp = strchr(line, '\n')) != NULL)
1702 *cp = '\0';
1703 lreply(220, "%s", line);
1705 (void) fflush(stdout);
1706 (void) fclose(fd);
1707 /* reply(220,) must follow */
1711 reply(220, "FTP server ready.");
1713 setjmp(errcatch);
1714 for (;;)
1715 yyparse();
1716 /* NOTREACHED */
1717 return 0;