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