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