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