|
|
1.1 root 1: /*
2: * Copyright (c) 1985 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted
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.
16: *
17: * @(#)ftpcmd.y 5.11 (Berkeley) 6/18/88
18: */
19:
20: /*
21: * Grammar for FTP commands.
22: * See RFC 765.
23: */
24:
25: %{
26:
27: #ifndef lint
28: static char sccsid[] = "@(#)ftpcmd.y 5.11 (Berkeley) 6/18/88";
29: #endif /* not lint */
30:
31: #include <sys/types.h>
32: #include <sys/socket.h>
33:
34: #include <netinet/in.h>
35:
36: #include <arpa/ftp.h>
37:
38: #include <stdio.h>
39: #include <signal.h>
40: #include <ctype.h>
41: #include <pwd.h>
42: #include <setjmp.h>
43: #include <syslog.h>
44:
45: extern struct sockaddr_in data_dest;
46: extern int logged_in;
47: extern struct passwd *pw;
48: extern int guest;
49: extern int logging;
50: extern int type;
51: extern int form;
52: extern int debug;
53: extern int timeout;
54: extern int pdata;
55: extern char hostname[];
56: extern char *globerr;
57: extern int usedefault;
58: extern int unique;
59: extern int transflag;
60: extern char tmpline[];
61: char **glob();
62:
63: static int cmd_type;
64: static int cmd_form;
65: static int cmd_bytesz;
66: char cbuf[512];
67: char *fromname;
68:
69: char *index();
70: %}
71:
72: %token
73: A B C E F I
74: L N P R S T
75:
76: SP CRLF COMMA STRING NUMBER
77:
78: USER PASS ACCT REIN QUIT PORT
79: PASV TYPE STRU MODE RETR STOR
80: APPE MLFL MAIL MSND MSOM MSAM
81: MRSQ MRCP ALLO REST RNFR RNTO
82: ABOR DELE CWD LIST NLST SITE
83: STAT HELP NOOP XMKD XRMD XPWD
84: XCUP STOU
85:
86: LEXERR
87:
88: %start cmd_list
89:
90: %%
91:
92: cmd_list: /* empty */
93: | cmd_list cmd
94: = {
95: fromname = (char *) 0;
96: }
97: | cmd_list rcmd
98: ;
99:
100: cmd: USER SP username CRLF
101: = {
102: extern struct passwd *getpwnam();
103:
104: logged_in = 0;
105: if (strcmp((char *) $3, "ftp") == 0 ||
106: strcmp((char *) $3, "anonymous") == 0) {
107: if ((pw = getpwnam("ftp")) != NULL) {
108: guest = 1;
109: reply(331,
110: "Guest login ok, send ident as password.");
111: }
112: else {
113: reply(530, "User %s unknown.", $3);
114: }
115: } else if (checkuser((char *) $3)) {
116: guest = 0;
117: pw = getpwnam((char *) $3);
118: if (pw == NULL) {
119: reply(530, "User %s unknown.", $3);
120: }
121: else {
122: reply(331, "Password required for %s.", $3);
123: }
124: } else {
125: reply(530, "User %s access denied.", $3);
126: }
127: free((char *) $3);
128: }
129: | PASS SP password CRLF
130: = {
131: pass((char *) $3);
132: free((char *) $3);
133: }
134: | PORT SP host_port CRLF
135: = {
136: usedefault = 0;
137: if (pdata > 0) {
138: (void) close(pdata);
139: }
140: pdata = -1;
141: reply(200, "PORT command successful.");
142: }
143: | PASV CRLF
144: = {
145: passive();
146: }
147: | TYPE SP type_code CRLF
148: = {
149: switch (cmd_type) {
150:
151: case TYPE_A:
152: if (cmd_form == FORM_N) {
153: reply(200, "Type set to A.");
154: type = cmd_type;
155: form = cmd_form;
156: } else
157: reply(504, "Form must be N.");
158: break;
159:
160: case TYPE_E:
161: reply(504, "Type E not implemented.");
162: break;
163:
164: case TYPE_I:
165: reply(200, "Type set to I.");
166: type = cmd_type;
167: break;
168:
169: case TYPE_L:
170: if (cmd_bytesz == 8) {
171: reply(200,
172: "Type set to L (byte size 8).");
173: type = cmd_type;
174: } else
175: reply(504, "Byte size must be 8.");
176: }
177: }
178: | STRU SP struct_code CRLF
179: = {
180: switch ($3) {
181:
182: case STRU_F:
183: reply(200, "STRU F ok.");
184: break;
185:
186: default:
187: reply(504, "Unimplemented STRU type.");
188: }
189: }
190: | MODE SP mode_code CRLF
191: = {
192: switch ($3) {
193:
194: case MODE_S:
195: reply(200, "MODE S ok.");
196: break;
197:
198: default:
199: reply(502, "Unimplemented MODE type.");
200: }
201: }
202: | ALLO SP NUMBER CRLF
203: = {
204: reply(202, "ALLO command ignored.");
205: }
206: | RETR check_login SP pathname CRLF
207: = {
208: if ($2 && $4 != NULL)
209: retrieve((char *) 0, (char *) $4);
210: if ($4 != NULL)
211: free((char *) $4);
212: }
213: | STOR check_login SP pathname CRLF
214: = {
215: if ($2 && $4 != NULL)
216: store((char *) $4, "w");
217: if ($4 != NULL)
218: free((char *) $4);
219: }
220: | APPE check_login SP pathname CRLF
221: = {
222: if ($2 && $4 != NULL)
223: store((char *) $4, "a");
224: if ($4 != NULL)
225: free((char *) $4);
226: }
227: | NLST check_login CRLF
228: = {
229: if ($2)
230: retrieve("/bin/ls", "");
231: }
232: | NLST check_login SP pathname CRLF
233: = {
234: if ($2 && $4 != NULL)
235: retrieve("/bin/ls %s", (char *) $4);
236: if ($4 != NULL)
237: free((char *) $4);
238: }
239: | LIST check_login CRLF
240: = {
241: if ($2)
242: retrieve("/bin/ls -lg", "");
243: }
244: | LIST check_login SP pathname CRLF
245: = {
246: if ($2 && $4 != NULL)
247: retrieve("/bin/ls -lg %s", (char *) $4);
248: if ($4 != NULL)
249: free((char *) $4);
250: }
251: | DELE check_login SP pathname CRLF
252: = {
253: if ($2 && $4 != NULL)
254: delete((char *) $4);
255: if ($4 != NULL)
256: free((char *) $4);
257: }
258: | RNTO SP pathname CRLF
259: = {
260: if (fromname) {
261: renamecmd(fromname, (char *) $3);
262: free(fromname);
263: fromname = (char *) 0;
264: } else {
265: reply(503, "Bad sequence of commands.");
266: }
267: free((char *) $3);
268: }
269: | ABOR CRLF
270: = {
271: reply(225, "ABOR command successful.");
272: }
273: | CWD check_login CRLF
274: = {
275: if ($2)
276: cwd(pw->pw_dir);
277: }
278: | CWD check_login SP pathname CRLF
279: = {
280: if ($2 && $4 != NULL)
281: cwd((char *) $4);
282: if ($4 != NULL)
283: free((char *) $4);
284: }
285: | HELP CRLF
286: = {
287: help((char *) 0);
288: }
289: | HELP SP STRING CRLF
290: = {
291: help((char *) $3);
292: }
293: | NOOP CRLF
294: = {
295: reply(200, "NOOP command successful.");
296: }
297: | XMKD check_login SP pathname CRLF
298: = {
299: if ($2 && $4 != NULL)
300: makedir((char *) $4);
301: if ($4 != NULL)
302: free((char *) $4);
303: }
304: | XRMD check_login SP pathname CRLF
305: = {
306: if ($2 && $4 != NULL)
307: removedir((char *) $4);
308: if ($4 != NULL)
309: free((char *) $4);
310: }
311: | XPWD check_login CRLF
312: = {
313: if ($2)
314: pwd();
315: }
316: | XCUP check_login CRLF
317: = {
318: if ($2)
319: cwd("..");
320: }
321: | STOU check_login SP pathname CRLF
322: = {
323: if ($2 && $4 != NULL) {
324: unique++;
325: store((char *) $4, "w");
326: unique = 0;
327: }
328: if ($4 != NULL)
329: free((char *) $4);
330: }
331: | QUIT CRLF
332: = {
333: reply(221, "Goodbye.");
334: dologout(0);
335: }
336: | error CRLF
337: = {
338: yyerrok;
339: }
340: ;
341:
342: rcmd: RNFR check_login SP pathname CRLF
343: = {
344: char *renamefrom();
345:
346: if ($2 && $4) {
347: fromname = renamefrom((char *) $4);
348: if (fromname == (char *) 0 && $4) {
349: free((char *) $4);
350: }
351: }
352: }
353: ;
354:
355: username: STRING
356: ;
357:
358: password: STRING
359: ;
360:
361: byte_size: NUMBER
362: ;
363:
364: host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
365: NUMBER COMMA NUMBER
366: = {
367: register char *a, *p;
368:
369: a = (char *)&data_dest.sin_addr;
370: a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
371: p = (char *)&data_dest.sin_port;
372: p[0] = $9; p[1] = $11;
373: data_dest.sin_family = AF_INET;
374: }
375: ;
376:
377: form_code: N
378: = {
379: $$ = FORM_N;
380: }
381: | T
382: = {
383: $$ = FORM_T;
384: }
385: | C
386: = {
387: $$ = FORM_C;
388: }
389: ;
390:
391: type_code: A
392: = {
393: cmd_type = TYPE_A;
394: cmd_form = FORM_N;
395: }
396: | A SP form_code
397: = {
398: cmd_type = TYPE_A;
399: cmd_form = $3;
400: }
401: | E
402: = {
403: cmd_type = TYPE_E;
404: cmd_form = FORM_N;
405: }
406: | E SP form_code
407: = {
408: cmd_type = TYPE_E;
409: cmd_form = $3;
410: }
411: | I
412: = {
413: cmd_type = TYPE_I;
414: }
415: | L
416: = {
417: cmd_type = TYPE_L;
418: cmd_bytesz = 8;
419: }
420: | L SP byte_size
421: = {
422: cmd_type = TYPE_L;
423: cmd_bytesz = $3;
424: }
425: /* this is for a bug in the BBN ftp */
426: | L byte_size
427: = {
428: cmd_type = TYPE_L;
429: cmd_bytesz = $2;
430: }
431: ;
432:
433: struct_code: F
434: = {
435: $$ = STRU_F;
436: }
437: | R
438: = {
439: $$ = STRU_R;
440: }
441: | P
442: = {
443: $$ = STRU_P;
444: }
445: ;
446:
447: mode_code: S
448: = {
449: $$ = MODE_S;
450: }
451: | B
452: = {
453: $$ = MODE_B;
454: }
455: | C
456: = {
457: $$ = MODE_C;
458: }
459: ;
460:
461: pathname: pathstring
462: = {
463: /*
464: * Problem: this production is used for all pathname
465: * processing, but only gives a 550 error reply.
466: * This is a valid reply in some cases but not in others.
467: */
468: if ($1 && strncmp((char *) $1, "~", 1) == 0) {
469: $$ = (int)*glob((char *) $1);
470: if (globerr != NULL) {
471: reply(550, globerr);
472: $$ = NULL;
473: }
474: free((char *) $1);
475: } else
476: $$ = $1;
477: }
478: ;
479:
480: pathstring: STRING
481: ;
482:
483: check_login: /* empty */
484: = {
485: if (logged_in)
486: $$ = 1;
487: else {
488: reply(530, "Please login with USER and PASS.");
489: $$ = 0;
490: }
491: }
492: ;
493:
494: %%
495:
496: extern jmp_buf errcatch;
497:
498: #define CMD 0 /* beginning of command */
499: #define ARGS 1 /* expect miscellaneous arguments */
500: #define STR1 2 /* expect SP followed by STRING */
501: #define STR2 3 /* expect STRING */
502: #define OSTR 4 /* optional STRING */
503:
504: struct tab {
505: char *name;
506: short token;
507: short state;
508: short implemented; /* 1 if command is implemented */
509: char *help;
510: };
511:
512: struct tab cmdtab[] = { /* In order defined in RFC 765 */
513: { "USER", USER, STR1, 1, "<sp> username" },
514: { "PASS", PASS, STR1, 1, "<sp> password" },
515: { "ACCT", ACCT, STR1, 0, "(specify account)" },
516: { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
517: { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
518: { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
519: { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
520: { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
521: { "STRU", STRU, ARGS, 1, "(specify file structure)" },
522: { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
523: { "RETR", RETR, STR1, 1, "<sp> file-name" },
524: { "STOR", STOR, STR1, 1, "<sp> file-name" },
525: { "APPE", APPE, STR1, 1, "<sp> file-name" },
526: { "MLFL", MLFL, OSTR, 0, "(mail file)" },
527: { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
528: { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
529: { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
530: { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
531: { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
532: { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
533: { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
534: { "REST", REST, STR1, 0, "(restart command)" },
535: { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
536: { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
537: { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
538: { "DELE", DELE, STR1, 1, "<sp> file-name" },
539: { "CWD", CWD, OSTR, 1, "[ <sp> directory-name]" },
540: { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
541: { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
542: { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
543: { "SITE", SITE, STR1, 0, "(get site parameters)" },
544: { "STAT", STAT, OSTR, 0, "(get server status)" },
545: { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
546: { "NOOP", NOOP, ARGS, 1, "" },
547: { "MKD", XMKD, STR1, 1, "<sp> path-name" },
548: { "XMKD", XMKD, STR1, 1, "<sp> path-name" },
549: { "RMD", XRMD, STR1, 1, "<sp> path-name" },
550: { "XRMD", XRMD, STR1, 1, "<sp> path-name" },
551: { "PWD", XPWD, ARGS, 1, "(return current directory)" },
552: { "XPWD", XPWD, ARGS, 1, "(return current directory)" },
553: { "CDUP", XCUP, ARGS, 1, "(change to parent directory)" },
554: { "XCUP", XCUP, ARGS, 1, "(change to parent directory)" },
555: { "STOU", STOU, STR1, 1, "<sp> file-name" },
556: { NULL, 0, 0, 0, 0 }
557: };
558:
559: struct tab *
560: lookup(cmd)
561: char *cmd;
562: {
563: register struct tab *p;
564:
565: for (p = cmdtab; p->name != NULL; p++)
566: if (strcmp(cmd, p->name) == 0)
567: return (p);
568: return (0);
569: }
570:
571: #include <arpa/telnet.h>
572:
573: /*
574: * getline - a hacked up version of fgets to ignore TELNET escape codes.
575: */
576: char *
577: getline(s, n, iop)
578: char *s;
579: register FILE *iop;
580: {
581: register c;
582: register char *cs;
583:
584: cs = s;
585: /* tmpline may contain saved command from urgent mode interruption */
586: for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
587: *cs++ = tmpline[c];
588: if (tmpline[c] == '\n') {
589: *cs++ = '\0';
590: if (debug) {
591: syslog(LOG_DEBUG, "FTPD: command: %s", s);
592: }
593: tmpline[0] = '\0';
594: return(s);
595: }
596: if (c == 0) {
597: tmpline[0] = '\0';
598: }
599: }
600: while (--n > 0 && (c = getc(iop)) != EOF) {
601: c = 0377 & c;
602: while (c == IAC) {
603: switch (c = 0377 & getc(iop)) {
604: case WILL:
605: case WONT:
606: c = 0377 & getc(iop);
607: printf("%c%c%c", IAC, WONT, c);
608: (void) fflush(stdout);
609: break;
610: case DO:
611: case DONT:
612: c = 0377 & getc(iop);
613: printf("%c%c%c", IAC, DONT, c);
614: (void) fflush(stdout);
615: break;
616: default:
617: break;
618: }
619: c = 0377 & getc(iop); /* try next character */
620: }
621: *cs++ = c;
622: if (c=='\n')
623: break;
624: }
625: if (c == EOF && cs == s)
626: return (NULL);
627: *cs++ = '\0';
628: if (debug) {
629: syslog(LOG_DEBUG, "FTPD: command: %s", s);
630: }
631: return (s);
632: }
633:
634: static int
635: toolong()
636: {
637: time_t now;
638: extern char *ctime();
639: extern time_t time();
640:
641: reply(421,
642: "Timeout (%d seconds): closing control connection.", timeout);
643: (void) time(&now);
644: if (logging) {
645: syslog(LOG_INFO,
646: "FTPD: User %s timed out after %d seconds at %s",
647: (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
648: }
649: dologout(1);
650: }
651:
652: yylex()
653: {
654: static int cpos, state;
655: register char *cp;
656: register struct tab *p;
657: int n;
658: char c;
659:
660: for (;;) {
661: switch (state) {
662:
663: case CMD:
664: (void) signal(SIGALRM, toolong);
665: (void) alarm((unsigned) timeout);
666: if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
667: reply(221, "You could at least say goodbye.");
668: dologout(0);
669: }
670: (void) alarm(0);
671: if (index(cbuf, '\r')) {
672: cp = index(cbuf, '\r');
673: cp[0] = '\n'; cp[1] = 0;
674: }
675: if (index(cbuf, ' '))
676: cpos = index(cbuf, ' ') - cbuf;
677: else
678: cpos = index(cbuf, '\n') - cbuf;
679: if (cpos == 0) {
680: cpos = 4;
681: }
682: c = cbuf[cpos];
683: cbuf[cpos] = '\0';
684: upper(cbuf);
685: p = lookup(cbuf);
686: cbuf[cpos] = c;
687: if (p != 0) {
688: if (p->implemented == 0) {
689: nack(p->name);
690: longjmp(errcatch,0);
691: /* NOTREACHED */
692: }
693: state = p->state;
694: yylval = (int) p->name;
695: return (p->token);
696: }
697: break;
698:
699: case OSTR:
700: if (cbuf[cpos] == '\n') {
701: state = CMD;
702: return (CRLF);
703: }
704: /* FALL THRU */
705:
706: case STR1:
707: if (cbuf[cpos] == ' ') {
708: cpos++;
709: state = STR2;
710: return (SP);
711: }
712: break;
713:
714: case STR2:
715: cp = &cbuf[cpos];
716: n = strlen(cp);
717: cpos += n - 1;
718: /*
719: * Make sure the string is nonempty and \n terminated.
720: */
721: if (n > 1 && cbuf[cpos] == '\n') {
722: cbuf[cpos] = '\0';
723: yylval = copy(cp);
724: cbuf[cpos] = '\n';
725: state = ARGS;
726: return (STRING);
727: }
728: break;
729:
730: case ARGS:
731: if (isdigit(cbuf[cpos])) {
732: cp = &cbuf[cpos];
733: while (isdigit(cbuf[++cpos]))
734: ;
735: c = cbuf[cpos];
736: cbuf[cpos] = '\0';
737: yylval = atoi(cp);
738: cbuf[cpos] = c;
739: return (NUMBER);
740: }
741: switch (cbuf[cpos++]) {
742:
743: case '\n':
744: state = CMD;
745: return (CRLF);
746:
747: case ' ':
748: return (SP);
749:
750: case ',':
751: return (COMMA);
752:
753: case 'A':
754: case 'a':
755: return (A);
756:
757: case 'B':
758: case 'b':
759: return (B);
760:
761: case 'C':
762: case 'c':
763: return (C);
764:
765: case 'E':
766: case 'e':
767: return (E);
768:
769: case 'F':
770: case 'f':
771: return (F);
772:
773: case 'I':
774: case 'i':
775: return (I);
776:
777: case 'L':
778: case 'l':
779: return (L);
780:
781: case 'N':
782: case 'n':
783: return (N);
784:
785: case 'P':
786: case 'p':
787: return (P);
788:
789: case 'R':
790: case 'r':
791: return (R);
792:
793: case 'S':
794: case 's':
795: return (S);
796:
797: case 'T':
798: case 't':
799: return (T);
800:
801: }
802: break;
803:
804: default:
805: fatal("Unknown state in scanner.");
806: }
807: yyerror((char *) 0);
808: state = CMD;
809: longjmp(errcatch,0);
810: }
811: }
812:
813: upper(s)
814: char *s;
815: {
816: while (*s != '\0') {
817: if (islower(*s))
818: *s = toupper(*s);
819: s++;
820: }
821: }
822:
823: copy(s)
824: char *s;
825: {
826: char *p;
827: extern char *malloc(), *strcpy();
828:
829: p = malloc((unsigned) strlen(s) + 1);
830: if (p == NULL)
831: fatal("Ran out of memory.");
832: (void) strcpy(p, s);
833: return ((int)p);
834: }
835:
836: help(s)
837: char *s;
838: {
839: register struct tab *c;
840: register int width, NCMDS;
841:
842: width = 0, NCMDS = 0;
843: for (c = cmdtab; c->name != NULL; c++) {
844: int len = strlen(c->name) + 1;
845:
846: if (len > width)
847: width = len;
848: NCMDS++;
849: }
850: width = (width + 8) &~ 7;
851: if (s == 0) {
852: register int i, j, w;
853: int columns, lines;
854:
855: lreply(214,
856: "The following commands are recognized (* =>'s unimplemented).");
857: columns = 76 / width;
858: if (columns == 0)
859: columns = 1;
860: lines = (NCMDS + columns - 1) / columns;
861: for (i = 0; i < lines; i++) {
862: printf(" ");
863: for (j = 0; j < columns; j++) {
864: c = cmdtab + j * lines + i;
865: printf("%s%c", c->name,
866: c->implemented ? ' ' : '*');
867: if (c + lines >= &cmdtab[NCMDS])
868: break;
869: w = strlen(c->name) + 1;
870: while (w < width) {
871: putchar(' ');
872: w++;
873: }
874: }
875: printf("\r\n");
876: }
877: (void) fflush(stdout);
878: reply(214, "Direct comments to ftp-bugs@%s.", hostname);
879: return;
880: }
881: upper(s);
882: c = lookup(s);
883: if (c == (struct tab *)0) {
884: reply(502, "Unknown command %s.", s);
885: return;
886: }
887: if (c->implemented)
888: reply(214, "Syntax: %s %s", c->name, c->help);
889: else
890: reply(214, "%-*s\t%s; unimplemented.", width, c->name, c->help);
891: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.