time: Use clock_gettime
[dragonfly.git] / libexec / ftpd / ftpcmd.y
blob944debbfcd4dfd663c3fc127a4eafb247500171a
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. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
30 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.67 2008/12/23 01:23:09 cperciva Exp $
31 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
35 * Grammar for FTP commands.
36 * See RFC 959.
41 #include <sys/param.h>
42 #include <sys/socket.h>
43 #include <sys/stat.h>
45 #include <netinet/in.h>
46 #include <arpa/ftp.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <glob.h>
51 #include <libutil.h>
52 #include <limits.h>
53 #include <md5.h>
54 #include <netdb.h>
55 #include <pwd.h>
56 #include <signal.h>
57 #include <stdint.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <time.h>
63 #include <unistd.h>
65 #include "extern.h"
66 #include "pathnames.h"
68 extern union sockunion data_dest, his_addr;
69 extern int hostinfo;
70 extern int logged_in;
71 extern struct passwd *pw;
72 extern int guest;
73 extern char *homedir;
74 extern int paranoid;
75 extern int logging;
76 extern int type;
77 extern int form;
78 extern int ftpdebug;
79 extern int timeout;
80 extern int maxtimeout;
81 extern int pdata;
82 extern char *hostname;
83 extern char proctitle[];
84 extern int usedefault;
85 extern char tmpline[];
86 extern int readonly;
87 extern int assumeutf8;
88 extern int noepsv;
89 extern int noretr;
90 extern int noguestretr;
91 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
93 off_t restart_point;
95 static int cmd_type;
96 static int cmd_form;
97 static int cmd_bytesz;
98 static int state;
99 char cbuf[512];
100 char *fromname = NULL;
102 extern int epsvall;
106 %union {
107 struct {
108 off_t o;
109 int i;
110 } u;
111 char *s;
114 %token
115 A B C E F I
116 L N P R S T
119 SP CRLF COMMA
121 USER PASS ACCT REIN QUIT PORT
122 PASV TYPE STRU MODE RETR STOR
123 APPE MLFL MAIL MSND MSOM MSAM
124 MRSQ MRCP ALLO REST RNFR RNTO
125 ABOR DELE CWD LIST NLST SITE
126 STAT HELP NOOP MKD RMD PWD
127 CDUP STOU SMNT SYST SIZE MDTM
128 LPRT LPSV EPRT EPSV FEAT
130 UMASK IDLE CHMOD MDFIVE
132 LEXERR NOTIMPL
134 %token <s> STRING
135 %token <u> NUMBER
137 %type <u.i> check_login octal_number byte_size
138 %type <u.i> check_login_ro check_login_epsv
139 %type <u.i> struct_code mode_code type_code form_code
140 %type <s> pathstring pathname password username
141 %type <s> ALL NOTIMPL
143 %start cmd_list
147 cmd_list
148 : /* empty */
149 | cmd_list cmd
151 if (fromname)
152 free(fromname);
153 fromname = NULL;
154 restart_point = 0;
156 | cmd_list rcmd
160 : USER SP username CRLF
162 user($3);
163 free($3);
165 | PASS SP password CRLF
167 pass($3);
168 free($3);
170 | PASS CRLF
172 pass("");
174 | PORT check_login SP host_port CRLF
176 if (epsvall) {
177 reply(501, "No PORT allowed after EPSV ALL.");
178 goto port_done;
180 if (!$2)
181 goto port_done;
182 if (port_check("PORT") == 1)
183 goto port_done;
184 #ifdef INET6
185 if ((his_addr.su_family != AF_INET6 ||
186 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
187 /* shoud never happen */
188 usedefault = 1;
189 reply(500, "Invalid address rejected.");
190 goto port_done;
192 port_check_v6("pcmd");
193 #endif
194 port_done:
197 | LPRT check_login SP host_long_port CRLF
199 if (epsvall) {
200 reply(501, "No LPRT allowed after EPSV ALL.");
201 goto lprt_done;
203 if (!$2)
204 goto lprt_done;
205 if (port_check("LPRT") == 1)
206 goto lprt_done;
207 #ifdef INET6
208 if (his_addr.su_family != AF_INET6) {
209 usedefault = 1;
210 reply(500, "Invalid address rejected.");
211 goto lprt_done;
213 if (port_check_v6("LPRT") == 1)
214 goto lprt_done;
215 #endif
216 lprt_done:
219 | EPRT check_login SP STRING CRLF
221 char delim;
222 char *tmp = NULL;
223 char *p, *q;
224 char *result[3];
225 struct addrinfo hints;
226 struct addrinfo *res;
227 int i;
229 if (epsvall) {
230 reply(501, "No EPRT allowed after EPSV ALL.");
231 goto eprt_done;
233 if (!$2)
234 goto eprt_done;
236 memset(&data_dest, 0, sizeof(data_dest));
237 tmp = strdup($4);
238 if (ftpdebug)
239 syslog(LOG_DEBUG, "%s", tmp);
240 if (!tmp) {
241 fatalerror("not enough core");
242 /*NOTREACHED*/
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;
259 *q++ = '\0';
260 result[i] = p;
261 if (ftpdebug)
262 syslog(LOG_DEBUG, "%d: %s", i, p);
263 p = q;
266 /* some more sanity check */
267 p = result[0];
268 while (*p) {
269 if (!isdigit(*p))
270 goto parsefail;
271 p++;
273 p = result[2];
274 while (*p) {
275 if (!isdigit(*p))
276 goto parsefail;
277 p++;
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;
302 #endif
303 free(tmp);
304 tmp = NULL;
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;
314 if (port_check_v6("EPRT") == 1)
315 goto eprt_done;
316 #endif
317 eprt_done:
318 free($4);
320 | PASV check_login CRLF
322 if (epsvall)
323 reply(501, "No PASV allowed after EPSV ALL.");
324 else if ($2)
325 passive();
327 | LPSV check_login CRLF
329 if (epsvall)
330 reply(501, "No LPSV allowed after EPSV ALL.");
331 else if ($2)
332 long_passive("LPSV", PF_UNSPEC);
334 | EPSV check_login_epsv SP NUMBER CRLF
336 if ($2) {
337 int pf;
338 switch ($4.i) {
339 case 1:
340 pf = PF_INET;
341 break;
342 #ifdef INET6
343 case 2:
344 pf = PF_INET6;
345 break;
346 #endif
347 default:
348 pf = -1; /*junk value*/
349 break;
351 long_passive("EPSV", pf);
354 | EPSV check_login_epsv SP ALL CRLF
356 if ($2) {
357 reply(200, "EPSV ALL command successful.");
358 epsvall++;
361 | EPSV check_login_epsv CRLF
363 if ($2)
364 long_passive("EPSV", PF_UNSPEC);
366 | TYPE check_login SP type_code CRLF
368 if ($2) {
369 switch (cmd_type) {
371 case TYPE_A:
372 if (cmd_form == FORM_N) {
373 reply(200, "Type set to A.");
374 type = cmd_type;
375 form = cmd_form;
376 } else
377 reply(504, "Form must be N.");
378 break;
380 case TYPE_E:
381 reply(504, "Type E not implemented.");
382 break;
384 case TYPE_I:
385 reply(200, "Type set to I.");
386 type = cmd_type;
387 break;
389 case TYPE_L:
390 #if CHAR_BIT == 8
391 if (cmd_bytesz == 8) {
392 reply(200,
393 "Type set to L (byte size 8).");
394 type = cmd_type;
395 } else
396 reply(504, "Byte size must be 8.");
397 #else /* CHAR_BIT == 8 */
398 UNIMPLEMENTED for CHAR_BIT != 8
399 #endif /* CHAR_BIT == 8 */
403 | STRU check_login SP struct_code CRLF
405 if ($2) {
406 switch ($4) {
408 case STRU_F:
409 reply(200, "STRU F accepted.");
410 break;
412 default:
413 reply(504, "Unimplemented STRU type.");
417 | MODE check_login SP mode_code CRLF
419 if ($2) {
420 switch ($4) {
422 case MODE_S:
423 reply(200, "MODE S accepted.");
424 break;
426 default:
427 reply(502, "Unimplemented MODE type.");
431 | ALLO check_login SP NUMBER CRLF
433 if ($2) {
434 reply(202, "ALLO command ignored.");
437 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
439 if ($2) {
440 reply(202, "ALLO command ignored.");
443 | RETR check_login SP pathname CRLF
445 if (noretr || (guest && noguestretr))
446 reply(500, "RETR command disabled.");
447 else if ($2 && $4 != NULL)
448 retrieve(NULL, $4);
450 if ($4 != NULL)
451 free($4);
453 | STOR check_login_ro SP pathname CRLF
455 if ($2 && $4 != NULL)
456 store($4, "w", 0);
457 if ($4 != NULL)
458 free($4);
460 | APPE check_login_ro SP pathname CRLF
462 if ($2 && $4 != NULL)
463 store($4, "a", 0);
464 if ($4 != NULL)
465 free($4);
467 | NLST check_login CRLF
469 if ($2)
470 send_file_list(".");
472 | NLST check_login SP pathstring CRLF
474 if ($2)
475 send_file_list($4);
476 free($4);
478 | LIST check_login CRLF
480 if ($2)
481 retrieve(_PATH_LS " -lgA", "");
483 | LIST check_login SP pathstring CRLF
485 if ($2)
486 retrieve(_PATH_LS " -lgA %s", $4);
487 free($4);
489 | STAT check_login SP pathname CRLF
491 if ($2 && $4 != NULL)
492 statfilecmd($4);
493 if ($4 != NULL)
494 free($4);
496 | STAT check_login CRLF
498 if ($2) {
499 statcmd();
502 | DELE check_login_ro SP pathname CRLF
504 if ($2 && $4 != NULL)
505 delete($4);
506 if ($4 != NULL)
507 free($4);
509 | RNTO check_login_ro SP pathname CRLF
511 if ($2 && $4 != NULL) {
512 if (fromname) {
513 renamecmd(fromname, $4);
514 free(fromname);
515 fromname = NULL;
516 } else {
517 reply(503, "Bad sequence of commands.");
520 if ($4 != NULL)
521 free($4);
523 | ABOR check_login CRLF
525 if ($2)
526 reply(225, "ABOR command successful.");
528 | CWD check_login CRLF
530 if ($2) {
531 cwd(homedir);
534 | CWD check_login SP pathname CRLF
536 if ($2 && $4 != NULL)
537 cwd($4);
538 if ($4 != NULL)
539 free($4);
541 | HELP CRLF
543 help(cmdtab, NULL);
545 | HELP SP STRING CRLF
547 char *cp = $3;
549 if (strncasecmp(cp, "SITE", 4) == 0) {
550 cp = $3 + 4;
551 if (*cp == ' ')
552 cp++;
553 if (*cp)
554 help(sitetab, cp);
555 else
556 help(sitetab, NULL);
557 } else
558 help(cmdtab, $3);
559 free($3);
561 | NOOP CRLF
563 reply(200, "NOOP command successful.");
565 | MKD check_login_ro SP pathname CRLF
567 if ($2 && $4 != NULL)
568 makedir($4);
569 if ($4 != NULL)
570 free($4);
572 | RMD check_login_ro SP pathname CRLF
574 if ($2 && $4 != NULL)
575 removedir($4);
576 if ($4 != NULL)
577 free($4);
579 | PWD check_login CRLF
581 if ($2)
582 pwd();
584 | CDUP check_login CRLF
586 if ($2)
587 cwd("..");
589 | SITE SP HELP CRLF
591 help(sitetab, NULL);
593 | SITE SP HELP SP STRING CRLF
595 help(sitetab, $5);
596 free($5);
598 | SITE SP MDFIVE check_login SP pathname CRLF
600 char p[64], *q;
602 if ($4 && $6) {
603 q = MD5File($6, p);
604 if (q != NULL)
605 reply(200, "MD5(%s) = %s", $6, p);
606 else
607 perror_reply(550, $6);
609 if ($6)
610 free($6);
612 | SITE SP UMASK check_login CRLF
614 int oldmask;
616 if ($4) {
617 oldmask = umask(0);
618 umask(oldmask);
619 reply(200, "Current UMASK is %03o.", oldmask);
622 | SITE SP UMASK check_login SP octal_number CRLF
624 int oldmask;
626 if ($4) {
627 if (($6 == -1) || ($6 > 0777)) {
628 reply(501, "Bad UMASK value.");
629 } else {
630 oldmask = umask($6);
631 reply(200,
632 "UMASK set to %03o (was %03o).",
633 $6, oldmask);
637 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
639 if ($4 && ($8 != NULL)) {
640 if (($6 == -1 ) || ($6 > 0777))
641 reply(501, "Bad mode value.");
642 else if (chmod($8, $6) < 0)
643 perror_reply(550, $8);
644 else
645 reply(200, "CHMOD command successful.");
647 if ($8 != NULL)
648 free($8);
650 | SITE SP check_login IDLE CRLF
652 if ($3)
653 reply(200,
654 "Current IDLE time limit is %d seconds; max %d.",
655 timeout, maxtimeout);
657 | SITE SP check_login IDLE SP NUMBER CRLF
659 if ($3) {
660 if ($6.i < 30 || $6.i > maxtimeout) {
661 reply(501,
662 "Maximum IDLE time must be between 30 and %d seconds.",
663 maxtimeout);
664 } else {
665 timeout = $6.i;
666 alarm(timeout);
667 reply(200,
668 "Maximum IDLE time set to %d seconds.",
669 timeout);
673 | STOU check_login_ro SP pathname CRLF
675 if ($2 && $4 != NULL)
676 store($4, "w", 1);
677 if ($4 != NULL)
678 free($4);
680 | FEAT CRLF
682 lreply(211, "Extensions supported:");
683 #if 0
684 /* XXX these two keywords are non-standard */
685 printf(" EPRT\r\n");
686 if (!noepsv)
687 printf(" EPSV\r\n");
688 #endif
689 printf(" MDTM\r\n");
690 printf(" REST STREAM\r\n");
691 printf(" SIZE\r\n");
692 if (assumeutf8) {
693 /* TVFS requires UTF8, see RFC 3659 */
694 printf(" TVFS\r\n");
695 printf(" UTF8\r\n");
697 reply(211, "End.");
699 | SYST check_login CRLF
701 if ($2) {
702 if (hostinfo)
703 #ifdef BSD
704 reply(215, "UNIX Type: L%d Version: BSD-%d",
705 CHAR_BIT, BSD);
706 #else /* BSD */
707 reply(215, "UNIX Type: L%d", CHAR_BIT);
708 #endif /* BSD */
709 else
710 reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
715 * SIZE is not in RFC959, but Postel has blessed it and
716 * it will be in the updated RFC.
718 * Return size of file in a format suitable for
719 * using with RESTART (we just count bytes).
721 | SIZE check_login SP pathname CRLF
723 if ($2 && $4 != NULL)
724 sizecmd($4);
725 if ($4 != NULL)
726 free($4);
730 * MDTM is not in RFC959, but Postel has blessed it and
731 * it will be in the updated RFC.
733 * Return modification time of file as an ISO 3307
734 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
735 * where xxx is the fractional second (of any precision,
736 * not necessarily 3 digits)
738 | MDTM check_login SP pathname CRLF
740 if ($2 && $4 != NULL) {
741 struct stat stbuf;
742 if (stat($4, &stbuf) < 0)
743 perror_reply(550, $4);
744 else if (!S_ISREG(stbuf.st_mode)) {
745 reply(550, "%s: not a plain file.", $4);
746 } else {
747 struct tm *t;
748 t = gmtime(&stbuf.st_mtime);
749 reply(213,
750 "%04d%02d%02d%02d%02d%02d",
751 1900 + t->tm_year,
752 t->tm_mon+1, t->tm_mday,
753 t->tm_hour, t->tm_min, t->tm_sec);
756 if ($4 != NULL)
757 free($4);
759 | QUIT CRLF
761 reply(221, "Goodbye.");
762 dologout(0);
764 | NOTIMPL
766 nack($1);
768 | error
770 yyclearin; /* discard lookahead data */
771 yyerrok; /* clear error condition */
772 state = CMD; /* reset lexer state */
775 rcmd
776 : RNFR check_login_ro SP pathname CRLF
778 restart_point = 0;
779 if ($2 && $4) {
780 if (fromname)
781 free(fromname);
782 fromname = NULL;
783 if (renamefrom($4))
784 fromname = $4;
785 else
786 free($4);
787 } else if ($4) {
788 free($4);
791 | REST check_login SP NUMBER CRLF
793 if ($2) {
794 if (fromname)
795 free(fromname);
796 fromname = NULL;
797 restart_point = $4.o;
798 reply(350, "Restarting at %jd. %s",
799 (intmax_t)restart_point,
800 "Send STORE or RETRIEVE to initiate transfer.");
805 username
806 : STRING
809 password
810 : /* empty */
812 $$ = (char *)calloc(1, sizeof(char));
814 | STRING
817 byte_size
818 : NUMBER
820 $$ = $1.i;
824 host_port
825 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
826 NUMBER COMMA NUMBER
828 char *a, *p;
830 data_dest.su_len = sizeof(struct sockaddr_in);
831 data_dest.su_family = AF_INET;
832 p = (char *)&data_dest.su_sin.sin_port;
833 p[0] = $9.i; p[1] = $11.i;
834 a = (char *)&data_dest.su_sin.sin_addr;
835 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
839 host_long_port
840 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
841 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
842 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
843 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
844 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
845 NUMBER
847 char *a, *p;
849 memset(&data_dest, 0, sizeof(data_dest));
850 data_dest.su_len = sizeof(struct sockaddr_in6);
851 data_dest.su_family = AF_INET6;
852 p = (char *)&data_dest.su_port;
853 p[0] = $39.i; p[1] = $41.i;
854 a = (char *)&data_dest.su_sin6.sin6_addr;
855 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
856 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
857 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
858 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
859 if (his_addr.su_family == AF_INET6) {
860 /* XXX more sanity checks! */
861 data_dest.su_sin6.sin6_scope_id =
862 his_addr.su_sin6.sin6_scope_id;
864 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
865 memset(&data_dest, 0, sizeof(data_dest));
867 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
868 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
869 NUMBER
871 char *a, *p;
873 memset(&data_dest, 0, sizeof(data_dest));
874 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
875 data_dest.su_family = AF_INET;
876 p = (char *)&data_dest.su_port;
877 p[0] = $15.i; p[1] = $17.i;
878 a = (char *)&data_dest.su_sin.sin_addr;
879 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
880 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
881 memset(&data_dest, 0, sizeof(data_dest));
885 form_code
888 $$ = FORM_N;
892 $$ = FORM_T;
896 $$ = FORM_C;
900 type_code
903 cmd_type = TYPE_A;
904 cmd_form = FORM_N;
906 | A SP form_code
908 cmd_type = TYPE_A;
909 cmd_form = $3;
913 cmd_type = TYPE_E;
914 cmd_form = FORM_N;
916 | E SP form_code
918 cmd_type = TYPE_E;
919 cmd_form = $3;
923 cmd_type = TYPE_I;
927 cmd_type = TYPE_L;
928 cmd_bytesz = CHAR_BIT;
930 | L SP byte_size
932 cmd_type = TYPE_L;
933 cmd_bytesz = $3;
935 /* this is for a bug in the BBN ftp */
936 | L byte_size
938 cmd_type = TYPE_L;
939 cmd_bytesz = $2;
943 struct_code
946 $$ = STRU_F;
950 $$ = STRU_R;
954 $$ = STRU_P;
958 mode_code
961 $$ = MODE_S;
965 $$ = MODE_B;
969 $$ = MODE_C;
973 pathname
974 : pathstring
976 if (logged_in && $1) {
977 char *p;
980 * Expand ~user manually since glob(3)
981 * will return the unexpanded pathname
982 * if the corresponding file/directory
983 * doesn't exist yet. Using sole glob(3)
984 * would break natural commands like
985 * MKD ~user/newdir
986 * or
987 * RNTO ~/newfile
989 if ((p = exptilde($1)) != NULL) {
990 $$ = expglob(p);
991 free(p);
992 } else
993 $$ = NULL;
994 free($1);
995 } else
996 $$ = $1;
1000 pathstring
1001 : STRING
1004 octal_number
1005 : NUMBER
1007 int ret, dec, multby, digit;
1010 * Convert a number that was read as decimal number
1011 * to what it would be if it had been read as octal.
1013 dec = $1.i;
1014 multby = 1;
1015 ret = 0;
1016 while (dec) {
1017 digit = dec%10;
1018 if (digit > 7) {
1019 ret = -1;
1020 break;
1022 ret += digit * multby;
1023 multby *= 8;
1024 dec /= 10;
1026 $$ = ret;
1031 check_login
1032 : /* empty */
1034 $$ = check_login1();
1038 check_login_epsv
1039 : /* empty */
1041 if (noepsv) {
1042 reply(500, "EPSV command disabled.");
1043 $$ = 0;
1045 else
1046 $$ = check_login1();
1050 check_login_ro
1051 : /* empty */
1053 if (readonly) {
1054 reply(550, "Permission denied.");
1055 $$ = 0;
1057 else
1058 $$ = check_login1();
1064 #define CMD 0 /* beginning of command */
1065 #define ARGS 1 /* expect miscellaneous arguments */
1066 #define STR1 2 /* expect SP followed by STRING */
1067 #define STR2 3 /* expect STRING */
1068 #define OSTR 4 /* optional SP then STRING */
1069 #define ZSTR1 5 /* optional SP then optional STRING */
1070 #define ZSTR2 6 /* optional STRING after SP */
1071 #define SITECMD 7 /* SITE command */
1072 #define NSTR 8 /* Number followed by a string */
1074 #define MAXGLOBARGS 1000
1076 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1078 struct tab {
1079 char *name;
1080 short token;
1081 short state;
1082 short implemented; /* 1 if command is implemented */
1083 char *help;
1086 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1087 { "USER", USER, STR1, 1, "<sp> username" },
1088 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1089 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1090 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1091 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1092 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1093 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1094 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1095 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1096 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1097 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1098 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1099 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1100 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1101 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1102 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1103 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1104 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1105 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1106 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1107 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1108 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1109 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1110 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1111 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1112 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1113 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1114 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1115 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1116 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1117 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1118 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1119 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1120 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1121 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1122 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1123 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1124 { "FEAT", FEAT, ARGS, 1, "(get extended features)" },
1125 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1126 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1127 { "NOOP", NOOP, ARGS, 1, "" },
1128 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1129 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1130 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1131 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1132 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1133 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1134 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1135 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1136 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1137 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1138 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1139 { NULL, 0, 0, 0, 0 }
1142 struct tab sitetab[] = {
1143 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1144 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1145 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1146 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1147 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1148 { NULL, 0, 0, 0, 0 }
1151 static char *copy(char *);
1152 static char *expglob(char *);
1153 static char *exptilde(char *);
1154 static void help(struct tab *, char *);
1155 static struct tab *
1156 lookup(struct tab *, char *);
1157 static int port_check(const char *);
1158 #ifdef INET6
1159 static int port_check_v6(const char *);
1160 #endif
1161 static void sizecmd(char *);
1162 static void toolong(int);
1163 #ifdef INET6
1164 static void v4map_data_dest(void);
1165 #endif
1166 static int yylex(void);
1168 static struct tab *
1169 lookup(struct tab *p, char *cmd)
1172 for (; p->name != NULL; p++)
1173 if (strcmp(cmd, p->name) == 0)
1174 return (p);
1175 return (0);
1178 #include <arpa/telnet.h>
1181 * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1184 get_line(char *s, int n, FILE *iop)
1186 int c;
1187 char *cs;
1188 sigset_t sset, osset;
1190 cs = s;
1191 /* tmpline may contain saved command from urgent mode interruption */
1192 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1193 *cs++ = tmpline[c];
1194 if (tmpline[c] == '\n') {
1195 *cs++ = '\0';
1196 if (ftpdebug)
1197 syslog(LOG_DEBUG, "command: %s", s);
1198 tmpline[0] = '\0';
1199 return(0);
1201 if (c == 0)
1202 tmpline[0] = '\0';
1204 /* SIGURG would interrupt stdio if not blocked during the read loop */
1205 sigemptyset(&sset);
1206 sigaddset(&sset, SIGURG);
1207 sigprocmask(SIG_BLOCK, &sset, &osset);
1208 while ((c = getc(iop)) != EOF) {
1209 c &= 0377;
1210 if (c == IAC) {
1211 if ((c = getc(iop)) == EOF)
1212 goto got_eof;
1213 c &= 0377;
1214 switch (c) {
1215 case WILL:
1216 case WONT:
1217 if ((c = getc(iop)) == EOF)
1218 goto got_eof;
1219 printf("%c%c%c", IAC, DONT, 0377&c);
1220 fflush(stdout);
1221 continue;
1222 case DO:
1223 case DONT:
1224 if ((c = getc(iop)) == EOF)
1225 goto got_eof;
1226 printf("%c%c%c", IAC, WONT, 0377&c);
1227 fflush(stdout);
1228 continue;
1229 case IAC:
1230 break;
1231 default:
1232 continue; /* ignore command */
1235 *cs++ = c;
1236 if (--n <= 0) {
1238 * If command doesn't fit into buffer, discard the
1239 * rest of the command and indicate truncation.
1240 * This prevents the command to be split up into
1241 * multiple commands.
1243 while (c != '\n' && (c = getc(iop)) != EOF)
1245 return (-2);
1247 if (c == '\n')
1248 break;
1250 got_eof:
1251 sigprocmask(SIG_SETMASK, &osset, NULL);
1252 if (c == EOF && cs == s)
1253 return (-1);
1254 *cs++ = '\0';
1255 if (ftpdebug) {
1256 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1257 /* Don't syslog passwords */
1258 syslog(LOG_DEBUG, "command: %.5s ???", s);
1259 } else {
1260 char *cp;
1261 int len;
1263 /* Don't syslog trailing CR-LF */
1264 len = strlen(s);
1265 cp = s + len - 1;
1266 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1267 --cp;
1268 --len;
1270 syslog(LOG_DEBUG, "command: %.*s", len, s);
1273 return (0);
1276 static void
1277 toolong(int signo)
1280 reply(421,
1281 "Timeout (%d seconds): closing control connection.", timeout);
1282 if (logging)
1283 syslog(LOG_INFO, "User %s timed out after %d seconds",
1284 (pw ? pw -> pw_name : "unknown"), timeout);
1285 dologout(1);
1288 static int
1289 yylex(void)
1291 static int cpos;
1292 char *cp, *cp2;
1293 struct tab *p;
1294 int n;
1295 char c;
1297 for (;;) {
1298 switch (state) {
1300 case CMD:
1301 signal(SIGALRM, toolong);
1302 alarm(timeout);
1303 n = get_line(cbuf, sizeof(cbuf)-1, stdin);
1304 if (n == -1) {
1305 reply(221, "You could at least say goodbye.");
1306 dologout(0);
1307 } else if (n == -2) {
1308 reply(500, "Command too long.");
1309 alarm(0);
1310 continue;
1312 alarm(0);
1313 #ifdef SETPROCTITLE
1314 if (strncasecmp(cbuf, "PASS", 4) != 0)
1315 setproctitle("%s: %s", proctitle, cbuf);
1316 #endif /* SETPROCTITLE */
1317 if ((cp = strchr(cbuf, '\r'))) {
1318 *cp++ = '\n';
1319 *cp = '\0';
1321 if ((cp = strpbrk(cbuf, " \n")))
1322 cpos = cp - cbuf;
1323 if (cpos == 0)
1324 cpos = 4;
1325 c = cbuf[cpos];
1326 cbuf[cpos] = '\0';
1327 upper(cbuf);
1328 p = lookup(cmdtab, cbuf);
1329 cbuf[cpos] = c;
1330 if (p != 0) {
1331 yylval.s = p->name;
1332 if (!p->implemented)
1333 return (NOTIMPL); /* state remains CMD */
1334 state = p->state;
1335 return (p->token);
1337 break;
1339 case SITECMD:
1340 if (cbuf[cpos] == ' ') {
1341 cpos++;
1342 return (SP);
1344 cp = &cbuf[cpos];
1345 if ((cp2 = strpbrk(cp, " \n")))
1346 cpos = cp2 - cbuf;
1347 c = cbuf[cpos];
1348 cbuf[cpos] = '\0';
1349 upper(cp);
1350 p = lookup(sitetab, cp);
1351 cbuf[cpos] = c;
1352 if (guest == 0 && p != 0) {
1353 yylval.s = p->name;
1354 if (!p->implemented) {
1355 state = CMD;
1356 return (NOTIMPL);
1358 state = p->state;
1359 return (p->token);
1361 state = CMD;
1362 break;
1364 case ZSTR1:
1365 case OSTR:
1366 if (cbuf[cpos] == '\n') {
1367 state = CMD;
1368 return (CRLF);
1370 /* FALLTHROUGH */
1372 case STR1:
1373 dostr1:
1374 if (cbuf[cpos] == ' ') {
1375 cpos++;
1376 state = state == OSTR ? STR2 : state+1;
1377 return (SP);
1379 break;
1381 case ZSTR2:
1382 if (cbuf[cpos] == '\n') {
1383 state = CMD;
1384 return (CRLF);
1386 /* FALLTHROUGH */
1388 case STR2:
1389 cp = &cbuf[cpos];
1390 n = strlen(cp);
1391 cpos += n - 1;
1393 * Make sure the string is nonempty and \n terminated.
1395 if (n > 1 && cbuf[cpos] == '\n') {
1396 cbuf[cpos] = '\0';
1397 yylval.s = copy(cp);
1398 cbuf[cpos] = '\n';
1399 state = ARGS;
1400 return (STRING);
1402 break;
1404 case NSTR:
1405 if (cbuf[cpos] == ' ') {
1406 cpos++;
1407 return (SP);
1409 if (isdigit(cbuf[cpos])) {
1410 cp = &cbuf[cpos];
1411 while (isdigit(cbuf[++cpos]))
1413 c = cbuf[cpos];
1414 cbuf[cpos] = '\0';
1415 yylval.u.i = atoi(cp);
1416 cbuf[cpos] = c;
1417 state = STR1;
1418 return (NUMBER);
1420 state = STR1;
1421 goto dostr1;
1423 case ARGS:
1424 if (isdigit(cbuf[cpos])) {
1425 cp = &cbuf[cpos];
1426 while (isdigit(cbuf[++cpos]))
1428 c = cbuf[cpos];
1429 cbuf[cpos] = '\0';
1430 yylval.u.i = atoi(cp);
1431 yylval.u.o = strtoull(cp, NULL, 10);
1432 cbuf[cpos] = c;
1433 return (NUMBER);
1435 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1436 && !isalnum(cbuf[cpos + 3])) {
1437 cpos += 3;
1438 return ALL;
1440 switch (cbuf[cpos++]) {
1442 case '\n':
1443 state = CMD;
1444 return (CRLF);
1446 case ' ':
1447 return (SP);
1449 case ',':
1450 return (COMMA);
1452 case 'A':
1453 case 'a':
1454 return (A);
1456 case 'B':
1457 case 'b':
1458 return (B);
1460 case 'C':
1461 case 'c':
1462 return (C);
1464 case 'E':
1465 case 'e':
1466 return (E);
1468 case 'F':
1469 case 'f':
1470 return (F);
1472 case 'I':
1473 case 'i':
1474 return (I);
1476 case 'L':
1477 case 'l':
1478 return (L);
1480 case 'N':
1481 case 'n':
1482 return (N);
1484 case 'P':
1485 case 'p':
1486 return (P);
1488 case 'R':
1489 case 'r':
1490 return (R);
1492 case 'S':
1493 case 's':
1494 return (S);
1496 case 'T':
1497 case 't':
1498 return (T);
1501 break;
1503 default:
1504 fatalerror("Unknown state in scanner.");
1506 state = CMD;
1507 return (LEXERR);
1511 void
1512 upper(char *s)
1514 while (*s != '\0') {
1515 if (islower(*s))
1516 *s = toupper(*s);
1517 s++;
1521 static char *
1522 copy(char *s)
1524 char *p;
1526 p = malloc(strlen(s) + 1);
1527 if (p == NULL)
1528 fatalerror("Ran out of memory.");
1529 strcpy(p, s);
1530 return (p);
1533 static void
1534 help(struct tab *ctab, char *s)
1536 struct tab *c;
1537 int width, NCMDS;
1538 char *type;
1540 if (ctab == sitetab)
1541 type = "SITE ";
1542 else
1543 type = "";
1544 width = 0, NCMDS = 0;
1545 for (c = ctab; c->name != NULL; c++) {
1546 int len = strlen(c->name);
1548 if (len > width)
1549 width = len;
1550 NCMDS++;
1552 width = (width + 8) &~ 7;
1553 if (s == 0) {
1554 int i, j, w;
1555 int columns, lines;
1557 lreply(214, "The following %scommands are recognized %s.",
1558 type, "(* =>'s unimplemented)");
1559 columns = 76 / width;
1560 if (columns == 0)
1561 columns = 1;
1562 lines = (NCMDS + columns - 1) / columns;
1563 for (i = 0; i < lines; i++) {
1564 printf(" ");
1565 for (j = 0; j < columns; j++) {
1566 c = ctab + j * lines + i;
1567 printf("%s%c", c->name,
1568 c->implemented ? ' ' : '*');
1569 if (c + lines >= &ctab[NCMDS])
1570 break;
1571 w = strlen(c->name) + 1;
1572 while (w < width) {
1573 putchar(' ');
1574 w++;
1577 printf("\r\n");
1579 fflush(stdout);
1580 if (hostinfo)
1581 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1582 else
1583 reply(214, "End.");
1584 return;
1586 upper(s);
1587 c = lookup(ctab, s);
1588 if (c == NULL) {
1589 reply(502, "Unknown command %s.", s);
1590 return;
1592 if (c->implemented)
1593 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1594 else
1595 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1596 c->name, c->help);
1599 static void
1600 sizecmd(char *filename)
1602 switch (type) {
1603 case TYPE_L:
1604 case TYPE_I: {
1605 struct stat stbuf;
1606 if (stat(filename, &stbuf) < 0)
1607 perror_reply(550, filename);
1608 else if (!S_ISREG(stbuf.st_mode))
1609 reply(550, "%s: not a plain file.", filename);
1610 else
1611 reply(213, "%jd", (intmax_t)stbuf.st_size);
1612 break; }
1613 case TYPE_A: {
1614 FILE *fin;
1615 int c;
1616 off_t count;
1617 struct stat stbuf;
1618 fin = fopen(filename, "r");
1619 if (fin == NULL) {
1620 perror_reply(550, filename);
1621 return;
1623 if (fstat(fileno(fin), &stbuf) < 0) {
1624 perror_reply(550, filename);
1625 fclose(fin);
1626 return;
1627 } else if (!S_ISREG(stbuf.st_mode)) {
1628 reply(550, "%s: not a plain file.", filename);
1629 fclose(fin);
1630 return;
1631 } else if (stbuf.st_size > MAXASIZE) {
1632 reply(550, "%s: too large for type A SIZE.", filename);
1633 fclose(fin);
1634 return;
1637 count = 0;
1638 while((c=getc(fin)) != EOF) {
1639 if (c == '\n') /* will get expanded to \r\n */
1640 count++;
1641 count++;
1643 fclose(fin);
1645 reply(213, "%jd", (intmax_t)count);
1646 break; }
1647 default:
1648 reply(504, "SIZE not implemented for type %s.",
1649 typenames[type]);
1653 /* Return 1, if port check is done. Return 0, if not yet. */
1654 static int
1655 port_check(const char *pcmd)
1657 if (his_addr.su_family == AF_INET) {
1658 if (data_dest.su_family != AF_INET) {
1659 usedefault = 1;
1660 reply(500, "Invalid address rejected.");
1661 return 1;
1663 if (paranoid &&
1664 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1665 memcmp(&data_dest.su_sin.sin_addr,
1666 &his_addr.su_sin.sin_addr,
1667 sizeof(data_dest.su_sin.sin_addr)))) {
1668 usedefault = 1;
1669 reply(500, "Illegal PORT range rejected.");
1670 } else {
1671 usedefault = 0;
1672 if (pdata >= 0) {
1673 close(pdata);
1674 pdata = -1;
1676 reply(200, "%s command successful.", pcmd);
1678 return 1;
1680 return 0;
1683 static int
1684 check_login1(void)
1686 if (logged_in)
1687 return 1;
1688 else {
1689 reply(530, "Please login with USER and PASS.");
1690 return 0;
1695 * Replace leading "~user" in a pathname by the user's login directory.
1696 * Returned string will be in a freshly malloced buffer unless it's NULL.
1698 static char *
1699 exptilde(char *s)
1701 char *p, *q;
1702 char *path, *user;
1703 struct passwd *ppw;
1705 if ((p = strdup(s)) == NULL)
1706 return (NULL);
1707 if (*p != '~')
1708 return (p);
1710 user = p + 1; /* skip tilde */
1711 if ((path = strchr(p, '/')) != NULL)
1712 *(path++) = '\0'; /* separate ~user from the rest of path */
1713 if (*user == '\0') /* no user specified, use the current user */
1714 user = pw->pw_name;
1715 /* read passwd even for the current user since we may be chrooted */
1716 if ((ppw = getpwnam(user)) != NULL) {
1717 /* user found, substitute login directory for ~user */
1718 if (path)
1719 asprintf(&q, "%s/%s", ppw->pw_dir, path);
1720 else
1721 q = strdup(ppw->pw_dir);
1722 free(p);
1723 p = q;
1724 } else {
1725 /* user not found, undo the damage */
1726 if (path)
1727 path[-1] = '/';
1729 return (p);
1733 * Expand glob(3) patterns possibly present in a pathname.
1734 * Avoid expanding to a pathname including '\r' or '\n' in order to
1735 * not disrupt the FTP protocol.
1736 * The expansion found must be unique.
1737 * Return the result as a malloced string, or NULL if an error occured.
1739 * Problem: this production is used for all pathname
1740 * processing, but only gives a 550 error reply.
1741 * This is a valid reply in some cases but not in others.
1743 static char *
1744 expglob(char *s)
1746 char *p, **pp, *rval;
1747 int flags = GLOB_BRACE | GLOB_NOCHECK;
1748 int n;
1749 glob_t gl;
1751 memset(&gl, 0, sizeof(gl));
1752 /*flags |= GLOB_LIMIT;*/
1753 gl.gl_matchc = MAXGLOBARGS;
1754 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1755 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1756 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1757 p = *pp;
1758 n++;
1760 if (n == 0)
1761 rval = strdup(s);
1762 else if (n == 1)
1763 rval = strdup(p);
1764 else {
1765 reply(550, "Wildcard is ambiguous.");
1766 rval = NULL;
1768 } else {
1769 reply(550, "Wildcard expansion error.");
1770 rval = NULL;
1772 globfree(&gl);
1773 return (rval);
1776 #ifdef INET6
1777 /* Return 1, if port check is done. Return 0, if not yet. */
1778 static int
1779 port_check_v6(const char *pcmd)
1781 if (his_addr.su_family == AF_INET6) {
1782 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1783 /* Convert data_dest into v4 mapped sockaddr.*/
1784 v4map_data_dest();
1785 if (data_dest.su_family != AF_INET6) {
1786 usedefault = 1;
1787 reply(500, "Invalid address rejected.");
1788 return 1;
1790 if (paranoid &&
1791 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1792 memcmp(&data_dest.su_sin6.sin6_addr,
1793 &his_addr.su_sin6.sin6_addr,
1794 sizeof(data_dest.su_sin6.sin6_addr)))) {
1795 usedefault = 1;
1796 reply(500, "Illegal PORT range rejected.");
1797 } else {
1798 usedefault = 0;
1799 if (pdata >= 0) {
1800 close(pdata);
1801 pdata = -1;
1803 reply(200, "%s command successful.", pcmd);
1805 return 1;
1807 return 0;
1810 static void
1811 v4map_data_dest(void)
1813 struct in_addr savedaddr;
1814 int savedport;
1816 if (data_dest.su_family != AF_INET) {
1817 usedefault = 1;
1818 reply(500, "Invalid address rejected.");
1819 return;
1822 savedaddr = data_dest.su_sin.sin_addr;
1823 savedport = data_dest.su_port;
1825 memset(&data_dest, 0, sizeof(data_dest));
1826 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1827 data_dest.su_sin6.sin6_family = AF_INET6;
1828 data_dest.su_sin6.sin6_port = savedport;
1829 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1830 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1831 (caddr_t)&savedaddr, sizeof(savedaddr));
1833 #endif