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