BSD 4_3_Net_2 development
[unix-history] / usr / src / contrib / isode / ftp-ftam / ftpd.c
CommitLineData
53102063
C
1/* ftpd.c - FTAM/FTP gateway */
2
3#ifndef lint
4static char *rcsid = "$Header: /f/osi/ftp-ftam/RCS/ftpd.c,v 7.1 91/02/22 09:24:24 mrose Interim $";
5#endif
6
7/*
8 * $Header: /f/osi/ftp-ftam/RCS/ftpd.c,v 7.1 91/02/22 09:24:24 mrose Interim $
9 *
10 * Author: John A. Scott <Scott@GATEWAY.MITRE.ORG>
11 * The MITRE Corporation
12 * Washington C3I Division
13 * 7525 Colshire Drive
14 * Mclean, Virginia 22102
15 * +1-703-883-5915
16 *
17 * $Log: ftpd.c,v $
18 * Revision 7.1 91/02/22 09:24:24 mrose
19 * Interim 6.8
20 *
21 * Revision 7.0 89/11/23 21:55:21 mrose
22 * Release 6.0
23 *
24 */
25
26/*
27 * NOTICE
28 *
29 * The MITRE Corporation (hereafter MITRE) makes this software available
30 * on an "as is" basis. No guarantees, either explicit or implied, are
31 * given as to performance or suitability.
32 *
33 */
34
35/*
36 * Shamelessly taken from UCB
37 */
38
39/*
40 * FTP server.
41 */
42#include "config.h"
43#include <sys/param.h>
44#include <sys/stat.h>
45#include <sys/ioctl.h>
46#include <sys/socket.h>
47#include <sys/file.h>
48
49#include <netinet/in.h>
50
51#include <arpa/ftp.h>
52#include <arpa/inet.h>
53
54#include <stdio.h>
55#include <signal.h>
56#include <pwd.h>
57#include <setjmp.h>
58#include <netdb.h>
59#include <errno.h>
60#include "general.h"
61#include "manifest.h"
62#include "logger.h"
63extern LLog _ftam_log, *ftam_log;
64#include <varargs.h>
65
66char *ctime();
67time_t time();
68void adios (), advise ();
69
70/*
71 * File containing login names
72 * NOT to be used on this machine.
73 * Commonly used to disallow uucp.
74 */
75/*
76 * This may be rework to provide bridge access control. JAS
77 */
78#define FTPUSERS "/usr/etc/ftpusers"
79
80extern int errno;
81extern char *sys_errlist[];
82extern char ftam_error[];
83extern char version[];
84
85struct sockaddr_in ctrl_addr;
86struct sockaddr_in data_source;
87struct sockaddr_in data_dest;
88struct sockaddr_in his_addr;
89
90int data;
91jmp_buf errcatch;
92int logged_in;
93int debug = 0;
94int watch = 1;
95int timeout;
96int logging = 0;
97int type;
98int form;
99int stru; /* avoid C keyword */
100int mode;
101int usedefault = 1; /* for data transfers */
102char hostname[32];
103char remotehost[32];
104char *osi_host = NULL;
105char *ftp_user;
106char *ftp_account;
107char *ftp_passwd;
108int verbose = 0;
109#define NVEC 100
110char *vec[NVEC];
111
112/*
113 * Timeout intervals for retrying connections
114 * to hosts that don't accept PORT cmds. This
115 * is a kludge, but given the problems with TCP...
116 */
117#define SWAITMAX 90 /* wait at most 90 seconds */
118#define SWAITINT 5 /* interval between retries */
119
120int swaitmax = SWAITMAX;
121int swaitint = SWAITINT;
122
123SFD lostconn();
124
125main(argc, argv)
126 int argc;
127 char *argv[];
128{
129 int addrlen;
130 char *ptr, *index();
131 struct servent *sp;
132
133 isodetailor (ptr = argv[0], 0);
134 argc--, argv++;
135 if (verbose = isatty (fileno (stderr)))
136 ll_dbinit (ftam_log, ptr);
137 else {
138 ftam_log -> ll_stat &= ~LLOGCLS;
139 ll_hdinit (ftam_log, ptr);
140 }
141
142 advise (NULLCP, "starting");
143
144 addrlen = sizeof his_addr;
145 if (getpeername (0, (struct sockaddr *) &his_addr, &addrlen) == NOTOK)
146 adios ("failed", "getpeername");
147 sp = getservbyname("ftp", "tcp");
148 if (sp == 0) {
149 advise(NULLCP, "ftp/tcp: unknown service");
150abort();
151 exit(1);
152 }
153 ctrl_addr.sin_port = sp->s_port;
154 data_source.sin_port = htons(ntohs((u_short) sp->s_port) - 1);
155 (void)signal(SIGPIPE, lostconn);
156 (void)signal(SIGCHLD, SIG_IGN);
157 (void)dup2(0, 1);
158 /* do telnet option negotiation here */
159 /*
160 * Set up default state
161 */
162 rcinit(); /* FTAM state initialize */
163 logged_in = 0;
164 data = -1;
165 type = TYPE_A;
166 form = FORM_N;
167 stru = STRU_F;
168 mode = MODE_S;
169 addrlen = sizeof ctrl_addr;
170 if (getsockname(0, (struct sockaddr *) &ctrl_addr, &addrlen) == NOTOK)
171 adios ("failed", "getsockname");
172 (void)gethostname(hostname, sizeof (hostname));
173 ptr = index(hostname,'.'); /* strip off domain name */
174 if (ptr) *ptr = '\0';
175 reply(220, "%s FTP/FTAM gateway (%s) ready.",
176 hostname, version);
177 for (;;) {
178 (void)setjmp(errcatch);
179 (void)yyparse();
180 }
181}
182
183SFD
184lostconn()
185{
186
187 advise (NULLCP,"lost connection");
188 dologout(-1);
189}
190
191char *
192savestr(s)
193 char *s;
194{
195 char *malloc();
196 char *new = malloc((unsigned) (strlen(s) + 1));
197
198 if (new != NULL)
199 (void)strcpy(new, s);
200 return (new);
201}
202
203retrieve(name)
204 char *name;
205{
206 int result;
207
208 /* FTAM file retrieval block function. Return values:
209 * OK -- file transfered without error
210 * NOTOK -- file transfer error
211 * DONE -- Problem opening TCP connection for transfer
212 * Error response made by dataconn routine.
213 */
214 vec[0] = "f_get";
215 vec[1] = name;
216 vec[2] = NULL;
217
218 if ((result = f_get(vec)) == NOTOK){
219 reply(550, "%s: %s.", name, ftam_error);
220 } else if (result == OK)
221 reply(226, "Transfer complete.");
222
223 data = -1;
224 return;
225}
226
227ftp_store(name, modeX)
228 char *name, *modeX;
229{
230 int result;
231/*
232 * f_put is FTAM file storage block function. First arguement
233 * controls file overwrite or append selection.
234 * OK -- file transfered without error
235 * NOTOK -- file transfer error
236 * DONE -- Problem opening TCP connection for transfer
237 * Error response made by dataconn routine.
238 */
239
240 vec[0] = strcmp(modeX,"a") ? "put" : "append";
241 vec[1] = name;
242 vec[2] = NULL;
243 if ((result = f_put(vec)) == NOTOK)
244 reply(550, "%s: %s.", name, ftam_error);
245 else if (result == OK)
246 reply(226, "Transfer complete.");
247 data = -1;
248}
249
250int
251getdatasock()
252{
253/* UCB data socket creation routine */
254 int s;
255#ifdef BSD43
256 int on = 1;
257#endif
258
259 if (data >= 0)
260 return (data);
261 s = socket(AF_INET, SOCK_STREAM, 0);
262 if (s < 0)
263 return (NOTOK);
264#ifndef BSD43
265 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) 0, 0) < 0)
266#else
267 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) < 0)
268#endif
269 goto bad;
270 /* anchor socket to avoid multi-homing problems */
271 data_source.sin_family = AF_INET;
272 data_source.sin_addr = ctrl_addr.sin_addr;
273 if (bind(s, (struct sockaddr *) &data_source, sizeof (data_source)) < 0)
274 goto bad;
275 return (s);
276bad:
277 (void)close(s);
278 return (NOTOK);
279}
280
281int
282dataconn(name)
283 char *name;
284{
285/* UCB data connection routine */
286 int retry = 0;
287
288 if (data >= 0) {
289 reply(125, "Using existing data connection for %s.",
290 name);
291 usedefault = 1;
292 return (data);
293 }
294 if (usedefault)
295 data_dest = his_addr;
296 usedefault = 1;
297 data = getdatasock();
298 if (data == NOTOK){
299 reply(425, "Can't create data socket (%s,%d): %s.",
300 inet_ntoa(data_source.sin_addr),
301 ntohs(data_source.sin_port),
302 sys_errlist[errno]);
303 return (NOTOK);
304 }
305 reply(150, "Opening data connection for %s (%s,%d).",
306 name, inet_ntoa(data_dest.sin_addr),
307 ntohs(data_dest.sin_port));
308 while (connect(data, (struct sockaddr *)&data_dest, sizeof (data_dest)) < 0) {
309 if (errno == EADDRINUSE && retry < swaitmax) {
310 sleep((unsigned) swaitint);
311 retry += swaitint;
312 continue;
313 }
314 reply(425, "Can't build data connection: %s.",
315 sys_errlist[errno]);
316 (void) close(data);
317 data = -1;
318 return (NOTOK);
319 }
320 return (data);
321}
322
323fatal(s)
324 char *s;
325{
326 reply(451, "Error in server: %s\n", s);
327 /* reply(221, "Closing connection due to server error.");*/
328 dologout(0);
329}
330
331#ifndef lint
332reply(va_alist)
333va_dcl
334{
335 int n;
336 va_list ap;
337
338 va_start (ap);
339
340 n = va_arg (ap, int);
341
342 _reply (n, ' ', ap);
343
344 va_end (ap);
345}
346
347lreply(va_alist)
348va_dcl
349{
350 int n;
351 va_list ap;
352
353 va_start (ap);
354
355 n = va_arg (ap, int);
356
357 _reply (n, '-', ap);
358
359 va_end (ap);
360}
361
362static _reply (n, c, ap)
363int n;
364char c;
365va_list ap;
366{
367 char buffer[BUFSIZ];
368
369 _asprintf (buffer, NULLCP, ap);
370
371 printf ("%d%c%s\r\n", n, c, buffer);
372 (void)fflush (stdout);
373
374 if (verbose)
375 advise (NULLCP,"<--- %d%c%s", n, c, buffer);
376}
377#else
378/* VARARGS2 */
379
380reply(n,fmt)
381int n;
382char *fmt;
383{
384 reply(n,fmt);
385}
386/* VARARGS2 */
387
388lreply(n,fmt)
389int n;
390char *fmt;
391{
392 lreply(n,fmt);
393}
394#endif
395
396replystr(s)
397 char *s;
398{
399 printf("%s\r\n", s);
400 (void)fflush(stdout);
401 if (verbose)
402 advise(NULLCP,"<--- %s", s);
403}
404
405ack(s)
406 char *s;
407{
408 reply(200, "%s command okay.", s);
409}
410
411nack(s)
412 char *s;
413{
414 reply(502, "%s command not implemented.", s);
415}
416
417/*ARGSUSED*/
418yyerror(s)
419char *s;
420{
421 reply(500, "Command not understood.");
422}
423
424ftp_delete(name)
425 char *name;
426{
427/* f_rm is the general purpose FTAM file/directory deletion routine.
428 * Change information is formatted in ftam_error.
429 */
430 vec[0] = "f_rm";
431 vec[1] = name;
432 vec[2] = NULL;
433
434 if (f_rm(vec) == NOTOK){
435 reply(550, "%s: %s.", name, ftam_error);
436 return;
437 }
438 ack("DELE");
439}
440
441makedir(name)
442 char *name;
443{
444
445/* f_mkdir is the FTAM directory creation routine */
446
447 vec[0] = "f_mkdir";
448 vec[1] = name;
449 vec[2] = NULL;
450
451 if (f_mkdir(vec) == NOTOK){
452 reply(550, "%s: %s.", name, ftam_error);
453 return;
454 }
455 ack("MKDIR");
456}
457
458removedir(name)
459 char *name;
460{
461
462/* f_rm is the general purpose FTAM file/directory deletion routine.
463 */
464 vec[0] = "f_rm";
465 vec[1] = name;
466 vec[2] = NULL;
467
468 if (f_rm(vec) == NOTOK){
469 reply(550, "%s: %s.", name, ftam_error);
470 return;
471 }
472 ack("RMDIR");
473}
474
475char *
476renamefrom(name)
477 char *name;
478{
479 reply(350, "Ready for destination name");
480 return (name);
481}
482
483renamecmd(from, to)
484 char *from, *to;
485{
486
487/* f_mv is FTAM block function to select and change attributes
488 * (i.e. file name)
489 */
490 vec[0] ="f_mv";
491 vec[1] = from;
492 vec[2] = to;
493 vec[3] = NULL;
494
495 if (f_mv(vec) == NOTOK){
496 reply(550, "rename: %s.", ftam_error);
497 return;
498 }
499 ack("RNTO");
500}
501
502dolog(sin)
503 struct sockaddr_in *sin;
504{
505#ifdef notanymore
506 struct hostent *hp = gethostbyaddr((char*)&sin->sin_addr,
507 sizeof (struct in_addr), AF_INET);
508#endif
509 time_t t;
510
511#ifdef notanymore
512 if (hp) {
513 (void)strncpy(remotehost, hp->h_name, sizeof (remotehost));
514 endhostent();
515 } else
516#endif
517 (void)strncpy(remotehost, inet_ntoa(sin->sin_addr),
518 sizeof (remotehost));
519 t = time((time_t*)0);
520 if (!logging)
521 return;
522 advise(NULLCP,"connection from %s at %s", remotehost, ctime(&t));
523}
524directory(how,name)
525char *how, *name;
526{
527
528 int result;
529/* f_ls does a directory contents transfer. The first arguement
530 * determines whether a name list (NLST) or long list (LIST) is returned.
531 * Results:
532 * OK -- list transfered without error
533 * NOTOK -- list transfer error
534 * DONE -- Problem opening TCP connection for transfer
535 * Error response made by dataconn routine.
536 */
537
538 vec[0] = strcmp(how,"NLST") ? "dir" : "ls";
539 vec[1] = name;
540 vec[2] = NULL;
541
542 if ((result = f_ls(vec)) == OK)
543 reply(226, "Transfer complete.");
544 else if (result == NOTOK)
545 reply(500, ftam_error);
546 data = -1;
547
548}
549/*
550 * Execute FTAM login if all necessary arguements present
551 */
552dologin()
553{
554
555 if (!ftp_user) {
556 reply(500,"Send USER command first");
557 return(0);
558 }
559 if (!ftp_passwd) {
560 reply(330,"Send PASS command");
561 return(0);
562 }
563 if (!osi_host){
564 /* Success is returned since most user FTP response scanners
565 * are not prepared for a continue at this point. The osi
566 * host may be specified by encoding it with the user name
567 * (i.e. user@osihost) or using the SITE command (SITE osihost).
568 * Gateway users are expected to know if the remote site
569 * requires account charging information. The bridge makes
570 * ACCT optional.
571 */
572 reply(200,"Specify OSI filestore with SITE command");
573 return(0);
574 }
575 vec[0] = "f_open";
576 vec[1] = osi_host;
577 vec[2] = ftp_user;
578 vec[3] = (ftp_account) ? "" : ftp_account;
579 vec[4] = ftp_passwd;
580
581 advise (NULLCP,
582 "attempting association with OSI filestore \"%s\" as initiator \"%s\"",
583 osi_host, ftp_user);
584
585 /* f_open performs the FTAM initialization (including login) */
586 if (f_open(vec) == NOTOK){
587 reply(500,"Login failed");
588 return(0);
589 }
590
591 reply(200,"Associated with OSI filestore %s", osi_host);
592 return(1);
593
594}
595
596/*
597 * exit with supplied status.
598 */
599dologout(status)
600 int status;
601{
602
603 vec[0] = "f_close";
604 vec[1] = NULL;
605 /* f_close performs the logout sequence and receives charging
606 * information
607 */
608 (void) f_close(vec);
609 if (status>=0)
610 reply(221,"Logged off. %s",ftam_error);
611 /* beware of flushing buffers after a SIGPIPE */
612 _exit(status);
613}
614
615
616/*
617 * Check user requesting login priviledges.
618 * Disallow anyone mentioned in the file FTPUSERS
619 * to allow people such as uucp to be avoided.
620 */
621checkuser(name)
622 register char *name;
623{
624 char line[BUFSIZ], *index();
625 FILE *fd, *fopen();
626 int found = 0;
627
628 fd = fopen(FTPUSERS, "r");
629 if (fd == NULL)
630 return (1);
631 while (fgets(line, sizeof (line), fd) != NULL) {
632 register char *cp = index(line, '\n');
633
634 if (cp)
635 *cp = '\0';
636 if (strcmp(line, name) == 0) {
637 found++;
638 break;
639 }
640 }
641 (void)fclose(fd);
642 return (!found);
643}
644