buildworld depends on hw.machine exported from the kernel being correct.
[dragonfly/port-amd64.git] / contrib / lukemftpd / src / ftpcmd.y
blob68a598be718e68515222af7fef93970d8fb31f9d
1 /* $NetBSD: ftpcmd.y,v 1.66 2001/12/01 10:25:30 lukem Exp $ */
3 /*-
4 * Copyright (c) 1997-2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
40 * Copyright (c) 1985, 1988, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 * SUCH DAMAGE.
71 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
75 * Grammar for FTP commands.
76 * See RFC 959.
80 #include "lukemftpd.h"
82 #include "extern.h"
83 #include "version.h"
85 static int cmd_type;
86 static int cmd_form;
87 static int cmd_bytesz;
89 char cbuf[FTP_BUFLEN];
90 char *cmdp;
91 char *fromname;
95 %union {
96 int i;
97 char *s;
100 %token
101 A B C E F I
102 L N P R S T
104 SP CRLF COMMA
106 USER PASS ACCT CWD CDUP SMNT
107 QUIT REIN PORT PASV TYPE STRU
108 MODE RETR STOR STOU APPE ALLO
109 REST RNFR RNTO ABOR DELE RMD
110 MKD PWD LIST NLST SITE SYST
111 STAT HELP NOOP
113 AUTH ADAT PROT PBSZ CCC MIC
114 CONF ENC
116 FEAT OPTS
118 SIZE MDTM MLST MLSD
120 LPRT LPSV EPRT EPSV
122 MAIL MLFL MRCP MRSQ MSAM MSND
123 MSOM
125 CHMOD IDLE RATEGET RATEPUT UMASK
127 LEXERR
129 %token <s> STRING
130 %token <s> ALL
131 %token <i> NUMBER
133 %type <i> check_login octal_number byte_size
134 %type <i> struct_code mode_code type_code form_code decimal_integer
135 %type <s> pathstring pathname password username
136 %type <s> mechanism_name base64data prot_code
138 %start cmd_sel
142 cmd_sel
143 : cmd
145 fromname = NULL;
146 restart_point = (off_t) 0;
149 | rcmd
154 /* RFC 959 */
155 : USER SP username CRLF
157 user($3);
158 free($3);
161 | PASS SP password CRLF
163 pass($3);
164 memset($3, 0, strlen($3));
165 free($3);
168 | CWD check_login CRLF
170 if ($2)
171 cwd(homedir);
174 | CWD check_login SP pathname CRLF
176 if ($2 && $4 != NULL)
177 cwd($4);
178 if ($4 != NULL)
179 free($4);
182 | CDUP check_login CRLF
184 if ($2)
185 cwd("..");
188 | QUIT CRLF
190 if (logged_in) {
191 reply(-221, "%s", "");
192 reply(0,
193 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
194 (LLT)total_data, PLURAL(total_data),
195 (LLT)total_files, PLURAL(total_files));
196 reply(0,
197 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
198 (LLT)total_bytes, PLURAL(total_bytes),
199 (LLT)total_xfers, PLURAL(total_xfers));
201 reply(221,
202 "Thank you for using the FTP service on %s.",
203 hostname);
204 if (logged_in && logging) {
205 syslog(LOG_INFO,
206 "Data traffic: " LLF " byte%s in " LLF " file%s",
207 (LLT)total_data, PLURAL(total_data),
208 (LLT)total_files, PLURAL(total_files));
209 syslog(LOG_INFO,
210 "Total traffic: " LLF " byte%s in " LLF " transfer%s",
211 (LLT)total_bytes, PLURAL(total_bytes),
212 (LLT)total_xfers, PLURAL(total_xfers));
215 dologout(0);
218 | PORT check_login SP host_port CRLF
220 if ($2)
221 port_check("PORT", AF_INET);
224 | LPRT check_login SP host_long_port4 CRLF
226 if ($2)
227 port_check("LPRT", AF_INET);
230 | LPRT check_login SP host_long_port6 CRLF
232 #ifdef INET6
233 if ($2)
234 port_check("LPRT", AF_INET6);
235 #else
236 reply(500, "IPv6 support not available.");
237 #endif
240 | EPRT check_login SP STRING CRLF
242 if ($2) {
243 if (extended_port($4) == 0)
244 port_check("EPRT", -1);
246 free($4);
249 | PASV check_login CRLF
251 if ($2) {
252 if (CURCLASS_FLAGS_ISSET(passive))
253 passive();
254 else
255 reply(500, "PASV mode not available.");
259 | LPSV check_login CRLF
261 if ($2) {
262 if (epsvall)
263 reply(501,
264 "LPSV disallowed after EPSV ALL");
265 else
266 long_passive("LPSV", PF_UNSPEC);
270 | EPSV check_login SP NUMBER CRLF
272 if ($2)
273 long_passive("EPSV", epsvproto2af($4));
276 | EPSV check_login SP ALL CRLF
278 if ($2) {
279 reply(200, "EPSV ALL command successful.");
280 epsvall++;
284 | EPSV check_login CRLF
286 if ($2)
287 long_passive("EPSV", PF_UNSPEC);
290 | TYPE check_login SP type_code CRLF
292 if ($2) {
294 switch (cmd_type) {
296 case TYPE_A:
297 if (cmd_form == FORM_N) {
298 reply(200, "Type set to A.");
299 type = cmd_type;
300 form = cmd_form;
301 } else
302 reply(504, "Form must be N.");
303 break;
305 case TYPE_E:
306 reply(504, "Type E not implemented.");
307 break;
309 case TYPE_I:
310 reply(200, "Type set to I.");
311 type = cmd_type;
312 break;
314 case TYPE_L:
315 #if NBBY == 8
316 if (cmd_bytesz == 8) {
317 reply(200,
318 "Type set to L (byte size 8).");
319 type = cmd_type;
320 } else
321 reply(504, "Byte size must be 8.");
322 #else /* NBBY == 8 */
323 UNIMPLEMENTED for NBBY != 8
324 #endif /* NBBY == 8 */
330 | STRU check_login SP struct_code CRLF
332 if ($2) {
333 switch ($4) {
335 case STRU_F:
336 reply(200, "STRU F ok.");
337 break;
339 default:
340 reply(504, "Unimplemented STRU type.");
345 | MODE check_login SP mode_code CRLF
347 if ($2) {
348 switch ($4) {
350 case MODE_S:
351 reply(200, "MODE S ok.");
352 break;
354 default:
355 reply(502, "Unimplemented MODE type.");
360 | RETR check_login SP pathname CRLF
362 if ($2 && $4 != NULL)
363 retrieve(NULL, $4);
364 if ($4 != NULL)
365 free($4);
368 | STOR SP pathname CRLF
370 if (check_write($3, 1))
371 store($3, "w", 0);
372 if ($3 != NULL)
373 free($3);
376 | STOU SP pathname CRLF
378 if (check_write($3, 1))
379 store($3, "w", 1);
380 if ($3 != NULL)
381 free($3);
384 | APPE SP pathname CRLF
386 if (check_write($3, 1))
387 store($3, "a", 0);
388 if ($3 != NULL)
389 free($3);
392 | ALLO check_login SP NUMBER CRLF
394 if ($2)
395 reply(202, "ALLO command ignored.");
398 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
400 if ($2)
401 reply(202, "ALLO command ignored.");
404 | RNTO SP pathname CRLF
406 if (check_write($3, 0)) {
407 if (fromname) {
408 renamecmd(fromname, $3);
409 free(fromname);
410 fromname = NULL;
411 } else {
412 reply(503, "Bad sequence of commands.");
415 if ($3 != NULL)
416 free($3);
419 | ABOR check_login CRLF
421 if (is_oob)
422 abor();
423 else if ($2)
424 reply(225, "ABOR command successful.");
427 | DELE SP pathname CRLF
429 if (check_write($3, 0))
430 delete($3);
431 if ($3 != NULL)
432 free($3);
435 | RMD SP pathname CRLF
437 if (check_write($3, 0))
438 removedir($3);
439 if ($3 != NULL)
440 free($3);
443 | MKD SP pathname CRLF
445 if (check_write($3, 0))
446 makedir($3);
447 if ($3 != NULL)
448 free($3);
451 | PWD check_login CRLF
453 if ($2)
454 pwd();
457 | LIST check_login CRLF
459 char *argv[] = { INTERNAL_LS, "-lgA", NULL };
461 if ($2)
462 retrieve(argv, "");
465 | LIST check_login SP pathname CRLF
467 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
469 if ($2 && $4 != NULL) {
470 argv[2] = $4;
471 retrieve(argv, $4);
473 if ($4 != NULL)
474 free($4);
477 | NLST check_login CRLF
479 if ($2)
480 send_file_list(".");
483 | NLST check_login SP pathname CRLF
485 if ($2)
486 send_file_list($4);
487 free($4);
490 | SITE SP HELP CRLF
492 help(sitetab, NULL);
495 | SITE SP CHMOD SP octal_number SP pathname CRLF
497 if (check_write($7, 0)) {
498 if ($5 > 0777)
499 reply(501,
500 "CHMOD: Mode value must be between 0 and 0777");
501 else if (chmod($7, $5) < 0)
502 perror_reply(550, $7);
503 else
504 reply(200, "CHMOD command successful.");
506 if ($7 != NULL)
507 free($7);
510 | SITE SP HELP SP STRING CRLF
512 help(sitetab, $5);
513 free($5);
516 | SITE SP IDLE check_login CRLF
518 if ($4) {
519 reply(200,
520 "Current IDLE time limit is %d seconds; max %d",
521 curclass.timeout, curclass.maxtimeout);
525 | SITE SP IDLE check_login SP NUMBER CRLF
527 if ($4) {
528 if ($6 < 30 || $6 > curclass.maxtimeout) {
529 reply(501,
530 "IDLE time limit must be between 30 and %d seconds",
531 curclass.maxtimeout);
532 } else {
533 curclass.timeout = $6;
534 (void) alarm(curclass.timeout);
535 reply(200,
536 "IDLE time limit set to %d seconds",
537 curclass.timeout);
542 | SITE SP RATEGET check_login CRLF
544 if ($4) {
545 reply(200,
546 "Current RATEGET is " LLF " bytes/sec",
547 (LLT)curclass.rateget);
551 | SITE SP RATEGET check_login SP STRING CRLF
553 char *p = $6;
554 LLT rate;
556 if ($4) {
557 rate = strsuftoll(p);
558 if (rate == -1)
559 reply(501, "Invalid RATEGET %s", p);
560 else if (curclass.maxrateget &&
561 rate > curclass.maxrateget)
562 reply(501,
563 "RATEGET " LLF " is larger than maximum RATEGET " LLF,
564 (LLT)rate,
565 (LLT)curclass.maxrateget);
566 else {
567 curclass.rateget = rate;
568 reply(200,
569 "RATEGET set to " LLF " bytes/sec",
570 (LLT)curclass.rateget);
573 free($6);
576 | SITE SP RATEPUT check_login CRLF
578 if ($4) {
579 reply(200,
580 "Current RATEPUT is " LLF " bytes/sec",
581 (LLT)curclass.rateput);
585 | SITE SP RATEPUT check_login SP STRING CRLF
587 char *p = $6;
588 LLT rate;
590 if ($4) {
591 rate = strsuftoll(p);
592 if (rate == -1)
593 reply(501, "Invalid RATEPUT %s", p);
594 else if (curclass.maxrateput &&
595 rate > curclass.maxrateput)
596 reply(501,
597 "RATEPUT " LLF " is larger than maximum RATEPUT " LLF,
598 (LLT)rate,
599 (LLT)curclass.maxrateput);
600 else {
601 curclass.rateput = rate;
602 reply(200,
603 "RATEPUT set to " LLF " bytes/sec",
604 (LLT)curclass.rateput);
607 free($6);
610 | SITE SP UMASK check_login CRLF
612 int oldmask;
614 if ($4) {
615 oldmask = umask(0);
616 (void) umask(oldmask);
617 reply(200, "Current UMASK is %03o", oldmask);
621 | SITE SP UMASK check_login SP octal_number CRLF
623 int oldmask;
625 if ($4 && CURCLASS_FLAGS_ISSET(modify)) {
626 if (($6 == -1) || ($6 > 0777)) {
627 reply(501, "Bad UMASK value");
628 } else {
629 oldmask = umask($6);
630 reply(200,
631 "UMASK set to %03o (was %03o)",
632 $6, oldmask);
637 | SYST CRLF
639 if (EMPTYSTR(version))
640 reply(215, "UNIX Type: L%d", NBBY);
641 else
642 reply(215, "UNIX Type: L%d Version: %s", NBBY,
643 version);
646 | STAT check_login SP pathname CRLF
648 if ($2 && $4 != NULL)
649 statfilecmd($4);
650 if ($4 != NULL)
651 free($4);
654 | STAT CRLF
656 if (is_oob)
657 statxfer();
658 else
659 statcmd();
662 | HELP CRLF
664 help(cmdtab, NULL);
667 | HELP SP STRING CRLF
669 char *cp = $3;
671 if (strncasecmp(cp, "SITE", 4) == 0) {
672 cp = $3 + 4;
673 if (*cp == ' ')
674 cp++;
675 if (*cp)
676 help(sitetab, cp);
677 else
678 help(sitetab, NULL);
679 } else
680 help(cmdtab, $3);
681 free($3);
684 | NOOP CRLF
686 reply(200, "NOOP command successful.");
689 /* RFC 2228 */
690 | AUTH SP mechanism_name CRLF
692 reply(502, "RFC 2228 authentication not implemented.");
693 free($3);
696 | ADAT SP base64data CRLF
698 reply(503,
699 "Please set authentication state with AUTH.");
700 free($3);
703 | PROT SP prot_code CRLF
705 reply(503,
706 "Please set protection buffer size with PBSZ.");
707 free($3);
710 | PBSZ SP decimal_integer CRLF
712 reply(503,
713 "Please set authentication state with AUTH.");
716 | CCC CRLF
718 reply(533, "No protection enabled.");
721 | MIC SP base64data CRLF
723 reply(502, "RFC 2228 authentication not implemented.");
724 free($3);
727 | CONF SP base64data CRLF
729 reply(502, "RFC 2228 authentication not implemented.");
730 free($3);
733 | ENC SP base64data CRLF
735 reply(502, "RFC 2228 authentication not implemented.");
736 free($3);
739 /* RFC 2389 */
740 | FEAT CRLF
743 feat();
746 | OPTS SP STRING CRLF
749 opts($3);
750 free($3);
754 /* extensions from draft-ietf-ftpext-mlst-11 */
757 * Return size of file in a format suitable for
758 * using with RESTART (we just count bytes).
760 | SIZE check_login SP pathname CRLF
762 if ($2 && $4 != NULL)
763 sizecmd($4);
764 if ($4 != NULL)
765 free($4);
769 * Return modification time of file as an ISO 3307
770 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
771 * where xxx is the fractional second (of any precision,
772 * not necessarily 3 digits)
774 | MDTM check_login SP pathname CRLF
776 if ($2 && $4 != NULL) {
777 struct stat stbuf;
778 if (stat($4, &stbuf) < 0)
779 perror_reply(550, $4);
780 else if (!S_ISREG(stbuf.st_mode)) {
781 reply(550, "%s: not a plain file.", $4);
782 } else {
783 struct tm *t;
785 t = gmtime(&stbuf.st_mtime);
786 reply(213,
787 "%04d%02d%02d%02d%02d%02d",
788 TM_YEAR_BASE + t->tm_year,
789 t->tm_mon+1, t->tm_mday,
790 t->tm_hour, t->tm_min, t->tm_sec);
793 if ($4 != NULL)
794 free($4);
797 | MLST check_login SP pathname CRLF
799 if ($2 && $4 != NULL)
800 mlst($4);
801 if ($4 != NULL)
802 free($4);
805 | MLST check_login CRLF
807 mlst(NULL);
810 | MLSD check_login SP pathname CRLF
812 if ($2 && $4 != NULL)
813 mlsd($4);
814 if ($4 != NULL)
815 free($4);
818 | MLSD check_login CRLF
820 mlsd(NULL);
823 | error CRLF
825 yyerrok;
829 rcmd
830 : REST check_login SP byte_size CRLF
832 if ($2) {
833 fromname = NULL;
834 restart_point = $4; /* XXX: $4 is only "int" */
835 reply(350,
836 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
837 (LLT)restart_point);
841 | RNFR SP pathname CRLF
843 restart_point = (off_t) 0;
844 if (check_write($3, 0))
845 fromname = renamefrom($3);
846 if ($3 != NULL)
847 free($3);
851 username
852 : STRING
855 password
856 : /* empty */
858 $$ = (char *)calloc(1, sizeof(char));
861 | STRING
864 byte_size
865 : NUMBER
868 host_port
869 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
870 NUMBER COMMA NUMBER
872 char *a, *p;
874 memset(&data_dest, 0, sizeof(data_dest));
875 data_dest.su_len = sizeof(struct sockaddr_in);
876 data_dest.su_family = AF_INET;
877 p = (char *)&data_dest.su_port;
878 p[0] = $9; p[1] = $11;
879 a = (char *)&data_dest.su_addr;
880 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
884 host_long_port4
885 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
886 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
887 NUMBER
889 char *a, *p;
891 memset(&data_dest, 0, sizeof(data_dest));
892 data_dest.su_len = sizeof(struct sockaddr_in);
893 data_dest.su_family = AF_INET;
894 p = (char *)&data_dest.su_port;
895 p[0] = $15; p[1] = $17;
896 a = (char *)&data_dest.su_addr;
897 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
899 /* reject invalid LPRT command */
900 if ($1 != 4 || $3 != 4 || $13 != 2)
901 memset(&data_dest, 0, sizeof(data_dest));
905 host_long_port6
906 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
907 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
908 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
909 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
910 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
911 NUMBER
913 #ifdef INET6
914 char *a, *p;
916 memset(&data_dest, 0, sizeof(data_dest));
917 data_dest.su_len = sizeof(struct sockaddr_in6);
918 data_dest.su_family = AF_INET6;
919 p = (char *)&data_dest.su_port;
920 p[0] = $39; p[1] = $41;
921 a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
922 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
923 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
924 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
925 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
926 if (his_addr.su_family == AF_INET6) {
927 /* XXX: more sanity checks! */
928 data_dest.su_scope_id = his_addr.su_scope_id;
930 #else
931 memset(&data_dest, 0, sizeof(data_dest));
932 #endif /* INET6 */
933 /* reject invalid LPRT command */
934 if ($1 != 6 || $3 != 16 || $37 != 2)
935 memset(&data_dest, 0, sizeof(data_dest));
939 form_code
942 $$ = FORM_N;
947 $$ = FORM_T;
952 $$ = FORM_C;
956 type_code
959 cmd_type = TYPE_A;
960 cmd_form = FORM_N;
963 | A SP form_code
965 cmd_type = TYPE_A;
966 cmd_form = $3;
971 cmd_type = TYPE_E;
972 cmd_form = FORM_N;
975 | E SP form_code
977 cmd_type = TYPE_E;
978 cmd_form = $3;
983 cmd_type = TYPE_I;
988 cmd_type = TYPE_L;
989 cmd_bytesz = NBBY;
992 | L SP byte_size
994 cmd_type = TYPE_L;
995 cmd_bytesz = $3;
998 /* this is for a bug in the BBN ftp */
999 | L byte_size
1001 cmd_type = TYPE_L;
1002 cmd_bytesz = $2;
1006 struct_code
1009 $$ = STRU_F;
1014 $$ = STRU_R;
1019 $$ = STRU_P;
1023 mode_code
1026 $$ = MODE_S;
1031 $$ = MODE_B;
1036 $$ = MODE_C;
1040 pathname
1041 : pathstring
1044 * Problem: this production is used for all pathname
1045 * processing, but only gives a 550 error reply.
1046 * This is a valid reply in some cases but not in
1047 * others.
1049 if (logged_in && $1 && *$1 == '~') {
1050 char *path, *home, *result;
1051 size_t len;
1053 path = strchr($1 + 1, '/');
1054 if (path != NULL)
1055 *path++ = '\0';
1056 if ($1[1] == '\0')
1057 home = homedir;
1058 else {
1059 struct passwd *hpw;
1061 if ((hpw = getpwnam($1 + 1)) != NULL)
1062 home = hpw->pw_dir;
1063 else
1064 home = $1;
1066 len = strlen(home) + 1;
1067 if (path != NULL)
1068 len += strlen(path) + 1;
1069 if ((result = malloc(len)) == NULL)
1070 fatal("Local resource failure: malloc");
1071 strlcpy(result, home, len);
1072 if (path != NULL) {
1073 strlcat(result, "/", len);
1074 strlcat(result, path, len);
1076 $$ = result;
1077 free($1);
1078 } else
1079 $$ = $1;
1083 pathstring
1084 : STRING
1087 octal_number
1088 : NUMBER
1090 int ret, dec, multby, digit;
1093 * Convert a number that was read as decimal number
1094 * to what it would be if it had been read as octal.
1096 dec = $1;
1097 multby = 1;
1098 ret = 0;
1099 while (dec) {
1100 digit = dec%10;
1101 if (digit > 7) {
1102 ret = -1;
1103 break;
1105 ret += digit * multby;
1106 multby *= 8;
1107 dec /= 10;
1109 $$ = ret;
1113 mechanism_name
1114 : STRING
1117 base64data
1118 : STRING
1121 prot_code
1122 : STRING
1125 decimal_integer
1126 : NUMBER
1129 check_login
1130 : /* empty */
1132 if (logged_in)
1133 $$ = 1;
1134 else {
1135 reply(530, "Please login with USER and PASS.");
1136 $$ = 0;
1137 hasyyerrored = 1;
1144 #define CMD 0 /* beginning of command */
1145 #define ARGS 1 /* expect miscellaneous arguments */
1146 #define STR1 2 /* expect SP followed by STRING */
1147 #define STR2 3 /* expect STRING */
1148 #define OSTR 4 /* optional SP then STRING */
1149 #define ZSTR1 5 /* SP then optional STRING */
1150 #define ZSTR2 6 /* optional STRING after SP */
1151 #define SITECMD 7 /* SITE command */
1152 #define NSTR 8 /* Number followed by a string */
1153 #define NOARGS 9 /* No arguments allowed */
1154 #define EOLN 10 /* End of line */
1156 struct tab cmdtab[] = {
1157 /* From RFC 959, in order defined (5.3.1) */
1158 { "USER", USER, STR1, 1, "<sp> username" },
1159 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1160 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1161 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1162 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1163 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1164 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1165 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1166 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1167 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1168 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1169 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1170 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1171 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1172 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1173 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1174 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1175 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1176 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1177 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1178 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1179 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1180 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1181 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1182 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1183 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1184 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1185 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1186 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1187 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1188 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1189 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1190 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1191 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1192 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1193 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1194 { "NOOP", NOOP, NOARGS, 2, "" },
1196 /* From RFC 2228, in order defined */
1197 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1198 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1199 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1200 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1201 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1202 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1203 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1204 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1206 /* From RFC 2389, in order defined */
1207 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1208 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1210 /* from draft-ietf-ftpext-mlst-11 */
1211 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1212 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1213 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1214 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1216 /* obsolete commands */
1217 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1218 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1219 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1220 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1221 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1222 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1223 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1224 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1225 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1226 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1227 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1228 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1230 { NULL, 0, 0, 0, 0 }
1233 struct tab sitetab[] = {
1234 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1235 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1236 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1237 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1238 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1239 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1240 { NULL, 0, 0, 0, NULL }
1243 static int check_write(const char *, int);
1244 static void help(struct tab *, const char *);
1245 static void port_check(const char *, int);
1246 static void toolong(int);
1247 static int yylex(void);
1249 extern int epsvall;
1252 * Check if a filename is allowed to be modified (isupload == 0) or
1253 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1255 static int
1256 check_write(const char *file, int isupload)
1258 if (file == NULL)
1259 return (0);
1260 if (! logged_in) {
1261 reply(530, "Please login with USER and PASS.");
1262 return (0);
1264 /* checking modify */
1265 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1266 reply(502, "No permission to use this command.");
1267 return (0);
1269 /* checking upload */
1270 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1271 reply(502, "No permission to use this command.");
1272 return (0);
1274 /* checking sanenames */
1275 if (CURCLASS_FLAGS_ISSET(sanenames)) {
1276 const char *p;
1278 if (file[0] == '.')
1279 goto insane_name;
1280 for (p = file; *p; p++) {
1281 if (isalnum(*p) || *p == '-' || *p == '+' ||
1282 *p == ',' || *p == '.' || *p == '_')
1283 continue;
1284 insane_name:
1285 reply(553, "File name `%s' not allowed.", file);
1286 return (0);
1289 return (1);
1292 struct tab *
1293 lookup(struct tab *p, const char *cmd)
1296 for (; p->name != NULL; p++)
1297 if (strcasecmp(cmd, p->name) == 0)
1298 return (p);
1299 return (0);
1302 #include <arpa/telnet.h>
1305 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1307 char *
1308 getline(char *s, int n, FILE *iop)
1310 int c;
1311 char *cs;
1313 cs = s;
1314 /* tmpline may contain saved command from urgent mode interruption */
1315 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1316 *cs++ = tmpline[c];
1317 if (tmpline[c] == '\n') {
1318 *cs++ = '\0';
1319 if (debug)
1320 syslog(LOG_DEBUG, "command: %s", s);
1321 tmpline[0] = '\0';
1322 return(s);
1324 if (c == 0)
1325 tmpline[0] = '\0';
1327 while ((c = getc(iop)) != EOF) {
1328 total_bytes++;
1329 total_bytes_in++;
1330 c &= 0377;
1331 if (c == IAC) {
1332 if ((c = getc(iop)) != EOF) {
1333 total_bytes++;
1334 total_bytes_in++;
1335 c &= 0377;
1336 switch (c) {
1337 case WILL:
1338 case WONT:
1339 c = getc(iop);
1340 total_bytes++;
1341 total_bytes_in++;
1342 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1343 (void) fflush(stdout);
1344 continue;
1345 case DO:
1346 case DONT:
1347 c = getc(iop);
1348 total_bytes++;
1349 total_bytes_in++;
1350 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1351 (void) fflush(stdout);
1352 continue;
1353 case IAC:
1354 break;
1355 default:
1356 continue; /* ignore command */
1360 *cs++ = c;
1361 if (--n <= 0 || c == '\n')
1362 break;
1364 if (c == EOF && cs == s)
1365 return (NULL);
1366 *cs++ = '\0';
1367 if (debug) {
1368 if ((curclass.type != CLASS_GUEST &&
1369 strncasecmp(s, "PASS ", 5) == 0) ||
1370 strncasecmp(s, "ACCT ", 5) == 0) {
1371 /* Don't syslog passwords */
1372 syslog(LOG_DEBUG, "command: %.4s ???", s);
1373 } else {
1374 char *cp;
1375 int len;
1377 /* Don't syslog trailing CR-LF */
1378 len = strlen(s);
1379 cp = s + len - 1;
1380 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1381 --cp;
1382 --len;
1384 syslog(LOG_DEBUG, "command: %.*s", len, s);
1387 return (s);
1390 static void
1391 toolong(int signo)
1394 reply(421,
1395 "Timeout (%d seconds): closing control connection.",
1396 curclass.timeout);
1397 if (logging)
1398 syslog(LOG_INFO, "User %s timed out after %d seconds",
1399 (pw ? pw->pw_name : "unknown"), curclass.timeout);
1400 dologout(1);
1403 void
1404 ftp_handle_line(char *cp)
1407 cmdp = cp;
1408 yyparse();
1411 void
1412 ftp_loop(void)
1415 while (1) {
1416 (void) signal(SIGALRM, toolong);
1417 (void) alarm(curclass.timeout);
1418 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1419 reply(221, "You could at least say goodbye.");
1420 dologout(0);
1422 (void) alarm(0);
1423 ftp_handle_line(cbuf);
1425 /*NOTREACHED*/
1428 static int
1429 yylex(void)
1431 static int cpos, state;
1432 char *cp, *cp2;
1433 struct tab *p;
1434 int n;
1435 char c;
1437 switch (state) {
1439 case CMD:
1440 hasyyerrored = 0;
1441 if ((cp = strchr(cmdp, '\r'))) {
1442 *cp = '\0';
1443 #if HAVE_SETPROCTITLE
1444 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1445 strncasecmp(cmdp, "ACCT", 4) != 0)
1446 setproctitle("%s: %s", proctitle, cmdp);
1447 #endif /* HAVE_SETPROCTITLE */
1448 *cp++ = '\n';
1449 *cp = '\0';
1451 if ((cp = strpbrk(cmdp, " \n")))
1452 cpos = cp - cmdp;
1453 if (cpos == 0)
1454 cpos = 4;
1455 c = cmdp[cpos];
1456 cmdp[cpos] = '\0';
1457 p = lookup(cmdtab, cmdp);
1458 cmdp[cpos] = c;
1459 if (p != NULL) {
1460 if (is_oob && ! CMD_OOB(p)) {
1461 /* command will be handled in-band */
1462 return (0);
1463 } else if (! CMD_IMPLEMENTED(p)) {
1464 reply(502, "%s command not implemented.",
1465 p->name);
1466 hasyyerrored = 1;
1467 break;
1469 state = p->state;
1470 yylval.s = p->name;
1471 return (p->token);
1473 break;
1475 case SITECMD:
1476 if (cmdp[cpos] == ' ') {
1477 cpos++;
1478 return (SP);
1480 cp = &cmdp[cpos];
1481 if ((cp2 = strpbrk(cp, " \n")))
1482 cpos = cp2 - cmdp;
1483 c = cmdp[cpos];
1484 cmdp[cpos] = '\0';
1485 p = lookup(sitetab, cp);
1486 cmdp[cpos] = c;
1487 if (p != NULL) {
1488 if (!CMD_IMPLEMENTED(p)) {
1489 reply(502, "SITE %s command not implemented.",
1490 p->name);
1491 hasyyerrored = 1;
1492 break;
1494 state = p->state;
1495 yylval.s = p->name;
1496 return (p->token);
1498 break;
1500 case OSTR:
1501 if (cmdp[cpos] == '\n') {
1502 state = EOLN;
1503 return (CRLF);
1505 /* FALLTHROUGH */
1507 case STR1:
1508 case ZSTR1:
1509 dostr1:
1510 if (cmdp[cpos] == ' ') {
1511 cpos++;
1512 state = state == OSTR ? STR2 : state+1;
1513 return (SP);
1515 break;
1517 case ZSTR2:
1518 if (cmdp[cpos] == '\n') {
1519 state = EOLN;
1520 return (CRLF);
1522 /* FALLTHROUGH */
1524 case STR2:
1525 cp = &cmdp[cpos];
1526 n = strlen(cp);
1527 cpos += n - 1;
1529 * Make sure the string is nonempty and \n terminated.
1531 if (n > 1 && cmdp[cpos] == '\n') {
1532 cmdp[cpos] = '\0';
1533 yylval.s = xstrdup(cp);
1534 cmdp[cpos] = '\n';
1535 state = ARGS;
1536 return (STRING);
1538 break;
1540 case NSTR:
1541 if (cmdp[cpos] == ' ') {
1542 cpos++;
1543 return (SP);
1545 if (isdigit(cmdp[cpos])) {
1546 cp = &cmdp[cpos];
1547 while (isdigit(cmdp[++cpos]))
1549 c = cmdp[cpos];
1550 cmdp[cpos] = '\0';
1551 yylval.i = atoi(cp);
1552 cmdp[cpos] = c;
1553 state = STR1;
1554 return (NUMBER);
1556 state = STR1;
1557 goto dostr1;
1559 case ARGS:
1560 if (isdigit(cmdp[cpos])) {
1561 cp = &cmdp[cpos];
1562 while (isdigit(cmdp[++cpos]))
1564 c = cmdp[cpos];
1565 cmdp[cpos] = '\0';
1566 yylval.i = atoi(cp);
1567 cmdp[cpos] = c;
1568 return (NUMBER);
1570 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1571 && !isalnum(cmdp[cpos + 3])) {
1572 yylval.s = xstrdup("ALL");
1573 cpos += 3;
1574 return ALL;
1576 switch (cmdp[cpos++]) {
1578 case '\n':
1579 state = EOLN;
1580 return (CRLF);
1582 case ' ':
1583 return (SP);
1585 case ',':
1586 return (COMMA);
1588 case 'A':
1589 case 'a':
1590 return (A);
1592 case 'B':
1593 case 'b':
1594 return (B);
1596 case 'C':
1597 case 'c':
1598 return (C);
1600 case 'E':
1601 case 'e':
1602 return (E);
1604 case 'F':
1605 case 'f':
1606 return (F);
1608 case 'I':
1609 case 'i':
1610 return (I);
1612 case 'L':
1613 case 'l':
1614 return (L);
1616 case 'N':
1617 case 'n':
1618 return (N);
1620 case 'P':
1621 case 'p':
1622 return (P);
1624 case 'R':
1625 case 'r':
1626 return (R);
1628 case 'S':
1629 case 's':
1630 return (S);
1632 case 'T':
1633 case 't':
1634 return (T);
1637 break;
1639 case NOARGS:
1640 if (cmdp[cpos] == '\n') {
1641 state = EOLN;
1642 return (CRLF);
1644 c = cmdp[cpos];
1645 cmdp[cpos] = '\0';
1646 reply(501, "'%s' command does not take any arguments.", cmdp);
1647 hasyyerrored = 1;
1648 cmdp[cpos] = c;
1649 break;
1651 case EOLN:
1652 state = CMD;
1653 return (0);
1655 default:
1656 fatal("Unknown state in scanner.");
1658 yyerror(NULL);
1659 state = CMD;
1660 is_oob = 0;
1661 longjmp(errcatch, 0);
1662 /* NOTREACHED */
1665 /* ARGSUSED */
1666 void
1667 yyerror(char *s)
1669 char *cp;
1671 if (hasyyerrored || is_oob)
1672 return;
1673 if ((cp = strchr(cmdp,'\n')) != NULL)
1674 *cp = '\0';
1675 reply(500, "'%s': command not understood.", cmdp);
1676 hasyyerrored = 1;
1679 static void
1680 help(struct tab *ctab, const char *s)
1682 struct tab *c;
1683 int width, NCMDS;
1684 char *htype;
1686 if (ctab == sitetab)
1687 htype = "SITE ";
1688 else
1689 htype = "";
1690 width = 0, NCMDS = 0;
1691 for (c = ctab; c->name != NULL; c++) {
1692 int len = strlen(c->name);
1694 if (len > width)
1695 width = len;
1696 NCMDS++;
1698 width = (width + 8) &~ 7;
1699 if (s == 0) {
1700 int i, j, w;
1701 int columns, lines;
1703 reply(-214, "%s", "");
1704 reply(0, "The following %scommands are recognized.", htype);
1705 reply(0, "(`-' = not implemented, `+' = supports options)");
1706 columns = 76 / width;
1707 if (columns == 0)
1708 columns = 1;
1709 lines = (NCMDS + columns - 1) / columns;
1710 for (i = 0; i < lines; i++) {
1711 cprintf(stdout, " ");
1712 for (j = 0; j < columns; j++) {
1713 c = ctab + j * lines + i;
1714 cprintf(stdout, "%s", c->name);
1715 w = strlen(c->name);
1716 if (! CMD_IMPLEMENTED(c)) {
1717 CPUTC('-', stdout);
1718 w++;
1720 if (CMD_HAS_OPTIONS(c)) {
1721 CPUTC('+', stdout);
1722 w++;
1724 if (c + lines >= &ctab[NCMDS])
1725 break;
1726 while (w < width) {
1727 CPUTC(' ', stdout);
1728 w++;
1731 cprintf(stdout, "\r\n");
1733 (void) fflush(stdout);
1734 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1735 return;
1737 c = lookup(ctab, s);
1738 if (c == (struct tab *)0) {
1739 reply(502, "Unknown command %s.", s);
1740 return;
1742 if (CMD_IMPLEMENTED(c))
1743 reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
1744 else
1745 reply(214, "%s%-*s\t%s; not implemented.", htype, width,
1746 c->name, c->help);
1750 * Check that the structures used for a PORT, LPRT or EPRT command are
1751 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1752 * If family != -1 check that his_addr.su_family == family.
1754 static void
1755 port_check(const char *cmd, int family)
1757 char h1[NI_MAXHOST], h2[NI_MAXHOST];
1758 char s1[NI_MAXHOST], s2[NI_MAXHOST];
1759 #ifdef NI_WITHSCOPEID
1760 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1761 #else
1762 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1763 #endif
1765 if (epsvall) {
1766 reply(501, "%s disallowed after EPSV ALL", cmd);
1767 return;
1770 if (family != -1 && his_addr.su_family != family) {
1771 port_check_fail:
1772 reply(500, "Illegal %s command rejected", cmd);
1773 return;
1776 if (data_dest.su_family != his_addr.su_family)
1777 goto port_check_fail;
1779 /* be paranoid, if told so */
1780 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1781 #ifdef INET6
1783 * be paranoid, there are getnameinfo implementation that does
1784 * not present scopeid portion
1786 if (data_dest.su_family == AF_INET6 &&
1787 data_dest.su_scope_id != his_addr.su_scope_id)
1788 goto port_check_fail;
1789 #endif
1791 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1792 h1, sizeof(h1), s1, sizeof(s1), niflags))
1793 goto port_check_fail;
1794 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1795 h2, sizeof(h2), s2, sizeof(s2), niflags))
1796 goto port_check_fail;
1798 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1799 goto port_check_fail;
1802 usedefault = 0;
1803 if (pdata >= 0) {
1804 (void) close(pdata);
1805 pdata = -1;
1807 reply(200, "%s command successful.", cmd);