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