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