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