|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)ftpd.c 4.28 (Berkeley) 9/22/83";
3: #endif
4:
5: #define CHOOSE_PORT
6: #define DEBUG
7: /*
8: * FTP server.
9: */
10: #include <sys/param.h>
11: #include <sys/stat.h>
12: #include <sys/file.h>
13: #include <wait.h>
14:
15: #include <sys/inet/in.h>
16: #include <sys/inet/tcp_user.h>
17:
18: #include <stdio.h>
19: #include <signal.h>
20: #include <pwd.h>
21: #include <setjmp.h>
22: #include <errno.h>
23: #include <utsname.h>
24:
25: #include <libc.h>
26:
27: #include "ftp.h"
28:
29: /*
30: * File containing login names
31: * NOT to be used on this machine.
32: * Commonly used to disallow uucp.
33: */
34: #define CTLPORT 21
35:
36: extern int errno;
37: extern char *sys_errlist[];
38: extern char *crypt();
39: extern FILE *popen(), *fopen();
40: extern int pclose(), fclose();
41:
42: char *defdest;
43: extern char dest[];
44:
45: int data;
46: jmp_buf errcatch;
47: int logged_in;
48: struct passwd *pw;
49: int debug;
50: int timeout;
51: int logging;
52: int type;
53: int form;
54: int stru; /* avoid C keyword */
55: int mode;
56: int usedefault = 1; /* for data transfers */
57: char hostname[32];
58: char remotehost[32];
59: int pasv;
60: char *home; /* pointer to home directory for glob */
61:
62: /*
63: * Timeout intervals for retrying connections
64: * to hosts that don't accept PORT cmds. This
65: * is a kludge, but given the problems with TCP...
66: */
67: #define SWAITMAX 90 /* wait at most 90 seconds */
68: #define SWAITINT 5 /* interval between retries */
69:
70: int swaitmax = SWAITMAX;
71: int swaitint = SWAITINT;
72:
73: int lostconn();
74: int reapchild();
75: FILE *dataconn();
76:
77: main(argc, argv)
78: int argc;
79: char *argv[];
80: {
81: int ctrl, s, options = 0;
82: char *cp;
83: int dev;
84: int status;
85: struct utsname uts;
86:
87: if(argc==2 && strcmp(argv[1], "-d")==0)
88: debug = 1;
89:
90: signal(SIGPIPE, SIG_IGN); /* used to be lostconn */
91: signal(SIGHUP, SIG_IGN);
92: signal (SIGCHLD, SIG_IGN);
93:
94: /*
95: * get source address
96: */
97: getsource();
98:
99: /*
100: * Set up default state
101: */
102: logged_in = 0;
103: data = -1;
104: type = TYPE_A;
105: form = FORM_N;
106: stru = STRU_F;
107: mode = MODE_S;
108: uname(&uts);
109: strcpy(hostname, uts.sysname);
110: reply(220, "%s FTP server ready.", hostname);
111: for (;;) {
112: setjmp(errcatch);
113: yyparse();
114: fprintf(stderr, "out of yyparse\n");
115: }
116: }
117:
118: /*
119: * parse the source string and extract system and network
120: */
121: getsource()
122: {
123: char *s;
124: char *e;
125:
126: e = getenv("CSOURCE");
127: if(e == 0){
128: fprintf(stderr, "Can't get source address\n");
129: exit(1);
130: }
131: s = strchr(e, ' ');
132: if(s)
133: *s = 0;
134: defdest = e;
135: }
136:
137: reapchild()
138: {
139: union wait status;
140:
141: while (wait3(&status, WNOHANG, 0) > 0)
142: ;
143: }
144:
145: lostconn()
146: {
147:
148: if (debug)
149: fprintf(stderr, "Lost connection.\n");
150: dologout(-1);
151: }
152:
153: pass(passwd)
154: char *passwd;
155: {
156: char *xpasswd, *savestr();
157: static struct passwd save;
158:
159: if (logged_in) {
160: reply(503, "already logged in.");
161: return;
162: }
163: xpasswd = crypt(passwd, pw->pw_passwd);
164: if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
165: reply(530, "Login incorrect.");
166: pw = NULL;
167: return;
168: }
169: dologin(pw);
170: setgid(pw->pw_gid);
171: setuid(pw->pw_uid);
172: if (chdir(pw->pw_dir)) {
173: reply(550, "User %s: can't change directory to $s.",
174: pw->pw_name, pw->pw_dir);
175: dologout(-1);
176: exit(1);
177: }
178: logged_in = 1;
179: /*
180: * Save everything so globbing doesn't
181: * clobber the fields.
182: */
183: save = *pw;
184: save.pw_name = savestr(pw->pw_name);
185: save.pw_passwd = savestr(pw->pw_passwd);
186: save.pw_comment = savestr(pw->pw_comment);
187: save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
188: save.pw_dir = savestr(pw->pw_dir);
189: save.pw_shell = savestr(pw->pw_shell);
190: pw = &save;
191: home = pw->pw_dir; /* home dir for globbing */
192: ack("login");
193: return;
194: bad:
195: pw = NULL;
196: }
197:
198: char *
199: savestr(s)
200: char *s;
201: {
202: char *malloc();
203: char *new = malloc(strlen(s) + 1);
204:
205: if (new != NULL)
206: strcpy(new, s);
207: return (new);
208: }
209:
210:
211: openfile(name)
212: char *name;
213: {
214: reply(550, "%s: can't open", name);
215: }
216:
217: pipette()
218: {
219: signal(SIGPIPE, pipette);
220: }
221:
222: retrieve(cmd, name)
223: char *cmd, *name;
224: {
225: FILE *fin, *dout;
226: struct stat st;
227: int (*closefunc)();
228:
229: if(!logged_in){
230: reply(550, "%s: login required.", name);
231: return;
232: }
233: if (cmd == 0) {
234: fin = fopen(name, "r"), closefunc = fclose;
235: } else {
236: char line[BUFSIZ];
237:
238: sprintf(line, cmd, name), name = line;
239: if(debug)
240: fprintf(stderr,"cmd is %s\n", line);
241: fin = popen(line, "r"), closefunc = pclose;
242: }
243: if (fin == NULL) {
244: if(debug)
245: fprintf(stderr, "bad popen()\n");
246: if (errno != 0)
247: reply(550, "%s: %s.", name, sys_errlist[errno]);
248: return;
249: }
250:
251: st.st_size = 0;
252: if (cmd == 0
253: && (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
254: reply(550, "%s: not a plain file.", name);
255: goto done;
256: }
257: dout = dataconn(name, st.st_size, "w");
258: if (dout == NULL)
259: goto done;
260: if (send_data(fin, dout) || ferror(dout))
261: reply(550, "%s: %s.", name, sys_errlist[errno]);
262:
263: else {
264: fclose(dout);
265: delay(timeout);
266: reply(226, "Transfer complete.");
267: }
268: data = -1;
269: done:
270: (*closefunc)(fin);
271: }
272:
273: store(name, mode)
274: char *name, *mode;
275: {
276: FILE *fout, *din;
277: int (*closefunc)(), dochown = 0;
278:
279: if(!logged_in){
280: reply(550, "%s: login required.", name);
281: return;
282: }
283: fout = fopen(name, mode), closefunc = fclose;
284: if (fout == NULL) {
285: reply(550, "%s: %s.", name, sys_errlist[errno]);
286: return;
287: }
288: din = dataconn(name, (off_t)-1, "r");
289: if (din == NULL)
290: goto done;
291: if (receive_data(din, fout) || ferror(fout))
292: reply(550, "%s: %s.", name, sys_errlist[errno]);
293: else {
294: fflush(fout);
295: delay(timeout);
296: reply(226, "Transfer complete.");
297: }
298: fclose(din);
299: data = -1;
300: done:
301: (*closefunc)(fout);
302: }
303:
304: FILE *
305: dataconn(name, size, mode)
306: char *name;
307: off_t size;
308: char *mode;
309: {
310: char sizebuf[32];
311: char dialstr[128];
312: char params[32];
313: FILE *file;
314: int retries;
315:
316: if (size >= 0)
317: sprintf (sizebuf, " (%ld bytes)", size);
318: else
319: (void) strcpy(sizebuf, "");
320:
321: if (data >= 0) {
322: reply(125, "Using existing data connection for %s%s.",
323: name, sizebuf);
324: usedefault = 1;
325: return (fdopen(data, mode));
326: }
327: if (usedefault)
328: sprintf(dialstr, "/cs/%s", defdest);
329: else
330: sprintf(dialstr, "/cs/%s", dest);
331:
332: /* make the connection */
333: /*sprintf(params, "port=%d", pasv?0:(CTLPORT - 1));/**/
334: for(retries=0; retries<5; retries++){
335: data = ipcopen(dialstr, params);
336: if(data>=0)
337: break;
338: sleep(1);
339: }
340: if(data<0 || (file=fdopen(data, mode))==NULL){
341: reply(425, "Can't open data socket (%s): %s.",
342: dialstr,
343: sys_errlist[errno]);
344: return (NULL);
345: }
346: reply(150, "Opening data connection for %s (%s,%d)%s.",
347: name,
348: dialstr,
349: sizebuf);
350: return (file);
351: }
352:
353: /*
354: * Tranfer the contents of "instr" to
355: * "outstr" peer using the appropriate
356: * encapulation of the date subject
357: * to Mode, Structure, and Type.
358: *
359: * NB: Form isn't handled.
360: */
361: send_data(instr, outstr)
362: FILE *instr, *outstr;
363: {
364: register int c;
365: int netfd, filefd, cnt = 0;
366: char buf[BUFSIZ];
367:
368: signal(SIGPIPE, pipette);
369: switch (type) {
370:
371: case TYPE_A:
372: while ((c = getc(instr)) != EOF) {
373: if (c == '\n') {
374: if (ferror (outstr))
375: return (1);
376: putc('\r', outstr);
377: }
378: putc(c, outstr);
379: if (c == '\r')
380: putc ('\0', outstr);
381: /* cnt++;
382: if (!(cnt%1000)) { fprintf(stderr, "%d ", cnt); fflush(stderr); } */
383: }
384: if (ferror (instr) || ferror (outstr)) {
385: fprintf(stderr,"error: inst %d, outstr %d\n", ferror(instr), ferror(outstr));
386: return (1);
387: }
388: return (0);
389:
390: case TYPE_I:
391: case TYPE_L:
392: netfd = fileno(outstr);
393: filefd = fileno(instr);
394:
395: while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
396: if (write(netfd, buf, cnt) < 0) {
397: fprintf(stderr, "write error, cnt=%d\n", cnt);
398: return (1);
399: }
400: return (cnt < 0);
401: }
402: reply(504,"Unimplemented TYPE %d in send_data", type);
403: return (1);
404: }
405:
406: /*
407: * Transfer data from peer to
408: * "outstr" using the appropriate
409: * encapulation of the data subject
410: * to Mode, Structure, and Type.
411: *
412: * N.B.: Form isn't handled.
413: */
414: receive_data(instr, outstr)
415: FILE *instr, *outstr;
416: {
417: register int c;
418: int cnt;
419: char buf[BUFSIZ];
420:
421:
422: signal(SIGPIPE, pipette);
423: switch (type) {
424:
425: case TYPE_I:
426: case TYPE_L:
427: while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
428: if (write(fileno(outstr), buf, cnt) < 0)
429: return (1);
430: return (cnt < 0);
431:
432: case TYPE_E:
433: reply(504, "TYPE E not implemented.");
434: return (1);
435:
436: case TYPE_A:
437: while ((c = getc(instr)) != EOF) {
438: if (c == '\r') {
439: if (ferror (outstr))
440: return (1);
441: if ((c = getc(instr)) != '\n')
442: putc ('\r', outstr);
443: if (c == '\0')
444: continue;
445: }
446: putc (c, outstr);
447: }
448: if (ferror (instr) || ferror (outstr))
449: return (1);
450: return (0);
451: }
452: fatal("Unknown type in receive_data.");
453: /*NOTREACHED*/
454: }
455:
456: fatal(s)
457: char *s;
458: {
459: reply(451, "Error in server: %s\n", s);
460: reply(221, "Closing connection due to server error.");
461: dologout(0);
462: }
463:
464: reply(n, s, args)
465: int n;
466: char *s;
467: {
468:
469: printf("%d ", n);
470: _doprnt(s, &args, stdout);
471: printf("\r\n");
472: fflush(stdout);
473: if (debug) {
474: fprintf(stderr, "<--- %d ", n);
475: _doprnt(s, &args, stderr);
476: fprintf(stderr, "\n");
477: fflush(stderr);
478: }
479: }
480:
481: lreply(n, s, args)
482: int n;
483: char *s;
484: {
485: printf("%d-", n);
486: _doprnt(s, &args, stdout);
487: printf("\r\n");
488: fflush(stdout);
489: if (debug) {
490: fprintf(stderr, "<--- %d-", n);
491: _doprnt(s, &args, stderr);
492: fprintf(stderr, "\n");
493: }
494: }
495:
496: replystr(s)
497: char *s;
498: {
499: printf("%s\r\n", s);
500: fflush(stdout);
501: if (debug)
502: fprintf(stderr, "<--- %s\n", s);
503: }
504:
505: ack(s)
506: char *s;
507: {
508: reply(200, "%s command okay.", s);
509: }
510:
511: nack(s)
512: char *s;
513: {
514: reply(502, "%s command not implemented.", s);
515: }
516:
517: yyerror()
518: {
519: reply(500, "Command not understood.");
520: }
521:
522: delete(name)
523: char *name;
524: {
525: struct stat st;
526:
527: if(!logged_in){
528: reply(550, "delete: login required.");
529: return;
530: }
531: if (stat(name, &st) < 0) {
532: reply(550, "%s: %s.", name, sys_errlist[errno]);
533: return;
534: }
535: if ((st.st_mode&S_IFMT) == S_IFDIR) {
536: if (rmdir(name) < 0) {
537: reply(550, "%s: %s.", name, sys_errlist[errno]);
538: return;
539: }
540: goto done;
541: }
542: if (unlink(name) < 0) {
543: reply(550, "%s: %s.", name, sys_errlist[errno]);
544: return;
545: }
546: done:
547: ack("DELE");
548: }
549:
550: cwd(path)
551: char *path;
552: {
553:
554: if(!logged_in){
555: reply(550, "cwd: login required.");
556: return;
557: }
558: if (chdir(path) < 0) {
559: reply(550, "%s: %s.", path, sys_errlist[errno]);
560: return;
561: }
562: ack("CWD");
563: }
564:
565: makedir(name)
566: char *name;
567: {
568: struct stat st;
569: int dochown = stat(name, &st) < 0;
570:
571: if(!logged_in){
572: reply(550, "makedir: login required.");
573: return;
574: }
575: if (mkdir(name, 0777) < 0) {
576: reply(550, "%s: %s.", name, sys_errlist[errno]);
577: return;
578: }
579: ack("MKDIR");
580: }
581:
582: removedir(name)
583: char *name;
584: {
585:
586: if(!logged_in){
587: reply(550, "removedir: login required.");
588: return;
589: }
590: if (rmdir(name) < 0) {
591: reply(550, "%s: %s.", name, sys_errlist[errno]);
592: return;
593: }
594: ack("RMDIR");
595: }
596:
597: #define MAXPATHLEN 256
598: pwd()
599: {
600: char path[MAXPATHLEN + 1];
601:
602: if (getwd(path) == NULL) {
603: reply(451, "%s.", path);
604: return;
605: }
606: reply(251, "\"%s\" is current directory.", path);
607: }
608:
609: char *
610: renamefrom(name)
611: char *name;
612: {
613: struct stat st;
614:
615: if(!logged_in){
616: reply(550, "rename: login required.");
617: return;
618: }
619: if (stat(name, &st) < 0) {
620: reply(550, "%s: %s.", name, sys_errlist[errno]);
621: return ((char *)0);
622: }
623: reply(350, "File exists, ready for destination name");
624: return (name);
625: }
626:
627: renamecmd(from, to)
628: char *from, *to;
629: {
630:
631: if(!logged_in){
632: reply(550, "rename: login required.");
633: return;
634: }
635: if (rename(from, to) < 0) {
636: reply(550, "rename: %s.", sys_errlist[errno]);
637: return;
638: }
639: ack("RNTO");
640: }
641:
642: #include <utmp.h>
643:
644: #define SCPYN(a, b) strncpy(a, b, sizeof (a))
645: struct utmp utmp;
646:
647: /*
648: * Record login in wtmp file.
649: */
650:
651: #define O_WRONLY 1
652: #define O_APPEND 1
653: dologin(pw)
654: struct passwd *pw;
655: {
656: int wtmp;
657: char line[32];
658:
659: pasv = 0;
660: wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
661: if (wtmp >= 0) {
662: /* hack, but must be unique and no tty line */
663: sprintf(line, "ftp%d", getpid());
664: SCPYN(utmp.ut_line, line);
665: SCPYN(utmp.ut_name, pw->pw_name);
666: utmp.ut_time = time(0);
667: (void) write(wtmp, (char *)&utmp, sizeof (utmp));
668: (void) close(wtmp);
669: }
670: }
671:
672: /*
673: * Record logout in wtmp file
674: * and exit with supplied status.
675: */
676: dologout(status)
677: int status;
678: {
679: int wtmp;
680:
681: if (!logged_in)
682: _exit(status);
683: wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
684: if (wtmp >= 0) {
685: SCPYN(utmp.ut_name, "");
686: utmp.ut_time = time(0);
687: (void) write(wtmp, (char *)&utmp, sizeof (utmp));
688: (void) close(wtmp);
689: }
690: /* beware of flushing buffers after a SIGPIPE */
691: _exit(status);
692: }
693:
694: static char *
695: nextarg(cpp)
696: char *cpp;
697: {
698: register char *cp = cpp;
699:
700: if (cp == 0)
701: return (cp);
702: while (*cp && *cp != ' ' && *cp != '\t')
703: cp++;
704: if (*cp == ' ' || *cp == '\t') {
705: *cp++ = '\0';
706: while (*cp == ' ' || *cp == '\t')
707: cp++;
708: }
709: if (cp == cpp)
710: return ((char *)0);
711: return (cp);
712: }
713:
714: rename(from, to)
715: {
716: if (-1 == link(from, to))
717: return(-1);
718: return(unlink(from));
719: }
720: inet_ntoa(addr)
721: long addr;
722: {
723: static char b[32];
724: unsigned char *xp = (unsigned char *)&addr;
725:
726: sprintf(b, "%d.%d.%d.%d", xp[3], xp[2], xp[1], xp[0]);
727: return(b);
728: }
729: delay()
730: {
731: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.