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