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