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