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