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