date and time created 85/06/06 11:06:25 by dist
[unix-history] / usr / src / libexec / ftpd / ftpcmd.y
CommitLineData
25d264e2
SL
1/*
2 * Grammar for FTP commands.
3 * See RFC 765.
4 */
5
6%{
7
8#ifndef lint
418345d1 9static char sccsid[] = "@(#)ftpcmd.y 4.13 (Berkeley) %G%";
25d264e2
SL
10#endif
11
12#include <sys/types.h>
13#include <sys/socket.h>
14
15#include <netinet/in.h>
16
23eeaca6
SL
17#include <arpa/ftp.h>
18
25d264e2 19#include <stdio.h>
5ac6fc46 20#include <signal.h>
25d264e2
SL
21#include <ctype.h>
22#include <pwd.h>
23#include <setjmp.h>
25d264e2
SL
24
25extern struct sockaddr_in data_dest;
26extern int logged_in;
27extern struct passwd *pw;
28extern int guest;
29extern int logging;
30extern int type;
31extern int form;
32extern int debug;
5ac6fc46 33extern int timeout;
25d264e2
SL
34extern char hostname[];
35extern char *globerr;
1d92d63d 36extern int usedefault;
25d264e2
SL
37char **glob();
38
39static int cmd_type;
40static int cmd_form;
41static int cmd_bytesz;
42
25d264e2
SL
43char *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
66cmd_list: /* empty */
67 | cmd_list cmd
68 ;
69
70cmd: USER SP username CRLF
71 = {
72 extern struct passwd *getpwnam();
73
5914191e
SL
74 if (strcmp($3, "ftp") == 0 ||
75 strcmp($3, "anonymous") == 0) {
76 if ((pw = getpwnam("ftp")) != NULL) {
77 guest = 1;
78 reply(331,
25d264e2 79 "Guest login ok, send ident as password.");
5914191e 80 }
0597ed04 81 } else if (checkuser($3)) {
25d264e2
SL
82 guest = 0;
83 pw = getpwnam($3);
84 reply(331, "Password required for %s.", $3);
85 }
25d264e2 86 if (pw == NULL)
19501145
SL
87 reply(530, "User %s unknown.", $3);
88 free($3);
25d264e2
SL
89 }
90 | PASS SP password CRLF
91 = {
92 pass($3);
93 free($3);
94 }
95 | PORT SP host_port CRLF
96 = {
1d92d63d 97 usedefault = 0;
25d264e2
SL
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 = {
8365e2f7 161 if ($2 && $4 != NULL)
25d264e2 162 retrieve(0, $4);
8365e2f7
SL
163 if ($4 != NULL)
164 free($4);
25d264e2
SL
165 }
166 | STOR check_login SP pathname CRLF
167 = {
8365e2f7 168 if ($2 && $4 != NULL)
25d264e2 169 store($4, "w");
8365e2f7
SL
170 if ($4 != NULL)
171 free($4);
25d264e2
SL
172 }
173 | APPE check_login SP pathname CRLF
174 = {
8365e2f7 175 if ($2 && $4 != NULL)
25d264e2 176 store($4, "a");
8365e2f7
SL
177 if ($4 != NULL)
178 free($4);
25d264e2
SL
179 }
180 | NLST check_login CRLF
181 = {
182 if ($2)
0c974096 183 retrieve("/bin/ls", "");
25d264e2
SL
184 }
185 | NLST check_login SP pathname CRLF
186 = {
8365e2f7 187 if ($2 && $4 != NULL)
0c974096 188 retrieve("/bin/ls %s", $4);
8365e2f7
SL
189 if ($4 != NULL)
190 free($4);
25d264e2
SL
191 }
192 | LIST check_login CRLF
193 = {
194 if ($2)
868e5613 195 retrieve("/bin/ls -lg", "");
25d264e2
SL
196 }
197 | LIST check_login SP pathname CRLF
198 = {
8365e2f7 199 if ($2 && $4 != NULL)
868e5613 200 retrieve("/bin/ls -lg %s", $4);
8365e2f7
SL
201 if ($4 != NULL)
202 free($4);
25d264e2
SL
203 }
204 | DELE check_login SP pathname CRLF
205 = {
8365e2f7 206 if ($2 && $4 != NULL)
25d264e2 207 delete($4);
8365e2f7
SL
208 if ($4 != NULL)
209 free($4);
25d264e2
SL
210 }
211 | CWD check_login CRLF
212 = {
213 if ($2)
214 cwd(pw->pw_dir);
215 }
216 | CWD check_login SP pathname CRLF
217 = {
8365e2f7 218 if ($2 && $4 != NULL)
25d264e2 219 cwd($4);
8365e2f7
SL
220 if ($4 != NULL)
221 free($4);
25d264e2
SL
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 = {
8365e2f7
SL
238 if ($2 && $4 != NULL)
239 makedir($4);
240 if ($4 != NULL)
241 free($4);
25d264e2
SL
242 }
243 | XRMD check_login SP pathname CRLF
244 = {
8365e2f7
SL
245 if ($2 && $4 != NULL)
246 removedir($4);
247 if ($4 != NULL)
248 free($4);
25d264e2
SL
249 }
250 | XPWD check_login CRLF
251 = {
252 if ($2)
8365e2f7 253 pwd();
25d264e2
SL
254 }
255 | XCUP check_login CRLF
256 = {
257 if ($2)
258 cwd("..");
259 }
260 | QUIT CRLF
261 = {
262 reply(221, "Goodbye.");
bb16805b 263 dologout(0);
25d264e2
SL
264 }
265 | error CRLF
266 = {
267 yyerrok;
268 }
269 ;
270
271username: STRING
272 ;
273
274password: STRING
275 ;
276
277byte_size: NUMBER
278 ;
279
280host_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;
5914191e 289 data_dest.sin_family = AF_INET;
25d264e2
SL
290 }
291 ;
292
293form_code: N
294 = {
295 $$ = FORM_N;
296 }
297 | T
298 = {
299 $$ = FORM_T;
300 }
301 | C
302 = {
303 $$ = FORM_C;
304 }
305 ;
306
307type_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
349struct_code: F
350 = {
351 $$ = STRU_F;
352 }
353 | R
354 = {
355 $$ = STRU_R;
356 }
357 | P
358 = {
359 $$ = STRU_P;
360 }
361 ;
362
363mode_code: S
364 = {
365 $$ = MODE_S;
366 }
367 | B
368 = {
369 $$ = MODE_B;
370 }
371 | C
372 = {
373 $$ = MODE_C;
374 }
375 ;
376
377pathname: pathstring
378 = {
379 if ($1 && strncmp($1, "~", 1) == 0) {
380 $$ = (int)*glob($1);
8365e2f7 381 if (globerr != NULL) {
25d264e2 382 reply(550, globerr);
8365e2f7
SL
383 $$ = NULL;
384 }
25d264e2
SL
385 free($1);
386 } else
387 $$ = $1;
388 }
389 ;
390
391pathstring: STRING
392 ;
393
394rename_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
407rename_from: RNFR check_login SP pathname CRLF
408 = {
409 char *from = 0, *renamefrom();
410
8365e2f7 411 if ($2 && $4)
25d264e2 412 from = renamefrom($4);
8365e2f7 413 if (from == 0 && $4)
25d264e2
SL
414 free($4);
415 $$ = (int)from;
416 }
417 ;
418
419rename_to: RNTO SP pathname CRLF
420 = {
421 $$ = $3;
422 }
423 ;
424
425check_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
438extern 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
446struct tab {
447 char *name;
448 short token;
449 short state;
450 short implemented; /* 1 if command is implemented */
451 char *help;
452};
453
454struct 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
496struct tab *
497lookup(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
23eeaca6 508#include <arpa/telnet.h>
25d264e2
SL
509
510/*
511 * getline - a hacked up version of fgets to ignore TELNET escape codes.
512 */
513char *
514getline(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)
418345d1 532 return (NULL);
25d264e2 533 *cs++ = '\0';
5ac6fc46
SL
534 if (debug) {
535 fprintf(stderr, "FTPD: command: %s", s);
536 if (c != '\n')
537 putc('\n', stderr);
538 fflush(stderr);
539 }
25d264e2
SL
540 return (s);
541}
542
5ac6fc46
SL
543static int
544toolong()
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 }
bb16805b 558 dologout(1);
5ac6fc46
SL
559}
560
25d264e2
SL
561yylex()
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:
5ac6fc46
SL
574 signal(SIGALRM, toolong);
575 alarm(timeout);
25d264e2
SL
576 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
577 reply(221, "You could at least say goodbye.");
bb16805b 578 dologout(0);
25d264e2 579 }
5ac6fc46 580 alarm(0);
25d264e2
SL
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
720upper(s)
721 char *s;
722{
723 while (*s != '\0') {
724 if (islower(*s))
725 *s = toupper(*s);
726 s++;
727 }
728}
729
730copy(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
743help(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 ? ' ' : '*');
8365e2f7 776 if (c + lines >= &cmdtab[NCMDS])
25d264e2 777 break;
25d264e2
SL
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}