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