Initial import from FreeBSD RELENG_4:
[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
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: src/libexec/ftpd/ftpcmd.y,v 1.16.2.19 2003/02/11 14:28:28 yar Exp $";
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 #include "pathnames.h"
76
77 extern  union sockunion data_dest, his_addr;
78 extern  int hostinfo;
79 extern  int logged_in;
80 extern  struct passwd *pw;
81 extern  int guest;
82 extern  char *homedir;
83 extern  int paranoid;
84 extern  int logging;
85 extern  int type;
86 extern  int form;
87 extern  int ftpdebug;
88 extern  int timeout;
89 extern  int maxtimeout;
90 extern  int pdata;
91 extern  char *hostname;
92 extern  char remotehost[];
93 extern  char proctitle[];
94 extern  int usedefault;
95 extern  int transflag;
96 extern  char tmpline[];
97 extern  int readonly;
98 extern  int noepsv;
99 extern  int noretr;
100 extern  int noguestretr;
101 extern  char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
102
103 off_t   restart_point;
104
105 static  int cmd_type;
106 static  int cmd_form;
107 static  int cmd_bytesz;
108 static  int state;
109 char    cbuf[512];
110 char    *fromname = (char *) 0;
111
112 extern int epsvall;
113
114 %}
115
116 %union {
117         struct {
118                 off_t   o;
119                 int     i;
120         } u;
121         char   *s;
122 }
123
124 %token
125         A       B       C       E       F       I
126         L       N       P       R       S       T
127         ALL
128
129         SP      CRLF    COMMA
130
131         USER    PASS    ACCT    REIN    QUIT    PORT
132         PASV    TYPE    STRU    MODE    RETR    STOR
133         APPE    MLFL    MAIL    MSND    MSOM    MSAM
134         MRSQ    MRCP    ALLO    REST    RNFR    RNTO
135         ABOR    DELE    CWD     LIST    NLST    SITE
136         STAT    HELP    NOOP    MKD     RMD     PWD
137         CDUP    STOU    SMNT    SYST    SIZE    MDTM
138         LPRT    LPSV    EPRT    EPSV
139
140         UMASK   IDLE    CHMOD   MDFIVE
141
142         LEXERR  NOTIMPL
143
144 %token  <s> STRING
145 %token  <u> NUMBER
146
147 %type   <u.i> check_login octal_number byte_size
148 %type   <u.i> check_login_ro check_login_epsv
149 %type   <u.i> struct_code mode_code type_code form_code
150 %type   <s> pathstring pathname password username
151 %type   <s> ALL NOTIMPL
152
153 %start  cmd_list
154
155 %%
156
157 cmd_list
158         : /* empty */
159         | cmd_list cmd
160                 {
161                         if (fromname)
162                                 free(fromname);
163                         fromname = (char *) 0;
164                         restart_point = (off_t) 0;
165                 }
166         | cmd_list rcmd
167         ;
168
169 cmd
170         : USER SP username CRLF
171                 {
172                         user($3);
173                         free($3);
174                 }
175         | PASS SP password CRLF
176                 {
177                         pass($3);
178                         free($3);
179                 }
180         | PASS CRLF
181                 {
182                         pass("");
183                 }
184         | PORT check_login SP host_port CRLF
185                 {
186                         if (epsvall) {
187                                 reply(501, "no PORT allowed after EPSV ALL");
188                                 goto port_done;
189                         }
190                         if (!$2)
191                                 goto port_done;
192                         if (port_check("PORT") == 1)
193                                 goto port_done;
194 #ifdef INET6
195                         if ((his_addr.su_family != AF_INET6 ||
196                              !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
197                                 /* shoud never happen */
198                                 usedefault = 1;
199                                 reply(500, "Invalid address rejected.");
200                                 goto port_done;
201                         }
202                         port_check_v6("pcmd");
203 #endif
204                 port_done:
205                 }
206         | LPRT check_login SP host_long_port CRLF
207                 {
208                         if (epsvall) {
209                                 reply(501, "no LPRT allowed after EPSV ALL");
210                                 goto lprt_done;
211                         }
212                         if (!$2)
213                                 goto lprt_done;
214                         if (port_check("LPRT") == 1)
215                                 goto lprt_done;
216 #ifdef INET6
217                         if (his_addr.su_family != AF_INET6) {
218                                 usedefault = 1;
219                                 reply(500, "Invalid address rejected.");
220                                 goto lprt_done;
221                         }
222                         if (port_check_v6("LPRT") == 1)
223                                 goto lprt_done;
224 #endif
225                 lprt_done:
226                 }
227         | EPRT check_login SP STRING CRLF
228                 {
229                         char delim;
230                         char *tmp = NULL;
231                         char *p, *q;
232                         char *result[3];
233                         struct addrinfo hints;
234                         struct addrinfo *res;
235                         int i;
236
237                         if (epsvall) {
238                                 reply(501, "no EPRT allowed after EPSV ALL");
239                                 goto eprt_done;
240                         }
241                         if (!$2)
242                                 goto eprt_done;
243
244                         memset(&data_dest, 0, sizeof(data_dest));
245                         tmp = strdup($4);
246                         if (ftpdebug)
247                                 syslog(LOG_DEBUG, "%s", tmp);
248                         if (!tmp) {
249                                 fatalerror("not enough core");
250                                 /*NOTREACHED*/
251                         }
252                         p = tmp;
253                         delim = p[0];
254                         p++;
255                         memset(result, 0, sizeof(result));
256                         for (i = 0; i < 3; i++) {
257                                 q = strchr(p, delim);
258                                 if (!q || *q != delim) {
259                 parsefail:
260                                         reply(500,
261                                                 "Invalid argument, rejected.");
262                                         if (tmp)
263                                                 free(tmp);
264                                         usedefault = 1;
265                                         goto eprt_done;
266                                 }
267                                 *q++ = '\0';
268                                 result[i] = p;
269                                 if (ftpdebug)
270                                         syslog(LOG_DEBUG, "%d: %s", i, p);
271                                 p = q;
272                         }
273
274                         /* some more sanity check */
275                         p = result[0];
276                         while (*p) {
277                                 if (!isdigit(*p))
278                                         goto parsefail;
279                                 p++;
280                         }
281                         p = result[2];
282                         while (*p) {
283                                 if (!isdigit(*p))
284                                         goto parsefail;
285                                 p++;
286                         }
287
288                         /* grab address */
289                         memset(&hints, 0, sizeof(hints));
290                         if (atoi(result[0]) == 1)
291                                 hints.ai_family = PF_INET;
292 #ifdef INET6
293                         else if (atoi(result[0]) == 2)
294                                 hints.ai_family = PF_INET6;
295 #endif
296                         else
297                                 hints.ai_family = PF_UNSPEC;    /*XXX*/
298                         hints.ai_socktype = SOCK_STREAM;
299                         i = getaddrinfo(result[1], result[2], &hints, &res);
300                         if (i)
301                                 goto parsefail;
302                         memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
303 #ifdef INET6
304                         if (his_addr.su_family == AF_INET6
305                             && data_dest.su_family == AF_INET6) {
306                                 /* XXX more sanity checks! */
307                                 data_dest.su_sin6.sin6_scope_id =
308                                         his_addr.su_sin6.sin6_scope_id;
309                         }
310 #endif
311                         free(tmp);
312                         tmp = NULL;
313
314                         if (port_check("EPRT") == 1)
315                                 goto eprt_done;
316 #ifdef INET6
317                         if (his_addr.su_family != AF_INET6) {
318                                 usedefault = 1;
319                                 reply(500, "Invalid address rejected.");
320                                 goto eprt_done;
321                         }
322                         if (port_check_v6("EPRT") == 1)
323                                 goto eprt_done;
324 #endif
325                 eprt_done:
326                         free($4);
327                 }
328         | PASV check_login CRLF
329                 {
330                         if (epsvall)
331                                 reply(501, "no PASV allowed after EPSV ALL");
332                         else if ($2)
333                                 passive();
334                 }
335         | LPSV check_login CRLF
336                 {
337                         if (epsvall)
338                                 reply(501, "no LPSV allowed after EPSV ALL");
339                         else if ($2)
340                                 long_passive("LPSV", PF_UNSPEC);
341                 }
342         | EPSV check_login_epsv SP NUMBER CRLF
343                 {
344                         if ($2) {
345                                 int pf;
346                                 switch ($4.i) {
347                                 case 1:
348                                         pf = PF_INET;
349                                         break;
350 #ifdef INET6
351                                 case 2:
352                                         pf = PF_INET6;
353                                         break;
354 #endif
355                                 default:
356                                         pf = -1;        /*junk value*/
357                                         break;
358                                 }
359                                 long_passive("EPSV", pf);
360                         }
361                 }
362         | EPSV check_login_epsv SP ALL CRLF
363                 {
364                         if ($2) {
365                                 reply(200,
366                                       "EPSV ALL command successful.");
367                                 epsvall++;
368                         }
369                 }
370         | EPSV check_login_epsv CRLF
371                 {
372                         if ($2)
373                                 long_passive("EPSV", PF_UNSPEC);
374                 }
375         | TYPE check_login SP type_code CRLF
376                 {
377                         if ($2) {
378                                 switch (cmd_type) {
379
380                                 case TYPE_A:
381                                         if (cmd_form == FORM_N) {
382                                                 reply(200, "Type set to A.");
383                                                 type = cmd_type;
384                                                 form = cmd_form;
385                                         } else
386                                                 reply(504, "Form must be N.");
387                                         break;
388
389                                 case TYPE_E:
390                                         reply(504, "Type E not implemented.");
391                                         break;
392
393                                 case TYPE_I:
394                                         reply(200, "Type set to I.");
395                                         type = cmd_type;
396                                         break;
397
398                                 case TYPE_L:
399 #if NBBY == 8
400                                         if (cmd_bytesz == 8) {
401                                                 reply(200,
402                                                     "Type set to L (byte size 8).");
403                                                 type = cmd_type;
404                                         } else
405                                                 reply(504, "Byte size must be 8.");
406 #else /* NBBY == 8 */
407                                         UNIMPLEMENTED for NBBY != 8
408 #endif /* NBBY == 8 */
409                                 }
410                         }
411                 }
412         | STRU check_login SP struct_code CRLF
413                 {
414                         if ($2) {
415                                 switch ($4) {
416
417                                 case STRU_F:
418                                         reply(200, "STRU F ok.");
419                                         break;
420
421                                 default:
422                                         reply(504, "Unimplemented STRU type.");
423                                 }
424                         }
425                 }
426         | MODE check_login SP mode_code CRLF
427                 {
428                         if ($2) {
429                                 switch ($4) {
430
431                                 case MODE_S:
432                                         reply(200, "MODE S ok.");
433                                         break;
434         
435                                 default:
436                                         reply(502, "Unimplemented MODE type.");
437                                 }
438                         }
439                 }
440         | ALLO check_login SP NUMBER CRLF
441                 {
442                         if ($2) {
443                                 reply(202, "ALLO command ignored.");
444                         }
445                 }
446         | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
447                 {
448                         if ($2) {
449                                 reply(202, "ALLO command ignored.");
450                         }
451                 }
452         | RETR check_login SP pathname CRLF
453                 {
454                         if (noretr || (guest && noguestretr))
455                                 reply(500, "RETR command is disabled");
456                         else if ($2 && $4 != NULL)
457                                 retrieve((char *) 0, $4);
458
459                         if ($4 != NULL)
460                                 free($4);
461                 }
462         | STOR check_login_ro SP pathname CRLF
463                 {
464                         if ($2 && $4 != NULL)
465                                 store($4, "w", 0);
466                         if ($4 != NULL)
467                                 free($4);
468                 }
469         | APPE check_login_ro SP pathname CRLF
470                 {
471                         if ($2 && $4 != NULL)
472                                 store($4, "a", 0);
473                         if ($4 != NULL)
474                                 free($4);
475                 }
476         | NLST check_login CRLF
477                 {
478                         if ($2)
479                                 send_file_list(".");
480                 }
481         | NLST check_login SP pathstring CRLF
482                 {
483                         if ($2)
484                                 send_file_list($4);
485                         free($4);
486                 }
487         | LIST check_login CRLF
488                 {
489                         if ($2)
490                                 retrieve(_PATH_LS " -lgA", "");
491                 }
492         | LIST check_login SP pathstring CRLF
493                 {
494                         if ($2)
495                                 retrieve(_PATH_LS " -lgA %s", $4);
496                         free($4);
497                 }
498         | STAT check_login SP pathname CRLF
499                 {
500                         if ($2 && $4 != NULL)
501                                 statfilecmd($4);
502                         if ($4 != NULL)
503                                 free($4);
504                 }
505         | STAT check_login CRLF
506                 {
507                         if ($2) {
508                                 statcmd();
509                         }
510                 }
511         | DELE check_login_ro SP pathname CRLF
512                 {
513                         if ($2 && $4 != NULL)
514                                 delete($4);
515                         if ($4 != NULL)
516                                 free($4);
517                 }
518         | RNTO check_login_ro SP pathname CRLF
519                 {
520                         if ($2 && $4 != NULL) {
521                                 if (fromname) {
522                                         renamecmd(fromname, $4);
523                                         free(fromname);
524                                         fromname = (char *) 0;
525                                 } else {
526                                         reply(503, "Bad sequence of commands.");
527                                 }
528                         }
529                         if ($4 != NULL)
530                                 free($4);
531                 }
532         | ABOR check_login CRLF
533                 {
534                         if ($2)
535                                 reply(225, "ABOR command successful.");
536                 }
537         | CWD check_login CRLF
538                 {
539                         if ($2) {
540                                 cwd(homedir);
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         | NOTIMPL
756                 {
757                         nack($1);
758                 }
759         | error
760                 {
761                         yyclearin;              /* discard lookahead data */
762                         yyerrok;                /* clear error condition */
763                         state = CMD;            /* reset lexer state */
764                 }
765         ;
766 rcmd
767         : RNFR check_login_ro SP pathname CRLF
768                 {
769                         restart_point = (off_t) 0;
770                         if ($2 && $4) {
771                                 if (fromname)
772                                         free(fromname);
773                                 fromname = (char *) 0;
774                                 if (renamefrom($4))
775                                         fromname = $4;
776                                 else
777                                         free($4);
778                         } else if ($4) {
779                                 free($4);
780                         }
781                 }
782         | REST check_login SP NUMBER CRLF
783                 {
784                         if ($2) {
785                                 if (fromname)
786                                         free(fromname);
787                                 fromname = (char *) 0;
788                                 restart_point = $4.o;
789                                 reply(350, "Restarting at %llu. %s",
790                                     restart_point,
791                                     "Send STORE or RETRIEVE to initiate transfer.");
792                         }
793                 }
794         ;
795
796 username
797         : STRING
798         ;
799
800 password
801         : /* empty */
802                 {
803                         $$ = (char *)calloc(1, sizeof(char));
804                 }
805         | STRING
806         ;
807
808 byte_size
809         : NUMBER
810                 {
811                         $$ = $1.i;
812                 }
813         ;
814
815 host_port
816         : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
817                 NUMBER COMMA NUMBER
818                 {
819                         char *a, *p;
820
821                         data_dest.su_len = sizeof(struct sockaddr_in);
822                         data_dest.su_family = AF_INET;
823                         p = (char *)&data_dest.su_sin.sin_port;
824                         p[0] = $9.i; p[1] = $11.i;
825                         a = (char *)&data_dest.su_sin.sin_addr;
826                         a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
827                 }
828         ;
829
830 host_long_port
831         : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
832                 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
833                 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
834                 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
835                 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
836                 NUMBER
837                 {
838                         char *a, *p;
839
840                         memset(&data_dest, 0, sizeof(data_dest));
841                         data_dest.su_len = sizeof(struct sockaddr_in6);
842                         data_dest.su_family = AF_INET6;
843                         p = (char *)&data_dest.su_port;
844                         p[0] = $39.i; p[1] = $41.i;
845                         a = (char *)&data_dest.su_sin6.sin6_addr;
846                         a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
847                         a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
848                         a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
849                         a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
850                         if (his_addr.su_family == AF_INET6) {
851                                 /* XXX more sanity checks! */
852                                 data_dest.su_sin6.sin6_scope_id =
853                                         his_addr.su_sin6.sin6_scope_id;
854                         }
855                         if ($1.i != 6 || $3.i != 16 || $37.i != 2)
856                                 memset(&data_dest, 0, sizeof(data_dest));
857                 }
858         | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
859                 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
860                 NUMBER
861                 {
862                         char *a, *p;
863
864                         memset(&data_dest, 0, sizeof(data_dest));
865                         data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
866                         data_dest.su_family = AF_INET;
867                         p = (char *)&data_dest.su_port;
868                         p[0] = $15.i; p[1] = $17.i;
869                         a = (char *)&data_dest.su_sin.sin_addr;
870                         a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
871                         if ($1.i != 4 || $3.i != 4 || $13.i != 2)
872                                 memset(&data_dest, 0, sizeof(data_dest));
873                 }
874         ;
875
876 form_code
877         : N
878                 {
879                         $$ = FORM_N;
880                 }
881         | T
882                 {
883                         $$ = FORM_T;
884                 }
885         | C
886                 {
887                         $$ = FORM_C;
888                 }
889         ;
890
891 type_code
892         : A
893                 {
894                         cmd_type = TYPE_A;
895                         cmd_form = FORM_N;
896                 }
897         | A SP form_code
898                 {
899                         cmd_type = TYPE_A;
900                         cmd_form = $3;
901                 }
902         | E
903                 {
904                         cmd_type = TYPE_E;
905                         cmd_form = FORM_N;
906                 }
907         | E SP form_code
908                 {
909                         cmd_type = TYPE_E;
910                         cmd_form = $3;
911                 }
912         | I
913                 {
914                         cmd_type = TYPE_I;
915                 }
916         | L
917                 {
918                         cmd_type = TYPE_L;
919                         cmd_bytesz = NBBY;
920                 }
921         | L SP byte_size
922                 {
923                         cmd_type = TYPE_L;
924                         cmd_bytesz = $3;
925                 }
926                 /* this is for a bug in the BBN ftp */
927         | L byte_size
928                 {
929                         cmd_type = TYPE_L;
930                         cmd_bytesz = $2;
931                 }
932         ;
933
934 struct_code
935         : F
936                 {
937                         $$ = STRU_F;
938                 }
939         | R
940                 {
941                         $$ = STRU_R;
942                 }
943         | P
944                 {
945                         $$ = STRU_P;
946                 }
947         ;
948
949 mode_code
950         : S
951                 {
952                         $$ = MODE_S;
953                 }
954         | B
955                 {
956                         $$ = MODE_B;
957                 }
958         | C
959                 {
960                         $$ = MODE_C;
961                 }
962         ;
963
964 pathname
965         : pathstring
966                 {
967                         /*
968                          * Problem: this production is used for all pathname
969                          * processing, but only gives a 550 error reply.
970                          * This is a valid reply in some cases but not in others.
971                          */
972                         if (logged_in && $1) {
973                                 glob_t gl;
974                                 char *p, **pp;
975                                 int flags =
976                                  GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
977                                 int n;
978
979                                 memset(&gl, 0, sizeof(gl));
980                                 flags |= GLOB_LIMIT;
981                                 gl.gl_matchc = MAXGLOBARGS;
982                                 if (glob($1, flags, NULL, &gl) ||
983                                     gl.gl_pathc == 0) {
984                                         reply(550, "wildcard expansion error");
985                                         $$ = NULL;
986                                 } else {
987                                         n = 0;
988                                         for (pp = gl.gl_pathv; *pp; pp++)
989                                                 if (strcspn(*pp, "\r\n") ==
990                                                     strlen(*pp)) {
991                                                         p = *pp;
992                                                         n++;
993                                                 }
994                                         if (n == 0)
995                                                 $$ = strdup($1);
996                                         else if (n == 1)
997                                                 $$ = strdup(p);
998                                         else {
999                                                 reply(550, "ambiguous");
1000                                                 $$ = NULL;
1001                                         }
1002                                 }
1003                                 globfree(&gl);
1004                                 free($1);
1005                         } else
1006                                 $$ = $1;
1007                 }
1008         ;
1009
1010 pathstring
1011         : STRING
1012         ;
1013
1014 octal_number
1015         : NUMBER
1016                 {
1017                         int ret, dec, multby, digit;
1018
1019                         /*
1020                          * Convert a number that was read as decimal number
1021                          * to what it would be if it had been read as octal.
1022                          */
1023                         dec = $1.i;
1024                         multby = 1;
1025                         ret = 0;
1026                         while (dec) {
1027                                 digit = dec%10;
1028                                 if (digit > 7) {
1029                                         ret = -1;
1030                                         break;
1031                                 }
1032                                 ret += digit * multby;
1033                                 multby *= 8;
1034                                 dec /= 10;
1035                         }
1036                         $$ = ret;
1037                 }
1038         ;
1039
1040
1041 check_login
1042         : /* empty */
1043                 {
1044                 $$ = check_login1();
1045                 }
1046         ;
1047
1048 check_login_epsv
1049         : /* empty */
1050                 {
1051                 if (noepsv) {
1052                         reply(500, "EPSV command disabled");
1053                         $$ = 0;
1054                 }
1055                 else
1056                         $$ = check_login1();
1057                 }
1058         ;
1059
1060 check_login_ro
1061         : /* empty */
1062                 {
1063                 if (readonly) {
1064                         reply(550, "Permission denied.");
1065                         $$ = 0;
1066                 }
1067                 else
1068                         $$ = check_login1();
1069                 }
1070         ;
1071
1072 %%
1073
1074 #define CMD     0       /* beginning of command */
1075 #define ARGS    1       /* expect miscellaneous arguments */
1076 #define STR1    2       /* expect SP followed by STRING */
1077 #define STR2    3       /* expect STRING */
1078 #define OSTR    4       /* optional SP then STRING */
1079 #define ZSTR1   5       /* optional SP then optional STRING */
1080 #define ZSTR2   6       /* optional STRING after SP */
1081 #define SITECMD 7       /* SITE command */
1082 #define NSTR    8       /* Number followed by a string */
1083
1084 #define MAXGLOBARGS     1000
1085
1086 #define MAXASIZE        10240   /* Deny ASCII SIZE on files larger than that */
1087
1088 struct tab {
1089         char    *name;
1090         short   token;
1091         short   state;
1092         short   implemented;    /* 1 if command is implemented */
1093         char    *help;
1094 };
1095
1096 struct tab cmdtab[] = {         /* In order defined in RFC 765 */
1097         { "USER", USER, STR1, 1,        "<sp> username" },
1098         { "PASS", PASS, ZSTR1, 1,       "[<sp> [password]]" },
1099         { "ACCT", ACCT, STR1, 0,        "(specify account)" },
1100         { "SMNT", SMNT, ARGS, 0,        "(structure mount)" },
1101         { "REIN", REIN, ARGS, 0,        "(reinitialize server state)" },
1102         { "QUIT", QUIT, ARGS, 1,        "(terminate service)", },
1103         { "PORT", PORT, ARGS, 1,        "<sp> b0, b1, b2, b3, b4, b5" },
1104         { "LPRT", LPRT, ARGS, 1,        "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1105         { "EPRT", EPRT, STR1, 1,        "<sp> |af|addr|port|" },
1106         { "PASV", PASV, ARGS, 1,        "(set server in passive mode)" },
1107         { "LPSV", LPSV, ARGS, 1,        "(set server in passive mode)" },
1108         { "EPSV", EPSV, ARGS, 1,        "[<sp> af|ALL]" },
1109         { "TYPE", TYPE, ARGS, 1,        "<sp> { A | E | I | L }" },
1110         { "STRU", STRU, ARGS, 1,        "(specify file structure)" },
1111         { "MODE", MODE, ARGS, 1,        "(specify transfer mode)" },
1112         { "RETR", RETR, STR1, 1,        "<sp> file-name" },
1113         { "STOR", STOR, STR1, 1,        "<sp> file-name" },
1114         { "APPE", APPE, STR1, 1,        "<sp> file-name" },
1115         { "MLFL", MLFL, OSTR, 0,        "(mail file)" },
1116         { "MAIL", MAIL, OSTR, 0,        "(mail to user)" },
1117         { "MSND", MSND, OSTR, 0,        "(mail send to terminal)" },
1118         { "MSOM", MSOM, OSTR, 0,        "(mail send to terminal or mailbox)" },
1119         { "MSAM", MSAM, OSTR, 0,        "(mail send to terminal and mailbox)" },
1120         { "MRSQ", MRSQ, OSTR, 0,        "(mail recipient scheme question)" },
1121         { "MRCP", MRCP, STR1, 0,        "(mail recipient)" },
1122         { "ALLO", ALLO, ARGS, 1,        "allocate storage (vacuously)" },
1123         { "REST", REST, ARGS, 1,        "<sp> offset (restart command)" },
1124         { "RNFR", RNFR, STR1, 1,        "<sp> file-name" },
1125         { "RNTO", RNTO, STR1, 1,        "<sp> file-name" },
1126         { "ABOR", ABOR, ARGS, 1,        "(abort operation)" },
1127         { "DELE", DELE, STR1, 1,        "<sp> file-name" },
1128         { "CWD",  CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
1129         { "XCWD", CWD,  OSTR, 1,        "[ <sp> directory-name ]" },
1130         { "LIST", LIST, OSTR, 1,        "[ <sp> path-name ]" },
1131         { "NLST", NLST, OSTR, 1,        "[ <sp> path-name ]" },
1132         { "SITE", SITE, SITECMD, 1,     "site-cmd [ <sp> arguments ]" },
1133         { "SYST", SYST, ARGS, 1,        "(get type of operating system)" },
1134         { "STAT", STAT, OSTR, 1,        "[ <sp> path-name ]" },
1135         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
1136         { "NOOP", NOOP, ARGS, 1,        "" },
1137         { "MKD",  MKD,  STR1, 1,        "<sp> path-name" },
1138         { "XMKD", MKD,  STR1, 1,        "<sp> path-name" },
1139         { "RMD",  RMD,  STR1, 1,        "<sp> path-name" },
1140         { "XRMD", RMD,  STR1, 1,        "<sp> path-name" },
1141         { "PWD",  PWD,  ARGS, 1,        "(return current directory)" },
1142         { "XPWD", PWD,  ARGS, 1,        "(return current directory)" },
1143         { "CDUP", CDUP, ARGS, 1,        "(change to parent directory)" },
1144         { "XCUP", CDUP, ARGS, 1,        "(change to parent directory)" },
1145         { "STOU", STOU, STR1, 1,        "<sp> file-name" },
1146         { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
1147         { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
1148         { NULL,   0,    0,    0,        0 }
1149 };
1150
1151 struct tab sitetab[] = {
1152         { "MD5", MDFIVE, STR1, 1,       "[ <sp> file-name ]" },
1153         { "UMASK", UMASK, ARGS, 1,      "[ <sp> umask ]" },
1154         { "IDLE", IDLE, ARGS, 1,        "[ <sp> maximum-idle-time ]" },
1155         { "CHMOD", CHMOD, NSTR, 1,      "<sp> mode <sp> file-name" },
1156         { "HELP", HELP, OSTR, 1,        "[ <sp> <string> ]" },
1157         { NULL,   0,    0,    0,        0 }
1158 };
1159
1160 static char     *copy __P((char *));
1161 static void      help __P((struct tab *, char *));
1162 static struct tab *
1163                  lookup __P((struct tab *, char *));
1164 static int       port_check __P((const char *));
1165 static int       port_check_v6 __P((const char *));
1166 static void      sizecmd __P((char *));
1167 static void      toolong __P((int));
1168 static void      v4map_data_dest __P((void));
1169 static int       yylex __P((void));
1170
1171 static struct tab *
1172 lookup(p, cmd)
1173         struct tab *p;
1174         char *cmd;
1175 {
1176
1177         for (; p->name != NULL; p++)
1178                 if (strcmp(cmd, p->name) == 0)
1179                         return (p);
1180         return (0);
1181 }
1182
1183 #include <arpa/telnet.h>
1184
1185 /*
1186  * getline - a hacked up version of fgets to ignore TELNET escape codes.
1187  */
1188 char *
1189 getline(s, n, iop)
1190         char *s;
1191         int n;
1192         FILE *iop;
1193 {
1194         int c;
1195         register char *cs;
1196
1197         cs = s;
1198 /* tmpline may contain saved command from urgent mode interruption */
1199         for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1200                 *cs++ = tmpline[c];
1201                 if (tmpline[c] == '\n') {
1202                         *cs++ = '\0';
1203                         if (ftpdebug)
1204                                 syslog(LOG_DEBUG, "command: %s", s);
1205                         tmpline[0] = '\0';
1206                         return(s);
1207                 }
1208                 if (c == 0)
1209                         tmpline[0] = '\0';
1210         }
1211         while ((c = getc(iop)) != EOF) {
1212                 c &= 0377;
1213                 if (c == IAC) {
1214                     if ((c = getc(iop)) != EOF) {
1215                         c &= 0377;
1216                         switch (c) {
1217                         case WILL:
1218                         case WONT:
1219                                 c = getc(iop);
1220                                 printf("%c%c%c", IAC, DONT, 0377&c);
1221                                 (void) fflush(stdout);
1222                                 continue;
1223                         case DO:
1224                         case DONT:
1225                                 c = getc(iop);
1226                                 printf("%c%c%c", IAC, WONT, 0377&c);
1227                                 (void) fflush(stdout);
1228                                 continue;
1229                         case IAC:
1230                                 break;
1231                         default:
1232                                 continue;       /* ignore command */
1233                         }
1234                     }
1235                 }
1236                 *cs++ = c;
1237                 if (--n <= 0 || c == '\n')
1238                         break;
1239         }
1240         if (c == EOF && cs == s)
1241                 return (NULL);
1242         *cs++ = '\0';
1243         if (ftpdebug) {
1244                 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1245                         /* Don't syslog passwords */
1246                         syslog(LOG_DEBUG, "command: %.5s ???", s);
1247                 } else {
1248                         register char *cp;
1249                         register int len;
1250
1251                         /* Don't syslog trailing CR-LF */
1252                         len = strlen(s);
1253                         cp = s + len - 1;
1254                         while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1255                                 --cp;
1256                                 --len;
1257                         }
1258                         syslog(LOG_DEBUG, "command: %.*s", len, s);
1259                 }
1260         }
1261         return (s);
1262 }
1263
1264 static void
1265 toolong(signo)
1266         int signo;
1267 {
1268
1269         reply(421,
1270             "Timeout (%d seconds): closing control connection.", timeout);
1271         if (logging)
1272                 syslog(LOG_INFO, "User %s timed out after %d seconds",
1273                     (pw ? pw -> pw_name : "unknown"), timeout);
1274         dologout(1);
1275 }
1276
1277 static int
1278 yylex()
1279 {
1280         static int cpos;
1281         char *cp, *cp2;
1282         struct tab *p;
1283         int n;
1284         char c;
1285
1286         for (;;) {
1287                 switch (state) {
1288
1289                 case CMD:
1290                         (void) signal(SIGALRM, toolong);
1291                         (void) alarm((unsigned) timeout);
1292                         if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1293                                 reply(221, "You could at least say goodbye.");
1294                                 dologout(0);
1295                         }
1296                         (void) alarm(0);
1297 #ifdef SETPROCTITLE
1298                         if (strncasecmp(cbuf, "PASS", 4) != 0)
1299                                 setproctitle("%s: %s", proctitle, cbuf);
1300 #endif /* SETPROCTITLE */
1301                         if ((cp = strchr(cbuf, '\r'))) {
1302                                 *cp++ = '\n';
1303                                 *cp = '\0';
1304                         }
1305                         if ((cp = strpbrk(cbuf, " \n")))
1306                                 cpos = cp - cbuf;
1307                         if (cpos == 0)
1308                                 cpos = 4;
1309                         c = cbuf[cpos];
1310                         cbuf[cpos] = '\0';
1311                         upper(cbuf);
1312                         p = lookup(cmdtab, cbuf);
1313                         cbuf[cpos] = c;
1314                         if (p != 0) {
1315                                 yylval.s = p->name;
1316                                 if (!p->implemented)
1317                                         return (NOTIMPL); /* state remains CMD */
1318                                 state = p->state;
1319                                 return (p->token);
1320                         }
1321                         break;
1322
1323                 case SITECMD:
1324                         if (cbuf[cpos] == ' ') {
1325                                 cpos++;
1326                                 return (SP);
1327                         }
1328                         cp = &cbuf[cpos];
1329                         if ((cp2 = strpbrk(cp, " \n")))
1330                                 cpos = cp2 - cbuf;
1331                         c = cbuf[cpos];
1332                         cbuf[cpos] = '\0';
1333                         upper(cp);
1334                         p = lookup(sitetab, cp);
1335                         cbuf[cpos] = c;
1336                         if (guest == 0 && p != 0) {
1337                                 yylval.s = p->name;
1338                                 if (!p->implemented) {
1339                                         state = CMD;
1340                                         return (NOTIMPL);
1341                                 }
1342                                 state = p->state;
1343                                 return (p->token);
1344                         }
1345                         state = CMD;
1346                         break;
1347
1348                 case ZSTR1:
1349                 case OSTR:
1350                         if (cbuf[cpos] == '\n') {
1351                                 state = CMD;
1352                                 return (CRLF);
1353                         }
1354                         /* FALLTHROUGH */
1355
1356                 case STR1:
1357                 dostr1:
1358                         if (cbuf[cpos] == ' ') {
1359                                 cpos++;
1360                                 state = state == OSTR ? STR2 : state+1;
1361                                 return (SP);
1362                         }
1363                         break;
1364
1365                 case ZSTR2:
1366                         if (cbuf[cpos] == '\n') {
1367                                 state = CMD;
1368                                 return (CRLF);
1369                         }
1370                         /* FALLTHROUGH */
1371
1372                 case STR2:
1373                         cp = &cbuf[cpos];
1374                         n = strlen(cp);
1375                         cpos += n - 1;
1376                         /*
1377                          * Make sure the string is nonempty and \n terminated.
1378                          */
1379                         if (n > 1 && cbuf[cpos] == '\n') {
1380                                 cbuf[cpos] = '\0';
1381                                 yylval.s = copy(cp);
1382                                 cbuf[cpos] = '\n';
1383                                 state = ARGS;
1384                                 return (STRING);
1385                         }
1386                         break;
1387
1388                 case NSTR:
1389                         if (cbuf[cpos] == ' ') {
1390                                 cpos++;
1391                                 return (SP);
1392                         }
1393                         if (isdigit(cbuf[cpos])) {
1394                                 cp = &cbuf[cpos];
1395                                 while (isdigit(cbuf[++cpos]))
1396                                         ;
1397                                 c = cbuf[cpos];
1398                                 cbuf[cpos] = '\0';
1399                                 yylval.u.i = atoi(cp);
1400                                 cbuf[cpos] = c;
1401                                 state = STR1;
1402                                 return (NUMBER);
1403                         }
1404                         state = STR1;
1405                         goto dostr1;
1406
1407                 case ARGS:
1408                         if (isdigit(cbuf[cpos])) {
1409                                 cp = &cbuf[cpos];
1410                                 while (isdigit(cbuf[++cpos]))
1411                                         ;
1412                                 c = cbuf[cpos];
1413                                 cbuf[cpos] = '\0';
1414                                 yylval.u.i = atoi(cp);
1415                                 yylval.u.o = strtoull(cp, (char **)NULL, 10);
1416                                 cbuf[cpos] = c;
1417                                 return (NUMBER);
1418                         }
1419                         if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1420                          && !isalnum(cbuf[cpos + 3])) {
1421                                 cpos += 3;
1422                                 return ALL;
1423                         }
1424                         switch (cbuf[cpos++]) {
1425
1426                         case '\n':
1427                                 state = CMD;
1428                                 return (CRLF);
1429
1430                         case ' ':
1431                                 return (SP);
1432
1433                         case ',':
1434                                 return (COMMA);
1435
1436                         case 'A':
1437                         case 'a':
1438                                 return (A);
1439
1440                         case 'B':
1441                         case 'b':
1442                                 return (B);
1443
1444                         case 'C':
1445                         case 'c':
1446                                 return (C);
1447
1448                         case 'E':
1449                         case 'e':
1450                                 return (E);
1451
1452                         case 'F':
1453                         case 'f':
1454                                 return (F);
1455
1456                         case 'I':
1457                         case 'i':
1458                                 return (I);
1459
1460                         case 'L':
1461                         case 'l':
1462                                 return (L);
1463
1464                         case 'N':
1465                         case 'n':
1466                                 return (N);
1467
1468                         case 'P':
1469                         case 'p':
1470                                 return (P);
1471
1472                         case 'R':
1473                         case 'r':
1474                                 return (R);
1475
1476                         case 'S':
1477                         case 's':
1478                                 return (S);
1479
1480                         case 'T':
1481                         case 't':
1482                                 return (T);
1483
1484                         }
1485                         break;
1486
1487                 default:
1488                         fatalerror("Unknown state in scanner.");
1489                 }
1490                 state = CMD;
1491                 return (LEXERR);
1492         }
1493 }
1494
1495 void
1496 upper(s)
1497         char *s;
1498 {
1499         while (*s != '\0') {
1500                 if (islower(*s))
1501                         *s = toupper(*s);
1502                 s++;
1503         }
1504 }
1505
1506 static char *
1507 copy(s)
1508         char *s;
1509 {
1510         char *p;
1511
1512         p = malloc((unsigned) strlen(s) + 1);
1513         if (p == NULL)
1514                 fatalerror("Ran out of memory.");
1515         (void) strcpy(p, s);
1516         return (p);
1517 }
1518
1519 static void
1520 help(ctab, s)
1521         struct tab *ctab;
1522         char *s;
1523 {
1524         struct tab *c;
1525         int width, NCMDS;
1526         char *type;
1527
1528         if (ctab == sitetab)
1529                 type = "SITE ";
1530         else
1531                 type = "";
1532         width = 0, NCMDS = 0;
1533         for (c = ctab; c->name != NULL; c++) {
1534                 int len = strlen(c->name);
1535
1536                 if (len > width)
1537                         width = len;
1538                 NCMDS++;
1539         }
1540         width = (width + 8) &~ 7;
1541         if (s == 0) {
1542                 int i, j, w;
1543                 int columns, lines;
1544
1545                 lreply(214, "The following %scommands are recognized %s.",
1546                     type, "(* =>'s unimplemented)");
1547                 columns = 76 / width;
1548                 if (columns == 0)
1549                         columns = 1;
1550                 lines = (NCMDS + columns - 1) / columns;
1551                 for (i = 0; i < lines; i++) {
1552                         printf("   ");
1553                         for (j = 0; j < columns; j++) {
1554                                 c = ctab + j * lines + i;
1555                                 printf("%s%c", c->name,
1556                                         c->implemented ? ' ' : '*');
1557                                 if (c + lines >= &ctab[NCMDS])
1558                                         break;
1559                                 w = strlen(c->name) + 1;
1560                                 while (w < width) {
1561                                         putchar(' ');
1562                                         w++;
1563                                 }
1564                         }
1565                         printf("\r\n");
1566                 }
1567                 (void) fflush(stdout);
1568                 if (hostinfo)
1569                         reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1570                 else
1571                         reply(214, "End.");
1572                 return;
1573         }
1574         upper(s);
1575         c = lookup(ctab, s);
1576         if (c == (struct tab *)0) {
1577                 reply(502, "Unknown command %s.", s);
1578                 return;
1579         }
1580         if (c->implemented)
1581                 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1582         else
1583                 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1584                     c->name, c->help);
1585 }
1586
1587 static void
1588 sizecmd(filename)
1589         char *filename;
1590 {
1591         switch (type) {
1592         case TYPE_L:
1593         case TYPE_I: {
1594                 struct stat stbuf;
1595                 if (stat(filename, &stbuf) < 0)
1596                         perror_reply(550, filename);
1597                 else if (!S_ISREG(stbuf.st_mode))
1598                         reply(550, "%s: not a plain file.", filename);
1599                 else
1600                         reply(213, "%qu", stbuf.st_size);
1601                 break; }
1602         case TYPE_A: {
1603                 FILE *fin;
1604                 int c;
1605                 off_t count;
1606                 struct stat stbuf;
1607                 fin = fopen(filename, "r");
1608                 if (fin == NULL) {
1609                         perror_reply(550, filename);
1610                         return;
1611                 }
1612                 if (fstat(fileno(fin), &stbuf) < 0) {
1613                         perror_reply(550, filename);
1614                         (void) fclose(fin);
1615                         return;
1616                 } else if (!S_ISREG(stbuf.st_mode)) {
1617                         reply(550, "%s: not a plain file.", filename);
1618                         (void) fclose(fin);
1619                         return;
1620                 } else if (stbuf.st_size > MAXASIZE) {
1621                         reply(550, "%s: too large for type A SIZE.", filename);
1622                         (void) fclose(fin);
1623                         return;
1624                 }
1625
1626                 count = 0;
1627                 while((c=getc(fin)) != EOF) {
1628                         if (c == '\n')  /* will get expanded to \r\n */
1629                                 count++;
1630                         count++;
1631                 }
1632                 (void) fclose(fin);
1633
1634                 reply(213, "%qd", count);
1635                 break; }
1636         default:
1637                 reply(504, "SIZE not implemented for type %s.",
1638                            typenames[type]);
1639         }
1640 }
1641
1642 /* Return 1, if port check is done. Return 0, if not yet. */
1643 static int
1644 port_check(pcmd)
1645         const char *pcmd;
1646 {
1647         if (his_addr.su_family == AF_INET) {
1648                 if (data_dest.su_family != AF_INET) {
1649                         usedefault = 1;
1650                         reply(500, "Invalid address rejected.");
1651                         return 1;
1652                 }
1653                 if (paranoid &&
1654                     ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1655                      memcmp(&data_dest.su_sin.sin_addr,
1656                             &his_addr.su_sin.sin_addr,
1657                             sizeof(data_dest.su_sin.sin_addr)))) {
1658                         usedefault = 1;
1659                         reply(500, "Illegal PORT range rejected.");
1660                 } else {
1661                         usedefault = 0;
1662                         if (pdata >= 0) {
1663                                 (void) close(pdata);
1664                                 pdata = -1;
1665                         }
1666                         reply(200, "%s command successful.", pcmd);
1667                 }
1668                 return 1;
1669         }
1670         return 0;
1671 }
1672
1673 static int
1674 check_login1()
1675 {
1676         if (logged_in)
1677                 return 1;
1678         else {
1679                 reply(530, "Please login with USER and PASS.");
1680                 return 0;
1681         }
1682 }
1683
1684 #ifdef INET6
1685 /* Return 1, if port check is done. Return 0, if not yet. */
1686 static int
1687 port_check_v6(pcmd)
1688         const char *pcmd;
1689 {
1690         if (his_addr.su_family == AF_INET6) {
1691                 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1692                         /* Convert data_dest into v4 mapped sockaddr.*/
1693                         v4map_data_dest();
1694                 if (data_dest.su_family != AF_INET6) {
1695                         usedefault = 1;
1696                         reply(500, "Invalid address rejected.");
1697                         return 1;
1698                 }
1699                 if (paranoid &&
1700                     ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1701                      memcmp(&data_dest.su_sin6.sin6_addr,
1702                             &his_addr.su_sin6.sin6_addr,
1703                             sizeof(data_dest.su_sin6.sin6_addr)))) {
1704                         usedefault = 1;
1705                         reply(500, "Illegal PORT range rejected.");
1706                 } else {
1707                         usedefault = 0;
1708                         if (pdata >= 0) {
1709                                 (void) close(pdata);
1710                                 pdata = -1;
1711                         }
1712                         reply(200, "%s command successful.", pcmd);
1713                 }
1714                 return 1;
1715         }
1716         return 0;
1717 }
1718
1719 static void
1720 v4map_data_dest()
1721 {
1722         struct in_addr savedaddr;
1723         int savedport;
1724
1725         if (data_dest.su_family != AF_INET) {
1726                 usedefault = 1;
1727                 reply(500, "Invalid address rejected.");
1728                 return;
1729         }
1730
1731         savedaddr = data_dest.su_sin.sin_addr;
1732         savedport = data_dest.su_port;
1733
1734         memset(&data_dest, 0, sizeof(data_dest));
1735         data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1736         data_dest.su_sin6.sin6_family = AF_INET6;
1737         data_dest.su_sin6.sin6_port = savedport;
1738         memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1739         memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1740                (caddr_t)&savedaddr, sizeof(savedaddr));
1741 }
1742 #endif