Merge from vendor branch GROFF:
[dragonfly.git] / libexec / ftpd / ftpcmd.y
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
1de703da
MD
34 *
35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
36 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.16.2.19 2003/02/11 14:28:28 yar Exp $
802341db 37 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
984263bc
MD
38 */
39
40/*
41 * Grammar for FTP commands.
42 * See RFC 959.
43 */
44
45%{
46
984263bc
MD
47#include <sys/param.h>
48#include <sys/socket.h>
49#include <sys/stat.h>
50
51#include <netinet/in.h>
52#include <arpa/ftp.h>
53
54#include <ctype.h>
55#include <errno.h>
56#include <glob.h>
57#include <libutil.h>
58#include <limits.h>
59#include <md5.h>
60#include <netdb.h>
61#include <pwd.h>
62#include <signal.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <syslog.h>
67#include <time.h>
68#include <unistd.h>
69
70#include "extern.h"
71#include "pathnames.h"
72
73extern union sockunion data_dest, his_addr;
74extern int hostinfo;
75extern int logged_in;
76extern struct passwd *pw;
77extern int guest;
78extern char *homedir;
79extern int paranoid;
80extern int logging;
81extern int type;
82extern int form;
83extern int ftpdebug;
84extern int timeout;
85extern int maxtimeout;
86extern int pdata;
87extern char *hostname;
88extern char remotehost[];
89extern char proctitle[];
90extern int usedefault;
91extern int transflag;
92extern char tmpline[];
93extern int readonly;
94extern int noepsv;
95extern int noretr;
96extern int noguestretr;
97extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
98
99off_t restart_point;
100
101static int cmd_type;
102static int cmd_form;
103static int cmd_bytesz;
104static int state;
105char cbuf[512];
106char *fromname = (char *) 0;
107
108extern int epsvall;
109
110%}
111
112%union {
113 struct {
114 off_t o;
115 int i;
116 } u;
117 char *s;
118}
119
120%token
121 A B C E F I
122 L N P R S T
123 ALL
124
125 SP CRLF COMMA
126
127 USER PASS ACCT REIN QUIT PORT
128 PASV TYPE STRU MODE RETR STOR
129 APPE MLFL MAIL MSND MSOM MSAM
130 MRSQ MRCP ALLO REST RNFR RNTO
131 ABOR DELE CWD LIST NLST SITE
132 STAT HELP NOOP MKD RMD PWD
133 CDUP STOU SMNT SYST SIZE MDTM
134 LPRT LPSV EPRT EPSV
135
136 UMASK IDLE CHMOD MDFIVE
137
138 LEXERR NOTIMPL
139
140%token <s> STRING
141%token <u> NUMBER
142
143%type <u.i> check_login octal_number byte_size
144%type <u.i> check_login_ro check_login_epsv
145%type <u.i> struct_code mode_code type_code form_code
146%type <s> pathstring pathname password username
147%type <s> ALL NOTIMPL
148
149%start cmd_list
150
151%%
152
153cmd_list
154 : /* empty */
155 | cmd_list cmd
156 {
157 if (fromname)
158 free(fromname);
159 fromname = (char *) 0;
160 restart_point = (off_t) 0;
161 }
162 | cmd_list rcmd
163 ;
164
165cmd
166 : USER SP username CRLF
167 {
168 user($3);
169 free($3);
170 }
171 | PASS SP password CRLF
172 {
173 pass($3);
174 free($3);
175 }
176 | PASS CRLF
177 {
178 pass("");
179 }
180 | PORT check_login SP host_port CRLF
181 {
182 if (epsvall) {
183 reply(501, "no PORT allowed after EPSV ALL");
184 goto port_done;
185 }
186 if (!$2)
187 goto port_done;
188 if (port_check("PORT") == 1)
189 goto port_done;
190#ifdef INET6
191 if ((his_addr.su_family != AF_INET6 ||
192 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
193 /* shoud never happen */
194 usedefault = 1;
195 reply(500, "Invalid address rejected.");
196 goto port_done;
197 }
198 port_check_v6("pcmd");
199#endif
200 port_done:
802341db 201 ;
984263bc
MD
202 }
203 | LPRT check_login SP host_long_port CRLF
204 {
205 if (epsvall) {
206 reply(501, "no LPRT allowed after EPSV ALL");
207 goto lprt_done;
208 }
209 if (!$2)
210 goto lprt_done;
211 if (port_check("LPRT") == 1)
212 goto lprt_done;
213#ifdef INET6
214 if (his_addr.su_family != AF_INET6) {
215 usedefault = 1;
216 reply(500, "Invalid address rejected.");
217 goto lprt_done;
218 }
219 if (port_check_v6("LPRT") == 1)
220 goto lprt_done;
221#endif
222 lprt_done:
802341db 223 ;
984263bc
MD
224 }
225 | EPRT check_login SP STRING CRLF
226 {
227 char delim;
228 char *tmp = NULL;
229 char *p, *q;
230 char *result[3];
231 struct addrinfo hints;
232 struct addrinfo *res;
233 int i;
234
235 if (epsvall) {
236 reply(501, "no EPRT allowed after EPSV ALL");
237 goto eprt_done;
238 }
239 if (!$2)
240 goto eprt_done;
241
242 memset(&data_dest, 0, sizeof(data_dest));
243 tmp = strdup($4);
244 if (ftpdebug)
245 syslog(LOG_DEBUG, "%s", tmp);
246 if (!tmp) {
247 fatalerror("not enough core");
248 /*NOTREACHED*/
249 }
250 p = tmp;
251 delim = p[0];
252 p++;
253 memset(result, 0, sizeof(result));
254 for (i = 0; i < 3; i++) {
255 q = strchr(p, delim);
256 if (!q || *q != delim) {
257 parsefail:
258 reply(500,
259 "Invalid argument, rejected.");
260 if (tmp)
261 free(tmp);
262 usedefault = 1;
263 goto eprt_done;
264 }
265 *q++ = '\0';
266 result[i] = p;
267 if (ftpdebug)
268 syslog(LOG_DEBUG, "%d: %s", i, p);
269 p = q;
270 }
271
272 /* some more sanity check */
273 p = result[0];
274 while (*p) {
275 if (!isdigit(*p))
276 goto parsefail;
277 p++;
278 }
279 p = result[2];
280 while (*p) {
281 if (!isdigit(*p))
282 goto parsefail;
283 p++;
284 }
285
286 /* grab address */
287 memset(&hints, 0, sizeof(hints));
288 if (atoi(result[0]) == 1)
289 hints.ai_family = PF_INET;
290#ifdef INET6
291 else if (atoi(result[0]) == 2)
292 hints.ai_family = PF_INET6;
293#endif
294 else
295 hints.ai_family = PF_UNSPEC; /*XXX*/
296 hints.ai_socktype = SOCK_STREAM;
297 i = getaddrinfo(result[1], result[2], &hints, &res);
298 if (i)
299 goto parsefail;
300 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
301#ifdef INET6
302 if (his_addr.su_family == AF_INET6
303 && data_dest.su_family == AF_INET6) {
304 /* XXX more sanity checks! */
305 data_dest.su_sin6.sin6_scope_id =
306 his_addr.su_sin6.sin6_scope_id;
307 }
308#endif
309 free(tmp);
310 tmp = NULL;
311
312 if (port_check("EPRT") == 1)
313 goto eprt_done;
314#ifdef INET6
315 if (his_addr.su_family != AF_INET6) {
316 usedefault = 1;
317 reply(500, "Invalid address rejected.");
318 goto eprt_done;
319 }
320 if (port_check_v6("EPRT") == 1)
321 goto eprt_done;
322#endif
323 eprt_done:
324 free($4);
325 }
326 | PASV check_login CRLF
327 {
328 if (epsvall)
329 reply(501, "no PASV allowed after EPSV ALL");
330 else if ($2)
331 passive();
332 }
333 | LPSV check_login CRLF
334 {
335 if (epsvall)
336 reply(501, "no LPSV allowed after EPSV ALL");
337 else if ($2)
338 long_passive("LPSV", PF_UNSPEC);
339 }
340 | EPSV check_login_epsv SP NUMBER CRLF
341 {
342 if ($2) {
343 int pf;
344 switch ($4.i) {
345 case 1:
346 pf = PF_INET;
347 break;
348#ifdef INET6
349 case 2:
350 pf = PF_INET6;
351 break;
352#endif
353 default:
354 pf = -1; /*junk value*/
355 break;
356 }
357 long_passive("EPSV", pf);
358 }
359 }
360 | EPSV check_login_epsv SP ALL CRLF
361 {
362 if ($2) {
363 reply(200,
364 "EPSV ALL command successful.");
365 epsvall++;
366 }
367 }
368 | EPSV check_login_epsv CRLF
369 {
370 if ($2)
371 long_passive("EPSV", PF_UNSPEC);
372 }
373 | TYPE check_login SP type_code CRLF
374 {
375 if ($2) {
376 switch (cmd_type) {
377
378 case TYPE_A:
379 if (cmd_form == FORM_N) {
380 reply(200, "Type set to A.");
381 type = cmd_type;
382 form = cmd_form;
383 } else
384 reply(504, "Form must be N.");
385 break;
386
387 case TYPE_E:
388 reply(504, "Type E not implemented.");
389 break;
390
391 case TYPE_I:
392 reply(200, "Type set to I.");
393 type = cmd_type;
394 break;
395
396 case TYPE_L:
397#if NBBY == 8
398 if (cmd_bytesz == 8) {
399 reply(200,
400 "Type set to L (byte size 8).");
401 type = cmd_type;
402 } else
403 reply(504, "Byte size must be 8.");
404#else /* NBBY == 8 */
405 UNIMPLEMENTED for NBBY != 8
406#endif /* NBBY == 8 */
407 }
408 }
409 }
410 | STRU check_login SP struct_code CRLF
411 {
412 if ($2) {
413 switch ($4) {
414
415 case STRU_F:
416 reply(200, "STRU F ok.");
417 break;
418
419 default:
420 reply(504, "Unimplemented STRU type.");
421 }
422 }
423 }
424 | MODE check_login SP mode_code CRLF
425 {
426 if ($2) {
427 switch ($4) {
428
429 case MODE_S:
430 reply(200, "MODE S ok.");
431 break;
432
433 default:
434 reply(502, "Unimplemented MODE type.");
435 }
436 }
437 }
438 | ALLO check_login SP NUMBER CRLF
439 {
440 if ($2) {
441 reply(202, "ALLO command ignored.");
442 }
443 }
444 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
445 {
446 if ($2) {
447 reply(202, "ALLO command ignored.");
448 }
449 }
450 | RETR check_login SP pathname CRLF
451 {
452 if (noretr || (guest && noguestretr))
453 reply(500, "RETR command is disabled");
454 else if ($2 && $4 != NULL)
455 retrieve((char *) 0, $4);
456
457 if ($4 != NULL)
458 free($4);
459 }
460 | STOR check_login_ro SP pathname CRLF
461 {
462 if ($2 && $4 != NULL)
463 store($4, "w", 0);
464 if ($4 != NULL)
465 free($4);
466 }
467 | APPE check_login_ro SP pathname CRLF
468 {
469 if ($2 && $4 != NULL)
470 store($4, "a", 0);
471 if ($4 != NULL)
472 free($4);
473 }
474 | NLST check_login CRLF
475 {
476 if ($2)
477 send_file_list(".");
478 }
479 | NLST check_login SP pathstring CRLF
480 {
481 if ($2)
482 send_file_list($4);
483 free($4);
484 }
485 | LIST check_login CRLF
486 {
487 if ($2)
488 retrieve(_PATH_LS " -lgA", "");
489 }
490 | LIST check_login SP pathstring CRLF
491 {
492 if ($2)
493 retrieve(_PATH_LS " -lgA %s", $4);
494 free($4);
495 }
496 | STAT check_login SP pathname CRLF
497 {
498 if ($2 && $4 != NULL)
499 statfilecmd($4);
500 if ($4 != NULL)
501 free($4);
502 }
503 | STAT check_login CRLF
504 {
505 if ($2) {
506 statcmd();
507 }
508 }
509 | DELE check_login_ro SP pathname CRLF
510 {
511 if ($2 && $4 != NULL)
512 delete($4);
513 if ($4 != NULL)
514 free($4);
515 }
516 | RNTO check_login_ro SP pathname CRLF
517 {
518 if ($2 && $4 != NULL) {
519 if (fromname) {
520 renamecmd(fromname, $4);
521 free(fromname);
522 fromname = (char *) 0;
523 } else {
524 reply(503, "Bad sequence of commands.");
525 }
526 }
527 if ($4 != NULL)
528 free($4);
529 }
530 | ABOR check_login CRLF
531 {
532 if ($2)
533 reply(225, "ABOR command successful.");
534 }
535 | CWD check_login CRLF
536 {
537 if ($2) {
538 cwd(homedir);
539 }
540 }
541 | CWD check_login SP pathname CRLF
542 {
543 if ($2 && $4 != NULL)
544 cwd($4);
545 if ($4 != NULL)
546 free($4);
547 }
548 | HELP CRLF
549 {
550 help(cmdtab, (char *) 0);
551 }
552 | HELP SP STRING CRLF
553 {
554 char *cp = $3;
555
556 if (strncasecmp(cp, "SITE", 4) == 0) {
557 cp = $3 + 4;
558 if (*cp == ' ')
559 cp++;
560 if (*cp)
561 help(sitetab, cp);
562 else
563 help(sitetab, (char *) 0);
564 } else
565 help(cmdtab, $3);
566 free($3);
567 }
568 | NOOP CRLF
569 {
570 reply(200, "NOOP command successful.");
571 }
572 | MKD check_login_ro SP pathname CRLF
573 {
574 if ($2 && $4 != NULL)
575 makedir($4);
576 if ($4 != NULL)
577 free($4);
578 }
579 | RMD check_login_ro SP pathname CRLF
580 {
581 if ($2 && $4 != NULL)
582 removedir($4);
583 if ($4 != NULL)
584 free($4);
585 }
586 | PWD check_login CRLF
587 {
588 if ($2)
589 pwd();
590 }
591 | CDUP check_login CRLF
592 {
593 if ($2)
594 cwd("..");
595 }
596 | SITE SP HELP CRLF
597 {
598 help(sitetab, (char *) 0);
599 }
600 | SITE SP HELP SP STRING CRLF
601 {
602 help(sitetab, $5);
603 free($5);
604 }
605 | SITE SP MDFIVE check_login SP pathname CRLF
606 {
607 char p[64], *q;
608
609 if ($4 && $6) {
610 q = MD5File($6, p);
611 if (q != NULL)
612 reply(200, "MD5(%s) = %s", $6, p);
613 else
614 perror_reply(550, $6);
615 }
616 if ($6)
617 free($6);
618 }
619 | SITE SP UMASK check_login CRLF
620 {
621 int oldmask;
622
623 if ($4) {
624 oldmask = umask(0);
625 (void) umask(oldmask);
626 reply(200, "Current UMASK is %03o", oldmask);
627 }
628 }
629 | SITE SP UMASK check_login SP octal_number CRLF
630 {
631 int oldmask;
632
633 if ($4) {
634 if (($6 == -1) || ($6 > 0777)) {
635 reply(501, "Bad UMASK value");
636 } else {
637 oldmask = umask($6);
638 reply(200,
639 "UMASK set to %03o (was %03o)",
640 $6, oldmask);
641 }
642 }
643 }
644 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
645 {
646 if ($4 && ($8 != NULL)) {
647 if (($6 == -1 ) || ($6 > 0777))
648 reply(501, "Bad mode value");
649 else if (chmod($8, $6) < 0)
650 perror_reply(550, $8);
651 else
652 reply(200, "CHMOD command successful.");
653 }
654 if ($8 != NULL)
655 free($8);
656 }
657 | SITE SP check_login IDLE CRLF
658 {
659 if ($3)
660 reply(200,
661 "Current IDLE time limit is %d seconds; max %d",
662 timeout, maxtimeout);
663 }
664 | SITE SP check_login IDLE SP NUMBER CRLF
665 {
666 if ($3) {
667 if ($6.i < 30 || $6.i > maxtimeout) {
668 reply(501,
669 "Maximum IDLE time must be between 30 and %d seconds",
670 maxtimeout);
671 } else {
672 timeout = $6.i;
673 (void) alarm((unsigned) timeout);
674 reply(200,
675 "Maximum IDLE time set to %d seconds",
676 timeout);
677 }
678 }
679 }
680 | STOU check_login_ro SP pathname CRLF
681 {
682 if ($2 && $4 != NULL)
683 store($4, "w", 1);
684 if ($4 != NULL)
685 free($4);
686 }
687 | SYST check_login CRLF
688 {
689 if ($2)
690#ifdef unix
691#ifdef BSD
692 reply(215, "UNIX Type: L%d Version: BSD-%d",
693 NBBY, BSD);
694#else /* BSD */
695 reply(215, "UNIX Type: L%d", NBBY);
696#endif /* BSD */
697#else /* unix */
698 reply(215, "UNKNOWN Type: L%d", NBBY);
699#endif /* unix */
700 }
701
702 /*
703 * SIZE is not in RFC959, but Postel has blessed it and
704 * it will be in the updated RFC.
705 *
706 * Return size of file in a format suitable for
707 * using with RESTART (we just count bytes).
708 */
709 | SIZE check_login SP pathname CRLF
710 {
711 if ($2 && $4 != NULL)
712 sizecmd($4);
713 if ($4 != NULL)
714 free($4);
715 }
716
717 /*
718 * MDTM is not in RFC959, but Postel has blessed it and
719 * it will be in the updated RFC.
720 *
721 * Return modification time of file as an ISO 3307
722 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
723 * where xxx is the fractional second (of any precision,
724 * not necessarily 3 digits)
725 */
726 | MDTM check_login SP pathname CRLF
727 {
728 if ($2 && $4 != NULL) {
729 struct stat stbuf;
730 if (stat($4, &stbuf) < 0)
731 reply(550, "%s: %s",
732 $4, strerror(errno));
733 else if (!S_ISREG(stbuf.st_mode)) {
734 reply(550, "%s: not a plain file.", $4);
735 } else {
736 struct tm *t;
737 t = gmtime(&stbuf.st_mtime);
738 reply(213,
739 "%04d%02d%02d%02d%02d%02d",
740 1900 + t->tm_year,
741 t->tm_mon+1, t->tm_mday,
742 t->tm_hour, t->tm_min, t->tm_sec);
743 }
744 }
745 if ($4 != NULL)
746 free($4);
747 }
748 | QUIT CRLF
749 {
750 reply(221, "Goodbye.");
751 dologout(0);
752 }
753 | NOTIMPL
754 {
755 nack($1);
756 }
757 | error
758 {
759 yyclearin; /* discard lookahead data */
760 yyerrok; /* clear error condition */
761 state = CMD; /* reset lexer state */
762 }
763 ;
764rcmd
765 : RNFR check_login_ro SP pathname CRLF
766 {
767 restart_point = (off_t) 0;
768 if ($2 && $4) {
769 if (fromname)
770 free(fromname);
771 fromname = (char *) 0;
772 if (renamefrom($4))
773 fromname = $4;
774 else
775 free($4);
776 } else if ($4) {
777 free($4);
778 }
779 }
780 | REST check_login SP NUMBER CRLF
781 {
782 if ($2) {
783 if (fromname)
784 free(fromname);
785 fromname = (char *) 0;
786 restart_point = $4.o;
787 reply(350, "Restarting at %llu. %s",
788 restart_point,
789 "Send STORE or RETRIEVE to initiate transfer.");
790 }
791 }
792 ;
793
794username
795 : STRING
796 ;
797
798password
799 : /* empty */
800 {
801 $$ = (char *)calloc(1, sizeof(char));
802 }
803 | STRING
804 ;
805
806byte_size
807 : NUMBER
808 {
809 $$ = $1.i;
810 }
811 ;
812
813host_port
814 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
815 NUMBER COMMA NUMBER
816 {
817 char *a, *p;
818
819 data_dest.su_len = sizeof(struct sockaddr_in);
820 data_dest.su_family = AF_INET;
821 p = (char *)&data_dest.su_sin.sin_port;
822 p[0] = $9.i; p[1] = $11.i;
823 a = (char *)&data_dest.su_sin.sin_addr;
824 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
825 }
826 ;
827
828host_long_port
829 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
830 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
831 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
832 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
833 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
834 NUMBER
835 {
836 char *a, *p;
837
838 memset(&data_dest, 0, sizeof(data_dest));
839 data_dest.su_len = sizeof(struct sockaddr_in6);
840 data_dest.su_family = AF_INET6;
841 p = (char *)&data_dest.su_port;
842 p[0] = $39.i; p[1] = $41.i;
843 a = (char *)&data_dest.su_sin6.sin6_addr;
844 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
845 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
846 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
847 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
848 if (his_addr.su_family == AF_INET6) {
849 /* XXX more sanity checks! */
850 data_dest.su_sin6.sin6_scope_id =
851 his_addr.su_sin6.sin6_scope_id;
852 }
853 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
854 memset(&data_dest, 0, sizeof(data_dest));
855 }
856 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
857 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
858 NUMBER
859 {
860 char *a, *p;
861
862 memset(&data_dest, 0, sizeof(data_dest));
863 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
864 data_dest.su_family = AF_INET;
865 p = (char *)&data_dest.su_port;
866 p[0] = $15.i; p[1] = $17.i;
867 a = (char *)&data_dest.su_sin.sin_addr;
868 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
869 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
870 memset(&data_dest, 0, sizeof(data_dest));
871 }
872 ;
873
874form_code
875 : N
876 {
877 $$ = FORM_N;
878 }
879 | T
880 {
881 $$ = FORM_T;
882 }
883 | C
884 {
885 $$ = FORM_C;
886 }
887 ;
888
889type_code
890 : A
891 {
892 cmd_type = TYPE_A;
893 cmd_form = FORM_N;
894 }
895 | A SP form_code
896 {
897 cmd_type = TYPE_A;
898 cmd_form = $3;
899 }
900 | E
901 {
902 cmd_type = TYPE_E;
903 cmd_form = FORM_N;
904 }
905 | E SP form_code
906 {
907 cmd_type = TYPE_E;
908 cmd_form = $3;
909 }
910 | I
911 {
912 cmd_type = TYPE_I;
913 }
914 | L
915 {
916 cmd_type = TYPE_L;
917 cmd_bytesz = NBBY;
918 }
919 | L SP byte_size
920 {
921 cmd_type = TYPE_L;
922 cmd_bytesz = $3;
923 }
924 /* this is for a bug in the BBN ftp */
925 | L byte_size
926 {
927 cmd_type = TYPE_L;
928 cmd_bytesz = $2;
929 }
930 ;
931
932struct_code
933 : F
934 {
935 $$ = STRU_F;
936 }
937 | R
938 {
939 $$ = STRU_R;
940 }
941 | P
942 {
943 $$ = STRU_P;
944 }
945 ;
946
947mode_code
948 : S
949 {
950 $$ = MODE_S;
951 }
952 | B
953 {
954 $$ = MODE_B;
955 }
956 | C
957 {
958 $$ = MODE_C;
959 }
960 ;
961
962pathname
963 : pathstring
964 {
965 /*
966 * Problem: this production is used for all pathname
967 * processing, but only gives a 550 error reply.
968 * This is a valid reply in some cases but not in others.
969 */
970 if (logged_in && $1) {
971 glob_t gl;
972 char *p, **pp;
973 int flags =
974 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
975 int n;
976
977 memset(&gl, 0, sizeof(gl));
978 flags |= GLOB_LIMIT;
979 gl.gl_matchc = MAXGLOBARGS;
980 if (glob($1, flags, NULL, &gl) ||
981 gl.gl_pathc == 0) {
982 reply(550, "wildcard expansion error");
983 $$ = NULL;
984 } else {
985 n = 0;
986 for (pp = gl.gl_pathv; *pp; pp++)
987 if (strcspn(*pp, "\r\n") ==
988 strlen(*pp)) {
989 p = *pp;
990 n++;
991 }
992 if (n == 0)
993 $$ = strdup($1);
994 else if (n == 1)
995 $$ = strdup(p);
996 else {
997 reply(550, "ambiguous");
998 $$ = NULL;
999 }
1000 }
1001 globfree(&gl);
1002 free($1);
1003 } else
1004 $$ = $1;
1005 }
1006 ;
1007
1008pathstring
1009 : STRING
1010 ;
1011
1012octal_number
1013 : NUMBER
1014 {
1015 int ret, dec, multby, digit;
1016
1017 /*
1018 * Convert a number that was read as decimal number
1019 * to what it would be if it had been read as octal.
1020 */
1021 dec = $1.i;
1022 multby = 1;
1023 ret = 0;
1024 while (dec) {
1025 digit = dec%10;
1026 if (digit > 7) {
1027 ret = -1;
1028 break;
1029 }
1030 ret += digit * multby;
1031 multby *= 8;
1032 dec /= 10;
1033 }
1034 $$ = ret;
1035 }
1036 ;
1037
1038
1039check_login
1040 : /* empty */
1041 {
1042 $$ = check_login1();
1043 }
1044 ;
1045
1046check_login_epsv
1047 : /* empty */
1048 {
1049 if (noepsv) {
1050 reply(500, "EPSV command disabled");
1051 $$ = 0;
1052 }
1053 else
1054 $$ = check_login1();
1055 }
1056 ;
1057
1058check_login_ro
1059 : /* empty */
1060 {
1061 if (readonly) {
1062 reply(550, "Permission denied.");
1063 $$ = 0;
1064 }
1065 else
1066 $$ = check_login1();
1067 }
1068 ;
1069
1070%%
1071
1072#define CMD 0 /* beginning of command */
1073#define ARGS 1 /* expect miscellaneous arguments */
1074#define STR1 2 /* expect SP followed by STRING */
1075#define STR2 3 /* expect STRING */
1076#define OSTR 4 /* optional SP then STRING */
1077#define ZSTR1 5 /* optional SP then optional STRING */
1078#define ZSTR2 6 /* optional STRING after SP */
1079#define SITECMD 7 /* SITE command */
1080#define NSTR 8 /* Number followed by a string */
1081
1082#define MAXGLOBARGS 1000
1083
1084#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1085
1086struct tab {
1087 char *name;
1088 short token;
1089 short state;
1090 short implemented; /* 1 if command is implemented */
1091 char *help;
1092};
1093
1094struct tab cmdtab[] = { /* In order defined in RFC 765 */
1095 { "USER", USER, STR1, 1, "<sp> username" },
1096 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1097 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1098 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1099 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1100 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1101 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1102 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1103 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1104 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1105 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1106 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1107 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1108 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1109 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1110 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1111 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1112 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1113 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1114 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1115 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1116 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1117 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1118 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1119 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1120 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1121 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1122 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1123 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1124 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1125 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1126 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1127 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1128 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1129 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1130 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1131 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1132 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1133 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1134 { "NOOP", NOOP, ARGS, 1, "" },
1135 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1136 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1137 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1138 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1139 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1140 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1141 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1142 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1143 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1144 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1145 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1146 { NULL, 0, 0, 0, 0 }
1147};
1148
1149struct tab sitetab[] = {
1150 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1151 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1152 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1153 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1154 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1155 { NULL, 0, 0, 0, 0 }
1156};
1157
3eefb696
MD
1158static char *copy (char *);
1159static void help (struct tab *, char *);
984263bc 1160static struct tab *
3eefb696
MD
1161 lookup (struct tab *, char *);
1162static int port_check (const char *);
1163static int port_check_v6 (const char *);
1164static void sizecmd (char *);
1165static void toolong (int);
1166static void v4map_data_dest (void);
1167static int yylex (void);
984263bc
MD
1168
1169static struct tab *
1170lookup(p, cmd)
1171 struct tab *p;
1172 char *cmd;
1173{
1174
1175 for (; p->name != NULL; p++)
1176 if (strcmp(cmd, p->name) == 0)
1177 return (p);
1178 return (0);
1179}
1180
1181#include <arpa/telnet.h>
1182
1183/*
1184 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1185 */
1186char *
1187getline(s, n, iop)
1188 char *s;
1189 int n;
1190 FILE *iop;
1191{
1192 int c;
1193 register char *cs;
1194
1195 cs = s;
1196/* tmpline may contain saved command from urgent mode interruption */
1197 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1198 *cs++ = tmpline[c];
1199 if (tmpline[c] == '\n') {
1200 *cs++ = '\0';
1201 if (ftpdebug)
1202 syslog(LOG_DEBUG, "command: %s", s);
1203 tmpline[0] = '\0';
1204 return(s);
1205 }
1206 if (c == 0)
1207 tmpline[0] = '\0';
1208 }
1209 while ((c = getc(iop)) != EOF) {
1210 c &= 0377;
1211 if (c == IAC) {
1212 if ((c = getc(iop)) != EOF) {
1213 c &= 0377;
1214 switch (c) {
1215 case WILL:
1216 case WONT:
1217 c = getc(iop);
1218 printf("%c%c%c", IAC, DONT, 0377&c);
1219 (void) fflush(stdout);
1220 continue;
1221 case DO:
1222 case DONT:
1223 c = getc(iop);
1224 printf("%c%c%c", IAC, WONT, 0377&c);
1225 (void) fflush(stdout);
1226 continue;
1227 case IAC:
1228 break;
1229 default:
1230 continue; /* ignore command */
1231 }
1232 }
1233 }
1234 *cs++ = c;
1235 if (--n <= 0 || c == '\n')
1236 break;
1237 }
1238 if (c == EOF && cs == s)
1239 return (NULL);
1240 *cs++ = '\0';
1241 if (ftpdebug) {
1242 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1243 /* Don't syslog passwords */
1244 syslog(LOG_DEBUG, "command: %.5s ???", s);
1245 } else {
1246 register char *cp;
1247 register int len;
1248
1249 /* Don't syslog trailing CR-LF */
1250 len = strlen(s);
1251 cp = s + len - 1;
1252 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1253 --cp;
1254 --len;
1255 }
1256 syslog(LOG_DEBUG, "command: %.*s", len, s);
1257 }
1258 }
1259 return (s);
1260}
1261
1262static void
1263toolong(signo)
1264 int signo;
1265{
1266
1267 reply(421,
1268 "Timeout (%d seconds): closing control connection.", timeout);
1269 if (logging)
1270 syslog(LOG_INFO, "User %s timed out after %d seconds",
1271 (pw ? pw -> pw_name : "unknown"), timeout);
1272 dologout(1);
1273}
1274
1275static int
1276yylex()
1277{
1278 static int cpos;
1279 char *cp, *cp2;
1280 struct tab *p;
1281 int n;
1282 char c;
1283
1284 for (;;) {
1285 switch (state) {
1286
1287 case CMD:
1288 (void) signal(SIGALRM, toolong);
1289 (void) alarm((unsigned) timeout);
1290 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1291 reply(221, "You could at least say goodbye.");
1292 dologout(0);
1293 }
1294 (void) alarm(0);
1295#ifdef SETPROCTITLE
1296 if (strncasecmp(cbuf, "PASS", 4) != 0)
1297 setproctitle("%s: %s", proctitle, cbuf);
1298#endif /* SETPROCTITLE */
1299 if ((cp = strchr(cbuf, '\r'))) {
1300 *cp++ = '\n';
1301 *cp = '\0';
1302 }
1303 if ((cp = strpbrk(cbuf, " \n")))
1304 cpos = cp - cbuf;
1305 if (cpos == 0)
1306 cpos = 4;
1307 c = cbuf[cpos];
1308 cbuf[cpos] = '\0';
1309 upper(cbuf);
1310 p = lookup(cmdtab, cbuf);
1311 cbuf[cpos] = c;
1312 if (p != 0) {
1313 yylval.s = p->name;
1314 if (!p->implemented)
1315 return (NOTIMPL); /* state remains CMD */
1316 state = p->state;
1317 return (p->token);
1318 }
1319 break;
1320
1321 case SITECMD:
1322 if (cbuf[cpos] == ' ') {
1323 cpos++;
1324 return (SP);
1325 }
1326 cp = &cbuf[cpos];
1327 if ((cp2 = strpbrk(cp, " \n")))
1328 cpos = cp2 - cbuf;
1329 c = cbuf[cpos];
1330 cbuf[cpos] = '\0';
1331 upper(cp);
1332 p = lookup(sitetab, cp);
1333 cbuf[cpos] = c;
1334 if (guest == 0 && p != 0) {
1335 yylval.s = p->name;
1336 if (!p->implemented) {
1337 state = CMD;
1338 return (NOTIMPL);
1339 }
1340 state = p->state;
1341 return (p->token);
1342 }
1343 state = CMD;
1344 break;
1345
1346 case ZSTR1:
1347 case OSTR:
1348 if (cbuf[cpos] == '\n') {
1349 state = CMD;
1350 return (CRLF);
1351 }
1352 /* FALLTHROUGH */
1353
1354 case STR1:
1355 dostr1:
1356 if (cbuf[cpos] == ' ') {
1357 cpos++;
1358 state = state == OSTR ? STR2 : state+1;
1359 return (SP);
1360 }
1361 break;
1362
1363 case ZSTR2:
1364 if (cbuf[cpos] == '\n') {
1365 state = CMD;
1366 return (CRLF);
1367 }
1368 /* FALLTHROUGH */
1369
1370 case STR2:
1371 cp = &cbuf[cpos];
1372 n = strlen(cp);
1373 cpos += n - 1;
1374 /*
1375 * Make sure the string is nonempty and \n terminated.
1376 */
1377 if (n > 1 && cbuf[cpos] == '\n') {
1378 cbuf[cpos] = '\0';
1379 yylval.s = copy(cp);
1380 cbuf[cpos] = '\n';
1381 state = ARGS;
1382 return (STRING);
1383 }
1384 break;
1385
1386 case NSTR:
1387 if (cbuf[cpos] == ' ') {
1388 cpos++;
1389 return (SP);
1390 }
1391 if (isdigit(cbuf[cpos])) {
1392 cp = &cbuf[cpos];
1393 while (isdigit(cbuf[++cpos]))
1394 ;
1395 c = cbuf[cpos];
1396 cbuf[cpos] = '\0';
1397 yylval.u.i = atoi(cp);
1398 cbuf[cpos] = c;
1399 state = STR1;
1400 return (NUMBER);
1401 }
1402 state = STR1;
1403 goto dostr1;
1404
1405 case ARGS:
1406 if (isdigit(cbuf[cpos])) {
1407 cp = &cbuf[cpos];
1408 while (isdigit(cbuf[++cpos]))
1409 ;
1410 c = cbuf[cpos];
1411 cbuf[cpos] = '\0';
1412 yylval.u.i = atoi(cp);
1413 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1414 cbuf[cpos] = c;
1415 return (NUMBER);
1416 }
1417 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1418 && !isalnum(cbuf[cpos + 3])) {
1419 cpos += 3;
1420 return ALL;
1421 }
1422 switch (cbuf[cpos++]) {
1423
1424 case '\n':
1425 state = CMD;
1426 return (CRLF);
1427
1428 case ' ':
1429 return (SP);
1430
1431 case ',':
1432 return (COMMA);
1433
1434 case 'A':
1435 case 'a':
1436 return (A);
1437
1438 case 'B':
1439 case 'b':
1440 return (B);
1441
1442 case 'C':
1443 case 'c':
1444 return (C);
1445
1446 case 'E':
1447 case 'e':
1448 return (E);
1449
1450 case 'F':
1451 case 'f':
1452 return (F);
1453
1454 case 'I':
1455 case 'i':
1456 return (I);
1457
1458 case 'L':
1459 case 'l':
1460 return (L);
1461
1462 case 'N':
1463 case 'n':
1464 return (N);
1465
1466 case 'P':
1467 case 'p':
1468 return (P);
1469
1470 case 'R':
1471 case 'r':
1472 return (R);
1473
1474 case 'S':
1475 case 's':
1476 return (S);
1477
1478 case 'T':
1479 case 't':
1480 return (T);
1481
1482 }
1483 break;
1484
1485 default:
1486 fatalerror("Unknown state in scanner.");
1487 }
1488 state = CMD;
1489 return (LEXERR);
1490 }
1491}
1492
1493void
1494upper(s)
1495 char *s;
1496{
1497 while (*s != '\0') {
1498 if (islower(*s))
1499 *s = toupper(*s);
1500 s++;
1501 }
1502}
1503
1504static char *
1505copy(s)
1506 char *s;
1507{
1508 char *p;
1509
1510 p = malloc((unsigned) strlen(s) + 1);
1511 if (p == NULL)
1512 fatalerror("Ran out of memory.");
1513 (void) strcpy(p, s);
1514 return (p);
1515}
1516
1517static void
1518help(ctab, s)
1519 struct tab *ctab;
1520 char *s;
1521{
1522 struct tab *c;
1523 int width, NCMDS;
1524 char *type;
1525
1526 if (ctab == sitetab)
1527 type = "SITE ";
1528 else
1529 type = "";
1530 width = 0, NCMDS = 0;
1531 for (c = ctab; c->name != NULL; c++) {
1532 int len = strlen(c->name);
1533
1534 if (len > width)
1535 width = len;
1536 NCMDS++;
1537 }
1538 width = (width + 8) &~ 7;
1539 if (s == 0) {
1540 int i, j, w;
1541 int columns, lines;
1542
1543 lreply(214, "The following %scommands are recognized %s.",
1544 type, "(* =>'s unimplemented)");
1545 columns = 76 / width;
1546 if (columns == 0)
1547 columns = 1;
1548 lines = (NCMDS + columns - 1) / columns;
1549 for (i = 0; i < lines; i++) {
1550 printf(" ");
1551 for (j = 0; j < columns; j++) {
1552 c = ctab + j * lines + i;
1553 printf("%s%c", c->name,
1554 c->implemented ? ' ' : '*');
1555 if (c + lines >= &ctab[NCMDS])
1556 break;
1557 w = strlen(c->name) + 1;
1558 while (w < width) {
1559 putchar(' ');
1560 w++;
1561 }
1562 }
1563 printf("\r\n");
1564 }
1565 (void) fflush(stdout);
1566 if (hostinfo)
1567 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1568 else
1569 reply(214, "End.");
1570 return;
1571 }
1572 upper(s);
1573 c = lookup(ctab, s);
1574 if (c == (struct tab *)0) {
1575 reply(502, "Unknown command %s.", s);
1576 return;
1577 }
1578 if (c->implemented)
1579 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1580 else
1581 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1582 c->name, c->help);
1583}
1584
1585static void
1586sizecmd(filename)
1587 char *filename;
1588{
1589 switch (type) {
1590 case TYPE_L:
1591 case TYPE_I: {
1592 struct stat stbuf;
1593 if (stat(filename, &stbuf) < 0)
1594 perror_reply(550, filename);
1595 else if (!S_ISREG(stbuf.st_mode))
1596 reply(550, "%s: not a plain file.", filename);
1597 else
1598 reply(213, "%qu", stbuf.st_size);
1599 break; }
1600 case TYPE_A: {
1601 FILE *fin;
1602 int c;
1603 off_t count;
1604 struct stat stbuf;
1605 fin = fopen(filename, "r");
1606 if (fin == NULL) {
1607 perror_reply(550, filename);
1608 return;
1609 }
1610 if (fstat(fileno(fin), &stbuf) < 0) {
1611 perror_reply(550, filename);
1612 (void) fclose(fin);
1613 return;
1614 } else if (!S_ISREG(stbuf.st_mode)) {
1615 reply(550, "%s: not a plain file.", filename);
1616 (void) fclose(fin);
1617 return;
1618 } else if (stbuf.st_size > MAXASIZE) {
1619 reply(550, "%s: too large for type A SIZE.", filename);
1620 (void) fclose(fin);
1621 return;
1622 }
1623
1624 count = 0;
1625 while((c=getc(fin)) != EOF) {
1626 if (c == '\n') /* will get expanded to \r\n */
1627 count++;
1628 count++;
1629 }
1630 (void) fclose(fin);
1631
1632 reply(213, "%qd", count);
1633 break; }
1634 default:
1635 reply(504, "SIZE not implemented for type %s.",
1636 typenames[type]);
1637 }
1638}
1639
1640/* Return 1, if port check is done. Return 0, if not yet. */
1641static int
1642port_check(pcmd)
1643 const char *pcmd;
1644{
1645 if (his_addr.su_family == AF_INET) {
1646 if (data_dest.su_family != AF_INET) {
1647 usedefault = 1;
1648 reply(500, "Invalid address rejected.");
1649 return 1;
1650 }
1651 if (paranoid &&
1652 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1653 memcmp(&data_dest.su_sin.sin_addr,
1654 &his_addr.su_sin.sin_addr,
1655 sizeof(data_dest.su_sin.sin_addr)))) {
1656 usedefault = 1;
1657 reply(500, "Illegal PORT range rejected.");
1658 } else {
1659 usedefault = 0;
1660 if (pdata >= 0) {
1661 (void) close(pdata);
1662 pdata = -1;
1663 }
1664 reply(200, "%s command successful.", pcmd);
1665 }
1666 return 1;
1667 }
1668 return 0;
1669}
1670
1671static int
1672check_login1()
1673{
1674 if (logged_in)
1675 return 1;
1676 else {
1677 reply(530, "Please login with USER and PASS.");
1678 return 0;
1679 }
1680}
1681
1682#ifdef INET6
1683/* Return 1, if port check is done. Return 0, if not yet. */
1684static int
1685port_check_v6(pcmd)
1686 const char *pcmd;
1687{
1688 if (his_addr.su_family == AF_INET6) {
1689 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1690 /* Convert data_dest into v4 mapped sockaddr.*/
1691 v4map_data_dest();
1692 if (data_dest.su_family != AF_INET6) {
1693 usedefault = 1;
1694 reply(500, "Invalid address rejected.");
1695 return 1;
1696 }
1697 if (paranoid &&
1698 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1699 memcmp(&data_dest.su_sin6.sin6_addr,
1700 &his_addr.su_sin6.sin6_addr,
1701 sizeof(data_dest.su_sin6.sin6_addr)))) {
1702 usedefault = 1;
1703 reply(500, "Illegal PORT range rejected.");
1704 } else {
1705 usedefault = 0;
1706 if (pdata >= 0) {
1707 (void) close(pdata);
1708 pdata = -1;
1709 }
1710 reply(200, "%s command successful.", pcmd);
1711 }
1712 return 1;
1713 }
1714 return 0;
1715}
1716
1717static void
1718v4map_data_dest()
1719{
1720 struct in_addr savedaddr;
1721 int savedport;
1722
1723 if (data_dest.su_family != AF_INET) {
1724 usedefault = 1;
1725 reply(500, "Invalid address rejected.");
1726 return;
1727 }
1728
1729 savedaddr = data_dest.su_sin.sin_addr;
1730 savedport = data_dest.su_port;
1731
1732 memset(&data_dest, 0, sizeof(data_dest));
1733 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1734 data_dest.su_sin6.sin6_family = AF_INET6;
1735 data_dest.su_sin6.sin6_port = savedport;
1736 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1737 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1738 (caddr_t)&savedaddr, sizeof(savedaddr));
1739}
1740#endif