typo...
[unix-history] / usr / src / libexec / ftpd / ftpd.c
CommitLineData
f644bb55 1/*
882508af 2 * Copyright (c) 1985, 1988 Regents of the University of California.
e2071415
KB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
b8c620d6
KB
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
f644bb55
DF
16 */
17
18#ifndef lint
19char copyright[] =
882508af 20"@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\
f644bb55 21 All rights reserved.\n";
e2071415 22#endif /* not lint */
f644bb55 23
c6d3d85f 24#ifndef lint
882508af 25static char sccsid[] = "@(#)ftpd.c 5.20 (Berkeley) %G%";
e2071415 26#endif /* not lint */
c6d3d85f
SL
27
28/*
29 * FTP server.
30 */
aa159ba8 31#include <sys/param.h>
c6d3d85f
SL
32#include <sys/stat.h>
33#include <sys/ioctl.h>
34#include <sys/socket.h>
bb16805b 35#include <sys/file.h>
c8b8c458 36#include <sys/wait.h>
c6d3d85f
SL
37
38#include <netinet/in.h>
39
23eeaca6 40#include <arpa/ftp.h>
1668a723 41#include <arpa/inet.h>
07fffe50 42#include <arpa/telnet.h>
23eeaca6 43
c6d3d85f
SL
44#include <stdio.h>
45#include <signal.h>
c6d3d85f
SL
46#include <pwd.h>
47#include <setjmp.h>
48#include <netdb.h>
28e19fb7 49#include <errno.h>
07fffe50 50#include <strings.h>
df2f99b4 51#include <syslog.h>
c6d3d85f 52
0597ed04
SL
53/*
54 * File containing login names
55 * NOT to be used on this machine.
56 * Commonly used to disallow uucp.
57 */
58#define FTPUSERS "/etc/ftpusers"
59
c6d3d85f
SL
60extern int errno;
61extern char *sys_errlist[];
882508af 62extern int sys_nerr;
c6d3d85f
SL
63extern char *crypt();
64extern char version[];
65extern char *home; /* pointer to home directory for glob */
5256bbac 66extern FILE *ftpd_popen(), *fopen(), *freopen();
882508af 67extern int ftpd_pclose(), fclose();
07fffe50
GM
68extern char *getline();
69extern char cbuf[];
c6d3d85f
SL
70
71struct sockaddr_in ctrl_addr;
72struct sockaddr_in data_source;
73struct sockaddr_in data_dest;
74struct sockaddr_in his_addr;
75
c6d3d85f 76int data;
07fffe50 77jmp_buf errcatch, urgcatch;
c6d3d85f
SL
78int logged_in;
79struct passwd *pw;
80int debug;
df2f99b4 81int timeout = 900; /* timeout after 15 minutes of inactivity */
9072bd8a 82int logging;
c6d3d85f
SL
83int guest;
84int type;
85int form;
86int stru; /* avoid C keyword */
87int mode;
8643b66e 88int usedefault = 1; /* for data transfers */
882508af 89int pdata = -1; /* for passive mode */
07fffe50
GM
90int transflag;
91char tmpline[7];
5256bbac
KB
92char hostname[MAXHOSTNAMELEN];
93char remotehost[MAXHOSTNAMELEN];
c6d3d85f 94
5ac6fc46
SL
95/*
96 * Timeout intervals for retrying connections
97 * to hosts that don't accept PORT cmds. This
98 * is a kludge, but given the problems with TCP...
99 */
100#define SWAITMAX 90 /* wait at most 90 seconds */
101#define SWAITINT 5 /* interval between retries */
102
103int swaitmax = SWAITMAX;
104int swaitint = SWAITINT;
105
c6d3d85f 106int lostconn();
07fffe50 107int myoob();
c6d3d85f 108FILE *getdatasock(), *dataconn();
c6d3d85f
SL
109
110main(argc, argv)
111 int argc;
112 char *argv[];
113{
d0604b39 114 int addrlen, on = 1;
c6d3d85f
SL
115 char *cp;
116
a3dc4d0e 117 addrlen = sizeof (his_addr);
882508af 118 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
df2f99b4 119 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
c6d3d85f
SL
120 exit(1);
121 }
a3dc4d0e 122 addrlen = sizeof (ctrl_addr);
882508af 123 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
df2f99b4 124 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
a3dc4d0e
MK
125 exit(1);
126 }
127 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
c6d3d85f 128 debug = 0;
df2f99b4 129 openlog("ftpd", LOG_PID, LOG_DAEMON);
c6d3d85f
SL
130 argc--, argv++;
131 while (argc > 0 && *argv[0] == '-') {
132 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
133
5ac6fc46
SL
134 case 'v':
135 debug = 1;
136 break;
137
c6d3d85f
SL
138 case 'd':
139 debug = 1;
c6d3d85f
SL
140 break;
141
9072bd8a
SL
142 case 'l':
143 logging = 1;
144 break;
145
5ac6fc46
SL
146 case 't':
147 timeout = atoi(++cp);
148 goto nextopt;
149 break;
150
c6d3d85f 151 default:
a3dc4d0e
MK
152 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
153 *cp);
c6d3d85f
SL
154 break;
155 }
5ac6fc46 156nextopt:
c6d3d85f
SL
157 argc--, argv++;
158 }
c0785a4d 159 (void) freopen("/dev/null", "w", stderr);
df2f99b4
GM
160 (void) signal(SIGPIPE, lostconn);
161 (void) signal(SIGCHLD, SIG_IGN);
a0ff3aa3 162 if ((int)signal(SIGURG, myoob) < 0)
df2f99b4 163 syslog(LOG_ERR, "signal: %m");
a0ff3aa3 164
d0604b39 165 /* handle urgent data inline */
5256bbac 166 /* Sequent defines this, but it doesn't work */
d0604b39 167#ifdef SO_OOBINLINE
5256bbac 168 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
d0604b39 169 syslog(LOG_ERR, "setsockopt: %m");
5256bbac 170#endif
882508af
MK
171 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
172 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
2584c5be 173 dolog(&his_addr);
a3dc4d0e
MK
174 /* do telnet option negotiation here */
175 /*
176 * Set up default state
177 */
a3dc4d0e
MK
178 data = -1;
179 type = TYPE_A;
180 form = FORM_N;
181 stru = STRU_F;
182 mode = MODE_S;
07fffe50 183 tmpline[0] = '\0';
df2f99b4 184 (void) gethostname(hostname, sizeof (hostname));
5256bbac 185 reply(220, "%s FTP server (%s) ready.", hostname, version);
882508af
MK
186 (void) setjmp(errcatch);
187 for (;;)
df2f99b4 188 (void) yyparse();
c6d3d85f 189}
3cb22fa5 190
c6d3d85f
SL
191lostconn()
192{
193
407329f7 194 if (debug)
df2f99b4 195 syslog(LOG_DEBUG, "lost connection");
407329f7 196 dologout(-1);
c6d3d85f
SL
197}
198
b206b3b1
KB
199static char ttyline[20];
200
d443d52b
KB
201/*
202 * Helper function for sgetpwnam().
203 */
204char *
205sgetsave(s)
206 char *s;
207{
d443d52b
KB
208 char *malloc();
209 char *new = malloc((unsigned) strlen(s) + 1);
d443d52b
KB
210
211 if (new == NULL) {
5256bbac 212 reply(553, "Local resource failure: malloc");
d443d52b
KB
213 dologout(1);
214 }
d443d52b 215 (void) strcpy(new, s);
d443d52b
KB
216 return (new);
217}
218
219/*
220 * Save the result of a getpwnam. Used for USER command, since
221 * the data returned must not be clobbered by any other command
222 * (e.g., globbing).
223 */
224struct passwd *
225sgetpwnam(name)
226 char *name;
227{
228 static struct passwd save;
229 register struct passwd *p;
230 char *sgetsave();
231
232 if ((p = getpwnam(name)) == NULL)
233 return (p);
234 if (save.pw_name) {
235 free(save.pw_name);
236 free(save.pw_passwd);
237 free(save.pw_comment);
238 free(save.pw_gecos);
239 free(save.pw_dir);
240 free(save.pw_shell);
241 }
242 save = *p;
243 save.pw_name = sgetsave(p->pw_name);
244 save.pw_passwd = sgetsave(p->pw_passwd);
245 save.pw_comment = sgetsave(p->pw_comment);
246 save.pw_gecos = sgetsave(p->pw_gecos);
247 save.pw_dir = sgetsave(p->pw_dir);
248 save.pw_shell = sgetsave(p->pw_shell);
249 return (&save);
250}
251
882508af
MK
252int login_attempts; /* number of failed login attempts */
253int askpasswd; /* had user command, ask for passwd */
254
255/*
256 * USER command.
257 * Sets global passwd pointer pw if named account exists
258 * and is acceptable; sets askpasswd if a PASS command is
259 * expected. If logged in previously, need to reset state.
260 * If name is "ftp" or "anonymous" and ftp account exists,
261 * set guest and pw, then just return.
262 * If account doesn't exist, ask for passwd anyway.
263 * Otherwise, check user requesting login privileges.
264 * Disallow anyone who does not have a standard
265 * shell returned by getusershell() (/etc/shells).
266 * Disallow anyone mentioned in the file FTPUSERS
267 * to allow people such as root and uucp to be avoided.
268 */
269user(name)
270 char *name;
271{
272 register char *cp;
273 FILE *fd;
274 char *shell;
275 char line[BUFSIZ], *index(), *getusershell();
276
277 if (logged_in) {
278 if (guest) {
279 reply(530, "Can't change user from guest login.");
280 return;
281 }
282 end_login();
283 }
284
285 guest = 0;
286 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
287 if ((pw = sgetpwnam("ftp")) != NULL) {
288 guest = 1;
289 askpasswd = 1;
290 reply(331, "Guest login ok, send ident as password.");
291 } else
292 reply(530, "User %s unknown.", name);
293 return;
294 }
295 if (pw = sgetpwnam(name)) {
296 if ((shell = pw->pw_shell) == NULL || *shell == 0)
297 shell = "/bin/sh";
298 while ((cp = getusershell()) != NULL)
299 if (strcmp(cp, shell) == 0)
300 break;
301 endusershell();
302 if (cp == NULL) {
303 reply(530, "User %s access denied.", name);
304 pw = (struct passwd *) NULL;
305 return;
306 }
307 if ((fd = fopen(FTPUSERS, "r")) != NULL) {
308 while (fgets(line, sizeof (line), fd) != NULL) {
309 if ((cp = index(line, '\n')) != NULL)
310 *cp = '\0';
311 if (strcmp(line, name) == 0) {
312 reply(530, "User %s access denied.", name);
313 pw = (struct passwd *) NULL;
314 return;
315 }
316 }
317 }
318 (void) fclose(fd);
319 }
320 reply(331, "Password required for %s.", name);
321 askpasswd = 1;
322 /*
323 * Delay before reading passwd after first failed
324 * attempt to slow down passwd-guessing programs.
325 */
326 if (login_attempts)
327 sleep((unsigned) login_attempts);
328}
329
330/*
331 * Terminate login as previous user, if any, resetting state;
332 * used when USER command is given or login fails.
333 */
334end_login()
335{
336
337 (void) seteuid((uid_t)0);
338 if (logged_in)
339 logwtmp(ttyline, "", "");
340 pw = NULL;
341 logged_in = 0;
342 guest = 0;
343}
344
c6d3d85f
SL
345pass(passwd)
346 char *passwd;
347{
882508af 348 char *xpasswd, *salt;
c6d3d85f 349
882508af 350 if (logged_in || askpasswd == 0) {
c6d3d85f
SL
351 reply(503, "Login with USER first.");
352 return;
353 }
882508af 354 askpasswd = 0;
c6d3d85f 355 if (!guest) { /* "ftp" is only account allowed no password */
882508af
MK
356 if (pw == NULL)
357 salt = "xx";
358 else
359 salt = pw->pw_passwd;
360 xpasswd = crypt(passwd, salt);
2584c5be 361 /* The strcmp does not catch null passwords! */
882508af
MK
362 if (pw == NULL || *pw->pw_passwd == '\0' ||
363 strcmp(xpasswd, pw->pw_passwd)) {
c6d3d85f
SL
364 reply(530, "Login incorrect.");
365 pw = NULL;
882508af
MK
366 if (login_attempts++ >= 5) {
367 syslog(LOG_ERR,
368 "repeated login failures from %s",
369 remotehost);
370 exit(0);
371 }
c6d3d85f
SL
372 return;
373 }
374 }
882508af
MK
375 login_attempts = 0; /* this time successful */
376 (void) setegid((gid_t)pw->pw_gid);
377 (void) initgroups(pw->pw_name, pw->pw_gid);
c6d3d85f 378 if (chdir(pw->pw_dir)) {
7ccaf1e3 379 reply(530, "User %s: can't change directory to %s.",
c6d3d85f 380 pw->pw_name, pw->pw_dir);
aa159ba8 381 goto bad;
c6d3d85f 382 }
5c50e19b 383
82be70b7 384 /* open wtmp before chroot */
b206b3b1
KB
385 (void)sprintf(ttyline, "ftp%d", getpid());
386 logwtmp(ttyline, pw->pw_name, remotehost);
82be70b7
KB
387 logged_in = 1;
388
882508af
MK
389 if (guest && chroot(pw->pw_dir) < 0) {
390 reply(550, "Can't set guest privileges.");
391 goto bad;
392 }
393 if (seteuid((uid_t)pw->pw_uid) < 0) {
394 reply(550, "Can't set uid.");
395 goto bad;
396 }
397 if (guest)
82be70b7 398 reply(230, "Guest login ok, access restrictions apply.");
882508af 399 else
82be70b7 400 reply(230, "User %s logged in.", pw->pw_name);
aa159ba8
SL
401 home = pw->pw_dir; /* home dir for globbing */
402 return;
403bad:
882508af
MK
404 /* Forget all about it... */
405 end_login();
aa159ba8
SL
406}
407
c6d3d85f
SL
408retrieve(cmd, name)
409 char *cmd, *name;
410{
411 FILE *fin, *dout;
412 struct stat st;
07fffe50 413 int (*closefunc)(), tmp;
c6d3d85f
SL
414
415 if (cmd == 0) {
6fcbb8a1
SL
416#ifdef notdef
417 /* no remote command execution -- it's a security hole */
5ac6fc46 418 if (*name == '|')
882508af
MK
419 fin = ftpd_popen(name + 1, "r"),
420 closefunc = ftpd_pclose;
c6d3d85f 421 else
6fcbb8a1 422#endif
c6d3d85f
SL
423 fin = fopen(name, "r"), closefunc = fclose;
424 } else {
425 char line[BUFSIZ];
426
df2f99b4 427 (void) sprintf(line, cmd, name), name = line;
882508af 428 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
c6d3d85f
SL
429 }
430 if (fin == NULL) {
edcef58b 431 if (errno != 0)
882508af 432 perror_reply(550, name);
c6d3d85f
SL
433 return;
434 }
435 st.st_size = 0;
436 if (cmd == 0 &&
437 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
438 reply(550, "%s: not a plain file.", name);
439 goto done;
440 }
441 dout = dataconn(name, st.st_size, "w");
442 if (dout == NULL)
443 goto done;
07fffe50 444 if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
882508af 445 perror_reply(550, name);
07fffe50
GM
446 }
447 else if (tmp == 0) {
c6d3d85f 448 reply(226, "Transfer complete.");
07fffe50 449 }
df2f99b4 450 (void) fclose(dout);
07fffe50
GM
451 data = -1;
452 pdata = -1;
c6d3d85f
SL
453done:
454 (*closefunc)(fin);
455}
456
882508af 457store(name, mode, unique)
c6d3d85f 458 char *name, *mode;
882508af 459 int unique;
c6d3d85f
SL
460{
461 FILE *fout, *din;
882508af
MK
462 int (*closefunc)(), tmp;
463 char *gunique();
c6d3d85f 464
6fcbb8a1
SL
465#ifdef notdef
466 /* no remote command execution -- it's a security hole */
5ac6fc46 467 if (name[0] == '|')
882508af 468 fout = ftpd_popen(&name[1], "w"), closefunc = ftpd_pclose;
6fcbb8a1
SL
469 else
470#endif
471 {
aa159ba8
SL
472 struct stat st;
473
882508af
MK
474 if (unique && stat(name, &st) == 0 &&
475 (name = gunique(name)) == NULL)
476 return;
477 fout = fopen(name, mode), closefunc = fclose;
aa159ba8 478 }
c6d3d85f 479 if (fout == NULL) {
882508af 480 perror_reply(553, name);
c6d3d85f
SL
481 return;
482 }
882508af 483 din = dataconn(name, (off_t)-1, "r");
c6d3d85f
SL
484 if (din == NULL)
485 goto done;
882508af
MK
486 if ((tmp = receive_data(din, fout)) > 0)
487 perror_reply(552, name);
488 else if (tmp == 0) {
489 if (ferror(fout) > 0)
490 perror_reply(552, name);
491 else if (unique)
492 reply(226, "Transfer complete (unique file name:%s).",
493 name);
494 else
495 reply(226, "Transfer complete.");
07fffe50 496 }
df2f99b4 497 (void) fclose(din);
07fffe50
GM
498 data = -1;
499 pdata = -1;
c6d3d85f
SL
500done:
501 (*closefunc)(fout);
502}
503
504FILE *
505getdatasock(mode)
506 char *mode;
507{
3083a381 508 int s, on = 1;
c6d3d85f
SL
509
510 if (data >= 0)
511 return (fdopen(data, mode));
bb16805b 512 s = socket(AF_INET, SOCK_STREAM, 0);
58846728 513 if (s < 0)
c6d3d85f 514 return (NULL);
882508af 515 (void) seteuid((uid_t)0);
df2f99b4 516 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
58846728 517 goto bad;
edcef58b
SL
518 /* anchor socket to avoid multi-homing problems */
519 data_source.sin_family = AF_INET;
520 data_source.sin_addr = ctrl_addr.sin_addr;
882508af 521 if (bind(s, (struct sockaddr *)&data_source, sizeof (data_source)) < 0)
58846728 522 goto bad;
882508af 523 (void) seteuid((uid_t)pw->pw_uid);
c6d3d85f 524 return (fdopen(s, mode));
58846728 525bad:
882508af 526 (void) seteuid((uid_t)pw->pw_uid);
df2f99b4 527 (void) close(s);
58846728 528 return (NULL);
c6d3d85f
SL
529}
530
531FILE *
532dataconn(name, size, mode)
533 char *name;
5ac6fc46 534 off_t size;
c6d3d85f
SL
535 char *mode;
536{
537 char sizebuf[32];
538 FILE *file;
5ac6fc46 539 int retry = 0;
c6d3d85f 540
882508af 541 if (size != (off_t) -1)
df2f99b4 542 (void) sprintf (sizebuf, " (%ld bytes)", size);
c6d3d85f
SL
543 else
544 (void) strcpy(sizebuf, "");
882508af 545 if (pdata >= 0) {
07fffe50
GM
546 struct sockaddr_in from;
547 int s, fromlen = sizeof(from);
548
882508af 549 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
07fffe50
GM
550 if (s < 0) {
551 reply(425, "Can't open data connection.");
552 (void) close(pdata);
553 pdata = -1;
554 return(NULL);
555 }
556 (void) close(pdata);
557 pdata = s;
4af2732a
MK
558 reply(150, "Opening %s mode data connection for %s%s.",
559 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
07fffe50
GM
560 return(fdopen(pdata, mode));
561 }
c6d3d85f
SL
562 if (data >= 0) {
563 reply(125, "Using existing data connection for %s%s.",
564 name, sizebuf);
8643b66e 565 usedefault = 1;
c6d3d85f
SL
566 return (fdopen(data, mode));
567 }
afa6f79b 568 if (usedefault)
5c81b68e 569 data_dest = his_addr;
5c81b68e 570 usedefault = 1;
c6d3d85f
SL
571 file = getdatasock(mode);
572 if (file == NULL) {
573 reply(425, "Can't create data socket (%s,%d): %s.",
bb16805b 574 inet_ntoa(data_source.sin_addr),
c6d3d85f 575 ntohs(data_source.sin_port),
882508af 576 errno < sys_nerr ? sys_errlist[errno] : "unknown error");
c6d3d85f
SL
577 return (NULL);
578 }
579 data = fileno(file);
882508af
MK
580 while (connect(data, (struct sockaddr *)&data_dest,
581 sizeof (data_dest)) < 0) {
5ac6fc46 582 if (errno == EADDRINUSE && retry < swaitmax) {
df2f99b4 583 sleep((unsigned) swaitint);
5ac6fc46
SL
584 retry += swaitint;
585 continue;
586 }
882508af 587 perror_reply(425, "Can't build data connection");
c6d3d85f
SL
588 (void) fclose(file);
589 data = -1;
590 return (NULL);
591 }
4af2732a
MK
592 reply(150, "Opening %s mode data connection for %s%s.",
593 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
c6d3d85f
SL
594 return (file);
595}
596
597/*
598 * Tranfer the contents of "instr" to
599 * "outstr" peer using the appropriate
600 * encapulation of the date subject
601 * to Mode, Structure, and Type.
602 *
603 * NB: Form isn't handled.
604 */
605send_data(instr, outstr)
606 FILE *instr, *outstr;
607{
608 register int c;
609 int netfd, filefd, cnt;
610 char buf[BUFSIZ];
611
07fffe50
GM
612 transflag++;
613 if (setjmp(urgcatch)) {
614 transflag = 0;
615 return(-1);
616 }
c6d3d85f
SL
617 switch (type) {
618
619 case TYPE_A:
620 while ((c = getc(instr)) != EOF) {
d33c618b 621 if (c == '\n') {
07fffe50
GM
622 if (ferror (outstr)) {
623 transflag = 0;
d33c618b 624 return (1);
07fffe50 625 }
d0604b39 626 (void) putc('\r', outstr);
d33c618b 627 }
d0604b39 628 (void) putc(c, outstr);
07fffe50
GM
629 /* if (c == '\r') */
630 /* putc ('\0', outstr); */
c6d3d85f 631 }
07fffe50
GM
632 transflag = 0;
633 if (ferror (instr) || ferror (outstr)) {
d33c618b 634 return (1);
07fffe50 635 }
c6d3d85f
SL
636 return (0);
637
638 case TYPE_I:
639 case TYPE_L:
640 netfd = fileno(outstr);
641 filefd = fileno(instr);
642
07fffe50
GM
643 while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
644 if (write(netfd, buf, cnt) < 0) {
645 transflag = 0;
c6d3d85f 646 return (1);
07fffe50
GM
647 }
648 }
649 transflag = 0;
c6d3d85f
SL
650 return (cnt < 0);
651 }
7ccaf1e3 652 reply(550, "Unimplemented TYPE %d in send_data", type);
07fffe50 653 transflag = 0;
7ccaf1e3 654 return (-1);
c6d3d85f
SL
655}
656
657/*
658 * Transfer data from peer to
659 * "outstr" using the appropriate
660 * encapulation of the data subject
661 * to Mode, Structure, and Type.
662 *
663 * N.B.: Form isn't handled.
664 */
665receive_data(instr, outstr)
666 FILE *instr, *outstr;
667{
668 register int c;
d33c618b 669 int cnt;
c6d3d85f
SL
670 char buf[BUFSIZ];
671
672
07fffe50
GM
673 transflag++;
674 if (setjmp(urgcatch)) {
675 transflag = 0;
676 return(-1);
677 }
c6d3d85f
SL
678 switch (type) {
679
680 case TYPE_I:
681 case TYPE_L:
07fffe50
GM
682 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
683 if (write(fileno(outstr), buf, cnt) < 0) {
684 transflag = 0;
c6d3d85f 685 return (1);
07fffe50
GM
686 }
687 }
688 transflag = 0;
c6d3d85f
SL
689 return (cnt < 0);
690
691 case TYPE_E:
7ccaf1e3 692 reply(553, "TYPE E not implemented.");
07fffe50 693 transflag = 0;
7ccaf1e3 694 return (-1);
c6d3d85f
SL
695
696 case TYPE_A:
c6d3d85f 697 while ((c = getc(instr)) != EOF) {
d0604b39 698 while (c == '\r') {
07fffe50
GM
699 if (ferror (outstr)) {
700 transflag = 0;
d33c618b 701 return (1);
07fffe50 702 }
d33c618b 703 if ((c = getc(instr)) != '\n')
d0604b39 704 (void) putc ('\r', outstr);
07fffe50
GM
705 /* if (c == '\0') */
706 /* continue; */
c6d3d85f 707 }
d0604b39 708 (void) putc (c, outstr);
c6d3d85f 709 }
07fffe50 710 transflag = 0;
d33c618b
SL
711 if (ferror (instr) || ferror (outstr))
712 return (1);
c6d3d85f
SL
713 return (0);
714 }
07fffe50 715 transflag = 0;
c6d3d85f
SL
716 fatal("Unknown type in receive_data.");
717 /*NOTREACHED*/
718}
719
720fatal(s)
721 char *s;
722{
723 reply(451, "Error in server: %s\n", s);
724 reply(221, "Closing connection due to server error.");
bb16805b 725 dologout(0);
c6d3d85f
SL
726}
727
882508af 728/* VARARGS2 */
13daac5e 729reply(n, s, p0, p1, p2, p3, p4)
c6d3d85f
SL
730 int n;
731 char *s;
732{
733
734 printf("%d ", n);
13daac5e 735 printf(s, p0, p1, p2, p3, p4);
c6d3d85f 736 printf("\r\n");
df2f99b4 737 (void) fflush(stdout);
c6d3d85f 738 if (debug) {
df2f99b4 739 syslog(LOG_DEBUG, "<--- %d ", n);
13daac5e 740 syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
c6d3d85f
SL
741 }
742}
743
882508af 744/* VARARGS2 */
13daac5e 745lreply(n, s, p0, p1, p2, p3, p4)
c6d3d85f
SL
746 int n;
747 char *s;
748{
749 printf("%d-", n);
13daac5e 750 printf(s, p0, p1, p2, p3, p4);
c6d3d85f 751 printf("\r\n");
df2f99b4 752 (void) fflush(stdout);
c6d3d85f 753 if (debug) {
df2f99b4 754 syslog(LOG_DEBUG, "<--- %d- ", n);
13daac5e 755 syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
c6d3d85f
SL
756 }
757}
758
c6d3d85f
SL
759ack(s)
760 char *s;
761{
7ccaf1e3 762 reply(250, "%s command successful.", s);
c6d3d85f
SL
763}
764
765nack(s)
766 char *s;
767{
768 reply(502, "%s command not implemented.", s);
769}
770
882508af 771/* ARGSUSED */
df2f99b4
GM
772yyerror(s)
773 char *s;
c6d3d85f 774{
07fffe50
GM
775 char *cp;
776
777 cp = index(cbuf,'\n');
778 *cp = '\0';
779 reply(500, "'%s': command not understood.",cbuf);
c6d3d85f
SL
780}
781
782delete(name)
783 char *name;
784{
785 struct stat st;
786
787 if (stat(name, &st) < 0) {
882508af 788 perror_reply(550, name);
c6d3d85f
SL
789 return;
790 }
791 if ((st.st_mode&S_IFMT) == S_IFDIR) {
792 if (rmdir(name) < 0) {
882508af 793 perror_reply(550, name);
c6d3d85f
SL
794 return;
795 }
796 goto done;
797 }
798 if (unlink(name) < 0) {
882508af 799 perror_reply(550, name);
c6d3d85f
SL
800 return;
801 }
802done:
803 ack("DELE");
804}
805
806cwd(path)
807 char *path;
808{
809
810 if (chdir(path) < 0) {
882508af 811 perror_reply(550, path);
c6d3d85f
SL
812 return;
813 }
814 ack("CWD");
815}
816
aa159ba8 817makedir(name)
c6d3d85f
SL
818 char *name;
819{
5256bbac 820 if (mkdir(name, 0777) < 0)
882508af 821 perror_reply(550, name);
5256bbac
KB
822 else
823 reply(257, "MKD command successful.");
c6d3d85f
SL
824}
825
aa159ba8 826removedir(name)
c6d3d85f
SL
827 char *name;
828{
829
830 if (rmdir(name) < 0) {
882508af 831 perror_reply(550, name);
c6d3d85f
SL
832 return;
833 }
7ccaf1e3 834 ack("RMD");
c6d3d85f
SL
835}
836
aa159ba8 837pwd()
c6d3d85f 838{
aa159ba8 839 char path[MAXPATHLEN + 1];
882508af 840 extern char *getwd();
c6d3d85f 841
882508af 842 if (getwd(path) == (char *)NULL) {
7ccaf1e3 843 reply(550, "%s.", path);
c6d3d85f
SL
844 return;
845 }
7ccaf1e3 846 reply(257, "\"%s\" is current directory.", path);
c6d3d85f
SL
847}
848
849char *
850renamefrom(name)
851 char *name;
852{
853 struct stat st;
854
855 if (stat(name, &st) < 0) {
882508af 856 perror_reply(550, name);
c6d3d85f
SL
857 return ((char *)0);
858 }
aa159ba8 859 reply(350, "File exists, ready for destination name");
c6d3d85f
SL
860 return (name);
861}
862
863renamecmd(from, to)
864 char *from, *to;
865{
866
867 if (rename(from, to) < 0) {
882508af 868 perror_reply(550, "rename");
c6d3d85f
SL
869 return;
870 }
871 ack("RNTO");
872}
873
c6d3d85f
SL
874dolog(sin)
875 struct sockaddr_in *sin;
876{
882508af 877 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
c6d3d85f 878 sizeof (struct in_addr), AF_INET);
882508af 879 time_t t, time();
df2f99b4 880 extern char *ctime();
c6d3d85f 881
882508af 882 if (hp)
df2f99b4 883 (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
882508af 884 else
df2f99b4 885 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
bb16805b
SL
886 sizeof (remotehost));
887 if (!logging)
888 return;
df2f99b4 889 t = time((time_t *) 0);
882508af
MK
890 syslog(LOG_INFO, "connection from %s at %s",
891 remotehost, ctime(&t));
c6d3d85f 892}
0597ed04 893
bb16805b
SL
894/*
895 * Record logout in wtmp file
896 * and exit with supplied status.
897 */
898dologout(status)
899 int status;
900{
632ff766 901 if (logged_in) {
882508af 902 (void) seteuid((uid_t)0);
b206b3b1 903 logwtmp(ttyline, "", "");
bb16805b 904 }
930b478b
SL
905 /* beware of flushing buffers after a SIGPIPE */
906 _exit(status);
bb16805b
SL
907}
908
07fffe50
GM
909myoob()
910{
d0604b39 911 char *cp;
07fffe50 912
d0604b39 913 /* only process if transfer occurring */
882508af 914 if (!transflag)
07fffe50 915 return;
07fffe50 916 cp = tmpline;
d0604b39 917 if (getline(cp, 7, stdin) == NULL) {
882508af 918 reply(221, "You could at least say goodbye.");
d0604b39
GM
919 dologout(0);
920 }
07fffe50 921 upper(cp);
c50979a2 922 if (strcmp(cp, "ABOR\r\n"))
07fffe50 923 return;
07fffe50
GM
924 tmpline[0] = '\0';
925 reply(426,"Transfer aborted. Data connection closed.");
926 reply(226,"Abort successful");
927 longjmp(urgcatch, 1);
928}
929
7ccaf1e3
KM
930/*
931 * Note: The 530 reply codes could be 4xx codes, except nothing is
932 * given in the state tables except 421 which implies an exit. (RFC959)
933 */
07fffe50
GM
934passive()
935{
936 int len;
937 struct sockaddr_in tmp;
938 register char *p, *a;
939
940 pdata = socket(AF_INET, SOCK_STREAM, 0);
941 if (pdata < 0) {
7ccaf1e3 942 reply(530, "Can't open passive connection");
07fffe50
GM
943 return;
944 }
945 tmp = ctrl_addr;
946 tmp.sin_port = 0;
882508af 947 (void) seteuid((uid_t)0);
df2f99b4 948 if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
882508af 949 (void) seteuid((uid_t)pw->pw_uid);
07fffe50
GM
950 (void) close(pdata);
951 pdata = -1;
7ccaf1e3 952 reply(530, "Can't open passive connection");
07fffe50
GM
953 return;
954 }
882508af 955 (void) seteuid((uid_t)pw->pw_uid);
07fffe50 956 len = sizeof(tmp);
882508af 957 if (getsockname(pdata, (struct sockaddr *) &tmp, &len) < 0) {
07fffe50
GM
958 (void) close(pdata);
959 pdata = -1;
7ccaf1e3 960 reply(530, "Can't open passive connection");
07fffe50
GM
961 return;
962 }
963 if (listen(pdata, 1) < 0) {
964 (void) close(pdata);
965 pdata = -1;
7ccaf1e3 966 reply(530, "Can't open passive connection");
07fffe50
GM
967 return;
968 }
969 a = (char *) &tmp.sin_addr;
970 p = (char *) &tmp.sin_port;
971
972#define UC(b) (((int) b) & 0xff)
973
974 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
975 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
976}
977
882508af
MK
978/*
979 * Generate unique name for file with basename "local".
980 * The file named "local" is already known to exist.
981 * Generates failure reply on error.
982 */
07fffe50
GM
983char *
984gunique(local)
985 char *local;
986{
987 static char new[MAXPATHLEN];
882508af 988 struct stat st;
07fffe50
GM
989 char *cp = rindex(local, '/');
990 int d, count=0;
07fffe50 991
882508af 992 if (cp)
07fffe50 993 *cp = '\0';
882508af
MK
994 d = stat(cp ? local : ".", &st);
995 if (cp)
07fffe50 996 *cp = '/';
07fffe50 997 if (d < 0) {
882508af 998 perror_reply(553, local);
07fffe50
GM
999 return((char *) 0);
1000 }
1001 (void) strcpy(new, local);
1002 cp = new + strlen(new);
1003 *cp++ = '.';
882508af
MK
1004 for (count = 1; count < 100; count++) {
1005 (void) sprintf(cp, "%d", count);
1006 if (stat(new, &st) < 0)
1007 return(new);
07fffe50 1008 }
882508af
MK
1009 reply(452, "Unique file name cannot be created.");
1010 return((char *) 0);
1011}
1012
1013/*
1014 * Format and send reply containing system error number.
1015 */
1016perror_reply(code, string)
1017 int code;
1018 char *string;
1019{
1020
1021 if (errno < sys_nerr)
1022 reply(code, "%s: %s.", string, sys_errlist[errno]);
1023 else
1024 reply(code, "%s: unknown error %d.", string, errno);
07fffe50 1025}