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