nfs: fix real/effective id mismatch in nfs_access
[dragonfly.git] / libexec / ftpd / ftpcmd.y
blob14e895d2fa0050d988c1c6a3fa97b885f85da266
1 /*
2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
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.
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.
33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
34 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.67 2008/12/23 01:23:09 cperciva Exp $
35 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
39 * Grammar for FTP commands.
40 * See RFC 959.
45 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <sys/stat.h>
49 #include <netinet/in.h>
50 #include <arpa/ftp.h>
52 #include <ctype.h>
53 #include <errno.h>
54 #include <glob.h>
55 #include <libutil.h>
56 #include <limits.h>
57 #include <md5.h>
58 #include <netdb.h>
59 #include <pwd.h>
60 #include <signal.h>
61 #include <stdint.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <syslog.h>
66 #include <time.h>
67 #include <unistd.h>
69 #include "extern.h"
70 #include "pathnames.h"
72 extern union sockunion data_dest, his_addr;
73 extern int hostinfo;
74 extern int logged_in;
75 extern struct passwd *pw;
76 extern int guest;
77 extern char *homedir;
78 extern int paranoid;
79 extern int logging;
80 extern int type;
81 extern int form;
82 extern int ftpdebug;
83 extern int timeout;
84 extern int maxtimeout;
85 extern int pdata;
86 extern char *hostname;
87 extern char proctitle[];
88 extern int usedefault;
89 extern char tmpline[];
90 extern int readonly;
91 extern int assumeutf8;
92 extern int noepsv;
93 extern int noretr;
94 extern int noguestretr;
95 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
97 off_t restart_point;
99 static int cmd_type;
100 static int cmd_form;
101 static int cmd_bytesz;
102 static int state;
103 char cbuf[512];
104 char *fromname = NULL;
106 extern int epsvall;
110 %union {
111 struct {
112 off_t o;
113 int i;
114 } u;
115 char *s;
118 %token
119 A B C E F I
120 L N P R S T
123 SP CRLF COMMA
125 USER PASS ACCT REIN QUIT PORT
126 PASV TYPE STRU MODE RETR STOR
127 APPE MLFL MAIL MSND MSOM MSAM
128 MRSQ MRCP ALLO REST RNFR RNTO
129 ABOR DELE CWD LIST NLST SITE
130 STAT HELP NOOP MKD RMD PWD
131 CDUP STOU SMNT SYST SIZE MDTM
132 LPRT LPSV EPRT EPSV FEAT
134 UMASK IDLE CHMOD MDFIVE
136 LEXERR NOTIMPL
138 %token <s> STRING
139 %token <u> NUMBER
141 %type <u.i> check_login octal_number byte_size
142 %type <u.i> check_login_ro check_login_epsv
143 %type <u.i> struct_code mode_code type_code form_code
144 %type <s> pathstring pathname password username
145 %type <s> ALL NOTIMPL
147 %start cmd_list
151 cmd_list
152 : /* empty */
153 | cmd_list cmd
155 if (fromname)
156 free(fromname);
157 fromname = NULL;
158 restart_point = 0;
160 | cmd_list rcmd
164 : USER SP username CRLF
166 user($3);
167 free($3);
169 | PASS SP password CRLF
171 pass($3);
172 free($3);
174 | PASS CRLF
176 pass("");
178 | PORT check_login SP host_port CRLF
180 if (epsvall) {
181 reply(501, "No PORT allowed after EPSV ALL.");
182 goto port_done;
184 if (!$2)
185 goto port_done;
186 if (port_check("PORT") == 1)
187 goto port_done;
188 #ifdef INET6
189 if ((his_addr.su_family != AF_INET6 ||
190 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
191 /* shoud never happen */
192 usedefault = 1;
193 reply(500, "Invalid address rejected.");
194 goto port_done;
196 port_check_v6("pcmd");
197 #endif
198 port_done:
201 | LPRT check_login SP host_long_port CRLF
203 if (epsvall) {
204 reply(501, "No LPRT allowed after EPSV ALL.");
205 goto lprt_done;
207 if (!$2)
208 goto lprt_done;
209 if (port_check("LPRT") == 1)
210 goto lprt_done;
211 #ifdef INET6
212 if (his_addr.su_family != AF_INET6) {
213 usedefault = 1;
214 reply(500, "Invalid address rejected.");
215 goto lprt_done;
217 if (port_check_v6("LPRT") == 1)
218 goto lprt_done;
219 #endif
220 lprt_done:
223 | EPRT check_login SP STRING CRLF
225 char delim;
226 char *tmp = NULL;
227 char *p, *q;
228 char *result[3];
229 struct addrinfo hints;
230 struct addrinfo *res;
231 int i;
233 if (epsvall) {
234 reply(501, "No EPRT allowed after EPSV ALL.");
235 goto eprt_done;
237 if (!$2)
238 goto eprt_done;
240 memset(&data_dest, 0, sizeof(data_dest));
241 tmp = strdup($4);
242 if (ftpdebug)
243 syslog(LOG_DEBUG, "%s", tmp);
244 if (!tmp) {
245 fatalerror("not enough core");
246 /*NOTREACHED*/
248 p = tmp;
249 delim = p[0];
250 p++;
251 memset(result, 0, sizeof(result));
252 for (i = 0; i < 3; i++) {
253 q = strchr(p, delim);
254 if (!q || *q != delim) {
255 parsefail:
256 reply(500,
257 "Invalid argument, rejected.");
258 if (tmp)
259 free(tmp);
260 usedefault = 1;
261 goto eprt_done;
263 *q++ = '\0';
264 result[i] = p;
265 if (ftpdebug)
266 syslog(LOG_DEBUG, "%d: %s", i, p);
267 p = q;
270 /* some more sanity check */
271 p = result[0];
272 while (*p) {
273 if (!isdigit(*p))
274 goto parsefail;
275 p++;
277 p = result[2];
278 while (*p) {
279 if (!isdigit(*p))
280 goto parsefail;
281 p++;
284 /* grab address */
285 memset(&hints, 0, sizeof(hints));
286 if (atoi(result[0]) == 1)
287 hints.ai_family = PF_INET;
288 #ifdef INET6
289 else if (atoi(result[0]) == 2)
290 hints.ai_family = PF_INET6;
291 #endif
292 else
293 hints.ai_family = PF_UNSPEC; /*XXX*/
294 hints.ai_socktype = SOCK_STREAM;
295 i = getaddrinfo(result[1], result[2], &hints, &res);
296 if (i)
297 goto parsefail;
298 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
299 #ifdef INET6
300 if (his_addr.su_family == AF_INET6
301 && data_dest.su_family == AF_INET6) {
302 /* XXX more sanity checks! */
303 data_dest.su_sin6.sin6_scope_id =
304 his_addr.su_sin6.sin6_scope_id;
306 #endif
307 free(tmp);
308 tmp = NULL;
310 if (port_check("EPRT") == 1)
311 goto eprt_done;
312 #ifdef INET6
313 if (his_addr.su_family != AF_INET6) {
314 usedefault = 1;
315 reply(500, "Invalid address rejected.");
316 goto eprt_done;
318 if (port_check_v6("EPRT") == 1)
319 goto eprt_done;
320 #endif
321 eprt_done:
322 free($4);
324 | PASV check_login CRLF
326 if (epsvall)
327 reply(501, "No PASV allowed after EPSV ALL.");
328 else if ($2)
329 passive();
331 | LPSV check_login CRLF
333 if (epsvall)
334 reply(501, "No LPSV allowed after EPSV ALL.");
335 else if ($2)
336 long_passive("LPSV", PF_UNSPEC);
338 | EPSV check_login_epsv SP NUMBER CRLF
340 if ($2) {
341 int pf;
342 switch ($4.i) {
343 case 1:
344 pf = PF_INET;
345 break;
346 #ifdef INET6
347 case 2:
348 pf = PF_INET6;
349 break;
350 #endif
351 default:
352 pf = -1; /*junk value*/
353 break;
355 long_passive("EPSV", pf);
358 | EPSV check_login_epsv SP ALL CRLF
360 if ($2) {
361 reply(200, "EPSV ALL command successful.");
362 epsvall++;
365 | EPSV check_login_epsv CRLF
367 if ($2)
368 long_passive("EPSV", PF_UNSPEC);
370 | TYPE check_login SP type_code CRLF
372 if ($2) {
373 switch (cmd_type) {
375 case TYPE_A:
376 if (cmd_form == FORM_N) {
377 reply(200, "Type set to A.");
378 type = cmd_type;
379 form = cmd_form;
380 } else
381 reply(504, "Form must be N.");
382 break;
384 case TYPE_E:
385 reply(504, "Type E not implemented.");
386 break;
388 case TYPE_I:
389 reply(200, "Type set to I.");
390 type = cmd_type;
391 break;
393 case TYPE_L:
394 #if CHAR_BIT == 8
395 if (cmd_bytesz == 8) {
396 reply(200,
397 "Type set to L (byte size 8).");
398 type = cmd_type;
399 } else
400 reply(504, "Byte size must be 8.");
401 #else /* CHAR_BIT == 8 */
402 UNIMPLEMENTED for CHAR_BIT != 8
403 #endif /* CHAR_BIT == 8 */
407 | STRU check_login SP struct_code CRLF
409 if ($2) {
410 switch ($4) {
412 case STRU_F:
413 reply(200, "STRU F accepted.");
414 break;
416 default:
417 reply(504, "Unimplemented STRU type.");
421 | MODE check_login SP mode_code CRLF
423 if ($2) {
424 switch ($4) {
426 case MODE_S:
427 reply(200, "MODE S accepted.");
428 break;
430 default:
431 reply(502, "Unimplemented MODE type.");
435 | ALLO check_login SP NUMBER CRLF
437 if ($2) {
438 reply(202, "ALLO command ignored.");
441 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
443 if ($2) {
444 reply(202, "ALLO command ignored.");
447 | RETR check_login SP pathname CRLF
449 if (noretr || (guest && noguestretr))
450 reply(500, "RETR command disabled.");
451 else if ($2 && $4 != NULL)
452 retrieve(NULL, $4);
454 if ($4 != NULL)
455 free($4);
457 | STOR check_login_ro SP pathname CRLF
459 if ($2 && $4 != NULL)
460 store($4, "w", 0);
461 if ($4 != NULL)
462 free($4);
464 | APPE check_login_ro SP pathname CRLF
466 if ($2 && $4 != NULL)
467 store($4, "a", 0);
468 if ($4 != NULL)
469 free($4);
471 | NLST check_login CRLF
473 if ($2)
474 send_file_list(".");
476 | NLST check_login SP pathstring CRLF
478 if ($2)
479 send_file_list($4);
480 free($4);
482 | LIST check_login CRLF
484 if ($2)
485 retrieve(_PATH_LS " -lgA", "");
487 | LIST check_login SP pathstring CRLF
489 if ($2)
490 retrieve(_PATH_LS " -lgA %s", $4);
491 free($4);
493 | STAT check_login SP pathname CRLF
495 if ($2 && $4 != NULL)
496 statfilecmd($4);
497 if ($4 != NULL)
498 free($4);
500 | STAT check_login CRLF
502 if ($2) {
503 statcmd();
506 | DELE check_login_ro SP pathname CRLF
508 if ($2 && $4 != NULL)
509 delete($4);
510 if ($4 != NULL)
511 free($4);
513 | RNTO check_login_ro SP pathname CRLF
515 if ($2 && $4 != NULL) {
516 if (fromname) {
517 renamecmd(fromname, $4);
518 free(fromname);
519 fromname = NULL;
520 } else {
521 reply(503, "Bad sequence of commands.");
524 if ($4 != NULL)
525 free($4);
527 | ABOR check_login CRLF
529 if ($2)
530 reply(225, "ABOR command successful.");
532 | CWD check_login CRLF
534 if ($2) {
535 cwd(homedir);
538 | CWD check_login SP pathname CRLF
540 if ($2 && $4 != NULL)
541 cwd($4);
542 if ($4 != NULL)
543 free($4);
545 | HELP CRLF
547 help(cmdtab, NULL);
549 | HELP SP STRING CRLF
551 char *cp = $3;
553 if (strncasecmp(cp, "SITE", 4) == 0) {
554 cp = $3 + 4;
555 if (*cp == ' ')
556 cp++;
557 if (*cp)
558 help(sitetab, cp);
559 else
560 help(sitetab, NULL);
561 } else
562 help(cmdtab, $3);
563 free($3);
565 | NOOP CRLF
567 reply(200, "NOOP command successful.");
569 | MKD check_login_ro SP pathname CRLF
571 if ($2 && $4 != NULL)
572 makedir($4);
573 if ($4 != NULL)
574 free($4);
576 | RMD check_login_ro SP pathname CRLF
578 if ($2 && $4 != NULL)
579 removedir($4);
580 if ($4 != NULL)
581 free($4);
583 | PWD check_login CRLF
585 if ($2)
586 pwd();
588 | CDUP check_login CRLF
590 if ($2)
591 cwd("..");
593 | SITE SP HELP CRLF
595 help(sitetab, NULL);
597 | SITE SP HELP SP STRING CRLF
599 help(sitetab, $5);
600 free($5);
602 | SITE SP MDFIVE check_login SP pathname CRLF
604 char p[64], *q;
606 if ($4 && $6) {
607 q = MD5File($6, p);
608 if (q != NULL)
609 reply(200, "MD5(%s) = %s", $6, p);
610 else
611 perror_reply(550, $6);
613 if ($6)
614 free($6);
616 | SITE SP UMASK check_login CRLF
618 int oldmask;
620 if ($4) {
621 oldmask = umask(0);
622 umask(oldmask);
623 reply(200, "Current UMASK is %03o.", oldmask);
626 | SITE SP UMASK check_login SP octal_number CRLF
628 int oldmask;
630 if ($4) {
631 if (($6 == -1) || ($6 > 0777)) {
632 reply(501, "Bad UMASK value.");
633 } else {
634 oldmask = umask($6);
635 reply(200,
636 "UMASK set to %03o (was %03o).",
637 $6, oldmask);
641 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
643 if ($4 && ($8 != NULL)) {
644 if (($6 == -1 ) || ($6 > 0777))
645 reply(501, "Bad mode value.");
646 else if (chmod($8, $6) < 0)
647 perror_reply(550, $8);
648 else
649 reply(200, "CHMOD command successful.");
651 if ($8 != NULL)
652 free($8);
654 | SITE SP check_login IDLE CRLF
656 if ($3)
657 reply(200,
658 "Current IDLE time limit is %d seconds; max %d.",
659 timeout, maxtimeout);
661 | SITE SP check_login IDLE SP NUMBER CRLF
663 if ($3) {
664 if ($6.i < 30 || $6.i > maxtimeout) {
665 reply(501,
666 "Maximum IDLE time must be between 30 and %d seconds.",
667 maxtimeout);
668 } else {
669 timeout = $6.i;
670 alarm(timeout);
671 reply(200,
672 "Maximum IDLE time set to %d seconds.",
673 timeout);
677 | STOU check_login_ro SP pathname CRLF
679 if ($2 && $4 != NULL)
680 store($4, "w", 1);
681 if ($4 != NULL)
682 free($4);
684 | FEAT CRLF
686 lreply(211, "Extensions supported:");
687 #if 0
688 /* XXX these two keywords are non-standard */
689 printf(" EPRT\r\n");
690 if (!noepsv)
691 printf(" EPSV\r\n");
692 #endif
693 printf(" MDTM\r\n");
694 printf(" REST STREAM\r\n");
695 printf(" SIZE\r\n");
696 if (assumeutf8) {
697 /* TVFS requires UTF8, see RFC 3659 */
698 printf(" TVFS\r\n");
699 printf(" UTF8\r\n");
701 reply(211, "End.");
703 | SYST check_login CRLF
705 if ($2) {
706 if (hostinfo)
707 #ifdef BSD
708 reply(215, "UNIX Type: L%d Version: BSD-%d",
709 CHAR_BIT, BSD);
710 #else /* BSD */
711 reply(215, "UNIX Type: L%d", CHAR_BIT);
712 #endif /* BSD */
713 else
714 reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
719 * SIZE is not in RFC959, but Postel has blessed it and
720 * it will be in the updated RFC.
722 * Return size of file in a format suitable for
723 * using with RESTART (we just count bytes).
725 | SIZE check_login SP pathname CRLF
727 if ($2 && $4 != NULL)
728 sizecmd($4);
729 if ($4 != NULL)
730 free($4);
734 * MDTM is not in RFC959, but Postel has blessed it and
735 * it will be in the updated RFC.
737 * Return modification time of file as an ISO 3307
738 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
739 * where xxx is the fractional second (of any precision,
740 * not necessarily 3 digits)
742 | MDTM check_login SP pathname CRLF
744 if ($2 && $4 != NULL) {
745 struct stat stbuf;
746 if (stat($4, &stbuf) < 0)
747 perror_reply(550, $4);
748 else if (!S_ISREG(stbuf.st_mode)) {
749 reply(550, "%s: not a plain file.", $4);
750 } else {
751 struct tm *t;
752 t = gmtime(&stbuf.st_mtime);
753 reply(213,
754 "%04d%02d%02d%02d%02d%02d",
755 1900 + t->tm_year,
756 t->tm_mon+1, t->tm_mday,
757 t->tm_hour, t->tm_min, t->tm_sec);
760 if ($4 != NULL)
761 free($4);
763 | QUIT CRLF
765 reply(221, "Goodbye.");
766 dologout(0);
768 | NOTIMPL
770 nack($1);
772 | error
774 yyclearin; /* discard lookahead data */
775 yyerrok; /* clear error condition */
776 state = CMD; /* reset lexer state */
779 rcmd
780 : RNFR check_login_ro SP pathname CRLF
782 restart_point = 0;
783 if ($2 && $4) {
784 if (fromname)
785 free(fromname);
786 fromname = NULL;
787 if (renamefrom($4))
788 fromname = $4;
789 else
790 free($4);
791 } else if ($4) {
792 free($4);
795 | REST check_login SP NUMBER CRLF
797 if ($2) {
798 if (fromname)
799 free(fromname);
800 fromname = NULL;
801 restart_point = $4.o;
802 reply(350, "Restarting at %jd. %s",
803 (intmax_t)restart_point,
804 "Send STORE or RETRIEVE to initiate transfer.");
809 username
810 : STRING
813 password
814 : /* empty */
816 $$ = (char *)calloc(1, sizeof(char));
818 | STRING
821 byte_size
822 : NUMBER
824 $$ = $1.i;
828 host_port
829 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
830 NUMBER COMMA NUMBER
832 char *a, *p;
834 data_dest.su_len = sizeof(struct sockaddr_in);
835 data_dest.su_family = AF_INET;
836 p = (char *)&data_dest.su_sin.sin_port;
837 p[0] = $9.i; p[1] = $11.i;
838 a = (char *)&data_dest.su_sin.sin_addr;
839 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
843 host_long_port
844 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
845 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
846 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
847 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
848 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
849 NUMBER
851 char *a, *p;
853 memset(&data_dest, 0, sizeof(data_dest));
854 data_dest.su_len = sizeof(struct sockaddr_in6);
855 data_dest.su_family = AF_INET6;
856 p = (char *)&data_dest.su_port;
857 p[0] = $39.i; p[1] = $41.i;
858 a = (char *)&data_dest.su_sin6.sin6_addr;
859 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
860 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
861 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
862 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
863 if (his_addr.su_family == AF_INET6) {
864 /* XXX more sanity checks! */
865 data_dest.su_sin6.sin6_scope_id =
866 his_addr.su_sin6.sin6_scope_id;
868 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
869 memset(&data_dest, 0, sizeof(data_dest));
871 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
872 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
873 NUMBER
875 char *a, *p;
877 memset(&data_dest, 0, sizeof(data_dest));
878 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
879 data_dest.su_family = AF_INET;
880 p = (char *)&data_dest.su_port;
881 p[0] = $15.i; p[1] = $17.i;
882 a = (char *)&data_dest.su_sin.sin_addr;
883 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
884 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
885 memset(&data_dest, 0, sizeof(data_dest));
889 form_code
892 $$ = FORM_N;
896 $$ = FORM_T;
900 $$ = FORM_C;
904 type_code
907 cmd_type = TYPE_A;
908 cmd_form = FORM_N;
910 | A SP form_code
912 cmd_type = TYPE_A;
913 cmd_form = $3;
917 cmd_type = TYPE_E;
918 cmd_form = FORM_N;
920 | E SP form_code
922 cmd_type = TYPE_E;
923 cmd_form = $3;
927 cmd_type = TYPE_I;
931 cmd_type = TYPE_L;
932 cmd_bytesz = CHAR_BIT;
934 | L SP byte_size
936 cmd_type = TYPE_L;
937 cmd_bytesz = $3;
939 /* this is for a bug in the BBN ftp */
940 | L byte_size
942 cmd_type = TYPE_L;
943 cmd_bytesz = $2;
947 struct_code
950 $$ = STRU_F;
954 $$ = STRU_R;
958 $$ = STRU_P;
962 mode_code
965 $$ = MODE_S;
969 $$ = MODE_B;
973 $$ = MODE_C;
977 pathname
978 : pathstring
980 if (logged_in && $1) {
981 char *p;
984 * Expand ~user manually since glob(3)
985 * will return the unexpanded pathname
986 * if the corresponding file/directory
987 * doesn't exist yet. Using sole glob(3)
988 * would break natural commands like
989 * MKD ~user/newdir
990 * or
991 * RNTO ~/newfile
993 if ((p = exptilde($1)) != NULL) {
994 $$ = expglob(p);
995 free(p);
996 } else
997 $$ = NULL;
998 free($1);
999 } else
1000 $$ = $1;
1004 pathstring
1005 : STRING
1008 octal_number
1009 : NUMBER
1011 int ret, dec, multby, digit;
1014 * Convert a number that was read as decimal number
1015 * to what it would be if it had been read as octal.
1017 dec = $1.i;
1018 multby = 1;
1019 ret = 0;
1020 while (dec) {
1021 digit = dec%10;
1022 if (digit > 7) {
1023 ret = -1;
1024 break;
1026 ret += digit * multby;
1027 multby *= 8;
1028 dec /= 10;
1030 $$ = ret;
1035 check_login
1036 : /* empty */
1038 $$ = check_login1();
1042 check_login_epsv
1043 : /* empty */
1045 if (noepsv) {
1046 reply(500, "EPSV command disabled.");
1047 $$ = 0;
1049 else
1050 $$ = check_login1();
1054 check_login_ro
1055 : /* empty */
1057 if (readonly) {
1058 reply(550, "Permission denied.");
1059 $$ = 0;
1061 else
1062 $$ = check_login1();
1068 #define CMD 0 /* beginning of command */
1069 #define ARGS 1 /* expect miscellaneous arguments */
1070 #define STR1 2 /* expect SP followed by STRING */
1071 #define STR2 3 /* expect STRING */
1072 #define OSTR 4 /* optional SP then STRING */
1073 #define ZSTR1 5 /* optional SP then optional STRING */
1074 #define ZSTR2 6 /* optional STRING after SP */
1075 #define SITECMD 7 /* SITE command */
1076 #define NSTR 8 /* Number followed by a string */
1078 #define MAXGLOBARGS 1000
1080 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1082 struct tab {
1083 char *name;
1084 short token;
1085 short state;
1086 short implemented; /* 1 if command is implemented */
1087 char *help;
1090 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1091 { "USER", USER, STR1, 1, "<sp> username" },
1092 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1093 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1094 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1095 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1096 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1097 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1098 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1099 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1100 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1101 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1102 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1103 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1104 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1105 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1106 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1107 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1108 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1109 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1110 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1111 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1112 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1113 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1114 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1115 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1116 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1117 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1118 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1119 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1120 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1121 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1122 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1123 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1124 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1125 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1126 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1127 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1128 { "FEAT", FEAT, ARGS, 1, "(get extended features)" },
1129 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1130 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1131 { "NOOP", NOOP, ARGS, 1, "" },
1132 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1133 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1134 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1135 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1136 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1137 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1138 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1139 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1140 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1141 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1142 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1143 { NULL, 0, 0, 0, 0 }
1146 struct tab sitetab[] = {
1147 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1148 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1149 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1150 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1151 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1152 { NULL, 0, 0, 0, 0 }
1155 static char *copy(char *);
1156 static char *expglob(char *);
1157 static char *exptilde(char *);
1158 static void help(struct tab *, char *);
1159 static struct tab *
1160 lookup(struct tab *, char *);
1161 static int port_check(const char *);
1162 #ifdef INET6
1163 static int port_check_v6(const char *);
1164 #endif
1165 static void sizecmd(char *);
1166 static void toolong(int);
1167 #ifdef INET6
1168 static void v4map_data_dest(void);
1169 #endif
1170 static int yylex(void);
1172 static struct tab *
1173 lookup(struct tab *p, char *cmd)
1176 for (; p->name != NULL; p++)
1177 if (strcmp(cmd, p->name) == 0)
1178 return (p);
1179 return (0);
1182 #include <arpa/telnet.h>
1185 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1188 getline(char *s, int n, FILE *iop)
1190 int c;
1191 char *cs;
1192 sigset_t sset, osset;
1194 cs = s;
1195 /* tmpline may contain saved command from urgent mode interruption */
1196 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1197 *cs++ = tmpline[c];
1198 if (tmpline[c] == '\n') {
1199 *cs++ = '\0';
1200 if (ftpdebug)
1201 syslog(LOG_DEBUG, "command: %s", s);
1202 tmpline[0] = '\0';
1203 return(0);
1205 if (c == 0)
1206 tmpline[0] = '\0';
1208 /* SIGURG would interrupt stdio if not blocked during the read loop */
1209 sigemptyset(&sset);
1210 sigaddset(&sset, SIGURG);
1211 sigprocmask(SIG_BLOCK, &sset, &osset);
1212 while ((c = getc(iop)) != EOF) {
1213 c &= 0377;
1214 if (c == IAC) {
1215 if ((c = getc(iop)) == EOF)
1216 goto got_eof;
1217 c &= 0377;
1218 switch (c) {
1219 case WILL:
1220 case WONT:
1221 if ((c = getc(iop)) == EOF)
1222 goto got_eof;
1223 printf("%c%c%c", IAC, DONT, 0377&c);
1224 fflush(stdout);
1225 continue;
1226 case DO:
1227 case DONT:
1228 if ((c = getc(iop)) == EOF)
1229 goto got_eof;
1230 printf("%c%c%c", IAC, WONT, 0377&c);
1231 fflush(stdout);
1232 continue;
1233 case IAC:
1234 break;
1235 default:
1236 continue; /* ignore command */
1239 *cs++ = c;
1240 if (--n <= 0) {
1242 * If command doesn't fit into buffer, discard the
1243 * rest of the command and indicate truncation.
1244 * This prevents the command to be split up into
1245 * multiple commands.
1247 while (c != '\n' && (c = getc(iop)) != EOF)
1249 return (-2);
1251 if (c == '\n')
1252 break;
1254 got_eof:
1255 sigprocmask(SIG_SETMASK, &osset, NULL);
1256 if (c == EOF && cs == s)
1257 return (-1);
1258 *cs++ = '\0';
1259 if (ftpdebug) {
1260 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1261 /* Don't syslog passwords */
1262 syslog(LOG_DEBUG, "command: %.5s ???", s);
1263 } else {
1264 char *cp;
1265 int len;
1267 /* Don't syslog trailing CR-LF */
1268 len = strlen(s);
1269 cp = s + len - 1;
1270 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1271 --cp;
1272 --len;
1274 syslog(LOG_DEBUG, "command: %.*s", len, s);
1277 return (0);
1280 static void
1281 toolong(int signo)
1284 reply(421,
1285 "Timeout (%d seconds): closing control connection.", timeout);
1286 if (logging)
1287 syslog(LOG_INFO, "User %s timed out after %d seconds",
1288 (pw ? pw -> pw_name : "unknown"), timeout);
1289 dologout(1);
1292 static int
1293 yylex(void)
1295 static int cpos;
1296 char *cp, *cp2;
1297 struct tab *p;
1298 int n;
1299 char c;
1301 for (;;) {
1302 switch (state) {
1304 case CMD:
1305 signal(SIGALRM, toolong);
1306 alarm(timeout);
1307 n = getline(cbuf, sizeof(cbuf)-1, stdin);
1308 if (n == -1) {
1309 reply(221, "You could at least say goodbye.");
1310 dologout(0);
1311 } else if (n == -2) {
1312 reply(500, "Command too long.");
1313 alarm(0);
1314 continue;
1316 alarm(0);
1317 #ifdef SETPROCTITLE
1318 if (strncasecmp(cbuf, "PASS", 4) != 0)
1319 setproctitle("%s: %s", proctitle, cbuf);
1320 #endif /* SETPROCTITLE */
1321 if ((cp = strchr(cbuf, '\r'))) {
1322 *cp++ = '\n';
1323 *cp = '\0';
1325 if ((cp = strpbrk(cbuf, " \n")))
1326 cpos = cp - cbuf;
1327 if (cpos == 0)
1328 cpos = 4;
1329 c = cbuf[cpos];
1330 cbuf[cpos] = '\0';
1331 upper(cbuf);
1332 p = lookup(cmdtab, cbuf);
1333 cbuf[cpos] = c;
1334 if (p != 0) {
1335 yylval.s = p->name;
1336 if (!p->implemented)
1337 return (NOTIMPL); /* state remains CMD */
1338 state = p->state;
1339 return (p->token);
1341 break;
1343 case SITECMD:
1344 if (cbuf[cpos] == ' ') {
1345 cpos++;
1346 return (SP);
1348 cp = &cbuf[cpos];
1349 if ((cp2 = strpbrk(cp, " \n")))
1350 cpos = cp2 - cbuf;
1351 c = cbuf[cpos];
1352 cbuf[cpos] = '\0';
1353 upper(cp);
1354 p = lookup(sitetab, cp);
1355 cbuf[cpos] = c;
1356 if (guest == 0 && p != 0) {
1357 yylval.s = p->name;
1358 if (!p->implemented) {
1359 state = CMD;
1360 return (NOTIMPL);
1362 state = p->state;
1363 return (p->token);
1365 state = CMD;
1366 break;
1368 case ZSTR1:
1369 case OSTR:
1370 if (cbuf[cpos] == '\n') {
1371 state = CMD;
1372 return (CRLF);
1374 /* FALLTHROUGH */
1376 case STR1:
1377 dostr1:
1378 if (cbuf[cpos] == ' ') {
1379 cpos++;
1380 state = state == OSTR ? STR2 : state+1;
1381 return (SP);
1383 break;
1385 case ZSTR2:
1386 if (cbuf[cpos] == '\n') {
1387 state = CMD;
1388 return (CRLF);
1390 /* FALLTHROUGH */
1392 case STR2:
1393 cp = &cbuf[cpos];
1394 n = strlen(cp);
1395 cpos += n - 1;
1397 * Make sure the string is nonempty and \n terminated.
1399 if (n > 1 && cbuf[cpos] == '\n') {
1400 cbuf[cpos] = '\0';
1401 yylval.s = copy(cp);
1402 cbuf[cpos] = '\n';
1403 state = ARGS;
1404 return (STRING);
1406 break;
1408 case NSTR:
1409 if (cbuf[cpos] == ' ') {
1410 cpos++;
1411 return (SP);
1413 if (isdigit(cbuf[cpos])) {
1414 cp = &cbuf[cpos];
1415 while (isdigit(cbuf[++cpos]))
1417 c = cbuf[cpos];
1418 cbuf[cpos] = '\0';
1419 yylval.u.i = atoi(cp);
1420 cbuf[cpos] = c;
1421 state = STR1;
1422 return (NUMBER);
1424 state = STR1;
1425 goto dostr1;
1427 case ARGS:
1428 if (isdigit(cbuf[cpos])) {
1429 cp = &cbuf[cpos];
1430 while (isdigit(cbuf[++cpos]))
1432 c = cbuf[cpos];
1433 cbuf[cpos] = '\0';
1434 yylval.u.i = atoi(cp);
1435 yylval.u.o = strtoull(cp, NULL, 10);
1436 cbuf[cpos] = c;
1437 return (NUMBER);
1439 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1440 && !isalnum(cbuf[cpos + 3])) {
1441 cpos += 3;
1442 return ALL;
1444 switch (cbuf[cpos++]) {
1446 case '\n':
1447 state = CMD;
1448 return (CRLF);
1450 case ' ':
1451 return (SP);
1453 case ',':
1454 return (COMMA);
1456 case 'A':
1457 case 'a':
1458 return (A);
1460 case 'B':
1461 case 'b':
1462 return (B);
1464 case 'C':
1465 case 'c':
1466 return (C);
1468 case 'E':
1469 case 'e':
1470 return (E);
1472 case 'F':
1473 case 'f':
1474 return (F);
1476 case 'I':
1477 case 'i':
1478 return (I);
1480 case 'L':
1481 case 'l':
1482 return (L);
1484 case 'N':
1485 case 'n':
1486 return (N);
1488 case 'P':
1489 case 'p':
1490 return (P);
1492 case 'R':
1493 case 'r':
1494 return (R);
1496 case 'S':
1497 case 's':
1498 return (S);
1500 case 'T':
1501 case 't':
1502 return (T);
1505 break;
1507 default:
1508 fatalerror("Unknown state in scanner.");
1510 state = CMD;
1511 return (LEXERR);
1515 void
1516 upper(char *s)
1518 while (*s != '\0') {
1519 if (islower(*s))
1520 *s = toupper(*s);
1521 s++;
1525 static char *
1526 copy(char *s)
1528 char *p;
1530 p = malloc(strlen(s) + 1);
1531 if (p == NULL)
1532 fatalerror("Ran out of memory.");
1533 strcpy(p, s);
1534 return (p);
1537 static void
1538 help(struct tab *ctab, char *s)
1540 struct tab *c;
1541 int width, NCMDS;
1542 char *type;
1544 if (ctab == sitetab)
1545 type = "SITE ";
1546 else
1547 type = "";
1548 width = 0, NCMDS = 0;
1549 for (c = ctab; c->name != NULL; c++) {
1550 int len = strlen(c->name);
1552 if (len > width)
1553 width = len;
1554 NCMDS++;
1556 width = (width + 8) &~ 7;
1557 if (s == 0) {
1558 int i, j, w;
1559 int columns, lines;
1561 lreply(214, "The following %scommands are recognized %s.",
1562 type, "(* =>'s unimplemented)");
1563 columns = 76 / width;
1564 if (columns == 0)
1565 columns = 1;
1566 lines = (NCMDS + columns - 1) / columns;
1567 for (i = 0; i < lines; i++) {
1568 printf(" ");
1569 for (j = 0; j < columns; j++) {
1570 c = ctab + j * lines + i;
1571 printf("%s%c", c->name,
1572 c->implemented ? ' ' : '*');
1573 if (c + lines >= &ctab[NCMDS])
1574 break;
1575 w = strlen(c->name) + 1;
1576 while (w < width) {
1577 putchar(' ');
1578 w++;
1581 printf("\r\n");
1583 fflush(stdout);
1584 if (hostinfo)
1585 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1586 else
1587 reply(214, "End.");
1588 return;
1590 upper(s);
1591 c = lookup(ctab, s);
1592 if (c == NULL) {
1593 reply(502, "Unknown command %s.", s);
1594 return;
1596 if (c->implemented)
1597 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1598 else
1599 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1600 c->name, c->help);
1603 static void
1604 sizecmd(char *filename)
1606 switch (type) {
1607 case TYPE_L:
1608 case TYPE_I: {
1609 struct stat stbuf;
1610 if (stat(filename, &stbuf) < 0)
1611 perror_reply(550, filename);
1612 else if (!S_ISREG(stbuf.st_mode))
1613 reply(550, "%s: not a plain file.", filename);
1614 else
1615 reply(213, "%jd", (intmax_t)stbuf.st_size);
1616 break; }
1617 case TYPE_A: {
1618 FILE *fin;
1619 int c;
1620 off_t count;
1621 struct stat stbuf;
1622 fin = fopen(filename, "r");
1623 if (fin == NULL) {
1624 perror_reply(550, filename);
1625 return;
1627 if (fstat(fileno(fin), &stbuf) < 0) {
1628 perror_reply(550, filename);
1629 fclose(fin);
1630 return;
1631 } else if (!S_ISREG(stbuf.st_mode)) {
1632 reply(550, "%s: not a plain file.", filename);
1633 fclose(fin);
1634 return;
1635 } else if (stbuf.st_size > MAXASIZE) {
1636 reply(550, "%s: too large for type A SIZE.", filename);
1637 fclose(fin);
1638 return;
1641 count = 0;
1642 while((c=getc(fin)) != EOF) {
1643 if (c == '\n') /* will get expanded to \r\n */
1644 count++;
1645 count++;
1647 fclose(fin);
1649 reply(213, "%jd", (intmax_t)count);
1650 break; }
1651 default:
1652 reply(504, "SIZE not implemented for type %s.",
1653 typenames[type]);
1657 /* Return 1, if port check is done. Return 0, if not yet. */
1658 static int
1659 port_check(const char *pcmd)
1661 if (his_addr.su_family == AF_INET) {
1662 if (data_dest.su_family != AF_INET) {
1663 usedefault = 1;
1664 reply(500, "Invalid address rejected.");
1665 return 1;
1667 if (paranoid &&
1668 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1669 memcmp(&data_dest.su_sin.sin_addr,
1670 &his_addr.su_sin.sin_addr,
1671 sizeof(data_dest.su_sin.sin_addr)))) {
1672 usedefault = 1;
1673 reply(500, "Illegal PORT range rejected.");
1674 } else {
1675 usedefault = 0;
1676 if (pdata >= 0) {
1677 close(pdata);
1678 pdata = -1;
1680 reply(200, "%s command successful.", pcmd);
1682 return 1;
1684 return 0;
1687 static int
1688 check_login1(void)
1690 if (logged_in)
1691 return 1;
1692 else {
1693 reply(530, "Please login with USER and PASS.");
1694 return 0;
1699 * Replace leading "~user" in a pathname by the user's login directory.
1700 * Returned string will be in a freshly malloced buffer unless it's NULL.
1702 static char *
1703 exptilde(char *s)
1705 char *p, *q;
1706 char *path, *user;
1707 struct passwd *ppw;
1709 if ((p = strdup(s)) == NULL)
1710 return (NULL);
1711 if (*p != '~')
1712 return (p);
1714 user = p + 1; /* skip tilde */
1715 if ((path = strchr(p, '/')) != NULL)
1716 *(path++) = '\0'; /* separate ~user from the rest of path */
1717 if (*user == '\0') /* no user specified, use the current user */
1718 user = pw->pw_name;
1719 /* read passwd even for the current user since we may be chrooted */
1720 if ((ppw = getpwnam(user)) != NULL) {
1721 /* user found, substitute login directory for ~user */
1722 if (path)
1723 asprintf(&q, "%s/%s", ppw->pw_dir, path);
1724 else
1725 q = strdup(ppw->pw_dir);
1726 free(p);
1727 p = q;
1728 } else {
1729 /* user not found, undo the damage */
1730 if (path)
1731 path[-1] = '/';
1733 return (p);
1737 * Expand glob(3) patterns possibly present in a pathname.
1738 * Avoid expanding to a pathname including '\r' or '\n' in order to
1739 * not disrupt the FTP protocol.
1740 * The expansion found must be unique.
1741 * Return the result as a malloced string, or NULL if an error occured.
1743 * Problem: this production is used for all pathname
1744 * processing, but only gives a 550 error reply.
1745 * This is a valid reply in some cases but not in others.
1747 static char *
1748 expglob(char *s)
1750 char *p, **pp, *rval;
1751 int flags = GLOB_BRACE | GLOB_NOCHECK;
1752 int n;
1753 glob_t gl;
1755 memset(&gl, 0, sizeof(gl));
1756 flags |= GLOB_LIMIT;
1757 gl.gl_matchc = MAXGLOBARGS;
1758 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1759 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1760 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1761 p = *pp;
1762 n++;
1764 if (n == 0)
1765 rval = strdup(s);
1766 else if (n == 1)
1767 rval = strdup(p);
1768 else {
1769 reply(550, "Wildcard is ambiguous.");
1770 rval = NULL;
1772 } else {
1773 reply(550, "Wildcard expansion error.");
1774 rval = NULL;
1776 globfree(&gl);
1777 return (rval);
1780 #ifdef INET6
1781 /* Return 1, if port check is done. Return 0, if not yet. */
1782 static int
1783 port_check_v6(const char *pcmd)
1785 if (his_addr.su_family == AF_INET6) {
1786 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1787 /* Convert data_dest into v4 mapped sockaddr.*/
1788 v4map_data_dest();
1789 if (data_dest.su_family != AF_INET6) {
1790 usedefault = 1;
1791 reply(500, "Invalid address rejected.");
1792 return 1;
1794 if (paranoid &&
1795 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1796 memcmp(&data_dest.su_sin6.sin6_addr,
1797 &his_addr.su_sin6.sin6_addr,
1798 sizeof(data_dest.su_sin6.sin6_addr)))) {
1799 usedefault = 1;
1800 reply(500, "Illegal PORT range rejected.");
1801 } else {
1802 usedefault = 0;
1803 if (pdata >= 0) {
1804 close(pdata);
1805 pdata = -1;
1807 reply(200, "%s command successful.", pcmd);
1809 return 1;
1811 return 0;
1814 static void
1815 v4map_data_dest(void)
1817 struct in_addr savedaddr;
1818 int savedport;
1820 if (data_dest.su_family != AF_INET) {
1821 usedefault = 1;
1822 reply(500, "Invalid address rejected.");
1823 return;
1826 savedaddr = data_dest.su_sin.sin_addr;
1827 savedport = data_dest.su_port;
1829 memset(&data_dest, 0, sizeof(data_dest));
1830 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1831 data_dest.su_sin6.sin6_family = AF_INET6;
1832 data_dest.su_sin6.sin6_port = savedport;
1833 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1834 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1835 (caddr_t)&savedaddr, sizeof(savedaddr));
1837 #endif