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