]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/libexec/ftpd/ftpcmd.y
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / 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 char *
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(s);
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 || c == '\n')
1248                         break;
1249         }
1250 got_eof:
1251         sigprocmask(SIG_SETMASK, &osset, NULL);
1252         if (c == EOF && cs == s)
1253                 return (NULL);
1254         *cs++ = '\0';
1255         if (ftpdebug) {
1256                 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1257                         /* Don't syslog passwords */
1258                         syslog(LOG_DEBUG, "command: %.5s ???", s);
1259                 } else {
1260                         register char *cp;
1261                         register int len;
1262
1263                         /* Don't syslog trailing CR-LF */
1264                         len = strlen(s);
1265                         cp = s + len - 1;
1266                         while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1267                                 --cp;
1268                                 --len;
1269                         }
1270                         syslog(LOG_DEBUG, "command: %.*s", len, s);
1271                 }
1272         }
1273         return (s);
1274 }
1275
1276 static void
1277 toolong(int signo)
1278 {
1279
1280         reply(421,
1281             "Timeout (%d seconds): closing control connection.", timeout);
1282         if (logging)
1283                 syslog(LOG_INFO, "User %s timed out after %d seconds",
1284                     (pw ? pw -> pw_name : "unknown"), timeout);
1285         dologout(1);
1286 }
1287
1288 static int
1289 yylex(void)
1290 {
1291         static int cpos;
1292         char *cp, *cp2;
1293         struct tab *p;
1294         int n;
1295         char c;
1296
1297         for (;;) {
1298                 switch (state) {
1299
1300                 case CMD:
1301                         (void) signal(SIGALRM, toolong);
1302                         (void) alarm(timeout);
1303                         if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1304                                 reply(221, "You could at least say goodbye.");
1305                                 dologout(0);
1306                         }
1307                         (void) alarm(0);
1308 #ifdef SETPROCTITLE
1309                         if (strncasecmp(cbuf, "PASS", 4) != 0)
1310                                 setproctitle("%s: %s", proctitle, cbuf);
1311 #endif /* SETPROCTITLE */
1312                         if ((cp = strchr(cbuf, '\r'))) {
1313                                 *cp++ = '\n';
1314                                 *cp = '\0';
1315                         }
1316                         if ((cp = strpbrk(cbuf, " \n")))
1317                                 cpos = cp - cbuf;
1318                         if (cpos == 0)
1319                                 cpos = 4;
1320                         c = cbuf[cpos];
1321                         cbuf[cpos] = '\0';
1322                         upper(cbuf);
1323                         p = lookup(cmdtab, cbuf);
1324                         cbuf[cpos] = c;
1325                         if (p != 0) {
1326                                 yylval.s = p->name;
1327                                 if (!p->implemented)
1328                                         return (NOTIMPL); /* state remains CMD */
1329                                 state = p->state;
1330                                 return (p->token);
1331                         }
1332                         break;
1333
1334                 case SITECMD:
1335                         if (cbuf[cpos] == ' ') {
1336                                 cpos++;
1337                                 return (SP);
1338                         }
1339                         cp = &cbuf[cpos];
1340                         if ((cp2 = strpbrk(cp, " \n")))
1341                                 cpos = cp2 - cbuf;
1342                         c = cbuf[cpos];
1343                         cbuf[cpos] = '\0';
1344                         upper(cp);
1345                         p = lookup(sitetab, cp);
1346                         cbuf[cpos] = c;
1347                         if (guest == 0 && p != 0) {
1348                                 yylval.s = p->name;
1349                                 if (!p->implemented) {
1350                                         state = CMD;
1351                                         return (NOTIMPL);
1352                                 }
1353                                 state = p->state;
1354                                 return (p->token);
1355                         }
1356                         state = CMD;
1357                         break;
1358
1359                 case ZSTR1:
1360                 case OSTR:
1361                         if (cbuf[cpos] == '\n') {
1362                                 state = CMD;
1363                                 return (CRLF);
1364                         }
1365                         /* FALLTHROUGH */
1366
1367                 case STR1:
1368                 dostr1:
1369                         if (cbuf[cpos] == ' ') {
1370                                 cpos++;
1371                                 state = state == OSTR ? STR2 : state+1;
1372                                 return (SP);
1373                         }
1374                         break;
1375
1376                 case ZSTR2:
1377                         if (cbuf[cpos] == '\n') {
1378                                 state = CMD;
1379                                 return (CRLF);
1380                         }
1381                         /* FALLTHROUGH */
1382
1383                 case STR2:
1384                         cp = &cbuf[cpos];
1385                         n = strlen(cp);
1386                         cpos += n - 1;
1387                         /*
1388                          * Make sure the string is nonempty and \n terminated.
1389                          */
1390                         if (n > 1 && cbuf[cpos] == '\n') {
1391                                 cbuf[cpos] = '\0';
1392                                 yylval.s = copy(cp);
1393                                 cbuf[cpos] = '\n';
1394                                 state = ARGS;
1395                                 return (STRING);
1396                         }
1397                         break;
1398
1399                 case NSTR:
1400                         if (cbuf[cpos] == ' ') {
1401                                 cpos++;
1402                                 return (SP);
1403                         }
1404                         if (isdigit(cbuf[cpos])) {
1405                                 cp = &cbuf[cpos];
1406                                 while (isdigit(cbuf[++cpos]))
1407                                         ;
1408                                 c = cbuf[cpos];
1409                                 cbuf[cpos] = '\0';
1410                                 yylval.u.i = atoi(cp);
1411                                 cbuf[cpos] = c;
1412                                 state = STR1;
1413                                 return (NUMBER);
1414                         }
1415                         state = STR1;
1416                         goto dostr1;
1417
1418                 case ARGS:
1419                         if (isdigit(cbuf[cpos])) {
1420                                 cp = &cbuf[cpos];
1421                                 while (isdigit(cbuf[++cpos]))
1422                                         ;
1423                                 c = cbuf[cpos];
1424                                 cbuf[cpos] = '\0';
1425                                 yylval.u.i = atoi(cp);
1426                                 yylval.u.o = strtoull(cp, NULL, 10);
1427                                 cbuf[cpos] = c;
1428                                 return (NUMBER);
1429                         }
1430                         if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1431                          && !isalnum(cbuf[cpos + 3])) {
1432                                 cpos += 3;
1433                                 return ALL;
1434                         }
1435                         switch (cbuf[cpos++]) {
1436
1437                         case '\n':
1438                                 state = CMD;
1439                                 return (CRLF);
1440
1441                         case ' ':
1442                                 return (SP);
1443
1444                         case ',':
1445                                 return (COMMA);
1446
1447                         case 'A':
1448                         case 'a':
1449                                 return (A);
1450
1451                         case 'B':
1452                         case 'b':
1453                                 return (B);
1454
1455                         case 'C':
1456                         case 'c':
1457                                 return (C);
1458
1459                         case 'E':
1460                         case 'e':
1461                                 return (E);
1462
1463                         case 'F':
1464                         case 'f':
1465                                 return (F);
1466
1467                         case 'I':
1468                         case 'i':
1469                                 return (I);
1470
1471                         case 'L':
1472                         case 'l':
1473                                 return (L);
1474
1475                         case 'N':
1476                         case 'n':
1477                                 return (N);
1478
1479                         case 'P':
1480                         case 'p':
1481                                 return (P);
1482
1483                         case 'R':
1484                         case 'r':
1485                                 return (R);
1486
1487                         case 'S':
1488                         case 's':
1489                                 return (S);
1490
1491                         case 'T':
1492                         case 't':
1493                                 return (T);
1494
1495                         }
1496                         break;
1497
1498                 default:
1499                         fatalerror("Unknown state in scanner.");
1500                 }
1501                 state = CMD;
1502                 return (LEXERR);
1503         }
1504 }
1505
1506 void
1507 upper(char *s)
1508 {
1509         while (*s != '\0') {
1510                 if (islower(*s))
1511                         *s = toupper(*s);
1512                 s++;
1513         }
1514 }
1515
1516 static char *
1517 copy(char *s)
1518 {
1519         char *p;
1520
1521         p = malloc(strlen(s) + 1);
1522         if (p == NULL)
1523                 fatalerror("Ran out of memory.");
1524         (void) strcpy(p, s);
1525         return (p);
1526 }
1527
1528 static void
1529 help(struct tab *ctab, char *s)
1530 {
1531         struct tab *c;
1532         int width, NCMDS;
1533         char *type;
1534
1535         if (ctab == sitetab)
1536                 type = "SITE ";
1537         else
1538                 type = "";
1539         width = 0, NCMDS = 0;
1540         for (c = ctab; c->name != NULL; c++) {
1541                 int len = strlen(c->name);
1542
1543                 if (len > width)
1544                         width = len;
1545                 NCMDS++;
1546         }
1547         width = (width + 8) &~ 7;
1548         if (s == 0) {
1549                 int i, j, w;
1550                 int columns, lines;
1551
1552                 lreply(214, "The following %scommands are recognized %s.",
1553                     type, "(* =>'s unimplemented)");
1554                 columns = 76 / width;
1555                 if (columns == 0)
1556                         columns = 1;
1557                 lines = (NCMDS + columns - 1) / columns;
1558                 for (i = 0; i < lines; i++) {
1559                         printf("   ");
1560                         for (j = 0; j < columns; j++) {
1561                                 c = ctab + j * lines + i;
1562                                 printf("%s%c", c->name,
1563                                         c->implemented ? ' ' : '*');
1564                                 if (c + lines >= &ctab[NCMDS])
1565                                         break;
1566                                 w = strlen(c->name) + 1;
1567                                 while (w < width) {
1568                                         putchar(' ');
1569                                         w++;
1570                                 }
1571                         }
1572                         printf("\r\n");
1573                 }
1574                 (void) fflush(stdout);
1575                 if (hostinfo)
1576                         reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1577                 else
1578                         reply(214, "End.");
1579                 return;
1580         }
1581         upper(s);
1582         c = lookup(ctab, s);
1583         if (c == NULL) {
1584                 reply(502, "Unknown command %s.", s);
1585                 return;
1586         }
1587         if (c->implemented)
1588                 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1589         else
1590                 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1591                     c->name, c->help);
1592 }
1593
1594 static void
1595 sizecmd(char *filename)
1596 {
1597         switch (type) {
1598         case TYPE_L:
1599         case TYPE_I: {
1600                 struct stat stbuf;
1601                 if (stat(filename, &stbuf) < 0)
1602                         perror_reply(550, filename);
1603                 else if (!S_ISREG(stbuf.st_mode))
1604                         reply(550, "%s: not a plain file.", filename);
1605                 else
1606                         reply(213, "%jd", (intmax_t)stbuf.st_size);
1607                 break; }
1608         case TYPE_A: {
1609                 FILE *fin;
1610                 int c;
1611                 off_t count;
1612                 struct stat stbuf;
1613                 fin = fopen(filename, "r");
1614                 if (fin == NULL) {
1615                         perror_reply(550, filename);
1616                         return;
1617                 }
1618                 if (fstat(fileno(fin), &stbuf) < 0) {
1619                         perror_reply(550, filename);
1620                         (void) fclose(fin);
1621                         return;
1622                 } else if (!S_ISREG(stbuf.st_mode)) {
1623                         reply(550, "%s: not a plain file.", filename);
1624                         (void) fclose(fin);
1625                         return;
1626                 } else if (stbuf.st_size > MAXASIZE) {
1627                         reply(550, "%s: too large for type A SIZE.", filename);
1628                         (void) fclose(fin);
1629                         return;
1630                 }
1631
1632                 count = 0;
1633                 while((c=getc(fin)) != EOF) {
1634                         if (c == '\n')  /* will get expanded to \r\n */
1635                                 count++;
1636                         count++;
1637                 }
1638                 (void) fclose(fin);
1639
1640                 reply(213, "%jd", (intmax_t)count);
1641                 break; }
1642         default:
1643                 reply(504, "SIZE not implemented for type %s.",
1644                            typenames[type]);
1645         }
1646 }
1647
1648 /* Return 1, if port check is done. Return 0, if not yet. */
1649 static int
1650 port_check(const char *pcmd)
1651 {
1652         if (his_addr.su_family == AF_INET) {
1653                 if (data_dest.su_family != AF_INET) {
1654                         usedefault = 1;
1655                         reply(500, "Invalid address rejected.");
1656                         return 1;
1657                 }
1658                 if (paranoid &&
1659                     ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1660                      memcmp(&data_dest.su_sin.sin_addr,
1661                             &his_addr.su_sin.sin_addr,
1662                             sizeof(data_dest.su_sin.sin_addr)))) {
1663                         usedefault = 1;
1664                         reply(500, "Illegal PORT range rejected.");
1665                 } else {
1666                         usedefault = 0;
1667                         if (pdata >= 0) {
1668                                 (void) close(pdata);
1669                                 pdata = -1;
1670                         }
1671                         reply(200, "%s command successful.", pcmd);
1672                 }
1673                 return 1;
1674         }
1675         return 0;
1676 }
1677
1678 static int
1679 check_login1(void)
1680 {
1681         if (logged_in)
1682                 return 1;
1683         else {
1684                 reply(530, "Please login with USER and PASS.");
1685                 return 0;
1686         }
1687 }
1688
1689 /*
1690  * Replace leading "~user" in a pathname by the user's login directory.
1691  * Returned string will be in a freshly malloced buffer unless it's NULL.
1692  */
1693 static char *
1694 exptilde(char *s)
1695 {
1696         char *p, *q;
1697         char *path, *user;
1698         struct passwd *ppw;
1699
1700         if ((p = strdup(s)) == NULL)
1701                 return (NULL);
1702         if (*p != '~')
1703                 return (p);
1704
1705         user = p + 1;   /* skip tilde */
1706         if ((path = strchr(p, '/')) != NULL)
1707                 *(path++) = '\0'; /* separate ~user from the rest of path */
1708         if (*user == '\0') /* no user specified, use the current user */
1709                 user = pw->pw_name;
1710         /* read passwd even for the current user since we may be chrooted */
1711         if ((ppw = getpwnam(user)) != NULL) {
1712                 /* user found, substitute login directory for ~user */
1713                 if (path)
1714                         asprintf(&q, "%s/%s", ppw->pw_dir, path);
1715                 else
1716                         q = strdup(ppw->pw_dir);
1717                 free(p);
1718                 p = q;
1719         } else {
1720                 /* user not found, undo the damage */
1721                 if (path)
1722                         path[-1] = '/';
1723         }
1724         return (p);
1725 }
1726
1727 /*
1728  * Expand glob(3) patterns possibly present in a pathname.
1729  * Avoid expanding to a pathname including '\r' or '\n' in order to
1730  * not disrupt the FTP protocol.
1731  * The expansion found must be unique.
1732  * Return the result as a malloced string, or NULL if an error occured.
1733  *
1734  * Problem: this production is used for all pathname
1735  * processing, but only gives a 550 error reply.
1736  * This is a valid reply in some cases but not in others.
1737  */
1738 static char *
1739 expglob(char *s)
1740 {
1741         char *p, **pp, *rval;
1742         int flags = GLOB_BRACE | GLOB_NOCHECK;
1743         int n;
1744         glob_t gl;
1745
1746         memset(&gl, 0, sizeof(gl));
1747         flags |= GLOB_LIMIT;
1748         gl.gl_matchc = MAXGLOBARGS;
1749         if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1750                 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1751                         if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1752                                 p = *pp;
1753                                 n++;
1754                         }
1755                 if (n == 0)
1756                         rval = strdup(s);
1757                 else if (n == 1)
1758                         rval = strdup(p);
1759                 else {
1760                         reply(550, "Wildcard is ambiguous.");
1761                         rval = NULL;
1762                 }
1763         } else {
1764                 reply(550, "Wildcard expansion error.");
1765                 rval = NULL;
1766         }
1767         globfree(&gl);
1768         return (rval);
1769 }
1770
1771 #ifdef INET6
1772 /* Return 1, if port check is done. Return 0, if not yet. */
1773 static int
1774 port_check_v6(const char *pcmd)
1775 {
1776         if (his_addr.su_family == AF_INET6) {
1777                 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1778                         /* Convert data_dest into v4 mapped sockaddr.*/
1779                         v4map_data_dest();
1780                 if (data_dest.su_family != AF_INET6) {
1781                         usedefault = 1;
1782                         reply(500, "Invalid address rejected.");
1783                         return 1;
1784                 }
1785                 if (paranoid &&
1786                     ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1787                      memcmp(&data_dest.su_sin6.sin6_addr,
1788                             &his_addr.su_sin6.sin6_addr,
1789                             sizeof(data_dest.su_sin6.sin6_addr)))) {
1790                         usedefault = 1;
1791                         reply(500, "Illegal PORT range rejected.");
1792                 } else {
1793                         usedefault = 0;
1794                         if (pdata >= 0) {
1795                                 (void) close(pdata);
1796                                 pdata = -1;
1797                         }
1798                         reply(200, "%s command successful.", pcmd);
1799                 }
1800                 return 1;
1801         }
1802         return 0;
1803 }
1804
1805 static void
1806 v4map_data_dest(void)
1807 {
1808         struct in_addr savedaddr;
1809         int savedport;
1810
1811         if (data_dest.su_family != AF_INET) {
1812                 usedefault = 1;
1813                 reply(500, "Invalid address rejected.");
1814                 return;
1815         }
1816
1817         savedaddr = data_dest.su_sin.sin_addr;
1818         savedport = data_dest.su_port;
1819
1820         memset(&data_dest, 0, sizeof(data_dest));
1821         data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1822         data_dest.su_sin6.sin6_family = AF_INET6;
1823         data_dest.su_sin6.sin6_port = savedport;
1824         memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1825         memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1826                (caddr_t)&savedaddr, sizeof(savedaddr));
1827 }
1828 #endif