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